Speed up FBX export of normals/tangents/bitangents with numpy #104449
@ -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)
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user