提交 454847c9 编写于 作者: B Ben Houston

manual merge of blender Clip (blend shape + node animation) support.

上级 1f858e1c
......@@ -39,8 +39,8 @@ logging.basicConfig(
bl_info = {
'name': "Three.js Format",
'author': "repsac, mrdoob, yomotsu, mpk, jpweeks, rkusa, tschw",
'version': (1, 4, 0),
'blender': (2, 73, 0),
'version': (1, 5, 0),
'blender': (2, 74, 0),
'location': "File > Export",
'description': "Export Three.js formatted JSON files.",
'warning': "Importer not included.",
......@@ -322,7 +322,7 @@ def restore_export_settings(properties, settings):
constants.INDEX_TYPE,
constants.EXPORT_OPTIONS[constants.INDEX_TYPE])
## }
## Materials {
properties.option_materials = settings.get(
constants.MATERIALS,
......@@ -414,10 +414,18 @@ def restore_export_settings(properties, settings):
constants.MORPH_TARGETS,
constants.EXPORT_OPTIONS[constants.MORPH_TARGETS])
properties.option_blend_shape = settings.get(
constants.BLEND_SHAPES,
constants.EXPORT_OPTIONS[constants.BLEND_SHAPES])
properties.option_animation_skeletal = settings.get(
constants.ANIMATION,
constants.EXPORT_OPTIONS[constants.ANIMATION])
properties.option_keyframes = settings.get(
constants.KEYFRAMES,
constants.EXPORT_OPTIONS[constants.KEYFRAMES])
properties.option_frame_step = settings.get(
constants.FRAME_STEP,
constants.EXPORT_OPTIONS[constants.FRAME_STEP])
......@@ -470,7 +478,9 @@ def set_settings(properties):
constants.HIERARCHY: properties.option_hierarchy,
constants.MORPH_TARGETS: properties.option_animation_morph,
constants.BLEND_SHAPES: properties.option_blend_shape,
constants.ANIMATION: properties.option_animation_skeletal,
constants.KEYFRAMES: properties.option_keyframes,
constants.FRAME_STEP: properties.option_frame_step,
constants.FRAME_INDEX_AS_TIME: properties.option_frame_index_as_time,
constants.INFLUENCES_PER_VERTEX: properties.option_influences
......@@ -684,12 +694,22 @@ class ExportThree(bpy.types.Operator, ExportHelper):
description="Export animation (morphs)",
default=constants.EXPORT_OPTIONS[constants.MORPH_TARGETS])
option_blend_shape = BoolProperty(
name="Blend Shape animation",
description="Export Blend Shapes",
default=constants.EXPORT_OPTIONS[constants.BLEND_SHAPES])
option_animation_skeletal = EnumProperty(
name="",
description="Export animation (skeletal)",
items=animation_options(),
default=constants.OFF)
option_keyframes = BoolProperty(
name="Keyframe animation",
description="Export animation (keyframes)",
default=constants.EXPORT_OPTIONS[constants.KEYFRAMES])
option_frame_index_as_time = BoolProperty(
name="Frame index as time",
description="Use (original) frame index as frame time",
......@@ -804,6 +824,7 @@ class ExportThree(bpy.types.Operator, ExportHelper):
row = layout.row()
row.prop(self.properties, 'option_index_type')
## }
layout.separator()
......@@ -831,12 +852,21 @@ class ExportThree(bpy.types.Operator, ExportHelper):
row = layout.row()
row.prop(self.properties, 'option_animation_morph')
row = layout.row()
row.prop(self.properties, 'option_blend_shape')
row = layout.row()
row.label(text="Skeletal animations:")
row = layout.row()
row.prop(self.properties, 'option_animation_skeletal')
row = layout.row()
row.label(text="Keyframe animations:")
row = layout.row()
row.prop(self.properties, 'option_keyframes')
layout.row()
row = layout.row()
row.prop(self.properties, 'option_influences')
......
......@@ -51,7 +51,6 @@ NUMERIC = {
'LinearMipMapNearestFilter': 1007,
'LinearMipMapLinearFilter': 1008
}
JSON = 'json'
EXTENSION = '.%s' % JSON
INDENT = 'indent'
......@@ -79,7 +78,11 @@ MAPS = 'maps'
FRAME_STEP = 'frameStep'
FRAME_INDEX_AS_TIME = 'frameIndexAsTime'
ANIMATION = 'animations'
CLIPS="clips"
KEYFRAMES = 'tracks'
MORPH_TARGETS = 'morphTargets'
MORPH_TARGETS_ANIM = 'morphTargetsAnimation'
BLEND_SHAPES = 'blendShapes'
POSE = 'pose'
REST = 'rest'
SKIN_INDICES = 'skinIndices'
......@@ -141,9 +144,11 @@ EXPORT_OPTIONS = {
COMPRESSION: None,
MAPS: False,
ANIMATION: OFF,
KEYFRAMES: False,
BONES: False,
SKINNING: False,
MORPH_TARGETS: False,
BLEND_SHAPES: False,
CAMERAS: False,
LIGHTS: False,
HIERARCHY: False,
......@@ -220,6 +225,8 @@ NORMAL = 'normal'
ITEM_SIZE = 'itemSize'
ARRAY = 'array'
FLOAT_32 = 'Float32Array'
VISIBLE = 'visible'
CAST_SHADOW = 'castShadow'
RECEIVE_SHADOW = 'receiveShadow'
......
......@@ -19,12 +19,13 @@ def _material(func):
"""
material = None
if isinstance(name, types.Material):
material = name
else:
elif name:
material = data.materials[name]
return func(material, *args, **kwargs)
return func(material, *args, **kwargs) if material else None
return inner
......
......@@ -68,7 +68,6 @@ def skeletal_animation(mesh, options):
return animations
@_mesh
def bones(mesh, options):
"""
......@@ -431,6 +430,74 @@ def morph_targets(mesh, options):
return manifest
@_mesh
def blend_shapes(mesh, options):
"""
:param mesh:
:param options:
"""
logger.debug("mesh.blend_shapes(%s, %s)", mesh, options)
manifest = []
if mesh.shape_keys:
logger.info("mesh.blend_shapes -- there's shape keys")
key_blocks = mesh.shape_keys.key_blocks
for key in key_blocks.keys()[1:]: # skip "Basis"
logger.info("mesh.blend_shapes -- key %s", key)
morph = []
for d in key_blocks[key].data:
co = d.co
morph.append([co.x, co.y, co.z])
manifest.append({
constants.NAME: key,
constants.VERTICES: morph
})
else:
logger.debug("No valid blend_shapes detected")
return manifest
@_mesh
def animated_blend_shapes(mesh, options):
"""
:param mesh:
:param options:
"""
logger.debug("mesh.animated_blend_shapes(%s, %s)", mesh, options)
tracks = []
shp = mesh.shape_keys
animCurves = shp.animation_data
if animCurves:
animCurves = animCurves.action.fcurves
for key in shp.key_blocks.keys()[1:]: # skip "Basis"
key_name = constants.MORPH_TARGETS+"["+key+"]"
found_animation = False
data_path = 'key_blocks["'+key+'"].value'
values = []
if animCurves:
for fcurve in animCurves:
if fcurve.data_path == data_path:
for xx in fcurve.keyframe_points:
values.append({ "time": xx.co.x, "value": xx.co.y })
found_animation = True
break # no need to continue
if found_animation:
tracks.append({
constants.NAME: key_name,
constants.TYPE: "number",
constants.KEYS: values
});
animation = [{
constants.KEYFRAMES: tracks,
constants.FPS: context.scene.render.fps,
constants.NAME: "default"
}]
return animation
@_mesh
def materials(mesh, options):
......
......@@ -201,11 +201,11 @@ def animated_xform(obj):
else:
i += 1
animation = {
animation = [{
constants.KEYFRAMES: tracks,
constants.FPS: context.scene.render.fps,
constants.NAME: obj.name
}
}]
return animation
@_object
......@@ -667,3 +667,6 @@ def _valid_node(obj, valid_types, options):
# if we get this far assume that the mesh is valid
return True
......@@ -45,7 +45,7 @@ class Geometry(base_classes.BaseNode):
ext = constants.PACK
key = ''
for key in (constants.MORPH_TARGETS, constants.ANIMATION):
for key in (constants.MORPH_TARGETS, constants.ANIMATION, constants.CLIPS):
if key in self.keys():
break
else:
......@@ -152,7 +152,7 @@ class Geometry(base_classes.BaseNode):
texture_registration = self.register_textures()
if texture_registration:
logger.info("%s has registered textures", self.node)
dirname = os.path.dirname(self.scene.filepath)
dirname = os.path.dirname(os.path.abspath(self.scene.filepath))
full_path = os.path.join(dirname, texture_folder)
io.copy_registered_textures(
full_path, texture_registration)
......@@ -206,7 +206,7 @@ class Geometry(base_classes.BaseNode):
"""
logger.debug("Geometry().write_animation(%s)", filepath)
for key in (constants.MORPH_TARGETS, constants.ANIMATION):
for key in (constants.MORPH_TARGETS, constants.ANIMATION, constants.CLIPS):
try:
data = self[key]
break
......@@ -245,7 +245,7 @@ class Geometry(base_classes.BaseNode):
constants.INDEX]
data = {}
anim_components = [constants.MORPH_TARGETS, constants.ANIMATION]
anim_components = [constants.MORPH_TARGETS, constants.ANIMATION, constants.MORPH_TARGETS_ANIM, constants.CLIPS]
if self.options.get(constants.EMBED_ANIMATION):
components.extend(anim_components)
else:
......@@ -564,6 +564,13 @@ class Geometry(base_classes.BaseNode):
logger.info("Parsing %s", constants.MORPH_TARGETS)
self[constants.MORPH_TARGETS] = api.mesh.morph_targets(
self.node, self.options) or []
elif self.options.get(constants.BLEND_SHAPES):
logger.info("Parsing %s", constants.BLEND_SHAPES)
mt = api.mesh.blend_shapes(self.node, self.options) or []
self[constants.MORPH_TARGETS] = mt
if len(mt) > 0: # there's blend shapes, let check for animation
self[constants.CLIPS] = api.mesh.animated_blend_shapes(self.node, self.options) or []
# In the moment there is no way to add extra data to a Geomtry in
# Three.js. In case there is some day, here is the code:
......
......@@ -119,6 +119,11 @@ class Object(base_classes.BaseNode):
elif self[constants.TYPE] in lights:
self._init_light()
no_anim = (None, False, constants.OFF)
if self.options.get(constants.KEYFRAMES) not in no_anim:
logger.info("Export Transform Animation for %s", self.node)
self[constants.CLIPS] = api.object.animated_xform(self.node)
if self.options.get(constants.HIERARCHY, False):
for child in api.object.children(self.node, self.scene.valid_types):
if not self.get(constants.CHILDREN):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册