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:
authorSamuli Raivio <bqqbarbhg>2019-08-29 13:01:39 +0300
committerBastien Montagne <montagne29@wanadoo.fr>2019-08-29 13:17:06 +0300
commitf1dd37b8ac8f2db93f56002bae24e70b2cbf986f (patch)
tree6119d996c0f6b833f76087965876ac539f172b88
parenta215a3c85ad950e38d344205701b4258201f8412 (diff)
Add optional subdivision surface support to the FBX exporter
Add option 'Export Subdivision Surface' to the FBX exporter (disabled by default). When enabled the exporter will write the **last active Catmull-Clark subdivision surface modifier** as FBX properties instead of applying it. Edge crease data is also written to the FBX file if 'Use Creases' is enabled in the subsurf modifier. Reviewers: mont29 Tags: #add-ons Differential Revision: https://developer.blender.org/D4982
-rw-r--r--io_scene_fbx/__init__.py17
-rw-r--r--io_scene_fbx/export_fbx_bin.py75
-rw-r--r--io_scene_fbx/fbx_utils.py4
-rw-r--r--io_scene_fbx/import_fbx.py75
4 files changed, 164 insertions, 7 deletions
diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py
index bb9528af..ebbf2a19 100644
--- a/io_scene_fbx/__init__.py
+++ b/io_scene_fbx/__init__.py
@@ -21,7 +21,7 @@
bl_info = {
"name": "FBX format",
"author": "Campbell Barton, Bastien Montagne, Jens Restemeier",
- "version": (4, 14, 15),
+ "version": (4, 15, 0),
"blender": (2, 80, 0),
"location": "File > Import-Export",
"description": "FBX IO meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions",
@@ -131,6 +131,12 @@ class ImportFBX(bpy.types.Operator, ImportHelper):
default=1.0,
)
+ use_subsurf: BoolProperty(
+ name="Import Subdivision Surface",
+ description="Import FBX subdivision information as subdivision surface modifiers",
+ default=False,
+ )
+
use_custom_props: BoolProperty(
name="Import User Properties",
description="Import user properties as custom properties",
@@ -205,6 +211,8 @@ class ImportFBX(bpy.types.Operator, ImportHelper):
layout.prop(self, "use_anim")
layout.prop(self, "anim_offset")
+ layout.prop(self, "use_subsurf")
+
layout.prop(self, "use_custom_props")
sub = layout.row()
sub.enabled = self.use_custom_props
@@ -334,6 +342,12 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
"(prefer 'Normals Only' option if your target importer understand split normals)",
default='OFF',
)
+ use_subsurf: BoolProperty(
+ name="Export Subdivision Surface",
+ description="Export the last Catmull-Rom subidivion modifier as FBX subdivision "
+ "(Does not apply the modifier even if 'Apply Modifiers' is enabled)",
+ default=False,
+ )
use_mesh_edges: BoolProperty(
name="Loose Edges",
description="Export loose edges (as two-vertices polygons)",
@@ -507,6 +521,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
sub.enabled = self.use_mesh_modifiers and False # disabled in 2.8...
sub.prop(self, "use_mesh_modifiers_render")
layout.prop(self, "mesh_smooth_type")
+ layout.prop(self, "use_subsurf")
layout.prop(self, "use_mesh_edges")
sub = layout.row()
#~ sub.enabled = self.mesh_smooth_type in {'OFF'}
diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index 9292d656..5cba16e6 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -49,7 +49,7 @@ from .fbx_utils import (
FBX_VERSION, FBX_HEADER_VERSION, FBX_SCENEINFO_VERSION, FBX_TEMPLATES_VERSION,
FBX_MODELS_VERSION,
FBX_GEOMETRY_VERSION, FBX_GEOMETRY_NORMAL_VERSION, FBX_GEOMETRY_BINORMAL_VERSION, FBX_GEOMETRY_TANGENT_VERSION,
- FBX_GEOMETRY_SMOOTHING_VERSION, FBX_GEOMETRY_VCOLOR_VERSION, FBX_GEOMETRY_UV_VERSION,
+ FBX_GEOMETRY_SMOOTHING_VERSION, FBX_GEOMETRY_CREASE_VERSION, FBX_GEOMETRY_VCOLOR_VERSION, FBX_GEOMETRY_UV_VERSION,
FBX_GEOMETRY_MATERIAL_VERSION, FBX_GEOMETRY_LAYER_VERSION,
FBX_GEOMETRY_SHAPE_VERSION, FBX_DEFORMER_SHAPE_VERSION, FBX_DEFORMER_SHAPECHANNEL_VERSION,
FBX_POSE_BIND_VERSION, FBX_DEFORMER_SKIN_VERSION, FBX_DEFORMER_CLUSTER_VERSION,
@@ -865,6 +865,30 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
if scene_data.settings.use_custom_props:
fbx_data_element_custom_properties(props, me)
+ # Subdivision levels. Take them from the first found subsurf modifier from the
+ # first object that has the mesh. Write crease information if the object has
+ # and subsurf modifier.
+ write_crease = False
+ if scene_data.settings.use_subsurf:
+ last_subsurf = None
+ for mod in me_obj.bdata.modifiers:
+ if not (mod.show_render or mod.show_viewport):
+ continue
+ if mod.type == 'SUBSURF' and mod.subdivision_type == 'CATMULL_CLARK':
+ last_subsurf = mod
+
+ if last_subsurf:
+ elem_data_single_int32(geom, b"Smoothness", 2) # Display control mesh and smoothed
+ elem_data_single_int32(geom, b"BoundaryRule", 2) # Round edges like Blender
+ elem_data_single_int32(geom, b"PreviewDivisionLevels", last_subsurf.levels)
+ elem_data_single_int32(geom, b"RenderDivisionLevels", last_subsurf.render_levels)
+
+ elem_data_single_int32(geom, b"PreserveBorders", 0)
+ elem_data_single_int32(geom, b"PreserveHardEdges", 0)
+ elem_data_single_int32(geom, b"PropagateEdgeHardness", 0)
+
+ write_crease = mod.use_creases
+
elem_data_single_int32(geom, b"GeometryVersion", FBX_GEOMETRY_VERSION)
# Vertex cos.
@@ -980,7 +1004,21 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
elem_data_single_int32_array(lay_smooth, b"Smoothing", t_ps) # Sight, int32 for bool...
del t_ps
- # TODO: Edge crease (LayerElementCrease).
+ # Edge crease for subdivision
+ if write_crease:
+ t_ec = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * edges_nbr
+ for e in me.edges:
+ if e.key not in edges_map:
+ continue # Only loose edges, in theory!
+ t_ec[edges_map[e.key]] = e.crease
+
+ lay_crease = elem_data_single_int32(geom, b"LayerElementEdgeCrease", 0)
+ elem_data_single_int32(lay_crease, b"Version", FBX_GEOMETRY_CREASE_VERSION)
+ elem_data_single_string(lay_crease, b"Name", b"")
+ elem_data_single_string(lay_crease, b"MappingInformationType", b"ByEdge")
+ elem_data_single_string(lay_crease, b"ReferenceInformationType", b"Direct")
+ elem_data_single_float64_array(lay_crease, b"EdgeCrease", t_ec)
+ del t_ec
# And we are done with edges!
del edges_map
@@ -1193,6 +1231,10 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
lay_smooth = elem_empty(layer, b"LayerElement")
elem_data_single_string(lay_smooth, b"Type", b"LayerElementSmoothing")
elem_data_single_int32(lay_smooth, b"TypedIndex", 0)
+ if write_crease:
+ lay_smooth = elem_empty(layer, b"LayerElement")
+ elem_data_single_string(lay_smooth, b"Type", b"LayerElementEdgeCrease")
+ elem_data_single_int32(lay_smooth, b"TypedIndex", 0)
if vcolnumber:
lay_vcol = elem_empty(layer, b"LayerElement")
elem_data_single_string(lay_vcol, b"Type", b"LayerElementColor")
@@ -2217,8 +2259,10 @@ def fbx_data_from_scene(scene, depsgraph, settings):
# We cannot use default mesh in that case, or material would not be the right ones...
use_org_data = not (is_ob_material or ob.type in BLENDER_OTHER_OBJECT_TYPES)
backup_pose_positions = []
+ tmp_mods = []
if use_org_data and ob.type == 'MESH':
# No need to create a new mesh in this case, if no modifier is active!
+ last_subsurf = None
for mod in ob.modifiers:
# For meshes, when armature export is enabled, disable Armature modifiers here!
# XXX Temp hacks here since currently we only have access to a viewport depsgraph...
@@ -2232,10 +2276,24 @@ def fbx_data_from_scene(scene, depsgraph, settings):
backup_pose_positions.append((armature, armature.pose_position))
armature.pose_position = 'REST'
elif mod.show_render or mod.show_viewport:
- use_org_data = False
+ # If exporting with subsurf collect the last Catmull-Clark subsurf modifier
+ # and disable it. We can use the original data as long as this is the first
+ # found applicable subsurf modifier.
+ if settings.use_subsurf and mod.type == 'SUBSURF' and mod.subdivision_type == 'CATMULL_CLARK':
+ if last_subsurf:
+ use_org_data = False
+ last_subsurf = mod
+ else:
+ use_org_data = False
+ if settings.use_subsurf and last_subsurf:
+ # XXX: When exporting with subsurf information temporarily disable
+ # the last subsurf modifier.
+ tmp_mods.append((last_subsurf, last_subsurf.show_render, last_subsurf.show_viewport))
+ last_subsurf.show_render = False
+ last_subsurf.show_viewport = False
if not use_org_data:
# If modifiers has been altered need to update dependency graph.
- if backup_pose_positions:
+ if backup_pose_positions or tmp_mods:
depsgraph.update()
ob_to_convert = ob.evaluated_get(depsgraph) if settings.use_mesh_modifiers else ob
# NOTE: The dependency graph might be re-evaluating multiple times, which could
@@ -2249,6 +2307,11 @@ def fbx_data_from_scene(scene, depsgraph, settings):
print((armature, pose_position))
armature.pose_position = pose_position
# Update now, so we don't leave modified state after last object was exported.
+ # Re-enable temporary disabled modifiers.
+ for mod, show_render, show_viewport in tmp_mods:
+ mod.show_render = show_render
+ mod.show_viewport = show_viewport
+ if backup_pose_positions or tmp_mods:
depsgraph.update()
if use_org_data:
data_meshes[ob_obj] = (get_blenderID_key(ob.data), ob.data, False)
@@ -2913,6 +2976,7 @@ def save_single(operator, scene, depsgraph, filepath="",
use_mesh_modifiers=True,
use_mesh_modifiers_render=True,
mesh_smooth_type='FACE',
+ use_subsurf=False,
use_armature_deform_only=False,
bake_anim=True,
bake_anim_use_all_bones=True,
@@ -2994,7 +3058,7 @@ def save_single(operator, scene, depsgraph, filepath="",
operator.report, (axis_up, axis_forward), global_matrix, global_scale, apply_unit_scale, unit_scale,
bake_space_transform, global_matrix_inv, global_matrix_inv_transposed,
context_objects, object_types, use_mesh_modifiers, use_mesh_modifiers_render,
- mesh_smooth_type, use_mesh_edges, use_tspace,
+ mesh_smooth_type, use_subsurf, use_mesh_edges, use_tspace,
armature_nodetype, use_armature_deform_only,
add_leaf_bones, bone_correction_matrix, bone_correction_matrix_inv,
bake_anim, bake_anim_use_all_bones, bake_anim_use_nla_strips, bake_anim_use_all_actions,
@@ -3066,6 +3130,7 @@ def defaults_unity3d():
"use_mesh_modifiers_render": True,
"use_mesh_edges": False,
"mesh_smooth_type": 'FACE',
+ "use_subsurf": False,
"use_tspace": False, # XXX Why? Unity is expected to support tspace import...
"use_armature_deform_only": True,
diff --git a/io_scene_fbx/fbx_utils.py b/io_scene_fbx/fbx_utils.py
index 19f32800..a496075d 100644
--- a/io_scene_fbx/fbx_utils.py
+++ b/io_scene_fbx/fbx_utils.py
@@ -51,6 +51,7 @@ FBX_GEOMETRY_NORMAL_VERSION = 101
FBX_GEOMETRY_BINORMAL_VERSION = 101
FBX_GEOMETRY_TANGENT_VERSION = 101
FBX_GEOMETRY_SMOOTHING_VERSION = 102
+FBX_GEOMETRY_CREASE_VERSION = 101
FBX_GEOMETRY_VCOLOR_VERSION = 101
FBX_GEOMETRY_UV_VERSION = 101
FBX_GEOMETRY_MATERIAL_VERSION = 101
@@ -1215,7 +1216,7 @@ FBXExportSettings = namedtuple("FBXExportSettings", (
"report", "to_axes", "global_matrix", "global_scale", "apply_unit_scale", "unit_scale",
"bake_space_transform", "global_matrix_inv", "global_matrix_inv_transposed",
"context_objects", "object_types", "use_mesh_modifiers", "use_mesh_modifiers_render",
- "mesh_smooth_type", "use_mesh_edges", "use_tspace",
+ "mesh_smooth_type", "use_subsurf", "use_mesh_edges", "use_tspace",
"armature_nodetype", "use_armature_deform_only", "add_leaf_bones",
"bone_correction_matrix", "bone_correction_matrix_inv",
"bake_anim", "bake_anim_use_all_bones", "bake_anim_use_nla_strips", "bake_anim_use_all_actions",
@@ -1245,6 +1246,7 @@ FBXImportSettings = namedtuple("FBXImportSettings", (
"use_custom_normals", "use_image_search",
"use_alpha_decals", "decal_offset",
"use_anim", "anim_offset",
+ "use_subsurf",
"use_custom_props", "use_custom_props_enum_as_string",
"nodal_material_wrap_map", "image_cache",
"ignore_leaf_bones", "force_connect_children", "automatic_bone_orientation", "bone_correction_matrix",
diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index d94a237c..90c27e5b 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -1112,6 +1112,45 @@ def blen_read_geom_layer_smooth(fbx_obj, mesh):
print("warning layer %r mapping type unsupported: %r" % (fbx_layer.id, fbx_layer_mapping))
return False
+def blen_read_geom_layer_edge_crease(fbx_obj, mesh):
+ fbx_layer = elem_find_first(fbx_obj, b'LayerElementEdgeCrease')
+
+ if fbx_layer is None:
+ return False
+
+ # all should be valid
+ (fbx_layer_name,
+ fbx_layer_mapping,
+ fbx_layer_ref,
+ ) = blen_read_geom_layerinfo(fbx_layer)
+
+ if fbx_layer_mapping != b'ByEdge':
+ return False
+
+ layer_id = b'EdgeCrease'
+ fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id))
+
+ # some models have bad edge data, we cant use this info...
+ if not mesh.edges:
+ print("warning skipping edge crease data, no valid edges...")
+ return False
+
+ if fbx_layer_mapping == b'ByEdge':
+ # some models have bad edge data, we cant use this info...
+ if not mesh.edges:
+ print("warning skipping edge crease data, no valid edges...")
+ return False
+
+ blen_data = mesh.edges
+ return blen_read_geom_array_mapped_edge(
+ mesh, blen_data, "crease",
+ fbx_layer_data, None,
+ fbx_layer_mapping, fbx_layer_ref,
+ 1, 1, layer_id,
+ )
+ else:
+ print("warning layer %r mapping type unsupported: %r" % (fbx_layer.id, fbx_layer_mapping))
+ return False
def blen_read_geom_layer_normal(fbx_obj, mesh, xform=None):
fbx_layer = elem_find_first(fbx_obj, b'LayerElementNormal')
@@ -1243,6 +1282,8 @@ def blen_read_geom(fbx_tmpl, fbx_obj, settings):
# must be after edge, face loading.
ok_smooth = blen_read_geom_layer_smooth(fbx_obj, mesh)
+ ok_crease = blen_read_geom_layer_edge_crease(fbx_obj, mesh)
+
ok_normals = False
if settings.use_custom_normals:
# Note: we store 'temp' normals in loops, since validate() may alter final mesh,
@@ -1276,6 +1317,9 @@ def blen_read_geom(fbx_tmpl, fbx_obj, settings):
if not ok_smooth:
mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons))
+ if ok_crease:
+ mesh.use_customdata_edge_crease = True
+
if settings.use_custom_props:
blen_read_custom_properties(fbx_obj, mesh, settings)
@@ -2270,6 +2314,7 @@ def load(operator, context, filepath="",
decal_offset=0.0,
use_anim=True,
anim_offset=1.0,
+ use_subsurf=False,
use_custom_props=True,
use_custom_props_enum_as_string=True,
ignore_leaf_bones=False,
@@ -2398,6 +2443,7 @@ def load(operator, context, filepath="",
use_custom_normals, use_image_search,
use_alpha_decals, decal_offset,
use_anim, anim_offset,
+ use_subsurf,
use_custom_props, use_custom_props_enum_as_string,
nodal_material_wrap_map, image_cache,
ignore_leaf_bones, force_connect_children, automatic_bone_orientation, bone_correction_matrix,
@@ -2799,6 +2845,35 @@ def load(operator, context, filepath="",
blend_shape_channels[bc_uuid] = keyblocks
_(); del _
+ if settings.use_subsurf:
+ perfmon.step("FBX import: Subdivision surfaces")
+
+ # Look through connections for subsurf in meshes and add it to the parent object
+ def _():
+ for fbx_link in fbx_connections.elems:
+ if fbx_link.props[0] != b'OO':
+ continue
+ if fbx_link.props_type[1:3] == b'LL':
+ c_src, c_dst = fbx_link.props[1:3]
+ parent = fbx_helper_nodes.get(c_dst)
+ if parent is None:
+ continue
+
+ child = fbx_helper_nodes.get(c_src)
+ if child is None:
+ fbx_sdata, bl_data = fbx_table_nodes.get(c_src, (None, None))
+ if fbx_sdata.id != b'Geometry':
+ continue
+
+ preview_levels = elem_prop_first(elem_find_first(fbx_sdata, b'PreviewDivisionLevels'))
+ render_levels = elem_prop_first(elem_find_first(fbx_sdata, b'RenderDivisionLevels'))
+ if isinstance(preview_levels, int) and isinstance(render_levels, int):
+ mod = parent.bl_obj.modifiers.new('subsurf', 'SUBSURF')
+ mod.levels = preview_levels
+ mod.render_levels = render_levels
+
+ _(); del _
+
if use_anim:
perfmon.step("FBX import: Animations...")