Skip to content

Commit d494ce8

Browse files
committed
1.2.2.4
Sprite fix streamlined texture and sprite conversion
1 parent 2ce7743 commit d494ce8

9 files changed

Lines changed: 435 additions & 286 deletions

File tree

UnityPy/EndianBinaryReader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import io
22
import struct
33

4-
from .math import Color, Matrix4x4, Quaternion, Vector2, Vector3, Vector4
4+
from .math import Color, Matrix4x4, Quaternion, Vector2, Vector3, Vector4, Rectangle
55

66

77
class EndianBinaryReader:
@@ -122,7 +122,7 @@ def read_vector4(self):
122122
return Vector4(self.read_float(), self.read_float(), self.read_float(), self.read_float())
123123

124124
def read_rectangle_f(self):
125-
return (self.read_float(), self.read_float(), self.read_float(), self.read_float())
125+
return Rectangle(self.read_float(), self.read_float(), self.read_float(), self.read_float())
126126

127127
def read_color4(self):
128128
return Color(self.read_float(), self.read_float(), self.read_float(), self.read_float())

UnityPy/classes/Sprite.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
from .Mesh import BoneWeights4, SubMesh, VertexData
44
from .NamedObject import NamedObject
55
from .PPtr import PPtr
6+
from ..export import SpriteHelper
67

78

89
class Sprite(NamedObject):
910

1011
@property
1112
def image(self):
12-
src_img = self.m_RD.texture.read().image
13-
box = (self.m_Rect[0], self.m_Rect[1], self.m_Rect[0]+self.m_Rect[2], self.m_Rect[1]+self.m_Rect[3])
14-
return ImageOps.flip(ImageOps.flip(src_img).crop(box))
13+
return SpriteHelper.get_image_from_sprite(self)
1514

1615
def __init__(self, reader):
1716
super().__init__(reader = reader)

UnityPy/classes/Texture2D.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
from ..enums import TextureFormat
22
from .Texture import Texture
33
from ..ResourceReader import ResourceReader
4-
from ..export.Texture2DConverter import Texture2DConverter
4+
from ..export import Texture2DConverter
55

66
class Texture2D(Texture):
77

88
@property
99
def image(self):
10-
tc = Texture2DConverter(self)
11-
return tc.ConvertToImage(True)
10+
return Texture2DConverter.get_image_from_texture2d(self)
1211

1312

1413
def __init__(self, reader):

