Skip to content

Commit 677cc6e

Browse files
authored
Merge pull request #335 from isHarryh/patch-mesh-2
Refactor MeshHelper
2 parents 536ec78 + 02a01bc commit 677cc6e

1 file changed

Lines changed: 49 additions & 63 deletions

File tree

UnityPy/helpers/MeshHelper.py

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import math
44
import struct
5-
from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple, TypeVar, Union, cast
5+
from typing import TYPE_CHECKING, List, Optional, Tuple, Union, cast
66

77
from ..classes.generated import (
88
ChannelInfo,
@@ -34,12 +34,6 @@
3434
Tuple3f = Tuple[float, float, float]
3535
Tuple4f = Tuple[float, float, float, float]
3636

37-
T = TypeVar("T")
38-
39-
40-
def flat_list_to_tuples(data: Sequence[T], item_size: int) -> List[tuple[T, ...]]:
41-
return [tuple(data[i : i + item_size]) for i in range(0, len(data), item_size)]
42-
4337

4438
def vector_list_to_tuples(
4539
data: Union[List[Vector2f], List[Vector3f], List[Vector4f]],
@@ -58,14 +52,12 @@ def vector_list_to_tuples(
5852
raise ValueError("Unknown vector type")
5953

6054

61-
def zeros(shape: Union[Tuple[int], Tuple[int, int]]) -> Union[List, List[List]]:
62-
if len(shape) == 1:
63-
return [0] * shape[0]
64-
elif len(shape) == 2:
65-
m, n = shape
66-
return [[0] * n for _ in range(m)]
67-
else:
68-
raise ValueError("Invalid shape")
55+
def lists_to_tuples(data: List[list]) -> List[tuple]:
56+
return [tuple(v) for v in data]
57+
58+
59+
def zeros(m: int, n: int) -> List[list]:
60+
return [[0] * n for _ in range(m)]
6961

7062

7163
def normalize(*vector: float) -> Tuple[float, ...]:
@@ -234,21 +226,12 @@ def copy_from_mesh(self):
234226

235227
if self.m_BoneWeights is None and mesh.m_Skin:
236228
# BoneInfluence == BoneWeight in terms of usage in UnityPy due to int simplification
237-
self.m_BoneWeights = zeros((len(mesh.m_Skin), 4))
238-
self.m_BoneIndices = zeros((len(mesh.m_Skin), 4))
239-
for skin, indices, weights in zip(mesh.m_Skin, self.m_BoneIndices, self.m_BoneWeights):
240-
indices[:] = [
241-
skin.boneIndex_0_,
242-
skin.boneIndex_1_,
243-
skin.boneIndex_2_,
244-
skin.boneIndex_3_,
245-
]
246-
weights[:] = [
247-
skin.weight_0_,
248-
skin.weight_1_,
249-
skin.weight_2_,
250-
skin.weight_3_,
251-
]
229+
self.m_BoneIndices = [
230+
(skin.boneIndex_0_, skin.boneIndex_1_, skin.boneIndex_2_, skin.boneIndex_3_) for skin in mesh.m_Skin
231+
]
232+
self.m_BoneWeights = [
233+
(skin.weight_0_, skin.weight_1_, skin.weight_2_, skin.weight_3_) for skin in mesh.m_Skin
234+
]
252235

253236
def copy_from_spriterenderdata(self):
254237
rd = self.src
@@ -401,10 +384,7 @@ def read_vertex_data(self, m_Channels: list[ChannelInfo], m_Streams: list[Stream
401384
buff = buff[::-1]
402385
componentBytes[componentDataSrc : componentDataSrc + component_byte_size] = buff
403386

404-
count = len(componentBytes) // component_byte_size
405-
component_data = struct.unpack(f">{count}{component_dtype}", componentBytes)
406-
component_data = flat_list_to_tuples(component_data, channel_dimension)
407-
387+
component_data = list(struct.iter_unpack(f">{channel_dimension}{component_dtype}", componentBytes))
408388
self.assign_channel_vertex_data(chn, component_data)
409389

410390
def assign_channel_vertex_data(self, channel: int, component_data: list):
@@ -483,10 +463,11 @@ def get_channel_component_size(self, m_Channel: ChannelInfo):
483463
def decompress_compressed_mesh(self):
484464
# TODO: m_Triangles????
485465

486-
# Vertex
487466
version = self.version
467+
assert isinstance(self.src, Mesh)
488468
m_CompressedMesh = self.src.m_CompressedMesh
489469

470+
# Vertex
490471
self.m_VertexCount = m_VertexCount = m_CompressedMesh.m_Vertices.m_NumItems // 3
491472

492473
if m_CompressedMesh.m_Vertices.m_NumItems > 0:
@@ -543,8 +524,8 @@ def decompress_compressed_mesh(self):
543524
normalData = unpack_floats(m_CompressedMesh.m_Normals, shape=(2,))
544525
signs = unpack_ints(m_CompressedMesh.m_NormalSigns)
545526

546-
self.m_Normals = zeros((self.m_VertexCount, 3))
547-
for srcNrm, sign, dstNrm in zip(normalData, signs, self.m_Normals):
527+
normals = zeros(self.m_VertexCount, 3)
528+
for srcNrm, sign, dstNrm in zip(normalData, signs, normals):
548529
x, y = srcNrm
549530
zsqr = 1 - x * x - y * y
550531
if zsqr >= 0:
@@ -555,13 +536,15 @@ def decompress_compressed_mesh(self):
555536
dstNrm[:] = normalize(x, y, z)
556537
if sign == 0:
557538
dstNrm[2] *= -1
539+
self.m_Normals = lists_to_tuples(normals)
558540

559541
# Tangent
560542
if m_CompressedMesh.m_Tangents.m_NumItems > 0:
561543
tangentData = unpack_floats(m_CompressedMesh.m_Tangents, shape=(2,))
562544
signs = unpack_ints(m_CompressedMesh.m_TangentSigns, shape=(2,))
563-
self.m_Tangents = zeros((self.m_VertexCount, 4))
564-
for srcTan, (sign_z, sign_w), dstTan in zip(tangentData, signs, self.m_Tangents):
545+
546+
tangents = zeros(self.m_VertexCount, 4)
547+
for srcTan, (sign_z, sign_w), dstTan in zip(tangentData, signs, tangents):
565548
x, y = srcTan
566549
zsqr = 1 - x * x - y * y
567550
z = 0
@@ -574,6 +557,7 @@ def decompress_compressed_mesh(self):
574557
z = -z
575558
w = 1.0 if sign_w > 0 else -1.0
576559
dstTan[:] = x, y, z, w
560+
self.m_Tangents = lists_to_tuples(tangents)
577561

578562
# FloatColor
579563
if version[0] >= 5: # 5.0 and up
@@ -589,14 +573,14 @@ def decompress_compressed_mesh(self):
589573
j = 0
590574
sum = 0
591575

592-
self.m_BoneWeights = zeros((self.m_VertexCount, 4))
593-
self.m_BoneIndices = zeros((self.m_VertexCount, 4))
576+
boneWeights = zeros(self.m_VertexCount, 4)
577+
boneIndices = zeros(self.m_VertexCount, 4)
594578

595579
boneIndicesIterator = iter(boneIndicesData)
596580
for weight, boneIndex in zip(weightsData, boneIndicesIterator):
597581
# read bone index and weight
598-
self.m_BoneWeights[vertexIndex][j] = weight / 31
599-
self.m_BoneIndices[vertexIndex][j] = boneIndex
582+
boneWeights[vertexIndex][j] = weight / 31
583+
boneIndices[vertexIndex][j] = boneIndex
600584

601585
j += 1
602586
sum += weight
@@ -612,13 +596,16 @@ def decompress_compressed_mesh(self):
612596
# we read three weights, but they don't add up to one. calculate the fourth one, and read
613597
# missing bone index. continue with next vertex.
614598
elif j == 3: #
615-
self.m_BoneWeights[vertexIndex][j] = 1 - sum
616-
self.m_BoneIndices[vertexIndex][j] = next(boneIndicesIterator)
599+
boneWeights[vertexIndex][j] = 1 - sum
600+
boneIndices[vertexIndex][j] = next(boneIndicesIterator)
617601

618602
vertexIndex += 1
619603
j = 0
620604
sum = 0
621605

606+
self.m_BoneWeights = lists_to_tuples(boneWeights)
607+
self.m_BoneIndices = lists_to_tuples(boneIndices)
608+
622609
# IndexBuffer
623610
if m_CompressedMesh.m_Triangles.m_NumItems > 0: #
624611
self.m_IndexBuffer = unpack_ints(m_CompressedMesh.m_Triangles)
@@ -637,10 +624,10 @@ def decompress_compressed_mesh(self):
637624

638625
def get_triangles(self) -> List[List[Tuple[int, ...]]]:
639626
assert self.m_IndexBuffer is not None
627+
assert self.src.m_SubMeshes is not None
640628

641629
submeshes: List[List[Tuple[int, ...]]] = []
642630

643-
assert self.src and self.src.m_SubMeshes is not None, "No submesh data!"
644631
for m_SubMesh in self.src.m_SubMeshes:
645632
firstIndex = m_SubMesh.firstByte // 2
646633
if not self.m_Use16BitIndices:
@@ -652,38 +639,37 @@ def get_triangles(self) -> List[List[Tuple[int, ...]]]:
652639
triangles: List[Tuple[int, ...]]
653640

654641
if topology == MeshTopology.Triangles:
655-
triangles = self.m_IndexBuffer[firstIndex : firstIndex + indexCount] # type: ignore
656-
triangles = [triangles[i : i + 3] for i in range(0, len(triangles), 3)] # type: ignore
657-
elif self.version[0] < 4 or topology == MeshTopology.TriangleStrip: # TriangleStrip
658-
# todo: use as_strided, then fix winding, finally remove degenerates
659-
triIndex = 0
660-
triangles = [None] * (indexCount - 2) # type: ignore
642+
triangles = [
643+
tuple(self.m_IndexBuffer[i : i + 3]) for i in range(firstIndex, firstIndex + indexCount, 3)
644+
]
661645

662-
for i in range(indexCount - 2):
663-
a, b, c = self.m_IndexBuffer[firstIndex + i : firstIndex + i + 3]
646+
elif self.version[0] < 4 or topology == MeshTopology.TriangleStrip:
647+
triangles = [()] * (indexCount - 2)
648+
triIndex = 0
649+
for i in range(firstIndex, firstIndex + indexCount - 2):
650+
a, b, c = self.m_IndexBuffer[i : i + 3]
664651
# skip degenerates
665652
if a == b or a == c or b == c:
666653
continue
667-
668654
# do the winding flip-flop of strips
669-
if i & 1:
670-
triangles[triIndex] = b, a, c
655+
if (i - firstIndex) & 1:
656+
triangles[triIndex] = (b, a, c)
671657
else:
672-
triangles[triIndex] = a, b, c
658+
triangles[triIndex] = (a, b, c)
673659
triIndex += 1
674-
675660
triangles = triangles[:triIndex]
661+
m_SubMesh.indexCount = len(triangles) * 3
676662

677663
elif topology == MeshTopology.Quads:
678664
# one quad is two triangles, so // 4 * 2 = // 2
679-
# TODO: use as_strided
680-
triangles = [None] * (indexCount // 2) # type: ignore
665+
triangles = [()] * (indexCount // 2)
681666
triIndex = 0
682667
for i in range(firstIndex, firstIndex + indexCount, 4):
683668
a, b, c, d = self.m_IndexBuffer[i : i + 4]
684-
triangles[triIndex] = a, b, c
685-
triangles[triIndex + 1] = a, c, d
669+
triangles[triIndex] = (a, b, c)
670+
triangles[triIndex + 1] = (a, c, d)
686671
triIndex += 2
672+
687673
else:
688674
raise ValueError("Failed getting triangles. Submesh topology is lines or points.")
689675

0 commit comments

Comments
 (0)