[Bf-extensions-cvs] [b54d306] master: FBX io: better handling for orientation and scale.

Bastien Montagne noreply at git.blender.org
Sun May 11 12:45:49 CEST 2014


Commit: b54d30693cc4f297c9940d6626d30a9ea497cee0
Author: Bastien Montagne
Date:   Sun May 11 12:32:16 2014 +0200
https://developer.blender.org/rBAb54d30693cc4f297c9940d6626d30a9ea497cee0

FBX io: better handling for orientation and scale.

Export: generate a valid scale value from Blender scene's Scale setting (if valid, else 1.0),
assuming 1BU = 1m and 1FBX unit = 1cm.

Import:
* Use by default axes and scale data in FBX file instead of manually specified ones (using same
conventions as in exporter).
* Also import FPS data from FBX file (crucial for future animations import!).

And a few fixes for issues found in those areas as well...

===================================================================

M	io_scene_fbx/__init__.py
M	io_scene_fbx/export_fbx_bin.py
M	io_scene_fbx/import_fbx.py

===================================================================

diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py
index 284005e..226508e 100644
--- a/io_scene_fbx/__init__.py
+++ b/io_scene_fbx/__init__.py
@@ -21,7 +21,7 @@
 bl_info = {
     "name": "Autodesk FBX format",
     "author": "Campbell Barton, Bastien Montagne",
-    "version": (3, 0, 0),
+    "version": (3, 1, 0),
     "blender": (2, 70, 0),
     "location": "File > Import-Export",
     "description": "Export FBX meshes, UV's, vertex colors, materials, "
@@ -69,26 +69,11 @@ class ImportFBX(bpy.types.Operator, ImportHelper):
     filename_ext = ".fbx"
     filter_glob = StringProperty(default="*.fbx", options={'HIDDEN'})
 
-    use_image_search = BoolProperty(
-        name="Image Search",
-        description="Search subdirs for any associated images (Warning, may be slow)",
-        default=True,
-    )
-
-    use_alpha_decals = BoolProperty(
-        name="Alpha Decals",
-        description="Treat materials with alpha as decals (no shadow casting)",
+    use_manual_orientation = BoolProperty(
+        name="Manual Orientation",
+        description="Specify orientation and scale, instead of using embeded data in FBX file",
         default=False,
-        options={'HIDDEN'}
     )
-    decal_offset = FloatProperty(
-        name="Decal Offset",
-        description="Displace geometry of alpha meshes",
-        min=0.0, max=1.0,
-        default=0.0,
-        options={'HIDDEN'}
-    )
-
     axis_forward = EnumProperty(
         name="Forward",
         items=(('X', "X Forward", ""),
@@ -117,21 +102,42 @@ class ImportFBX(bpy.types.Operator, ImportHelper):
         default=1.0,
     )
 
-    def execute(self, context):
-        from mathutils import Matrix
+    use_image_search = BoolProperty(
+        name="Image Search",
+        description="Search subdirs for any associated images (Warning, may be slow)",
+        default=True,
+    )
 
-        keywords = self.as_keywords(ignore=("axis_forward",
-                                            "axis_up",
-                                            "global_scale",
-                                            "filter_glob",
-                                            "directory",
-                                            ))
+    use_alpha_decals = BoolProperty(
+        name="Alpha Decals",
+        description="Treat materials with alpha as decals (no shadow casting)",
+        default=False,
+        options={'HIDDEN'}
+    )
+    decal_offset = FloatProperty(
+        name="Decal Offset",
+        description="Displace geometry of alpha meshes",
+        min=0.0, max=1.0,
+        default=0.0,
+        options={'HIDDEN'}
+    )
 
-        global_matrix = (Matrix.Scale(self.global_scale, 4) *
-                         axis_conversion(from_forward=self.axis_forward,
-                                         from_up=self.axis_up,
-                                         ).to_4x4())
-        keywords["global_matrix"] = global_matrix
+    def draw(self, context):
+        layout = self.layout
+
+        layout.prop(self, "use_manual_orientation"),
+        sub = layout.column()
+        sub.enabled = self.use_manual_orientation
+        sub.prop(self, "axis_forward")
+        sub.prop(self, "axis_up")
+        sub.prop(self, "global_scale")
+
+        layout.prop(self, "use_image_search")
+        #layout.prop(self, "use_alpha_decals")
+        layout.prop(self, "decal_offset")
+
+    def execute(self, context):
+        keywords = self.as_keywords(ignore=("filter_glob", "directory"))
         keywords["use_cycles"] = (context.scene.render.engine == 'CYCLES')
 
         from . import import_fbx
diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index ff43d56..481eded 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -2240,11 +2240,14 @@ def fbx_header_elements(root, scene_data, time=None):
 
     ##### Start of GlobalSettings element.
     global_settings = elem_empty(root, b"GlobalSettings")
+    scene = scene_data.scene
 
     elem_data_single_int32(global_settings, b"Version", 1000)
 
     props = elem_properties(global_settings)
     up_axis, front_axis, coord_axis = RIGHT_HAND_AXES[scene_data.settings.to_axes]
+    # Currently not sure about that, but looks like default unit of FBX is cm...
+    scale_factor = (1.0 if scene.unit_settings.system == 'NONE' else scene.unit_settings.scale_length) * 100
     elem_props_set(props, "p_integer", b"UpAxis", up_axis[0])
     elem_props_set(props, "p_integer", b"UpAxisSign", up_axis[1])
     elem_props_set(props, "p_integer", b"FrontAxis", front_axis[0])
@@ -2253,15 +2256,15 @@ def fbx_header_elements(root, scene_data, time=None):
     elem_props_set(props, "p_integer", b"CoordAxisSign", coord_axis[1])
     elem_props_set(props, "p_integer", b"OriginalUpAxis", -1)
     elem_props_set(props, "p_integer", b"OriginalUpAxisSign", 1)
-    elem_props_set(props, "p_double", b"UnitScaleFactor", 1.0)
-    elem_props_set(props, "p_double", b"OriginalUnitScaleFactor", 1.0)
+    elem_props_set(props, "p_double", b"UnitScaleFactor", scale_factor)
+    elem_props_set(props, "p_double", b"OriginalUnitScaleFactor", scale_factor)
     elem_props_set(props, "p_color_rgb", b"AmbientColor", (0.0, 0.0, 0.0))
     elem_props_set(props, "p_string", b"DefaultCamera", "Producer Perspective")
 
     # Global timing data.
-    r = scene_data.scene.render
-    fps = r.fps / r.fps_base
-    fbx_fps, fbx_fps_mode = FBX_FRAMERATES[0]  # Custom framerate.
+    r = scene.render
+    _, fbx_fps_mode = FBX_FRAMERATES[0]  # Custom framerate.
+    fbx_fps = fps = r.fps / r.fps_base
     for ref_fps, fps_mode in FBX_FRAMERATES:
         if similar_values(fps, ref_fps):
             fbx_fps = ref_fps
diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index ecb152f..199a165 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -178,6 +178,24 @@ def elem_props_get_number(elem, elem_prop_id, default=None):
     return default
 
 
+def elem_props_get_integer(elem, elem_prop_id, default=None):
+    elem_prop = elem_props_find_first(elem, elem_prop_id)
+    if elem_prop is not None:
+        assert(elem_prop.props[0] == elem_prop_id)
+        if elem_prop.props[1] == b'int':
+            assert(elem_prop.props[1] == b'int')
+            assert(elem_prop.props[2] == b'Integer')
+        elif elem_prop.props[1] == b'ULongLong':
+            assert(elem_prop.props[1] == b'ULongLong')
+            assert(elem_prop.props[2] == b'')
+
+        # we could allow other number types
+        assert(elem_prop.props_type[4] in {data_types.INT32, data_types.INT64})
+
+        return elem_prop.props[4]
+    return default
+
+
 def elem_props_get_bool(elem, elem_prop_id, default=None):
     elem_prop = elem_props_find_first(elem, elem_prop_id)
     if elem_prop is not None:
@@ -873,7 +891,10 @@ def is_ascii(filepath, size):
 
 
 def load(operator, context, filepath="",
-         global_matrix=None,
+         use_manual_orientation=False,
+         axis_forward='-Z',
+         axis_up='Y',
+         global_scale=1.0,
          use_cycles=True,
          use_image_search=False,
          use_alpha_decals=False,
@@ -882,10 +903,12 @@ def load(operator, context, filepath="",
     global fbx_elem_nil
     fbx_elem_nil = FBXElem('', (), (), ())
 
-    global_scale = (sum(global_matrix.to_scale()) / 3.0) if global_matrix else 1.0
-
     import os
+    from bpy_extras.io_utils import axis_conversion
+    from mathutils import Matrix
+
     from . import parse_fbx
+    from .fbx_utils import RIGHT_HAND_AXES, FBX_FRAMERATES
 
     # detect ascii files
     if is_ascii(filepath, 24):
@@ -929,6 +952,43 @@ def load(operator, context, filepath="",
 
     scene = context.scene
 
+
+    #### Get some info from GlobalSettings.
+
+    fbx_settings = elem_find_first(elem_root, b'GlobalSettings')
+    fbx_settings_props = elem_find_first(fbx_settings, b'Properties70')
+    if fbx_settings is None or fbx_settings_props is None:
+        operator.report({'ERROR'}, "No 'GlobalSettings' found in file %r" % filepath)
+        return {'CANCELLED'}
+
+    # Compute global matrix and scale.
+    if not use_manual_orientation:
+        axis_forward = (elem_props_get_integer(fbx_settings_props, b'FrontAxis', 1),
+                        elem_props_get_integer(fbx_settings_props, b'FrontAxisSign', 1))
+        axis_up = (elem_props_get_integer(fbx_settings_props, b'UpAxis', 2),
+                   elem_props_get_integer(fbx_settings_props, b'UpAxisSign', 1))
+        axis_coord = (elem_props_get_integer(fbx_settings_props, b'CoordAxis', 0),
+                      elem_props_get_integer(fbx_settings_props, b'CoordAxisSign', 1))
+        print(axis_up, axis_forward, axis_coord)
+        axis_up, axis_forward = {v: k for k, v in RIGHT_HAND_AXES.items()}.get((axis_up, axis_forward, axis_coord), ('Z', 'Y'))
+        print(axis_up, axis_forward)
+        # FBX base unit seems to be the centimeter, while raw Blender Unit is equivalent to the meter...
+        global_scale = elem_props_get_number(fbx_settings_props, b'UnitScaleFactor', 100.0) / 100.0
+    global_matrix = (Matrix.Scale(global_scale, 4) *
+                     axis_conversion(from_forward=axis_forward, from_up=axis_up).to_4x4())
+
+    # Compute framerate settings.
+    custom_fps = elem_props_get_number(fbx_settings_props, b'CustomFrameRate', 25.0)
+    time_mode = elem_props_get_enum(fbx_settings_props, b'TimeMode')
+    real_fps = {eid: val for val, eid in FBX_FRAMERATES[1:]}.get(time_mode, custom_fps)
+    if real_fps < 0.0:
+        real_fps = 25.0
+    scene.render.fps = round(real_fps)
+    scene.render.fps_base = scene.render.fps / real_fps
+
+
+    #### And now, the "real" data.
+
     fbx_defs = elem_find_first(elem_root, b'Definitions')  # can be None
     fbx_nodes = elem_find_first(elem_root, b'Objects')
     fbx_connections = elem_find_first(elem_root, b'Connections')



More information about the Bf-extensions-cvs mailing list