Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2011-08-01 04:47:09 +0400
committerCampbell Barton <ideasman42@gmail.com>2011-08-01 04:47:09 +0400
commit4281300d70f0e064af60d0ab3f3a24051ae7adc9 (patch)
tree93fde2bd977559afafa916ed317b64434554ae24 /io_scene_fbx
parent6a4dc849d2e681bb17d511f6e3e599260188fd08 (diff)
patch [#28118] Add XNA requirements to the official FBX exporter
from John Brown (jcbdigger) The patch has been modified, some of the changes I rather see applied as separate patches. This commit adds/changes: - Option to export XNA compatible armature rotations. - Option not to export mesh edges. - Always export armatures as 'Limb' type. - dont write default cameras or camera switch when cameras are disabled. - fix for (harmless) error where armature connections were written out twice.
Diffstat (limited to 'io_scene_fbx')
-rw-r--r--io_scene_fbx/__init__.py62
-rw-r--r--io_scene_fbx/export_fbx.py146
2 files changed, 145 insertions, 63 deletions
diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py
index 856696c2..8e3c6a23 100644
--- a/io_scene_fbx/__init__.py
+++ b/io_scene_fbx/__init__.py
@@ -21,10 +21,10 @@
bl_info = {
"name": "Autodesk FBX format",
"author": "Campbell Barton",
- "blender": (2, 5, 7),
- "api": 35622,
+ "blender": (2, 5, 8),
+ "api": 38691,
"location": "File > Import-Export",
- "description": "Import-Export FBX meshes, UV's, vertex colors, materials, textures, cameras and lamps",
+ "description": "Export FBX meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions",
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
"Scripts/Import-Export/Autodesk_FBX",
@@ -62,8 +62,8 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
# to the class instance from the operator settings before calling.
use_selection = BoolProperty(name="Selected Objects", description="Export selected objects on visible layers", default=False)
-# EXP_OBS_SCENE = BoolProperty(name="Scene Objects", description="Export all objects in this scene", default=True)
- global_scale = FloatProperty(name="Scale", description="Scale all data, (Note! some imports dont support scaled armatures)", min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, default=1.0)
+ # XNA does not support scaled armatures (JCB)
+ global_scale = FloatProperty(name="Scale", description="Scale all data. Some importers do not support scaled armatures!", min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, default=1.0)
axis_forward = EnumProperty(
name="Forward",
@@ -71,7 +71,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
('Y', "Y Forward", ""),
('Z', "Z Forward", ""),
('-X', "-X Forward", ""),
- ('-Y', "-Y Forward", ""),
+ ('-Y', "-Y Forward (Blender)", ""),
('-Z', "-Z Forward", ""),
),
default='-Z',
@@ -81,7 +81,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
name="Up",
items=(('X', "X Up", ""),
('Y', "Y Up", ""),
- ('Z', "Z Up", ""),
+ ('Z', "Z Up (Blender)", ""),
('-X', "-X Up", ""),
('-Y', "-Y Up", ""),
('-Z', "-Z Up", ""),
@@ -112,13 +112,21 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
default='FACE',
)
+ # XNA does not use the edge information (JCB)
+ use_edges = BoolProperty(name="Include Edges", description="Edges may not be necessary and can cause errors with some importers!", default=False)
# EXP_MESH_HQ_NORMALS = BoolProperty(name="HQ Normals", description="Generate high quality normals", default=True)
# armature animation
- ANIM_ENABLE = BoolProperty(name="Enable Animation", description="Export keyframe animation", default=True)
+ ANIM_ENABLE = BoolProperty(name="Include Animation", description="Export keyframe animation", default=True)
+ ANIM_ACTION_ALL = BoolProperty(name="All Actions", description="Export all actions for armatures or just the currently selected action", default=True)
ANIM_OPTIMIZE = BoolProperty(name="Optimize Keyframes", description="Remove double keyframes", default=True)
ANIM_OPTIMIZE_PRECISSION = FloatProperty(name="Precision", description="Tolerence for comparing double keyframes (higher for greater accuracy)", min=1, max=16, soft_min=1, soft_max=16, default=6.0)
-# ANIM_ACTION_ALL = BoolProperty(name="Current Action", description="Use actions currently applied to the armatures (use scene start/end frame)", default=True)
- ANIM_ACTION_ALL = BoolProperty(name="All Actions", description="Use all actions for armatures, if false, use current action", default=False)
+ # XNA needs different names for each take having the first one always called Default_Take is unhelpful (JCB)
+ # XNA usually errors if the textures are not in the same folder as the FBX file (JCB)
+ # XNA - validation to avoid incompatible settings. I will understand if this is not kept in the generic version. (JCB)
+ # It would be nice to have this for XNA, UDK, Unity and Sunburn if others could provide the details. (JCB)
+ xna_validate = BoolProperty(name="XNA Strict Options", description="Make sure options are compatible with Microsoft XNA", default=False)
+ # The armature rotation does not work for XNA and setting the global matrix to identity is not sufficient on its own (JCB)
+ use_rotate_workaround = BoolProperty(name="XNA Rotate Fix", description="Disable global rotation, for XNA compatibility", default=False)
batch_mode = EnumProperty(
name="Batch Mode",
@@ -133,23 +141,51 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
path_mode = path_reference_mode
+ # Validate that the options are compatible with XNA (JCB)
+ def _validate_xna_options(self):
+ if not self.xna_validate:
+ return False
+ changed = False
+ if not self.use_rotate_workaround:
+ changed = True
+ self.use_rotate_workaround = True
+ if self.global_scale != 1.0 or self.mesh_smooth_type != 'OFF':
+ changed = True
+ self.global_scale = 1.0
+ self.mesh_smooth_type = 'OFF'
+ if self.ANIM_OPTIMIZE or self.use_edges:
+ changed = True
+ self.ANIM_OPTIMIZE = False
+ self.use_edges = False
+ if self.object_types & {'CAMERA', 'LAMP', 'EMPTY'}:
+ changed = True
+ self.object_types -= {'CAMERA', 'LAMP', 'EMPTY'}
+ return changed
+
@property
def check_extension(self):
return self.batch_mode == 'OFF'
def check(self, context):
- return axis_conversion_ensure(self, "axis_forward", "axis_up")
+ is_xna_change = self._validate_xna_options()
+ is_axis_change = axis_conversion_ensure(self, "axis_forward", "axis_up")
+ if is_xna_change or is_axis_change:
+ return True
+ else:
+ return False
def execute(self, context):
from mathutils import Matrix
if not self.filepath:
raise Exception("filepath not set")
+ # Armature rotation causes a mess in XNA there are also other changes in the main script to avoid rotation (JCB)
global_matrix = Matrix()
global_matrix[0][0] = global_matrix[1][1] = global_matrix[2][2] = self.global_scale
- global_matrix = global_matrix * axis_conversion(to_forward=self.axis_forward, to_up=self.axis_up).to_4x4()
+ if not self.use_rotate_workaround:
+ global_matrix = global_matrix * axis_conversion(to_forward=self.axis_forward, to_up=self.axis_up).to_4x4()
- keywords = self.as_keywords(ignore=("axis_forward", "axis_up", "global_scale", "check_existing", "filter_glob"))
+ keywords = self.as_keywords(ignore=("axis_forward", "axis_up", "global_scale", "check_existing", "filter_glob", "xna_validate"))
keywords["global_matrix"] = global_matrix
from . import export_fbx
diff --git a/io_scene_fbx/export_fbx.py b/io_scene_fbx/export_fbx.py
index 61c27059..ce64ebee 100644
--- a/io_scene_fbx/export_fbx.py
+++ b/io_scene_fbx/export_fbx.py
@@ -202,12 +202,20 @@ def save_single(operator, scene, filepath="",
ANIM_ACTION_ALL=False,
use_metadata=True,
path_mode='AUTO',
+ use_edges=True,
+ use_rotate_workaround=False,
):
import bpy_extras.io_utils
+ # Only used for camera and lamp rotations
mtx_x90 = Matrix.Rotation(math.pi / 2.0, 3, 'X')
+ # Used for mesh and armature rotations
mtx4_z90 = Matrix.Rotation(math.pi / 2.0, 4, 'Z')
+ # Rotation does not work for XNA animations. I do not know why but they end up a mess! (JCB)
+ if use_rotate_workaround:
+ # Set rotation to Matrix Identity for XNA (JCB)
+ mtx4_z90.identity()
if global_matrix is None:
global_matrix = Matrix()
@@ -449,6 +457,11 @@ def save_single(operator, scene, filepath="",
loc = tuple(loc)
rot = tuple(rot.to_euler()) # quat -> euler
scale = tuple(scale)
+
+ # Essential for XNA to use the original matrix not rotated nor scaled (JCB)
+ if use_rotate_workaround:
+ matrix = ob.matrix_local
+
else:
# This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore
#if ob and not matrix: matrix = ob.matrix_world * global_matrix
@@ -1010,12 +1023,12 @@ def save_single(operator, scene, filepath="",
)
# matrixOnly is not used at the moment
- def write_null(my_null=None, fbxName=None):
+ def write_null(my_null=None, fbxName=None, fbxType="Null", fbxTypeFlags="Null"):
# ob can be null
if not fbxName:
fbxName = my_null.fbxName
- file.write('\n\tModel: "Model::%s", "Null" {' % fbxName)
+ file.write('\n\tModel: "Model::%s", "%s" {' % (fbxName, fbxType))
file.write('\n\t\tVersion: 232')
if my_null:
@@ -1024,15 +1037,16 @@ def save_single(operator, scene, filepath="",
poseMatrix = write_object_props()[3]
pose_items.append((fbxName, poseMatrix))
+
+ file.write('\n\t\t}'
+ '\n\t\tMultiLayer: 0'
+ '\n\t\tMultiTake: 1'
+ '\n\t\tShading: Y'
+ '\n\t\tCulling: "CullingOff"'
+ )
- file.write('''
- }
- MultiLayer: 0
- MultiTake: 1
- Shading: Y
- Culling: "CullingOff"
- TypeFlags: "Null"
- }''')
+ file.write('\n\t\tTypeFlags: "%s"' % fbxTypeFlags)
+ file.write('\n\t}')
# Material Settings
if world:
@@ -1312,7 +1326,7 @@ def save_single(operator, scene, filepath="",
# convert into lists once.
me_vertices = me.vertices[:]
- me_edges = me.edges[:]
+ me_edges = me.edges[:] if use_edges else ()
me_faces = me.faces[:]
poseMatrix = write_object_props(my_mesh.blenObject, None, my_mesh.parRelMatrix())[3]
@@ -2059,13 +2073,31 @@ def save_single(operator, scene, filepath="",
del tmp_obmapping
# Finished finding groups we use
+
+ # == WRITE OBJECTS TO THE FILE ==
+ # == From now on we are building the FBX file from the information collected above (JCB)
materials = [(sane_matname(mat_tex_pair), mat_tex_pair) for mat_tex_pair in materials.keys()]
textures = [(sane_texname(tex), tex) for tex in textures.keys() if tex]
materials.sort(key=lambda m: m[0]) # sort by name
textures.sort(key=lambda m: m[0])
- camera_count = 8
+ camera_count = 8 if 'CAMERA' in object_types else 0
+
+ # sanity checks
+ try:
+ assert(not (ob_meshes and ('MESH' not in object_types)))
+ assert(not (materials and ('MESH' not in object_types)))
+ assert(not (textures and ('MESH' not in object_types)))
+ assert(not (textures and ('MESH' not in object_types)))
+
+ assert(not (ob_lights and ('LAMP' not in object_types)))
+
+ assert(not (ob_cameras and ('CAMERA' not in object_types)))
+ except AssertionError:
+ import traceback
+ traceback.print_exc()
+
file.write('''
; Object definitions
@@ -2137,8 +2169,7 @@ Definitions: {
}''' % tmp)
del tmp
- # we could avoid writing this possibly but for now just write it
-
+ # Bind pose is essential for XNA if the 'MESH' is included (JCB)
file.write('''
ObjectType: "Pose" {
Count: 1
@@ -2163,14 +2194,17 @@ Definitions: {
Objects: {''')
- # To comply with other FBX FILES
- write_camera_switch()
+ if 'CAMERA' in object_types:
+ # To comply with other FBX FILES
+ write_camera_switch()
for my_null in ob_null:
write_null(my_null)
+ # XNA requires the armature to be a Limb (JCB)
+ # Note, 2.58 and previous wrote these as normal empties and it worked mostly (except for XNA)
for my_arm in ob_arms:
- write_null(my_arm)
+ write_null(my_arm, fbxType="Limb", fbxTypeFlags="Skeleton")
for my_cam in ob_cameras:
write_camera(my_cam)
@@ -2185,7 +2219,8 @@ Objects: {''')
for my_bone in ob_bones:
write_bone(my_bone)
- write_camera_default()
+ if 'CAMERA' in object_types:
+ write_camera_default()
for matname, (mat, tex) in materials:
write_material(matname, mat) # We only need to have a material per image pair, but no need to write any image info into the material (dumb fbx standard)
@@ -2220,9 +2255,10 @@ Objects: {''')
if me in iter(my_bone.blenMeshes.values()):
write_sub_deformer_skin(my_mesh, my_bone, weights)
- # Write pose's really weird, only needed when an armature and mesh are used together
- # each by themselves dont need pose data. for now only pose meshes and bones
+ # Write pose is really weird, only needed when an armature and mesh are used together
+ # each by themselves do not need pose data. For now only pose meshes and bones
+ # Bind pose is essential for XNA if the 'MESH' is included (JCB)
file.write('''
Pose: "Pose::BIND_POSES", "BindPose" {
Type: "BindPose"
@@ -2265,11 +2301,15 @@ Objects: {''')
Relations: {''')
+ # Nulls are likely to cause problems for XNA
+
for my_null in ob_null:
file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_null.fbxName)
+ # Armature must be a Limb for XNA
+ # Note, 2.58 and previous wrote these as normal empties and it worked mostly (except for XNA)
for my_arm in ob_arms:
- file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_arm.fbxName)
+ file.write('\n\tModel: "Model::%s", "Limb" {\n\t}' % my_arm.fbxName)
for my_mesh in ob_meshes:
file.write('\n\tModel: "Model::%s", "Mesh" {\n\t}' % my_mesh.fbxName)
@@ -2337,7 +2377,7 @@ Relations: {''')
Connections: {''')
- # NOTE - The FBX SDK dosnt care about the order but some importers DO!
+ # NOTE - The FBX SDK does not care about the order but some importers DO!
# for instance, defining the material->mesh connection
# before the mesh->parent crashes cinema4d
@@ -2369,20 +2409,19 @@ Connections: {''')
for texname, tex in textures:
file.write('\n\tConnect: "OO", "Video::%s", "Texture::%s"' % (texname, texname))
- for my_mesh in ob_meshes:
- if my_mesh.fbxArm:
- file.write('\n\tConnect: "OO", "Deformer::Skin %s", "Model::%s"' % (my_mesh.fbxName, my_mesh.fbxName))
+ if 'MESH' in object_types:
+ for my_mesh in ob_meshes:
+ if my_mesh.fbxArm:
+ file.write('\n\tConnect: "OO", "Deformer::Skin %s", "Model::%s"' % (my_mesh.fbxName, my_mesh.fbxName))
- #for bonename, bone, obname, me, armob in ob_bones:
- for my_bone in ob_bones:
- for fbxMeshObName in my_bone.blenMeshes: # .keys()
- file.write('\n\tConnect: "OO", "SubDeformer::Cluster %s %s", "Deformer::Skin %s"' % (fbxMeshObName, my_bone.fbxName, fbxMeshObName))
+ for my_bone in ob_bones:
+ for fbxMeshObName in my_bone.blenMeshes: # .keys()
+ file.write('\n\tConnect: "OO", "SubDeformer::Cluster %s %s", "Deformer::Skin %s"' % (fbxMeshObName, my_bone.fbxName, fbxMeshObName))
- # limbs -> deformers
- # for bonename, bone, obname, me, armob in ob_bones:
- for my_bone in ob_bones:
- for fbxMeshObName in my_bone.blenMeshes: # .keys()
- file.write('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s %s"' % (my_bone.fbxName, fbxMeshObName, my_bone.fbxName))
+ # limbs -> deformers
+ for my_bone in ob_bones:
+ for fbxMeshObName in my_bone.blenMeshes: # .keys()
+ file.write('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s %s"' % (my_bone.fbxName, fbxMeshObName, my_bone.fbxName))
#for bonename, bone, obname, me, armob in ob_bones:
for my_bone in ob_bones:
@@ -2400,8 +2439,10 @@ Connections: {''')
for fbxGroupName in ob_base.fbxGroupNames:
file.write('\n\tConnect: "OO", "Model::%s", "GroupSelection::%s"' % (ob_base.fbxName, fbxGroupName))
- for my_arm in ob_arms:
- file.write('\n\tConnect: "OO", "Model::%s", "Model::Scene"' % my_arm.fbxName)
+
+ # I think the following always duplicates the armature connection because it is also in ob_all_typegroups above! (JCB)
+ # for my_arm in ob_arms:
+ # file.write('\n\tConnect: "OO", "Model::%s", "Model::Scene"' % my_arm.fbxName)
file.write('\n}')
@@ -2864,24 +2905,29 @@ def save(operator, context,
return {'FINISHED'} # so the script wont run after we have batch exported.
-
-
-
+# APPLICATION REQUIREMENTS
+# Please update the lists for UDK, Unity, XNA etc. on the following web page:
+# http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Import-Export/UnifiedFBX
+
+# XNA FBX Requirements (JCB 29 July 2011)
+# - Armature must be parented to the scene
+# - Armature must be a 'Limb' never a 'null'. This is in several places.
+# - First bone must be parented to the armature.
+# - Rotation must be completely disabled including
+# always returning the original matrix in In object_tx().
+# It is the animation that gets distorted during rotation!
+# - Lone edges cause intermittent errors in the XNA content pipeline!
+# I have added a warning message and excluded them.
+# - Bind pose must be included with the 'MESH'
+# Typical settings for XNA export
+# No Cameras, No Lamps, No Edges, No face smoothing, No Default_Take, Armature as bone, Disable rotation
+
+# NOTE TO Campbell -
+# Can any or all of the following notes be removed because some have been here for a long time? (JCB 27 July 2011)
# NOTES (all line numbers correspond to original export_fbx.py (under release/scripts)
-# - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print
# - get rid of bpy.path.clean_name somehow
-# + fixed: isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565
# + get rid of BPyObject_getObjectArmature, move it in RNA?
-# - BATCH_ENABLE and BATCH_GROUP options: line 327
# - implement all BPyMesh_* used here with RNA
# - getDerivedObjects is not fully replicated with .dupli* funcs
-# - talk to Campbell, this code won't work? lines 1867-1875
# - don't know what those colbits are, do we need them? they're said to be deprecated in DNA_object_types.h: 1886-1893
# - no hq normals: 1900-1901
-
-# TODO
-
-# - bpy.data.remove_scene: line 366
-# - bpy.sys.time move to bpy.sys.util?
-# - new scene creation, activation: lines 327-342, 368
-# - uses bpy.path.abspath, *.relpath - replace at least relpath