[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [2205] trunk/py/scripts/addons/ io_scene_fbx: patch [#28118] Add XNA requirements to the official FBX exporter

Campbell Barton ideasman42 at gmail.com
Mon Aug 1 02:47:10 CEST 2011


Revision: 2205
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-extensions&revision=2205
Author:   campbellbarton
Date:     2011-08-01 00:47:09 +0000 (Mon, 01 Aug 2011)
Log Message:
-----------
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.

Modified Paths:
--------------
    trunk/py/scripts/addons/io_scene_fbx/__init__.py
    trunk/py/scripts/addons/io_scene_fbx/export_fbx.py

Modified: trunk/py/scripts/addons/io_scene_fbx/__init__.py
===================================================================
--- trunk/py/scripts/addons/io_scene_fbx/__init__.py	2011-07-31 09:33:04 UTC (rev 2204)
+++ trunk/py/scripts/addons/io_scene_fbx/__init__.py	2011-08-01 00:47:09 UTC (rev 2205)
@@ -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 @@
     # 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 @@
                    ('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 @@
             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 @@
             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 @@
 
     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

Modified: trunk/py/scripts/addons/io_scene_fbx/export_fbx.py
===================================================================
--- trunk/py/scripts/addons/io_scene_fbx/export_fbx.py	2011-07-31 09:33:04 UTC (rev 2204)
+++ trunk/py/scripts/addons/io_scene_fbx/export_fbx.py	2011-08-01 00:47:09 UTC (rev 2205)
@@ -202,12 +202,20 @@
         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 @@
             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 @@
                    )
 
     # 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 @@
             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 @@
 
         # 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 @@
 
     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)
 

@@ Diff output truncated at 10240 characters. @@


More information about the Bf-extensions-cvs mailing list