FBX IO: Material index access with attributes #104646

Merged
Bastien Montagne merged 1 commits from Mysteryem/blender-addons:attribute_access_material_index_pr into main 2023-06-26 13:05:47 +02:00
2 changed files with 24 additions and 10 deletions

View File

@ -1423,11 +1423,13 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
elem_data_single_int32(lay_ma, b"Version", FBX_GEOMETRY_MATERIAL_VERSION) elem_data_single_int32(lay_ma, b"Version", FBX_GEOMETRY_MATERIAL_VERSION)
elem_data_single_string(lay_ma, b"Name", b"") elem_data_single_string(lay_ma, b"Name", b"")
nbr_mats = len(me_fbxmaterials_idx) nbr_mats = len(me_fbxmaterials_idx)
if nbr_mats > 1: multiple_fbx_mats = nbr_mats > 1
bl_pm_dtype = np.uintc # If a mesh does not have more than one material its material_index attribute can be ignored.
# If a mesh has multiple materials but all its polygons are assigned to the first material, its
# material_index attribute may not exist.
t_pm = None if not multiple_fbx_mats else MESH_ATTRIBUTE_MATERIAL_INDEX.get_ndarray(attributes)
if t_pm is not None:
fbx_pm_dtype = np.int32 fbx_pm_dtype = np.int32
t_pm = np.empty(len(me.polygons), dtype=bl_pm_dtype)
me.polygons.foreach_get("material_index", t_pm)
# We have to validate mat indices, and map them to FBX indices. # We have to validate mat indices, and map them to FBX indices.
# Note a mat might not be in me_fbxmaterials_idx (e.g. node mats are ignored). # Note a mat might not be in me_fbxmaterials_idx (e.g. node mats are ignored).
@ -1439,7 +1441,10 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
# Set material indices that are out of bounds to the default material index # Set material indices that are out of bounds to the default material index
mat_idx_limit = len(me_blmaterials) mat_idx_limit = len(me_blmaterials)
t_pm[t_pm >= mat_idx_limit] = def_me_blmaterial_idx # Material indices shouldn't be negative, but they technically could be. Viewing as unsigned before
# checking for indices that are too large means that a single >= check will pick up both negative
# indices and indices that are too large.
t_pm[t_pm.view("u%i" % t_pm.itemsize) >= mat_idx_limit] = def_me_blmaterial_idx
# Map to FBX indices. Materials not in me_fbxmaterials_idx will be set to the default material index. # Map to FBX indices. Materials not in me_fbxmaterials_idx will be set to the default material index.
blmat_fbx_idx = np.fromiter((me_fbxmaterials_idx.get(m, def_ma) for m in me_blmaterials), blmat_fbx_idx = np.fromiter((me_fbxmaterials_idx.get(m, def_ma) for m in me_blmaterials),
@ -1453,11 +1458,18 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
# indices??? *sigh*). # indices??? *sigh*).
elem_data_single_string(lay_ma, b"ReferenceInformationType", b"IndexToDirect") elem_data_single_string(lay_ma, b"ReferenceInformationType", b"IndexToDirect")
elem_data_single_int32_array(lay_ma, b"Materials", t_pm) elem_data_single_int32_array(lay_ma, b"Materials", t_pm)
del t_pm
else: else:
elem_data_single_string(lay_ma, b"MappingInformationType", b"AllSame") elem_data_single_string(lay_ma, b"MappingInformationType", b"AllSame")
elem_data_single_string(lay_ma, b"ReferenceInformationType", b"IndexToDirect") elem_data_single_string(lay_ma, b"ReferenceInformationType", b"IndexToDirect")
elem_data_single_int32_array(lay_ma, b"Materials", [0]) if multiple_fbx_mats:
# There's no material_index attribute, so every material index is effectively zero.
# In the order of the mesh's materials, get the FBX index of the first material that is exported.
all_same_idx = next(me_fbxmaterials_idx[m] for m in me_blmaterials if m in me_fbxmaterials_idx)
else:
# There's only one fbx material, so the index will always be zero.
all_same_idx = 0
elem_data_single_int32_array(lay_ma, b"Materials", [all_same_idx])
del t_pm
# And the "layer TOC"... # And the "layer TOC"...

View File

@ -1205,12 +1205,14 @@ def blen_read_geom_layer_material(fbx_obj, mesh):
layer_id = b'Materials' layer_id = b'Materials'
fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id)) fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id))
blen_data = mesh.polygons blen_data = MESH_ATTRIBUTE_MATERIAL_INDEX.ensure(mesh.attributes).data
fbx_item_size = 1
assert(fbx_item_size == MESH_ATTRIBUTE_MATERIAL_INDEX.item_size)
blen_read_geom_array_mapped_polygon( blen_read_geom_array_mapped_polygon(
mesh, blen_data, "material_index", np.uintc, mesh, blen_data, MESH_ATTRIBUTE_MATERIAL_INDEX.foreach_attribute, MESH_ATTRIBUTE_MATERIAL_INDEX.dtype,
fbx_layer_data, None, fbx_layer_data, None,
fbx_layer_mapping, fbx_layer_ref, fbx_layer_mapping, fbx_layer_ref,
1, 1, layer_id, 1, fbx_item_size, layer_id,
) )