UnityPy/export/SpriteHelper.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import PIL
2+
from enum import IntEnum
3+
4+
from .Texture2DConverter import get_image_from_texture2d
5+
from ..math import Rectangle
6+
7+
8+
# should be imported from Sprite, but not possible
9+
class SpritePackingRotation(IntEnum):
10+
kSPRNone = 0,
11+
kSPRFlipHorizontal = 1,
12+
kSPRFlipVertical = 2,
13+
kSPRRotate180 = 3,
14+
kSPRRotate90 = 4
15+
16+
17+
def get_image_from_sprite(m_Sprite) -> PIL.Image:
18+
try:
19+
atlas = m_Sprite.m_SpriteAtlas
20+
sprite_atlas_data = atlas.m_RenderDataKey
21+
22+
m_Texture2D = sprite_atlas_data.m_Texture2D
23+
# textureRect
24+
texture_rect = sprite_atlas_data.textureRect
25+
texture_rect_offset = sprite_atlas_data.textureRectOffset
26+
settings_raw = sprite_atlas_data.settingsRaw
27+
except:
28+
m_Texture2D = m_Sprite.m_RD.texture
29+
texture_rect = m_Sprite.m_RD.textureRect
30+
texture_rect_offset = m_Sprite.m_RD.textureRectOffset
31+
settings_raw = m_Sprite.m_RD.settingsRaw
32+
finally:
33+
original_image = get_image_from_texture2d(m_Texture2D.read(), False)
34+
# textureRectI = Rectangle.round(textureRect)
35+
sprite_image = original_image.crop((texture_rect.left, texture_rect.top, texture_rect.right,
36+
texture_rect.bottom))
37+
38+
if settings_raw.packed == 1:
39+
rotation = settings_raw.packingRotation
40+
if rotation == SpritePackingRotation.kSPRFlipHorizontal:
41+
sprite_image = sprite_image.transpose(PIL.Image.FLIP_TOP_BOTTOM)
42+
# spriteImage = RotateFlip(RotateFlipType.RotateNoneFlipX)
43+
elif rotation == SpritePackingRotation.kSPRFlipVertical:
44+
sprite_image = sprite_image.transpose(PIL.Image.FLIP_LEFT_RIGHT)
45+
# spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipY)
46+
elif rotation == SpritePackingRotation.kSPRRotate180:
47+
sprite_image = sprite_image.transpose(PIL.Image.ROTATE_180)
48+
# spriteImage.RotateFlip(RotateFlipType.Rotate180FlipNone)
49+
elif rotation == SpritePackingRotation.kSPRRotate90:
50+
sprite_image = sprite_image.transpose(PIL.Image.ROTATE_270)
51+
# spriteImage.RotateFlip(RotateFlipType.Rotate270FlipNone)
52+
53+
# Tight
54+
"""
55+
FillPath has to be figured out
56+
57+
if settingsRaw.packingMode == SpritePackingMode.kSPMTight:
58+
try:
59+
triangles = GetTriangles(m_Sprite.m_RD)
60+
points = triangles.Select(x => x.Select(y => new PointF(y.X, y.Y)).ToArray())
61+
#using (var path = new GraphicsPath())
62+
for p in points:
63+
path.AddPolygon(p)
64+
65+
matr = Matrix()
66+
67+
version = m_Sprite.version
68+
if version[0] < 5 or (version[0] == 5 and version[1] < 4) or (version[0] == 5 and version[1] == 4 and version[2] <= 1): #5.4.1p3 down
69+
matr.Translate(m_Sprite.m_Rect.Width * 0.5 - textureRectOffset.X, m_Sprite.m_Rect.Height * 0.5 - textureRectOffset.Y)
70+
else:
71+
matr.Translate(m_Sprite.m_Rect.Width * m_Sprite.m_Pivot.X - textureRectOffset.X, m_Sprite.m_Rect.Height * m_Sprite.m_Pivot.Y - textureRectOffset.Y)
72+
matr.Scale(m_Sprite.m_PixelsToUnits, m_Sprite.m_PixelsToUnits)
73+
path.Transform(matr)
74+
75+
bitmap = PIL.Image.new(originalImage.mode ,(textureRectI.Width, textureRectI.Height))
76+
bitmap.FillPath(spriteImage, path)
77+
return ImageOps.flip(bitmap)
78+
79+
except:
80+
pass
81+
"""
82+
# Rectangle
83+
return sprite_image.transpose(PIL.Image.FLIP_TOP_BOTTOM)
84+
return None
85+
86+
87+
"""
88+
private static Vector2[][] GetTriangles(SpriteRenderData m_RD)
89+
{
90+
if (m_RD.vertices != null) #5.6 down
91+
{
92+
var vertices = m_RD.vertices.Select(x => (Vector2)x.pos).ToArray();
93+
var triangleCount = m_RD.indices.Length / 3;
94+
var triangles = new Vector2[triangleCount][];
95+
for (int i = 0; i < triangleCount; i++)
96+
{
97+
var first = m_RD.indices[i * 3];
98+
var second = m_RD.indices[i * 3 + 1];
99+
var third = m_RD.indices[i * 3 + 2];
100+
var triangle = new[] { vertices[first], vertices[second], vertices[third] };
101+
triangles[i] = triangle;
102+
}
103+
return triangles;
104+
}
105+
106+
return GetTriangles(m_RD.m_VertexData, m_RD.m_SubMeshes, m_RD.m_IndexBuffer); #5.6 and up
107+
}
108+
109+
private static Vector2[][] GetTriangles(VertexData m_VertexData, SubMesh[] m_SubMeshes, byte[] m_IndexBuffer)
110+
{
111+
var triangles = new List<Vector2[]>();
112+
var m_Channel = m_VertexData.m_Channels[0]; #kShaderChannelVertex
113+
var m_Stream = m_VertexData.m_Streams[m_Channel.stream];
114+
using (BinaryReader vertexReader = new BinaryReader(new MemoryStream(m_VertexData.m_DataSize)),
115+
indexReader = new BinaryReader(new MemoryStream(m_IndexBuffer)))
116+
{
117+
foreach (var subMesh in m_SubMeshes)
118+
{
119+
vertexReader.BaseStream.Position = m_Stream.offset + subMesh.firstVertex * m_Stream.stride + m_Channel.offset;
120+
121+
var vertices = new Vector2[subMesh.vertexCount];
122+
for (int v = 0; v < subMesh.vertexCount; v++)
123+
{
124+
vertices[v] = vertexReader.ReadVector3();
125+
vertexReader.BaseStream.Position += m_Stream.stride - 12;
126+
}
127+
128+
indexReader.BaseStream.Position = subMesh.firstByte;
129+
130+
var triangleCount = subMesh.indexCount / 3u;
131+
for (int i = 0; i < triangleCount; i++)
132+
{
133+
var first = indexReader.ReadUInt16() - subMesh.firstVertex;
134+
var second = indexReader.ReadUInt16() - subMesh.firstVertex;
135+
var third = indexReader.ReadUInt16() - subMesh.firstVertex;
136+
var triangle = new[] { vertices[first], vertices[second], vertices[third] };
137+
triangles.Add(triangle);
138+
}
139+
}
140+
}
141+
return triangles.ToArray();
142+
}
143+
}
144+
}
145+
"""

0 commit comments

Comments
 (0)