diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py index 16188eee7..6eacdbbdd 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 ( units_blender_to_fbx_factor, units_convertor, units_convertor_iter, matrix4_to_array, similar_values, similar_values_iter, astype_view_signedness, fast_first_axis_unique, # Mesh transform helpers. - vcos_transformed_gen, nors_transformed_gen, vcos_transformed, nors_transformed, + vcos_transformed_gen, vcos_transformed, nors_transformed, # UUID from key. get_fbx_uuid_from_key, # Key generators. @@ -1033,28 +1033,35 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): # but this does not seem well supported by apps currently... me.calc_normals_split() - t_ln = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * len(me.loops) * 3 + ln_bl_dtype = np.single + ln_fbx_dtype = np.float64 + t_ln = np.empty(len(me.loops) * 3, dtype=ln_bl_dtype) me.loops.foreach_get("normal", t_ln) - t_ln = nors_transformed_gen(t_ln, geom_mat_no) + t_ln = nors_transformed(t_ln, geom_mat_no, ln_fbx_dtype) if 0: - t_ln = tuple(t_ln) # No choice... :/ - + lnidx_fbx_dtype = np.int32 lay_nor = elem_data_single_int32(geom, b"LayerElementNormal", 0) elem_data_single_int32(lay_nor, b"Version", FBX_GEOMETRY_NORMAL_VERSION) elem_data_single_string(lay_nor, b"Name", b"") elem_data_single_string(lay_nor, b"MappingInformationType", b"ByPolygonVertex") elem_data_single_string(lay_nor, b"ReferenceInformationType", b"IndexToDirect") - ln2idx = tuple(set(t_ln)) - elem_data_single_float64_array(lay_nor, b"Normals", chain(*ln2idx)) + # Tuple of unique sorted normals and then the index in the unique sorted normals of each normal in t_ln. + # Since we don't care about how the normals are sorted, only that they're unique, we can use the fast unique + # helper function. + t_ln, t_lnidx = fast_first_axis_unique(t_ln.reshape(-1, 3), return_inverse=True) + + # Convert to the type for fbx + t_lnidx = astype_view_signedness(t_lnidx, lnidx_fbx_dtype) + + elem_data_single_float64_array(lay_nor, b"Normals", t_ln) # Normal weights, no idea what it is. - # t_lnw = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * len(ln2idx) + # t_lnw = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * len(t_ln) # elem_data_single_float64_array(lay_nor, b"NormalsW", t_lnw) - ln2idx = {nor: idx for idx, nor in enumerate(ln2idx)} - elem_data_single_int32_array(lay_nor, b"NormalsIndex", (ln2idx[n] for n in t_ln)) + elem_data_single_int32_array(lay_nor, b"NormalsIndex", t_lnidx) - del ln2idx + del t_lnidx # del t_lnw else: lay_nor = elem_data_single_int32(geom, b"LayerElementNormal", 0) @@ -1062,7 +1069,7 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): elem_data_single_string(lay_nor, b"Name", b"") elem_data_single_string(lay_nor, b"MappingInformationType", b"ByPolygonVertex") elem_data_single_string(lay_nor, b"ReferenceInformationType", b"Direct") - elem_data_single_float64_array(lay_nor, b"Normals", chain(*t_ln)) + elem_data_single_float64_array(lay_nor, b"Normals", t_ln) # Normal weights, no idea what it is. # t_ln = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * len(me.loops) # elem_data_single_float64_array(lay_nor, b"NormalsW", t_ln) @@ -1073,9 +1080,10 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): tspacenumber = len(me.uv_layers) if tspacenumber: # We can only compute tspace on tessellated meshes, need to check that here... - t_lt = [None] * len(me.polygons) + lt_bl_dtype = np.uintc + t_lt = np.empty(len(me.polygons), dtype=lt_bl_dtype) me.polygons.foreach_get("loop_total", t_lt) - if any((lt > 4 for lt in t_lt)): + if (t_lt > 4).any(): del t_lt scene_data.settings.report( {'WARNING'}, @@ -1084,7 +1092,7 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): else: del t_lt num_loops = len(me.loops) - t_ln = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * num_loops * 3 + t_ln = np.empty(num_loops * 3, dtype=ln_bl_dtype) # t_lnw = array.array(data_types.ARRAY_FLOAT64, (0.0,)) * len(me.loops) uv_names = [uvlayer.name for uvlayer in me.uv_layers] # Annoying, `me.calc_tangent` errors in case there is no geometry... @@ -1102,7 +1110,7 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): elem_data_single_string(lay_nor, b"MappingInformationType", b"ByPolygonVertex") elem_data_single_string(lay_nor, b"ReferenceInformationType", b"Direct") elem_data_single_float64_array(lay_nor, b"Binormals", - chain(*nors_transformed_gen(t_ln, geom_mat_no))) + nors_transformed(t_ln, geom_mat_no, ln_fbx_dtype)) # Binormal weights, no idea what it is. # elem_data_single_float64_array(lay_nor, b"BinormalsW", t_lnw) @@ -1115,7 +1123,7 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): elem_data_single_string(lay_nor, b"MappingInformationType", b"ByPolygonVertex") elem_data_single_string(lay_nor, b"ReferenceInformationType", b"Direct") elem_data_single_float64_array(lay_nor, b"Tangents", - chain(*nors_transformed_gen(t_ln, geom_mat_no))) + nors_transformed(t_ln, geom_mat_no, ln_fbx_dtype)) # Tangent weights, no idea what it is. # elem_data_single_float64_array(lay_nor, b"TangentsW", t_lnw) diff --git a/io_scene_fbx/fbx_utils.py b/io_scene_fbx/fbx_utils.py index 9698a3609..816e6b731 100644 --- a/io_scene_fbx/fbx_utils.py +++ b/io_scene_fbx/fbx_utils.py @@ -265,13 +265,6 @@ def vcos_transformed_gen(raw_cos, m=None): gen = zip(*(iter(raw_cos),) * 3) return gen if m is None else (m @ Vector(v) for v in gen) -def nors_transformed_gen(raw_nors, m=None): - # Great, now normals are also expected 4D! - # XXX Back to 3D normals for now! - # gen = zip(*(iter(raw_nors),) * 3 + (_infinite_gen(1.0),)) - gen = zip(*(iter(raw_nors),) * 3) - return gen if m is None else (m @ Vector(v) for v in gen) - def _mat4_vec3_array_multiply(mat4, vec3_array, dtype=None, return_4d=False): """Multiply a 4d matrix by each 3d vector in an array and return as an array of either 3d or 4d vectors.