update custom normals importing

This commit is contained in:
yyc12345 2023-11-14 22:16:12 +08:00
parent fd17d3b5c9
commit d128ffcde5

View File

@ -93,10 +93,14 @@ def _flat_face_uv_index(uv_idx: array.array, uv_array: array.array) -> typing.It
yield uv_array[pos] yield uv_array[pos]
yield uv_array[pos + 1] yield uv_array[pos + 1]
def _nest_custom_split_normal(nml_idx: array.array, nml_array: array.array) -> typing.Iterator[UTIL_virtools_types.ConstVxVector3]: def _nest_custom_split_normal(nml_array: array.array) -> typing.Iterator[UTIL_virtools_types.ConstVxVector3]:
for idx in nml_idx: # following statement create a iterator for normals array by `iter(nml_array)`
pos: int = idx * 3 # then triple it, because iterator is a reference type, so 3 items of this tuple is pointed to the same iterator and share the same iteration progress.
yield (nml_array[pos], nml_array[pos + 1], nml_array[pos + 2]) # then use star macro to pass it to zip, it will cause zip receive 3 params pointing to the same iterator.
# now zip() will call 3 params __next__() function from left to right.
# zip will get following iteration result because all iterator are the same one: (0, 1, 2), (3, 4, 5) and etc (number is index to corresponding value).
# finally, use tuple to expand it to a tuple, not a generator.
return tuple(zip(*(iter(nml_array), ) * 3))
class TemporaryMesh(): class TemporaryMesh():
""" """
@ -451,21 +455,21 @@ class MeshWriter():
self.__mAssocMesh.loops.foreach_set('vertex_index', self.__mFacePosIndices) self.__mAssocMesh.loops.foreach_set('vertex_index', self.__mFacePosIndices)
# add face vertex nml by function # add face vertex nml by function
self.__mAssocMesh.loops.foreach_set('normal', self.__mAssocMesh.loops.foreach_set('normal',
list(_flat_face_nml_index(self.__mFaceNmlIndices, self.__mVertexNormal)) tuple(_flat_face_nml_index(self.__mFaceNmlIndices, self.__mVertexNormal))
) )
# add face vertex uv by function # add face vertex uv by function
self.__mAssocMesh.uv_layers.active.uv.foreach_set('vector', self.__mAssocMesh.uv_layers.active.uv.foreach_set('vector',
list(_flat_face_uv_index(self.__mFaceUvIndices, self.__mVertexUV)) tuple(_flat_face_uv_index(self.__mFaceUvIndices, self.__mVertexUV))
) # NOTE: blender 3.5 changed. UV must be visited by .uv, not the .data ) # NOTE: blender 3.5 changed. UV must be visited by .uv, not the .data
# iterate face to set face data # iterate face to set face data
fVertexIdx: int = 0 f_vertex_idx: int = 0
for fi in range(len(self.__mFaceVertexCount)): for fi in range(len(self.__mFaceVertexCount)):
# set start loop # set start loop
# NOTE: blender 3.6 changed. Loop setting in polygon do not need set loop_total any more. # NOTE: blender 3.6 changed. Loop setting in polygon do not need set loop_total any more.
# the loop_total will be auto calculated by the next loop_start. # the loop_total will be auto calculated by the next loop_start.
# loop_total become read-only # loop_total become read-only
self.__mAssocMesh.polygons[fi].loop_start = fVertexIdx self.__mAssocMesh.polygons[fi].loop_start = f_vertex_idx
# set material index # set material index
self.__mAssocMesh.polygons[fi].material_index = self.__mFaceMtlIdx[fi] self.__mAssocMesh.polygons[fi].material_index = self.__mFaceMtlIdx[fi]
@ -475,7 +479,7 @@ class MeshWriter():
self.__mAssocMesh.polygons[fi].use_smooth = True self.__mAssocMesh.polygons[fi].use_smooth = True
# inc vertex idx # inc vertex idx
fVertexIdx += self.__mFaceVertexCount[fi] f_vertex_idx += self.__mFaceVertexCount[fi]
# validate mesh. # validate mesh.
# it is IMPORTANT that do NOT delete custom data # it is IMPORTANT that do NOT delete custom data
@ -485,17 +489,19 @@ class MeshWriter():
self.__mAssocMesh.update(calc_edges = False, calc_edges_loose = False) self.__mAssocMesh.update(calc_edges = False, calc_edges_loose = False)
# set custom split normal data # set custom split normal data
# if the validate() change the mesh, skip this and output error. # this operation must copy preserved normal data from loops, not the array data in this class,
# this change is detected by the loops count changes, not the return value of validate(). because only the changes of loops can let following throw errors. # because the validate() may change the mesh and if change happended, an error will occur when applying normals (not matched loops count).
# this should not happend in normal case, just a stupid patch for "线框Level1.Level.NMO" loading. # this should not happend in normal case, for testing, please load "Level_1.NMO" (Ballance Level 1).
if len(self.__mAssocMesh.loops) == len(self.__mFacePosIndices):
# copy data from loops preserved in validate().
loops_normals = array.array('f', [0.0] * (len(self.__mAssocMesh.loops) * 3))
self.__mAssocMesh.loops.foreach_get('normal', loops_normals)
# apply data
self.__mAssocMesh.normals_split_custom_set( self.__mAssocMesh.normals_split_custom_set(
tuple(_nest_custom_split_normal(self.__mFaceNmlIndices, self.__mVertexNormal)) tuple(_nest_custom_split_normal(loops_normals))
) )
# enable auto smooth. it is IMPORTANT # enable auto smooth. it is IMPORTANT
self.__mAssocMesh.use_auto_smooth = True self.__mAssocMesh.use_auto_smooth = True
else:
print(f'The custom normals of mesh "{self.__mAssocMesh.name}" can not be assigned because validate() changes the mesh. Check your mesh data first!')
def __clear_mesh(self): def __clear_mesh(self):
if not self.is_valid(): if not self.is_valid():