Skip to content

Instantly share code, notes, and snippets.

@Pentan
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pentan/f6722930908b740f4a48 to your computer and use it in GitHub Desktop.
Save Pentan/f6722930908b740f4a48 to your computer and use it in GitHub Desktop.
Strange shapekey utility W.I.P.
bl_info = {
"name": "Nakawari Shapekey Utilities",
"author": "Pentan",
"version": (0, 1),
"blender": (2, 70, 0),
"location": "Object > Nakawari",
"description": "This addon makes Nakawari shapekeys for 2D hand drawn like motions.",
"warning": "experimental",
"wiki_url": "",
"category": "Animation"}
import bpy
from mathutils import Vector
#####
class GengaData(bpy.types.PropertyGroup):
tsume_enum = [
('NONE', 'None', ''),
('SAKI', 'Saki', ''),
('ATO', 'Ato', ''),
('RYOU', 'Ryou', '')]
frame = bpy.props.IntProperty(name="Genga Frame")
#tsume_type = bpy.props.IntProperty(name="Tume Type")
tsume_type = bpy.props.EnumProperty(items=tsume_enum, default="NONE", name="Tume Type")
#####
class GENGA_UL_List(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
#print("draw list")
if self.layout_type in {'DEFAULT', 'COMPACT'}:
#layout.label("[{}] frame:{} tsume:{}".format(index, item.frame, item.tsume_type))
row = layout.row()
row.label("f:{:06}".format(item.frame))
#row.label("tsume:{}".format(item.tsume_type))
row.prop(data=item, text="", property="tsume_type")
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
layout.label("{}:{}".format(item.frame, item.tsume_type))
class NakawariUIPanel(bpy.types.Panel):
"""Nakawari propertiess"""
bl_label = "Nakawari"
bl_idname = "OBJECT_PT_nkwr_properties"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = 'object'
def draw(self, context):
layout = self.layout
obj = context.object
layout.template_list("GENGA_UL_List", "", obj, "nakawari_genga_frames", obj, "nakawari_active_genga_index")
layout.operator("object.nkwr_add_genga")
layout.operator("object.nkwr_remove_genga")
layout.separator()
layout.operator("object.nkwr_generate_mesh_object")
layout.prop(data=obj, text="Choose basis shape", property="nakawari_use_basis_frame")
layout.prop(data=obj, text="basis frame", property="nakawari_basis_frame")
#####
class AddGengaFrameOperator(bpy.types.Operator):
"""Add Genga Frame"""
bl_idname = "object.nkwr_add_genga"
bl_label = "Add Genga"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return len(context.selected_objects) > 0
def execute(self, context):
curframe = context.scene.frame_current
for obj in context.selected_objects:
genga_frames = obj.nakawari_genga_frames
frame_found = False
gf = None
for f in genga_frames:
if f.frame == curframe:
f.frame = curframe
frame_found = True
gf = f
break
if frame_found == False:
# add new
f = genga_frames.add()
f.frame = curframe
i = len(genga_frames) - 2
while i >= 0:
if curframe > genga_frames[i].frame:
break
genga_frames.move(i, i + 1)
i -= 1
obj.nakawari_active_genga_index = max(0, i+1)
gf = f
#print("{} genga added: {}: ".format(obj.name, gf.frame))
return {'FINISHED'}
class RemoveGengaFrameOperator(bpy.types.Operator):
"""Remove Genga Frame"""
bl_idname = "object.nkwr_remove_genga"
bl_label = "Remove Genga"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
return len(context.selected_objects) > 0
def execute(self, context):
for obj in context.selected_objects:
genga_frames = obj.nakawari_genga_frames
active_id = obj.nakawari_active_genga_index
if active_id >= 0 and active_id < len(genga_frames):
print("{} removed {}".format(obj.name, active_id))
genga_frames.remove(active_id)
active_genga_index_changed(self, context)
return {'FINISHED'}
class GenerateNkawariMeshObjOperator(bpy.types.Operator):
"""Generate Nakawari Mesh Object"""
bl_idname = "object.nkwr_generate_mesh_object"
bl_label = "Generate Object"
bl_options = {'UNDO'}
def apply_transforms(sel, dstverts, srcverts, camera, obj):
cm = camera.matrix_world.inverted()
om = obj.matrix_world
m = cm * om
for (i, v) in enumerate(srcverts):
dstverts[i].co = m * v.co
def set_tsume_curve(self, kp0, kp1, tsume_type):
#print("set_tsume_curve:{}".format(tsume_type))
kp0.handle_left_type = 'FREE'
kp0.handle_right_type = 'FREE'
kp1.handle_left_type = 'FREE'
kp1.handle_right_type = 'FREE'
if tsume_type == 'NONE':
kp0.interpolation = 'LINEAR'
else:
kp0.interpolation = 'BEZIER'
if tsume_type == 'SAKI':
vx0 = 0.3333333
vy0 = 0.0
vx1 = -0.3333333
vy1 = -0.6666667
elif tsume_type == 'ATO':
vx0 = 0.3333333
vy0 = 0.6666667
vx1 = -0.3333333
vy1 = 0.0
else:
vx0 = 0.3904115 # Blender default value
vy0 = 0.0
vx1 = -0.3904115
vy1 = 0.0
kpw = kp1.co.x - kp0.co.x
kph = kp1.co.y - kp0.co.y
vx0 *= kpw
vy0 *= kph
vx1 *= kpw
vy1 *= kph
kp0.handle_right.x = kp0.co.x + vx0
kp0.handle_right.y = kp0.co.y + vy0
kp1.handle_left.x = kp1.co.x + vx1
kp1.handle_left.y = kp1.co.y + vy1
def set_fcurve_ipo(self, obj, shapekey, genga_frames, genga_id):
curgenga = genga_frames[genga_id]
#print("set_fcurve_ipo:{}({})".format(genga_id, curgenga.frame))
fcurves = obj.data.shape_keys.animation_data.action.fcurves
data_path = 'key_blocks["' + shapekey.name + '"].value'
#print("fcurves;{},path:{}".format(len(fcurves), data_path))
for crv in fcurves:
#print(crv.data_path)
if crv.data_path == data_path:
#print("{} found".format(data_path))
for i, kp in enumerate(crv.keyframe_points):
#print("kp[{}]:{}".format(i, kp.co.x))
if kp.co.x == curgenga.frame:
if i > 0:
prekp = crv.keyframe_points[i - 1]
self.set_tsume_curve(prekp, kp, curgenga.tsume_type)
if i < len(crv.keyframe_points) - 1:
nxtkp = crv.keyframe_points[i + 1]
nxtgenga = genga_frames[genga_id + 1]
self.set_tsume_curve(kp, nxtkp, nxtgenga.tsume_type)
break
@classmethod
def poll(cls, context):
return len(context.selected_objects) > 0
def execute(self, context):
sc = bpy.context.scene
camera = sc.camera
first_frame = sc.frame_current
frame_start = sc.frame_start
tomesh_settings = 'PREVIEW' # modifier apply quality
for obj in context.selected_objects:
#print(obj.name) ###
# create object
#sc.frame_set(frame_start)
if obj.nakawari_use_basis_frame:
# use basis shape frame
sc.frame_set(obj.nakawari_basis_frame)
mesh = obj.to_mesh(sc, True, tomesh_settings)
self.apply_transforms(mesh.vertices, mesh.vertices, camera, obj)
else:
# basis shape is not choosed. use undeformed mesh.
apply_ignore = {'ARMATURE'}
modf_settings = {}
for modf in obj.modifiers:
if modf.type in apply_ignore:
modf_settings[modf.name] = (modf.show_render, modf.show_viewport)
modf.show_render = False
modf.show_viewport = False
mesh = obj.to_mesh(sc, True, tomesh_settings)
# recover modifier settings
for modf in obj.modifiers:
if modf.type in apply_ignore:
modf.show_render = modf_settings[modf.name][0]
modf.show_viewport = modf_settings[modf.name][1]
mesh.update()
newobj = bpy.data.objects.new("{}_NAKAWARI".format(obj.name), mesh)
# base mesh
newobj.shape_key_add("Basis")
newobj.data.update()
genga_frames = obj.nakawari_genga_frames
# make nakawari meshes
for i, genga in enumerate(genga_frames):
#print("{},{}".format(genga.frame, genga.tsume_type))
# get genga_frame's mesh
sc.frame_set(genga.frame)
mesh = obj.to_mesh(sc, True, tomesh_settings)
# add that mesh as shapekey
shapekey = newobj.shape_key_add("{}_{:04}".format(obj.name, genga.frame))
shape_key_index = len(newobj.data.shape_keys.key_blocks) - 1
newobj.active_shape_key_index = shape_key_index
self.apply_transforms(shapekey.data, mesh.vertices, camera, obj)
### reflesh shapekey object to insert key frame
shapekey = newobj.data.shape_keys.key_blocks[shape_key_index]
# previous
f = genga_frames[i-1].frame if i > 0 else frame_start
shapekey.value = 0.0
shapekey.keyframe_insert("value", frame=f)
# current
shapekey.value = 1.0
shapekey.keyframe_insert("value", frame=genga.frame)
# next
if i < len(genga_frames)-1:
f = genga_frames[i + 1].frame
shapekey.value = 0.0
shapekey.keyframe_insert("value", frame=f)
self.set_fcurve_ipo(newobj, shapekey, genga_frames, i)
newobj.data.update()
# cleaning
bpy.data.meshes.remove(mesh)
sc.objects.link(newobj)
sc.frame_set(first_frame)
return {'FINISHED'}
def active_genga_index_changed(self, context):
obj = context.object
genga_frames = obj.nakawari_genga_frames
active_index = obj.nakawari_active_genga_index
if active_index >= 0 and active_index < len(genga_frames):
context.scene.frame_current = genga_frames[active_index].frame
"""
def use_basis_shape_frame_changed(self, context):
print("use basis frame:{}".format(self.nakawari_use_basis_frame))
"""
#####
class CurveSegment:
def __init__(self, lp, rp):
self.left_point = lp
self.right_point = rp
#print("new segment({},{})".format(lp.co.x, rp.co.x))
def is_overlap(self, lp, rp):
return (lp.co.x == self.left_point.co.x) and (rp.co.x == self.right_point.co.x)
def left_handle_vector(self):
return Vector((
self.left_point.handle_right.x - self.left_point.co.x,
self.left_point.handle_right.y - self.left_point.co.y
))
def right_handle_vector(self):
return Vector((
self.right_point.handle_left.x - self.right_point.co.x,
self.right_point.handle_left.y - self.right_point.co.y
))
class NormalizeTumeFCurveOperator(bpy.types.Operator):
"""Normalize Tume FCurve"""
bl_idname = "graph.nakawari_normalize_tsume_fcurve"
bl_label = "Normalize Tume FCurve"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
obj = context.active_object
fcurves = None
if obj:
if obj.type == 'MESH':
shape_keys = obj.data.shape_keys
if shape_keys:
if shape_keys.animation_data:
act = shape_keys.animation_data.action
if act:
fcurves = act.fcurves
return (obj and fcurves)
def execute(self, context):
# find selected fcurve from active object
obj = context.active_object
mesh = obj.data
fcurves = mesh.shape_keys.animation_data.action.fcurves
# find selected
selcurve = None
for crv in fcurves:
if crv.select:
if selcurve:
self.report({'ERROR'}, "Please select 1 sorce curve")
return {'FINISHED'}
else:
selcurve = crv
# find interval
# all segments
frame_cur = context.scene.frame_current
keyframe_points = selcurve.keyframe_points
segments = []
for i, p in enumerate(keyframe_points[:-1]):
#print("keyframe_points[{}].co.x:{}".format(i, p.co.x))
segments.append(CurveSegment(p, keyframe_points[i+1]))
# or nearest?
# or selected?
# normalize
for crv in fcurves:
if crv != selcurve:
kf_points = crv.keyframe_points
for i, p in enumerate(kf_points[:-1]):
lp = p
rp = kf_points[i + 1]
for seg in segments:
if seg.is_overlap(lp, rp):
seg_lv = seg.left_handle_vector()
seg_rv = seg.right_handle_vector()
lp.interpolation = seg.left_point.interpolation
lp.co.y = 1.0 - seg.left_point.co.y
lp.handle_right.x = lp.co.x + seg_lv.x
lp.handle_right.y = lp.co.y - seg_lv.y
rp.interpolation = seg.right_point.interpolation
rp.co.y = 1.0 - seg.right_point.co.y
rp.handle_left.x = rp.co.x + seg_rv.x
rp.handle_left.y = rp.co.y - seg_rv.y
#print("normalized({},{})".format(lp.co.x, rp.co.x))
#else:
# print("skiped({},{})".format(lp.co.x, rp.co.x))
return {'FINISHED'}
#####
def register():
bpy.utils.register_class(AddGengaFrameOperator)
bpy.utils.register_class(RemoveGengaFrameOperator)
bpy.utils.register_class(GenerateNkawariMeshObjOperator)
bpy.utils.register_class(GENGA_UL_List)
bpy.utils.register_class(NakawariUIPanel)
bpy.utils.register_class(GengaData)
bpy.types.Object.nakawari_genga_frames = bpy.props.CollectionProperty(type=GengaData)
bpy.types.Object.nakawari_active_genga_index = bpy.props.IntProperty(name="Active Genga Frame", update=active_genga_index_changed)
bpy.types.Object.nakawari_use_basis_frame = bpy.props.BoolProperty(name="Use Basis Frame", default=False) #, update=use_basis_shape_frame_changed)
bpy.types.Object.nakawari_basis_frame = bpy.props.IntProperty(name="Basis Frame", default=1)
bpy.utils.register_class(NormalizeTumeFCurveOperator)
def unregister():
bpy.utils.unregister_class(AddGengaFrameOperator)
bpy.utils.unregister_class(RemoveGengaFrameOperator)
bpy.utils.unregister_class(GenerateNkawariMeshObjOperator)
bpy.utils.unregister_class(GENGA_UL_List)
bpy.utils.unregister_class(NakawariUIPanel)
bpy.utils.unregister_class(GengaData)
del bpy.types.Object.nakawari_genga_frames
del bpy.types.Object.nakawari_active_genga_index
del bpy.types.Object.nakawari_use_basis_frame
del bpy.types.Object.nakawari_basis_frame
bpy.utils.unregister_class(NormalizeTumeFCurveOperator)
if __name__ == "__main__":
register()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment