diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py index feea84369b476aa80e3a2fcc317940c04faf9119..185e8336d42eb100dcce82e514afdd74e88a4791 100644 --- a/io_scene_fbx/__init__.py +++ b/io_scene_fbx/__init__.py @@ -3,8 +3,8 @@ bl_info = { "name": "FBX format", "author": "Campbell Barton, Bastien Montagne, Jens Restemeier", - "version": (4, 36, 3), - "blender": (3, 2, 0), + "version": (4, 37, 0), + "blender": (3, 4, 0), "location": "File > Import-Export", "description": "FBX IO meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions", "warning": "", @@ -89,6 +89,15 @@ class ImportFBX(bpy.types.Operator, ImportHelper): description="Import custom normals, if available (otherwise Blender will recompute them)", default=True, ) + colors_type: EnumProperty( + name="Vertex Colors", + items=(('NONE', "None", "Do not import color attributes"), + ('SRGB', "sRGB", "Expect file colors in sRGB color space"), + ('LINEAR', "Linear", "Expect file colors in linear color space"), + ), + description="Import vertex color attributes", + default='SRGB', + ) use_image_search: BoolProperty( name="Image Search", @@ -230,6 +239,7 @@ class FBX_PT_import_include(bpy.types.Panel): sub.enabled = operator.use_custom_props sub.prop(operator, "use_custom_props_enum_as_string") layout.prop(operator, "use_image_search") + layout.prop(operator, "colors_type") class FBX_PT_import_transform(bpy.types.Panel): @@ -463,6 +473,15 @@ class ExportFBX(bpy.types.Operator, ExportHelper): "(prefer 'Normals Only' option if your target importer understand split normals)", default='OFF', ) + colors_type: EnumProperty( + name="Vertex Colors", + items=(('NONE', "None", "Do not export color attributes"), + ('SRGB', "sRGB", "Export colors in sRGB color space"), + ('LINEAR', "Linear", "Export colors in linear color space"), + ), + description="Export vertex color attributes", + default='SRGB', + ) use_subsurf: BoolProperty( name="Export Subdivision Surface", description="Export the last Catmull-Rom subdivision modifier as FBX subdivision " @@ -767,6 +786,7 @@ class FBX_PT_export_geometry(bpy.types.Panel): sub = layout.row() #~ sub.enabled = operator.mesh_smooth_type in {'OFF'} sub.prop(operator, "use_tspace") + layout.prop(operator, "colors_type") class FBX_PT_export_armature(bpy.types.Panel): diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py index f560b19c98bd2aa58008d4b5bec71b3033df184c..a4a96bf54c846c0c2f14e93d9ae813f9b722ee66 100644 --- a/io_scene_fbx/export_fbx_bin.py +++ b/io_scene_fbx/export_fbx_bin.py @@ -1111,14 +1111,20 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): me.free_normals_split() # Write VertexColor Layers. - vcolnumber = len(me.vertex_colors) + colors_type = scene_data.settings.colors_type + vcolnumber = 0 if colors_type == 'NONE' else len(me.color_attributes) if vcolnumber: def _coltuples_gen(raw_cols): return zip(*(iter(raw_cols),) * 4) - t_lc = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * len(me.loops) * 4 - for colindex, collayer in enumerate(me.vertex_colors): - collayer.data.foreach_get("color", t_lc) + color_prop_name = "color_srgb" if colors_type == 'SRGB' else "color" + + for colindex, collayer in enumerate(me.color_attributes): + is_point = collayer.domain == "POINT" + vcollen = len(me.vertices if is_point else me.loops) + t_lc = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * vcollen * 4 + collayer.data.foreach_get(color_prop_name, t_lc) + lay_vcol = elem_data_single_int32(geom, b"LayerElementColor", colindex) elem_data_single_int32(lay_vcol, b"Version", FBX_GEOMETRY_VCOLOR_VERSION) elem_data_single_string_unicode(lay_vcol, b"Name", collayer.name) @@ -1129,9 +1135,16 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): elem_data_single_float64_array(lay_vcol, b"Colors", chain(*col2idx)) # Flatten again... col2idx = {col: idx for idx, col in enumerate(col2idx)} - elem_data_single_int32_array(lay_vcol, b"ColorIndex", (col2idx[c] for c in _coltuples_gen(t_lc))) + col_indices = list(col2idx[c] for c in _coltuples_gen(t_lc)) + if is_point: + # for "point" domain colors, we could directly emit them + # with a "ByVertex" mapping type, but some software does not + # properly understand that. So expand to full "ByPolygonVertex" + # index map. + col_indices = list((col_indices[c.vertex_index] for c in me.loops)) + elem_data_single_int32_array(lay_vcol, b"ColorIndex", col_indices) del col2idx - del t_lc + del t_lc del _coltuples_gen # Write UV layers. @@ -3021,6 +3034,7 @@ def save_single(operator, scene, depsgraph, filepath="", use_custom_props=False, bake_space_transform=False, armature_nodetype='NULL', + colors_type='SRGB', **kwargs ): @@ -3088,7 +3102,7 @@ def save_single(operator, scene, depsgraph, filepath="", 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, bake_anim_step, bake_anim_simplify_factor, bake_anim_force_startend_keying, - False, media_settings, use_custom_props, + False, media_settings, use_custom_props, colors_type, ) import bpy_extras.io_utils @@ -3155,6 +3169,7 @@ def defaults_unity3d(): "use_mesh_modifiers_render": True, "use_mesh_edges": False, "mesh_smooth_type": 'FACE', + "colors_type": 'SRGB', "use_subsurf": False, "use_tspace": False, # XXX Why? Unity is expected to support tspace import... "use_triangles": False, diff --git a/io_scene_fbx/fbx_utils.py b/io_scene_fbx/fbx_utils.py index 41c63ef8e3ab0bee2f8f96b7242062cb3cd80e50..e52cd9eb141bf5fb78b88fb82b45ce7943bdaf09 100644 --- a/io_scene_fbx/fbx_utils.py +++ b/io_scene_fbx/fbx_utils.py @@ -1220,7 +1220,7 @@ FBXExportSettings = namedtuple("FBXExportSettings", ( "bone_correction_matrix", "bone_correction_matrix_inv", "bake_anim", "bake_anim_use_all_bones", "bake_anim_use_nla_strips", "bake_anim_use_all_actions", "bake_anim_step", "bake_anim_simplify_factor", "bake_anim_force_startend_keying", - "use_metadata", "media_settings", "use_custom_props", + "use_metadata", "media_settings", "use_custom_props", "colors_type", )) # Helper container gathering some data we need multiple times: @@ -1249,5 +1249,5 @@ FBXImportSettings = namedtuple("FBXImportSettings", ( "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", - "use_prepost_rot", + "use_prepost_rot", "colors_type", )) diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py index 72d666c1c4396762f1de576f9e9f399fbc240166..00376f38c925814c95bcee35f40af6dcb4ed0eda 100644 --- a/io_scene_fbx/import_fbx.py +++ b/io_scene_fbx/import_fbx.py @@ -1061,7 +1061,12 @@ def blen_read_geom_layer_uv(fbx_obj, mesh): ) -def blen_read_geom_layer_color(fbx_obj, mesh): +def blen_read_geom_layer_color(fbx_obj, mesh, colors_type): + if colors_type == 'NONE': + return + use_srgb = colors_type == 'SRGB' + layer_type = 'BYTE_COLOR' if use_srgb else 'FLOAT_COLOR' + color_prop_name = "color_srgb" if use_srgb else "color" # almost same as UV's for layer_id in (b'LayerElementColor',): for fbx_layer in elem_find_iter(fbx_obj, layer_id): @@ -1074,8 +1079,7 @@ def blen_read_geom_layer_color(fbx_obj, mesh): fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, b'Colors')) fbx_layer_index = elem_prop_first(elem_find_first(fbx_layer, b'ColorIndex')) - # Always init our new layers with full white opaque color. - color_lay = mesh.vertex_colors.new(name=fbx_layer_name, do_init=False) + color_lay = mesh.color_attributes.new(name=fbx_layer_name, type=layer_type, domain='CORNER') if color_lay is None: print("Failed to add {%r %r} vertex color layer to %r (probably too many of them?)" @@ -1090,7 +1094,7 @@ def blen_read_geom_layer_color(fbx_obj, mesh): continue blen_read_geom_array_mapped_polyloop( - mesh, blen_data, "color", + mesh, blen_data, color_prop_name, fbx_layer_data, fbx_layer_index, fbx_layer_mapping, fbx_layer_ref, 4, 4, layer_id, @@ -1289,7 +1293,7 @@ def blen_read_geom(fbx_tmpl, fbx_obj, settings): blen_read_geom_layer_material(fbx_obj, mesh) blen_read_geom_layer_uv(fbx_obj, mesh) - blen_read_geom_layer_color(fbx_obj, mesh) + blen_read_geom_layer_color(fbx_obj, mesh, settings.colors_type) if fbx_edges: # edges in fact index the polygons (NOT the vertices) @@ -2365,7 +2369,8 @@ def load(operator, context, filepath="", automatic_bone_orientation=False, primary_bone_axis='Y', secondary_bone_axis='X', - use_prepost_rot=True): + use_prepost_rot=True, + colors_type='SRGB'): global fbx_elem_nil fbx_elem_nil = FBXElem('', (), (), ()) @@ -2504,7 +2509,7 @@ def load(operator, context, filepath="", 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, - use_prepost_rot, + use_prepost_rot, colors_type, ) # #### And now, the "real" data.