diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py index 284005eb29b6d5e221c97f2d7343167addc3b68b..226508e14074f7a2f3b642b939b6a2a0d597ffc2 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 ff43d5648de727bb2df510a73f64cddc091dfb35..481eded3e95466fc3640a7f3191dc3c2ed8d965b 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 ecb152fe95ccd4829cbf554401c38d44c0d8fbe3..199a1653a43d684138f6f4fad18ed809b58754f7 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')