full WIP parent operator

master
pullusb 2023-12-13 10:22:09 +01:00
parent 8cccc7ba8c
commit b3edd51284
3 changed files with 64 additions and 42 deletions

View File

@ -8,6 +8,7 @@ from gp_interpolate.utils import (get_gp_draw_plane,
create_plane, create_plane,
following_keys, following_keys,
place_object_to_ref_facing_cam, place_object_to_ref_facing_cam,
empty_at,
attr_set) attr_set)
@ -27,18 +28,11 @@ class GP_OT_parent_layer(bpy.types.Operator):
cls.poll_message_set("Need a Grease pencil object with an active layer") cls.poll_message_set("Need a Grease pencil object with an active layer")
return False return False
direct_parent : bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}) direct_parent : bpy.props.BoolProperty(default=False, options={'SKIP_SAVE'})
def parent_and_compensate(self, context): def parent_and_compensate(self, obj, bone=None):
settings = context.scene.gp_interpo_settings '''If bone is None, parent layer only to object'''
bone = None self.lay.parent = obj
if settings.target_rig.type == 'ARMATURE':
bone = settings.target_rig.pose.bones.get(settings.target_bone)
if not bone:
self.report({'ERROR'}, f'{settings.target_bone} not found in armature {settings.target_rig.name}')
return {'CANCELLED'}
self.lay.parent = settings.target_rig
if bone: if bone:
self.lay.parent_type = 'BONE' self.lay.parent_type = 'BONE'
self.lay.parent_bone = bone.name self.lay.parent_bone = bone.name
@ -65,18 +59,24 @@ class GP_OT_parent_layer(bpy.types.Operator):
if not settings.target_rig: if not settings.target_rig:
self.report({'ERROR'}, 'No object Selected') self.report({'ERROR'}, 'No object Selected')
return {'CANCELLED'} return {'CANCELLED'}
if not settings.target_bone: self.bone = None
self.report({'ERROR'}, 'No Bone Selected') if settings.target_rig.type == 'ARMATURE':
return {'CANCELLED'} if not settings.target_bone:
self.report({'ERROR'}, 'No Bone Selected')
return {'CANCELLED'}
self.bone = settings.target_rig.pose.bones.get(settings.target_bone)
if not self.bone:
self.report({'ERROR'}, f'{settings.target_bone} not found in armature {settings.target_rig.name}')
return {'CANCELLED'}
if self.direct_parent: if self.direct_parent:
return self.parent_and_compensate(context) return self.parent_and_compensate(settings.target_rig, settings.target_bone)
return {'FINISHED'}
return {'FINISHED'} ## Place on a 2d intermediate
parent_name = f'ref_{self.gp.name}_{self.lay.info}' parent_name = f'ref_{self.gp.name}_{self.lay.info}'
parent_collec_name = 'gp_parents' parent_collec_name = 'gp_parents'
included_cols = [c.name for c in self.gp.users_collection] included_cols = [c.name for c in self.gp.users_collection]
@ -87,20 +87,17 @@ class GP_OT_parent_layer(bpy.types.Operator):
col = bpy.data.collections.get(parent_collec_name) col = bpy.data.collections.get(parent_collec_name)
if not col: if not col:
col = bpy.data.collections.new(parent_collec_name) col = bpy.data.collections.new(parent_collec_name)
if col.name not in bpy.context.scene.collection.children: if col.name not in bpy.context.scene.collection.children:
bpy.context.scene.collection.children.link(col) bpy.context.scene.collection.children.link(col)
col.hide_viewport = True ## hide parent collection ?
# col.hide_viewport = True
# Get/create meshplane # Get/create empty parent
parent = bpy.data.objects.get(parent_name) parent = bpy.data.objects.get(parent_name)
if not parent: if not parent:
parent = create_plane(name=parent_name) parent = empty_at(name=parent_name, size=0.1, show_name=True)
parent.select_set(False) parent.select_set(False)
if parent.name not in col.objects:
col.objects.link(parent)
## Prepare context manager ## Prepare context manager
store_list = [ store_list = [
(context.tool_settings, 'use_keyframe_insert_auto', False), (context.tool_settings, 'use_keyframe_insert_auto', False),
@ -109,31 +106,32 @@ class GP_OT_parent_layer(bpy.types.Operator):
for vlc in context.view_layer.layer_collection.children: for vlc in context.view_layer.layer_collection.children:
store_list.append( store_list.append(
(vlc, 'exclude', vlc.name not in included_cols), (vlc, 'exclude', vlc.name not in included_cols),
# (),
# (vlc, 'hide_viewport', vlc.name not in included_cols), # viewport viz # (vlc, 'hide_viewport', vlc.name not in included_cols), # viewport viz
) )
## Offset at curent frame to compensate for object, GP (and GP layer ?) transformations ## Offset at curent frame to compensate for object, GP (and GP layer ?) transformations
## If GP object is animated, animate parent obj to compensate ## If GP object is animated, animate parent obj to compensate
## How to smart-test if self.gp is animated in space ? ## How to smart-test if self.gp is animated in space ?
if not is_animated(self.gp): # if not is_animated(self.gp):
# direct parent with one offset
pass
else: with attr_set(store_list):
with attr_set(store_list): parent.animation_data_clear() # Kill animation data to redo it
for i in range(scn.frame_start, scn.frame_end+1):
scn.frame_set(i)
place_object_to_ref_facing_cam(parent, place_object_to_ref_facing_cam(parent,
ref_ob=settings.target_rig, ref_ob=settings.target_rig,
bone=bone, bone=self.bone,
set_rotation=settings.use_bone_rotation set_rotation=settings.use_bone_rotation)
) parent.keyframe_insert('location')
parent.keyframe_insert('rotation_euler')
parent.keyframe_insert('scale')
self.parent_and_compensate(parent)
print('Done') print('Parent Done')
return {'FINISHED'} return {'FINISHED'}

5
ui.py
View File

@ -59,7 +59,10 @@ class GP_PT_interpolate(bpy.types.Panel):
if settings.mode == 'FRAME': if settings.mode == 'FRAME':
col.prop(settings, 'padding') col.prop(settings, 'padding')
layout.operator('gp.parent_layer', text='Parent Layer To Target') col = layout.column()
col.label(text='Layer Parent')
col.operator('gp.parent_layer', text='Direct Parent').direct_parent = True
col.operator('gp.parent_layer', text='Empty Parent').direct_parent = False
classes = ( classes = (
GP_PT_interpolate, GP_PT_interpolate,

View File

@ -91,6 +91,27 @@ def ray_cast_point(point, origin, depsgraph):
return object_hit, np.array(hit_location), tri, tri_indices return object_hit, np.array(hit_location), tri, tri_indices
def empty_at(name='Empty', pos=(0,0,0), collection=None, type='PLAIN_AXES', size=1, show_name=False):
'''
Create an empty at given Vector3 position.
Optional type (default 'PLAIN_AXES') in ,'ARROWS','SINGLE_ARROW','CIRCLE','CUBE','SPHERE','CONE','IMAGE'
default size is 1.0
'''
mire = bpy.data.objects.get(name)
if not mire:
mire = bpy.data.objects.new(name, None)
if collection is None:
collection = bpy.context.collection
if mire.name not in collection.all_objects:
collection.objects.link(mire)
mire.location = pos
mire.empty_display_type = type
mire.empty_display_size = size
mire.show_name = show_name
return mire
def plane_on_bone(bone, arm=None, cam=None, set_rotation=True, mesh=True): def plane_on_bone(bone, arm=None, cam=None, set_rotation=True, mesh=True):
''' '''
bone (posebone): reference pose bone bone (posebone): reference pose bone