animation mode to create all keys at once
parent
a56a9ea537
commit
77e3049d5d
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue