animation mode to create all keys at once

master
pullusb 2023-12-07 12:16:18 +01:00
parent a56a9ea537
commit 77e3049d5d
4 changed files with 77 additions and 43 deletions

View File

@ -1,7 +1,7 @@
bl_info = {
"name": "gp interpolate",
"author": "Christophe Seux, Samuel Bernou",
"version": (0, 1, 2),
"version": (0, 2, 0),
"blender": (3, 6, 0),
"location": "Sidebar > Gpencil Tab > Interpolate",
"description": "Interpolate Grease pencil strokes over 3D",

View File

@ -20,13 +20,26 @@ from mathutils.geometry import (barycentric_transform,
tessellate_polygon)
def following_key(forward=True):
def following_keys(forward=True, all_keys=True) -> list:# -> list[int] | list | None:
'''return a lsit of int or an empty list'''
direction = 1 if forward else -1
cur_frame = bpy.context.scene.frame_current
settings = bpy.context.scene.gp_interpo_settings
if settings.mode == 'FRAME':
return cur_frame + (settings.padding * direction)
if all_keys:
scn = bpy.context.scene
if forward:
limit = scn.frame_preview_end if scn.use_preview_range else scn.frame_end
else:
limit = scn.frame_preview_start if scn.use_preview_range else scn.frame_start
limit += direction # offset by one for limit to be in range
return list(range(cur_frame, limit, settings.padding * direction))
else:
return [cur_frame + (settings.padding * direction)]
elif settings.mode == 'GPKEY':
layers = bpy.context.object.data.layers
@ -42,18 +55,26 @@ def following_key(forward=True):
frames = [k.co.x for fc in arm.animation_data.action.fcurves for k in fc.keyframe_points]
if not frames:
return
return []
# Sort frames (invert if looking backward)
frames.sort(reversed=not forward)
if all_keys:
frames = list(set(frames))
if forward:
frame_list = [int(f) for f in frames if f > cur_frame]
else:
frame_list = [int(f) for f in frames if f < cur_frame]
return frame_list
frames.sort()
if forward:
new = next((f for f in frames if f > cur_frame), None)
else:
below = [f for f in frames if f < cur_frame]
if not below:
return
new = below[-1]
return int(new)
new = next((f for f in frames if f < cur_frame), None)
if new is None:
return []
return [int(new)]
## TODO: add bake animation to empty for later GP layer parenting
@ -88,9 +109,9 @@ class GP_OT_interpolate_stroke(bpy.types.Operator):
# auto_key_status = context.tool_settings.use_keyframe_insert_auto
# context.tool_settings.use_keyframe_insert_auto = True
## Determine on what key to jump
frame_to_jump = following_key(forward=self.next)
if frame_to_jump is None:
## Determine on what key/keys to jump
frames_to_jump = following_keys(forward=self.next, all_keys=settings.use_animation)
if not len(frames_to_jump):
self.report({'WARNING'}, 'No keyframe available in this direction')
return {'CANCELLED'}
@ -214,38 +235,45 @@ class GP_OT_interpolate_stroke(bpy.types.Operator):
# Copy stroke selection, jump frame and paste
bpy.ops.gpencil.copy()
scn.frame_set(frame_to_jump)
plan_co, plane_no = get_gp_draw_plane(gp)
bpy.ops.gpencil.paste()
if settings.method == 'BONE':
bone_plane = plane_on_bone(settings.target_rig.pose.bones.get(settings.target_bone),
arm=settings.target_rig,
set_rotation=settings.use_bone_rotation,
mesh=True)
dg = bpy.context.evaluated_depsgraph_get()
matrix_inv = np.array(gp.matrix_world.inverted(), dtype='float64')#.inverted()
new_strokes = gp.data.layers.active.active_frame.strokes[-len(strokes_data):]
wm = bpy.context.window_manager # Pgs
for new_stroke, stroke_data in zip(new_strokes, strokes_data):
world_co_3d = [] # np.array(len()dtype='float64')#np.
for stroke, point_co, object_hit, hit_location, tri_a, tri_indices in stroke_data:
eval_ob = object_hit.evaluated_get(dg)
tri_b = [eval_ob.data.vertices[i].co for i in tri_indices]
tri_b = matrix_transform(tri_b, eval_ob.matrix_world)
wm.progress_begin(frames_to_jump[0], frames_to_jump[-1]) # Pgs
for f in frames_to_jump:
wm.progress_update(f) # Pgs
scn.frame_set(f)
plan_co, plane_no = get_gp_draw_plane(gp)
bpy.ops.gpencil.paste()
if settings.method == 'BONE':
bone_plane = plane_on_bone(settings.target_rig.pose.bones.get(settings.target_bone),
arm=settings.target_rig,
set_rotation=settings.use_bone_rotation,
mesh=True)
new_loc = barycentric_transform(hit_location, *tri_a, *tri_b)
world_co_3d.append(new_loc)
dg = bpy.context.evaluated_depsgraph_get()
matrix_inv = np.array(gp.matrix_world.inverted(), dtype='float64')#.inverted()
new_strokes = gp.data.layers.active.active_frame.strokes[-len(strokes_data):]
# Reproject on plane
new_world_co_3d = [intersect_line_plane(origin, p, plan_co, plane_no) for p in world_co_3d]
new_local_co_3d = matrix_transform(new_world_co_3d, matrix_inv)
for new_stroke, stroke_data in zip(new_strokes, strokes_data):
world_co_3d = [] # np.array(len()dtype='float64')#np.
for stroke, point_co, object_hit, hit_location, tri_a, tri_indices in stroke_data:
eval_ob = object_hit.evaluated_get(dg)
tri_b = [eval_ob.data.vertices[i].co for i in tri_indices]
tri_b = matrix_transform(tri_b, eval_ob.matrix_world)
new_loc = barycentric_transform(hit_location, *tri_a, *tri_b)
world_co_3d.append(new_loc)
nb_points = len(new_stroke.points)
new_stroke.points.foreach_set('co', new_local_co_3d.reshape(nb_points*3))
new_stroke.points.update()
# Reproject on plane
new_world_co_3d = [intersect_line_plane(origin, p, plan_co, plane_no) for p in world_co_3d]
new_local_co_3d = matrix_transform(new_world_co_3d, matrix_inv)
nb_points = len(new_stroke.points)
new_stroke.points.foreach_set('co', new_local_co_3d.reshape(nb_points*3))
new_stroke.points.update()
wm.progress_end() # Pgs
## Reset autokey status
# context.tool_settings.use_keyframe_insert_auto = auto_key_status # (Done in context manager)
@ -284,8 +312,8 @@ class GP_OT_interpolate_stroke(bpy.types.Operator):
context.tool_settings.use_keyframe_insert_auto = True
## Determine on what key to jump
frame_to_jump = following_key(forward=self.next)
if frame_to_jump is None:
frames_to_jump = following_keys(forward=self.next)
if frames_to_jump is None:
self.report({'WARNING'}, 'No keyframe available in this direction')
return {'CANCELLED'}
@ -376,7 +404,7 @@ class GP_OT_interpolate_stroke(bpy.types.Operator):
bpy.ops.gpencil.copy()
scn.frame_set(frame_to_jump)
scn.frame_set(frames_to_jump)
plan_co, plane_no = get_gp_draw_plane(gp)

View File

@ -24,6 +24,11 @@ class GP_PG_interpolate_settings(PropertyGroup):
description='Select method for interpolating strokes'
)
use_animation : BoolProperty(
name='Animatation',
default=True,
description='Apply the interpolation on the remaining range')
search_range : FloatProperty(
name="Search Range",
description="Search range size when points are out of mesh",

1
ui.py
View File

@ -30,6 +30,7 @@ class GP_PT_interpolate(bpy.types.Panel):
col.prop(settings, 'method', text='Method')
col.prop(settings, 'use_animation', text='Animation')
if settings.method == 'BONE':
col = layout.column(align=True)