Straight curve creation method
1.1.0 - added: another method to add curve placing character in two positionmaster
parent
848bda410d
commit
f07d395289
|
@ -1,5 +1,9 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
1.1.0
|
||||||
|
|
||||||
|
- added: another method to add curve placing character in two position
|
||||||
|
|
||||||
1.0.0
|
1.0.0
|
||||||
|
|
||||||
- fix: broken algo for extreme keys range detection
|
- fix: broken algo for extreme keys range detection
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
import bpy, re
|
||||||
|
from . import fn
|
||||||
|
|
||||||
|
## step 1 : Create the curve
|
||||||
|
|
||||||
|
# need to determine a specific acceleration envelope factor to go to end of curve
|
||||||
|
# (set on curves)
|
||||||
|
|
||||||
|
def remove_spacetime_keys(context=None):
|
||||||
|
if not context:
|
||||||
|
context = bpy.context
|
||||||
|
# remove all spacetime markers and walk collection
|
||||||
|
col_name = 'walk_markers'
|
||||||
|
walk_col = context.scene.collection.children.get(col_name)
|
||||||
|
if walk_col:
|
||||||
|
for o in reversed(walk_col.objects):
|
||||||
|
if o.name.startswith('spacetime_marker_'):
|
||||||
|
bpy.data.objects.remove(o)
|
||||||
|
bpy.data.collections.remove(walk_col)
|
||||||
|
|
||||||
|
class UAC_OT_create_a_b_step(bpy.types.Operator):
|
||||||
|
bl_idname = "anim.create_a_b_step"
|
||||||
|
bl_label = "Create Two Points Curve"
|
||||||
|
bl_description = "Create a straight curve between two defined position and time"
|
||||||
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||||
|
|
||||||
|
# first time ops is launched just set an empty (with market time)
|
||||||
|
# second time (if a mark empty exists determine a AB path to set curve)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object and context.object.type == 'ARMATURE'
|
||||||
|
|
||||||
|
def create_marker(self, context):
|
||||||
|
m_name = f'spacetime_marker_{context.scene.frame_current}'
|
||||||
|
mark = bpy.data.objects.new(m_name, None)
|
||||||
|
mark.location = self.position
|
||||||
|
mark.empty_display_size = 2
|
||||||
|
self.walk_col.objects.link(mark)
|
||||||
|
return mark
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
ob = context.object
|
||||||
|
prefs = fn.get_addon_prefs()
|
||||||
|
root_name = prefs.tgt_bone
|
||||||
|
root = ob.pose.bones.get(root_name)
|
||||||
|
self.position = (ob.matrix_world @ root.matrix).to_translation()
|
||||||
|
|
||||||
|
col_name = 'walk_markers'
|
||||||
|
self.walk_col = fn.get_col(col_name, create=True) # link in scene collection by default
|
||||||
|
|
||||||
|
markers = [o for o in self.walk_col.objects if o.type == 'EMPTY' and o.name.startswith('spacetime_marker_')]
|
||||||
|
if not markers:
|
||||||
|
self.create_marker(context)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
# Create the second marker and append to marker list
|
||||||
|
markers.append(self.create_marker(context))
|
||||||
|
markers.sort(key=lambda x: x.name) # sort by time (frame written in name)
|
||||||
|
|
||||||
|
a = markers[0].location
|
||||||
|
b = markers[1].location - a # remove a postion to get position in curve object space
|
||||||
|
|
||||||
|
# Set the curve and constraint
|
||||||
|
curve = fn.generate_curve(location=a, direction=b, name='curve_path', context=context)
|
||||||
|
settings = context.scene.anim_cycle_settings
|
||||||
|
settings.path_to_follow = curve
|
||||||
|
fn.create_follow_path_constraint(ob, curve)
|
||||||
|
|
||||||
|
# refresh evaluation so constraint shows up correctly
|
||||||
|
bpy.context.scene.frame_set(bpy.context.scene.frame_current)
|
||||||
|
|
||||||
|
# set offset animation ??? (but movement not in sync until action is retimed in NLA)
|
||||||
|
|
||||||
|
# remove all markers or keep for later reuse ?
|
||||||
|
remove_spacetime_keys(context=None)
|
||||||
|
# if speed calculation is done later need to know start and end frame...
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class UAC_OT_remove_a_b_step(bpy.types.Operator):
|
||||||
|
bl_idname = "anim.remove_a_b_step"
|
||||||
|
bl_label = "Remove First Position"
|
||||||
|
bl_description = "remove first point defining step"
|
||||||
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||||
|
|
||||||
|
# Define what is first and last according to time
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object and context.object.type == 'ARMATURE'
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
remove_spacetime_keys(context=None)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
classes=(
|
||||||
|
UAC_OT_create_a_b_step,
|
||||||
|
UAC_OT_remove_a_b_step,
|
||||||
|
)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for cls in classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
for cls in reversed(classes):
|
||||||
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
|
# if __name__ == "__main__":
|
||||||
|
# register()
|
|
@ -3,118 +3,6 @@ from . import fn
|
||||||
|
|
||||||
## step 1 : Create the curve and/or follow path constraint, snap to ground
|
## step 1 : Create the curve and/or follow path constraint, snap to ground
|
||||||
|
|
||||||
def create_follow_path_constraint(ob, curve, follow_curve=True):
|
|
||||||
prefs = fn.get_addon_prefs()
|
|
||||||
root_name = prefs.tgt_bone
|
|
||||||
root = ob.pose.bones.get(root_name)
|
|
||||||
|
|
||||||
if not root:
|
|
||||||
return ('ERROR', f'posebone {root_name} not found in armature {ob.name} check addon preferences to change name')
|
|
||||||
|
|
||||||
# Clear bone follow path constraint
|
|
||||||
exiting_fp_constraints = [c for c in root.constraints if c.type == 'FOLLOW_PATH']
|
|
||||||
for c in exiting_fp_constraints:
|
|
||||||
root.constraints.remove(c)
|
|
||||||
|
|
||||||
# loc = ob.matrix_world @ root.matrix.to_translation()
|
|
||||||
if root.name == ('world', 'root') and root.location != (0,0,0):
|
|
||||||
old_loc = root.location
|
|
||||||
root.location = (0,0,0)
|
|
||||||
print(f'root moved from {old_loc} to (0,0,0) to counter follow curve offset')
|
|
||||||
|
|
||||||
const = root.constraints.new('FOLLOW_PATH')
|
|
||||||
const.target = curve
|
|
||||||
# axis only in this case, should be in addon to prefs
|
|
||||||
|
|
||||||
## determine which axis to use... maybe found orientation in world space from matrix_basis ?
|
|
||||||
root_world_base_direction = root.bone.matrix_local @ fn.get_direction_vector_from_enum(bpy.context.scene.anim_cycle_settings.forward_axis)
|
|
||||||
const.forward_axis = fn.orentation_track_from_vector(root_world_base_direction) # 'TRACK_NEGATIVE_Y' # bpy.context.scene.anim_cycle_settings.forward_axis # 'FORWARD_X'
|
|
||||||
print('const.forward_axis: ', const.forward_axis)
|
|
||||||
const.use_curve_follow = True
|
|
||||||
return curve, const
|
|
||||||
|
|
||||||
def snap_curve():
|
|
||||||
obj = bpy.context.object
|
|
||||||
|
|
||||||
curve = const = None
|
|
||||||
if obj.type == 'ARMATURE':
|
|
||||||
curve, const = fn.get_follow_curve_from_armature(obj)
|
|
||||||
|
|
||||||
to_follow = bpy.context.scene.anim_cycle_settings.path_to_follow
|
|
||||||
if not curve and not to_follow:
|
|
||||||
return ('ERROR', f'No curve pointed by "Path" filed')
|
|
||||||
|
|
||||||
# get curve from field
|
|
||||||
if not curve:
|
|
||||||
curve, const = create_follow_path_constraint(obj, to_follow)
|
|
||||||
if isinstance(curve, str):
|
|
||||||
return (curve, const) # those are error message
|
|
||||||
|
|
||||||
# if obj.type == 'CURVE':
|
|
||||||
# return ('ERROR', f'Select the armature related to curve {obj.name}')
|
|
||||||
# else:
|
|
||||||
# return ('ERROR', 'Not an armature object')
|
|
||||||
|
|
||||||
gnd = fn.get_gnd()
|
|
||||||
if not gnd:
|
|
||||||
return
|
|
||||||
|
|
||||||
curve_act = None
|
|
||||||
anim_frame = None
|
|
||||||
# if it's on a snap curve, fetch original
|
|
||||||
if '_snap' in curve.name:
|
|
||||||
org_name = re.sub(r'_snap\.?\d{0,3}$', '', curve.name)
|
|
||||||
org_curve = bpy.context.scene.objects.get(org_name)
|
|
||||||
if org_curve:
|
|
||||||
const.target = org_curve
|
|
||||||
|
|
||||||
# keep action
|
|
||||||
if curve.data.animation_data and curve.data.animation_data.action:
|
|
||||||
curve_act = curve.data.animation_data.action
|
|
||||||
|
|
||||||
anim_frame = curve.data.path_duration
|
|
||||||
# delete old snap
|
|
||||||
bpy.data.objects.remove(curve)
|
|
||||||
# assign old curve as main one
|
|
||||||
curve = org_curve
|
|
||||||
|
|
||||||
nc = curve.copy()
|
|
||||||
name = re.sub(r'\.\d{3}$', '', curve.name) + '_snap'
|
|
||||||
const.target = nc
|
|
||||||
nc.name = name
|
|
||||||
nc.data = curve.data.copy()
|
|
||||||
nc.data.name = name + '_data'
|
|
||||||
if curve_act:
|
|
||||||
nc.data.animation_data_create()
|
|
||||||
nc.data.animation_data.action = curve_act
|
|
||||||
if anim_frame:
|
|
||||||
nc.data.path_duration = anim_frame
|
|
||||||
|
|
||||||
curve.users_collection[0].objects.link(nc)
|
|
||||||
|
|
||||||
## If object mode is Curve subdivide it (TODO if nurbs needs conversion)
|
|
||||||
#-# subdivide the curve (if curve is not nurbs)
|
|
||||||
|
|
||||||
# bpy.ops.object.mode_set(mode='EDIT')
|
|
||||||
# bpy.ops.curve.select_all(action='SELECT')
|
|
||||||
# bpy.ops.curve.subdivide(number_cuts=4)
|
|
||||||
# bpy.ops.object.mode_set(mode='OBJECT')
|
|
||||||
|
|
||||||
# shrinkwrap or cast on ground
|
|
||||||
mod = nc.modifiers.new('Shrinkwrap', 'SHRINKWRAP')
|
|
||||||
# mod.wrap_method = 'TARGET_PROJECT'
|
|
||||||
mod.wrap_method = 'PROJECT'
|
|
||||||
mod.wrap_mode = 'ON_SURFACE'
|
|
||||||
mod.use_project_z = True
|
|
||||||
mod.use_negative_direction = True
|
|
||||||
mod.use_positive_direction = True
|
|
||||||
mod.target = gnd
|
|
||||||
|
|
||||||
# Apply and decimate
|
|
||||||
bpy.ops.object.modifier_apply({'object': nc}, modifier="Shrinkwrap", report=False)
|
|
||||||
bpy.context.scene.anim_cycle_settings.path_to_follow = nc
|
|
||||||
# return 0, nc
|
|
||||||
|
|
||||||
class UAC_OT_create_curve_path(bpy.types.Operator):
|
class UAC_OT_create_curve_path(bpy.types.Operator):
|
||||||
bl_idname = "anim.create_curve_path"
|
bl_idname = "anim.create_curve_path"
|
||||||
bl_label = "Create Curve"
|
bl_label = "Create Curve"
|
||||||
|
@ -149,13 +37,12 @@ class UAC_OT_create_curve_path(bpy.types.Operator):
|
||||||
|
|
||||||
settings.path_to_follow = curve
|
settings.path_to_follow = curve
|
||||||
|
|
||||||
create_follow_path_constraint(ob, curve)
|
fn.create_follow_path_constraint(ob, curve)
|
||||||
|
|
||||||
# refresh evaluation so constraint shows up correctly
|
# refresh evaluation so constraint shows up correctly
|
||||||
bpy.context.scene.frame_set(bpy.context.scene.frame_current)
|
bpy.context.scene.frame_set(bpy.context.scene.frame_current)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class UAC_OT_create_follow_path(bpy.types.Operator):
|
class UAC_OT_create_follow_path(bpy.types.Operator):
|
||||||
bl_idname = "anim.create_follow_path"
|
bl_idname = "anim.create_follow_path"
|
||||||
bl_label = "Create Follow Path Constraint"
|
bl_label = "Create Follow Path Constraint"
|
||||||
|
@ -170,7 +57,7 @@ class UAC_OT_create_follow_path(bpy.types.Operator):
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
ob = context.object
|
ob = context.object
|
||||||
curve = context.scene.anim_cycle_settings.path_to_follow
|
curve = context.scene.anim_cycle_settings.path_to_follow
|
||||||
err, const = create_follow_path_constraint(ob, curve)
|
err, const = fn.create_follow_path_constraint(ob, curve)
|
||||||
if isinstance(err, str):
|
if isinstance(err, str):
|
||||||
self.report({'ERROR'}, err)
|
self.report({'ERROR'}, err)
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
@ -187,7 +74,7 @@ class UAC_OT_snap_curve_to_ground(bpy.types.Operator):
|
||||||
return context.object and context.object.type == 'ARMATURE'
|
return context.object and context.object.type == 'ARMATURE'
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
err = snap_curve()
|
err = fn.snap_curve()
|
||||||
if err:
|
if err:
|
||||||
self.report({err[0]}, err[1])
|
self.report({err[0]}, err[1])
|
||||||
if err[0] == 'ERROR':
|
if err[0] == 'ERROR':
|
||||||
|
@ -221,11 +108,10 @@ class UAC_OT_edit_curve(bpy.types.Operator):
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
# set mode to object set curve as active and go Edit
|
# set mode to object set curve as active and go Edit
|
||||||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
fn.go_edit_mode(curve)
|
||||||
curve.select_set(True)
|
# curve context.mode -> EDIT_CURVE
|
||||||
context.view_layer.objects.active = curve
|
|
||||||
bpy.ops.object.mode_set(mode='EDIT', toggle=False) # EDIT_CURVE
|
|
||||||
|
|
||||||
|
# Deselect armature object
|
||||||
b.id_data.select_set(False)
|
b.id_data.select_set(False)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ bl_info = {
|
||||||
"name": "Unfold Anim Cycle",
|
"name": "Unfold Anim Cycle",
|
||||||
"description": "Anim tools to develop walk/run cycles along a curve",
|
"description": "Anim tools to develop walk/run cycles along a curve",
|
||||||
"author": "Samuel Bernou",
|
"author": "Samuel Bernou",
|
||||||
"version": (1, 0, 0),
|
"version": (1, 1, 0),
|
||||||
"blender": (3, 0, 0),
|
"blender": (3, 0, 0),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
@ -17,6 +17,7 @@ if 'bpy' in locals():
|
||||||
imp.reload(preferences)
|
imp.reload(preferences)
|
||||||
imp.reload(OP_setup)
|
imp.reload(OP_setup)
|
||||||
imp.reload(OP_setup_curve_path)
|
imp.reload(OP_setup_curve_path)
|
||||||
|
imp.reload(OP_setup_curve_a_to_b)
|
||||||
imp.reload(OP_animate_path)
|
imp.reload(OP_animate_path)
|
||||||
imp.reload(OP_expand_cycle_step)
|
imp.reload(OP_expand_cycle_step)
|
||||||
imp.reload(OP_snap_contact)
|
imp.reload(OP_snap_contact)
|
||||||
|
@ -28,6 +29,7 @@ else:
|
||||||
from . import preferences
|
from . import preferences
|
||||||
from . import OP_setup
|
from . import OP_setup
|
||||||
from . import OP_setup_curve_path
|
from . import OP_setup_curve_path
|
||||||
|
from . import OP_setup_curve_a_to_b
|
||||||
from . import OP_animate_path
|
from . import OP_animate_path
|
||||||
from . import OP_expand_cycle_step
|
from . import OP_expand_cycle_step
|
||||||
from . import OP_snap_contact
|
from . import OP_snap_contact
|
||||||
|
@ -43,6 +45,7 @@ mods = (
|
||||||
preferences,
|
preferences,
|
||||||
OP_setup,
|
OP_setup,
|
||||||
OP_setup_curve_path,
|
OP_setup_curve_path,
|
||||||
|
OP_setup_curve_a_to_b,
|
||||||
OP_animate_path,
|
OP_animate_path,
|
||||||
OP_expand_cycle_step,
|
OP_expand_cycle_step,
|
||||||
OP_snap_contact,
|
OP_snap_contact,
|
||||||
|
|
146
fn.py
146
fn.py
|
@ -244,8 +244,7 @@ def orentation_track_from_vector(input_vector) -> str:
|
||||||
def get_root_name(context=None):
|
def get_root_name(context=None):
|
||||||
'''return name of rig root name'''
|
'''return name of rig root name'''
|
||||||
|
|
||||||
if context is None:
|
context = context or bpy.context
|
||||||
context = bpy.context
|
|
||||||
|
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
|
|
||||||
|
@ -258,11 +257,12 @@ def get_root_name(context=None):
|
||||||
|
|
||||||
return settings.tgt_bone
|
return settings.tgt_bone
|
||||||
|
|
||||||
|
## --- curve funcs
|
||||||
|
|
||||||
def generate_curve(location=(0,0,0), direction=(1,0,0), name='curve_path', enter_edit=True, context=None):
|
def generate_curve(location=(0,0,0), direction=(1,0,0), name='curve_path', enter_edit=True, context=None):
|
||||||
'''Create curve at provided location and direction vector'''
|
'''Create curve at provided location and direction vector'''
|
||||||
|
|
||||||
if context is None:
|
context = context or bpy.context
|
||||||
context = bpy.context
|
|
||||||
|
|
||||||
## using ops (dirty)
|
## using ops (dirty)
|
||||||
# bpy.ops.curve.primitive_bezier_curve_add(radius=1, enter_editmode=enter_edit, align='WORLD', location=location, scale=(1, 1, 1))
|
# bpy.ops.curve.primitive_bezier_curve_add(radius=1, enter_editmode=enter_edit, align='WORLD', location=location, scale=(1, 1, 1))
|
||||||
|
@ -318,6 +318,121 @@ def generate_curve(location=(0,0,0), direction=(1,0,0), name='curve_path', enter
|
||||||
|
|
||||||
return curve
|
return curve
|
||||||
|
|
||||||
|
|
||||||
|
def create_follow_path_constraint(ob, curve, follow_curve=True):
|
||||||
|
prefs = get_addon_prefs()
|
||||||
|
root_name = prefs.tgt_bone
|
||||||
|
root = ob.pose.bones.get(root_name)
|
||||||
|
|
||||||
|
if not root:
|
||||||
|
return ('ERROR', f'posebone {root_name} not found in armature {ob.name} check addon preferences to change name')
|
||||||
|
|
||||||
|
# Clear bone follow path constraint
|
||||||
|
exiting_fp_constraints = [c for c in root.constraints if c.type == 'FOLLOW_PATH']
|
||||||
|
for c in exiting_fp_constraints:
|
||||||
|
root.constraints.remove(c)
|
||||||
|
|
||||||
|
# loc = ob.matrix_world @ root.matrix.to_translation()
|
||||||
|
if root.name == ('world', 'root') and root.location != (0,0,0):
|
||||||
|
old_loc = root.location
|
||||||
|
root.location = (0,0,0)
|
||||||
|
print(f'root moved from {old_loc} to (0,0,0) to counter follow curve offset')
|
||||||
|
|
||||||
|
const = root.constraints.new('FOLLOW_PATH')
|
||||||
|
const.target = curve
|
||||||
|
# axis only in this case, should be in addon to prefs
|
||||||
|
|
||||||
|
## determine which axis to use... maybe found orientation in world space from matrix_basis ?
|
||||||
|
root_world_base_direction = root.bone.matrix_local @ get_direction_vector_from_enum(bpy.context.scene.anim_cycle_settings.forward_axis)
|
||||||
|
const.forward_axis = orentation_track_from_vector(root_world_base_direction) # 'TRACK_NEGATIVE_Y' # bpy.context.scene.anim_cycle_settings.forward_axis # 'FORWARD_X'
|
||||||
|
print('const.forward_axis: ', const.forward_axis)
|
||||||
|
const.use_curve_follow = True
|
||||||
|
return curve, const
|
||||||
|
|
||||||
|
def snap_curve():
|
||||||
|
obj = bpy.context.object
|
||||||
|
|
||||||
|
curve = const = None
|
||||||
|
if obj.type == 'ARMATURE':
|
||||||
|
curve, const = get_follow_curve_from_armature(obj)
|
||||||
|
|
||||||
|
to_follow = bpy.context.scene.anim_cycle_settings.path_to_follow
|
||||||
|
if not curve and not to_follow:
|
||||||
|
return ('ERROR', f'No curve pointed by "Path" filed')
|
||||||
|
|
||||||
|
# get curve from field
|
||||||
|
if not curve:
|
||||||
|
curve, const = create_follow_path_constraint(obj, to_follow)
|
||||||
|
if isinstance(curve, str):
|
||||||
|
return (curve, const) # those are error message
|
||||||
|
|
||||||
|
# if obj.type == 'CURVE':
|
||||||
|
# return ('ERROR', f'Select the armature related to curve {obj.name}')
|
||||||
|
# else:
|
||||||
|
# return ('ERROR', 'Not an armature object')
|
||||||
|
|
||||||
|
gnd = get_gnd()
|
||||||
|
if not gnd:
|
||||||
|
return
|
||||||
|
|
||||||
|
curve_act = None
|
||||||
|
anim_frame = None
|
||||||
|
# if it's on a snap curve, fetch original
|
||||||
|
if '_snap' in curve.name:
|
||||||
|
org_name = re.sub(r'_snap\.?\d{0,3}$', '', curve.name)
|
||||||
|
org_curve = bpy.context.scene.objects.get(org_name)
|
||||||
|
if org_curve:
|
||||||
|
const.target = org_curve
|
||||||
|
|
||||||
|
# keep action
|
||||||
|
if curve.data.animation_data and curve.data.animation_data.action:
|
||||||
|
curve_act = curve.data.animation_data.action
|
||||||
|
|
||||||
|
anim_frame = curve.data.path_duration
|
||||||
|
# delete old snap
|
||||||
|
bpy.data.objects.remove(curve)
|
||||||
|
# assign old curve as main one
|
||||||
|
curve = org_curve
|
||||||
|
|
||||||
|
nc = curve.copy()
|
||||||
|
name = re.sub(r'\.\d{3}$', '', curve.name) + '_snap'
|
||||||
|
const.target = nc
|
||||||
|
nc.name = name
|
||||||
|
nc.data = curve.data.copy()
|
||||||
|
nc.data.name = name + '_data'
|
||||||
|
if curve_act:
|
||||||
|
nc.data.animation_data_create()
|
||||||
|
nc.data.animation_data.action = curve_act
|
||||||
|
if anim_frame:
|
||||||
|
nc.data.path_duration = anim_frame
|
||||||
|
|
||||||
|
curve.users_collection[0].objects.link(nc)
|
||||||
|
|
||||||
|
## If object mode is Curve subdivide it (TODO if nurbs needs conversion)
|
||||||
|
#-# subdivide the curve (if curve is not nurbs)
|
||||||
|
|
||||||
|
# bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
# bpy.ops.curve.select_all(action='SELECT')
|
||||||
|
# bpy.ops.curve.subdivide(number_cuts=4)
|
||||||
|
# bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
|
# shrinkwrap or cast on ground
|
||||||
|
mod = nc.modifiers.new('Shrinkwrap', 'SHRINKWRAP')
|
||||||
|
# mod.wrap_method = 'TARGET_PROJECT'
|
||||||
|
mod.wrap_method = 'PROJECT'
|
||||||
|
mod.wrap_mode = 'ON_SURFACE'
|
||||||
|
mod.use_project_z = True
|
||||||
|
mod.use_negative_direction = True
|
||||||
|
mod.use_positive_direction = True
|
||||||
|
mod.target = gnd
|
||||||
|
|
||||||
|
# Apply and decimate
|
||||||
|
bpy.ops.object.modifier_apply({'object': nc}, modifier="Shrinkwrap", report=False)
|
||||||
|
bpy.context.scene.anim_cycle_settings.path_to_follow = nc
|
||||||
|
# return 0, nc
|
||||||
|
|
||||||
|
## --- action funcs
|
||||||
|
|
||||||
def update_action(act):
|
def update_action(act):
|
||||||
'''update fcurves (often broken after generation through API)'''
|
'''update fcurves (often broken after generation through API)'''
|
||||||
|
|
||||||
|
@ -508,4 +623,25 @@ def create_cycle_modifiers(ob=None):
|
||||||
continue
|
continue
|
||||||
# print(f'Adding cycle modifier {fc.data_path}')
|
# print(f'Adding cycle modifier {fc.data_path}')
|
||||||
_m = fc.modifiers.new(type='CYCLES')
|
_m = fc.modifiers.new(type='CYCLES')
|
||||||
fc.update()
|
fc.update()
|
||||||
|
|
||||||
|
## Get collection, create if necessary
|
||||||
|
def get_col(name, parent=None, create=True):
|
||||||
|
parent = parent or bpy.context.scene.collection
|
||||||
|
col = bpy.data.collections.get(name)
|
||||||
|
if not col and create:
|
||||||
|
col = bpy.data.collections.new(name)
|
||||||
|
|
||||||
|
if col not in parent.children[:]:
|
||||||
|
parent.children.link(col)
|
||||||
|
|
||||||
|
return col
|
||||||
|
|
||||||
|
def go_edit_mode(ob, context=None):
|
||||||
|
'''set mode to object, set passed obhject as active and go Edit'''
|
||||||
|
|
||||||
|
context = context or bpy.context
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||||
|
ob.select_set(True)
|
||||||
|
context.view_layer.objects.active = ob
|
||||||
|
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
|
@ -38,6 +38,14 @@ class UAC_PT_walk_cycle_anim_panel(bpy.types.Panel):
|
||||||
|
|
||||||
if not settings.path_to_follow and not constrained:
|
if not settings.path_to_follow and not constrained:
|
||||||
layout.operator('anim.create_curve_path', text='Create Curve at Root Position', icon='CURVE_BEZCURVE')
|
layout.operator('anim.create_curve_path', text='Create Curve at Root Position', icon='CURVE_BEZCURVE')
|
||||||
|
if (w_co := context.scene.collection.children.get('walk_markers')) and w_co.objects:
|
||||||
|
row=layout.row(align=True)
|
||||||
|
row.label(text=f"1st Point Frame {w_co.objects[0].name.split('_')[-1]}")
|
||||||
|
row.operator('anim.remove_a_b_step', text='', icon='X') # Remove
|
||||||
|
layout.operator('anim.create_a_b_step', text='Set 2nd Point', icon='CURVE_PATH')
|
||||||
|
else:
|
||||||
|
layout.operator('anim.create_a_b_step', text='Set 1st Point', icon='CURVE_PATH')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
layout.operator('uac.edit_curve', text='Edit Curve', icon='OUTLINER_DATA_CURVE') # FORCE_CURVE
|
layout.operator('uac.edit_curve', text='Edit Curve', icon='OUTLINER_DATA_CURVE') # FORCE_CURVE
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue