FBX: Add support to export other geometries (curves, metaballs, etc.) as meshes, and implement the 'apply modifiers' option.

This commit is contained in:
Bastien Montagne 2014-04-10 15:08:53 +02:00
parent aa634bcea0
commit 39cde16787
2 changed files with 59 additions and 17 deletions

View File

@ -209,6 +209,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
('LAMP', "Lamp", ""),
('ARMATURE', "Armature", ""),
('MESH', "Mesh", ""),
('OTHER', "Other", "Other geometry types, like curve, metaball, etc. (converted to meshes)"),
),
description="Which kind of object to export",
default={'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH'},
@ -216,7 +217,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
use_mesh_modifiers = BoolProperty(
name="Apply Modifiers",
description="Apply modifiers to mesh objects",
description="Apply modifiers to mesh objects (except Armature ones!)",
default=True,
)
mesh_smooth_type = EnumProperty(
@ -354,6 +355,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
layout.prop(self, "bake_anim")
col = layout.column()
col.enabled = self.bake_anim
col.prop(self, "bake_anim_use_nla_strips")
col.prop(self, "bake_anim_step")
col.prop(self, "bake_anim_simplify_factor")
else:

View File

@ -77,6 +77,10 @@ MAT_CONVERT_CAMERA = Matrix.Rotation(math.pi / 2.0, 4, 'Y') # Blender is -Z, FB
MAT_CONVERT_BONE = Matrix()
BLENDER_OTHER_OBJECT_TYPES = {'CURVE', 'SURFACE', 'FONT', 'META'}
BLENDER_OBJECT_TYPES_MESHLIKE = {'MESH'} | BLENDER_OTHER_OBJECT_TYPES
# Lamps.
FBX_LIGHT_TYPES = {
'POINT': 0, # Point.
@ -973,7 +977,7 @@ def use_bake_space_transform(scene_data, obj):
# Also, do not apply it to children objects.
# TODO: Check whether this can work for bones too...
return (scene_data.settings.bake_space_transform and not isinstance(obj, (PoseBone, Bone)) and
obj.type in {'MESH'} and not has_valid_parent(scene_data, obj))
obj.type in BLENDER_OBJECT_TYPES_MESHLIKE and not has_valid_parent(scene_data, obj))
def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_space=False):
@ -1218,7 +1222,7 @@ def fbx_data_camera_elements(root, cam_obj, scene_data):
elem_data_single_float64(cam, b"CameraOrthoZoom", 1.0)
def fbx_data_mesh_elements(root, me, scene_data):
def fbx_data_mesh_elements(root, me_obj, scene_data):
"""
Write the Mesh (Geometry) data block.
"""
@ -1227,7 +1231,7 @@ def fbx_data_mesh_elements(root, me, scene_data):
while 1:
yield val
me_key, me_obj = scene_data.data_meshes[me]
me_key, me, _free = scene_data.data_meshes[me_obj]
# No gscale/gmat here, all data are supposed to be in object space.
smooth_type = scene_data.settings.mesh_smooth_type
@ -1858,7 +1862,7 @@ def fbx_data_object_elements(root, obj, scene_data):
obj_type = b"Null" # default, sort of empty...
if isinstance(obj, Bone):
obj_type = b"LimbNode"
elif (obj.type == 'MESH'):
elif (obj.type in BLENDER_OBJECT_TYPES_MESHLIKE):
obj_type = b"Mesh"
elif (obj.type == 'LAMP'):
obj_type = b"Light"
@ -2055,7 +2059,7 @@ def fbx_mat_properties_from_texture(tex):
return tex_fbx_props
def fbx_skeleton_from_armature(scene, settings, armature, objects, bones_to_posebones,
def fbx_skeleton_from_armature(scene, settings, armature, objects, data_meshes, bones_to_posebones,
data_bones, data_deformers, arm_parents):
"""
Create skeleton from armature/bones (NodeAttribute/LimbNode and Model/LimbNode), and for each deformed mesh,
@ -2102,7 +2106,7 @@ def fbx_skeleton_from_armature(scene, settings, armature, objects, bones_to_pose
# Note: bindpose have no relations at all (no connections), so no need for any preprocess for them.
# Create skin & clusters relations (note skins are connected to geometry, *not* model!).
me = obj.data
_key, me, _free = data_meshes[obj]
clusters = {bo: get_blender_bone_cluster_key(armature, me, bo) for bo in used_bones}
data_deformers.setdefault(armature, {})[me] = (get_blender_armature_skin_key(armature, me), obj, clusters)
@ -2287,10 +2291,29 @@ def fbx_data_from_scene(scene, settings):
data_lamps = OrderedDict((obj.data, get_blenderID_key(obj.data)) for obj in objects if obj.type == 'LAMP')
# Unfortunately, FBX camera data contains object-level data (like position, orientation, etc.)...
data_cameras = OrderedDict((obj, get_blenderID_key(obj.data)) for obj in objects if obj.type == 'CAMERA')
data_meshes = OrderedDict((obj.data, (get_blenderID_key(obj.data), obj)) for obj in objects if obj.type == 'MESH')
# Yep! Contains nothing, but needed!
data_empties = OrderedDict((obj, get_blender_empty_key(obj)) for obj in objects if obj.type == 'EMPTY')
data_meshes = OrderedDict()
for obj in objects:
if obj.type not in BLENDER_OBJECT_TYPES_MESHLIKE:
continue
if settings.use_mesh_modifiers or obj.type in BLENDER_OTHER_OBJECT_TYPES:
tmp_mods = []
if obj.type == 'MESH' and settings.bake_anim:
# For meshes, when anim export is enabled, disable Armature modifiers here!
for mod in obj.modifiers:
if mod.type == 'ARMATURE':
tmp_mods.append((mod, mod.show_render))
mod.show_render = False
tmp_me = obj.to_mesh(scene, apply_modifiers=True, settings='RENDER')
data_meshes[obj] = (get_blenderID_key(tmp_me), tmp_me, True)
# Re-enable temporary disabled modifiers.
for mod, show_render in tmp_mods:
mod.show_render = show_render
else:
data_meshes[obj] = (get_blenderID_key(obj.data), obj.data, False)
# Armatures!
data_bones = OrderedDict()
data_deformers = OrderedDict()
@ -2299,7 +2322,7 @@ def fbx_data_from_scene(scene, settings):
for obj in tuple(objects.keys()):
if obj.type not in {'ARMATURE'}:
continue
fbx_skeleton_from_armature(scene, settings, obj, objects, bones_to_posebones,
fbx_skeleton_from_armature(scene, settings, obj, objects, data_meshes, bones_to_posebones,
data_bones, data_deformers, arm_parents)
# Some world settings are embedded in FBX materials...
@ -2315,7 +2338,7 @@ def fbx_data_from_scene(scene, settings):
data_materials = OrderedDict()
for obj in objects:
# Only meshes for now!
if not isinstance(obj, Object) or obj.type not in {'MESH'}:
if not isinstance(obj, Object) or obj.type not in BLENDER_OBJECT_TYPES_MESHLIKE:
continue
for mat_s in obj.material_slots:
mat = mat_s.material
@ -2492,15 +2515,16 @@ def fbx_data_from_scene(scene, settings):
elif obj.type == 'LAMP':
lamp_key = data_lamps[obj.data]
connections.append((b"OO", get_fbxuid_from_key(lamp_key), get_fbxuid_from_key(obj_key), None))
elif obj.type == 'MESH':
mesh_key, _obj = data_meshes[obj.data]
elif obj.type in BLENDER_OBJECT_TYPES_MESHLIKE:
mesh_key, _me, _free = data_meshes[obj]
connections.append((b"OO", get_fbxuid_from_key(mesh_key), get_fbxuid_from_key(obj_key), None))
# Deformers (armature-to-geometry, only for meshes currently)...
for arm, deformed_meshes in data_deformers.items():
for me, (skin_key, _obj, clusters) in deformed_meshes.items():
for me, (skin_key, obj, clusters) in deformed_meshes.items():
# skin -> geometry
mesh_key, _obj = data_meshes[me]
mesh_key, _me, _free = data_meshes[obj]
assert(me == _me)
connections.append((b"OO", get_fbxuid_from_key(skin_key), get_fbxuid_from_key(mesh_key), None))
for bo, clstr_key in clusters.items():
# cluster -> skin
@ -2571,6 +2595,16 @@ def fbx_data_from_scene(scene, settings):
)
def fbx_scene_data_cleanup(scene_data):
"""
Some final cleanup...
"""
# Delete temp meshes.
for _key, me, free in scene_data.data_meshes.values():
if free:
bpy.data.meshes.remove(me)
##### Top-level FBX elements generators. #####
def fbx_header_elements(root, scene_data, time=None):
@ -2742,8 +2776,8 @@ def fbx_objects_elements(root, scene_data):
for cam in scene_data.data_cameras.keys():
fbx_data_camera_elements(objects, cam, scene_data)
for mesh in scene_data.data_meshes.keys():
fbx_data_mesh_elements(objects, mesh, scene_data)
for me_obj in scene_data.data_meshes.keys():
fbx_data_mesh_elements(objects, me_obj, scene_data)
for obj in scene_data.objects.keys():
fbx_data_object_elements(objects, obj, scene_data)
@ -2839,7 +2873,10 @@ def save_single(operator, scene, filepath="",
):
if object_types is None:
object_types = {'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH'}
object_types = {'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH', 'OTHER'}
if 'OTHER' in object_types:
object_types |= BLENDER_OTHER_OBJECT_TYPES
global_scale = global_matrix.median_scale
global_matrix_inv = global_matrix.inverted()
@ -2898,6 +2935,9 @@ def save_single(operator, scene, filepath="",
# Animation.
fbx_takes_elements(root, scene_data)
# Cleanup!
fbx_scene_data_cleanup(scene_data)
# And we are down, we can write the whole thing!
encode_bin.write(filepath, root, FBX_VERSION)