diff --git a/Runtime/PSXMesh.cs b/Runtime/PSXMesh.cs
index 59e6c37..dc926d4 100644
--- a/Runtime/PSXMesh.cs
+++ b/Runtime/PSXMesh.cs
@@ -28,7 +28,7 @@ namespace SplashEdit.RuntimeCode
public PSXVertex v1;
public PSXVertex v2;
- public PSXTexture2D Texture;
+ public int TextureIndex;
public readonly PSXVertex[] Vertexes => new PSXVertex[] { v0, v1, v2 };
}
@@ -82,79 +82,78 @@ namespace SplashEdit.RuntimeCode
/// Height of the texture (default is 256).
/// Optional transform to convert vertices to world space.
/// A new PSXMesh containing the converted triangles.
- public static PSXMesh CreateFromUnityRenderer(Renderer renderer, float GTEScaling, Transform transform, List textures)
+ public static PSXMesh CreateFromUnityRenderer(Renderer renderer, float GTEScaling, Transform transform, List textures)
+{
+ PSXMesh psxMesh = new PSXMesh { Triangles = new List() };
+ Material[] materials = renderer.sharedMaterials;
+ Mesh mesh = renderer.GetComponent().sharedMesh;
+
+ for (int submeshIndex = 0; submeshIndex < materials.Length; submeshIndex++)
+ {
+ int[] submeshTriangles = mesh.GetTriangles(submeshIndex);
+ Material material = materials[submeshIndex];
+ Texture2D texture = material.mainTexture as Texture2D;
+
+ // Find texture index instead of the texture itself
+ int textureIndex = -1;
+ if (texture != null)
{
- PSXMesh psxMesh = new PSXMesh { Triangles = new List() };
-
- // Get materials and mesh.
- Material[] materials = renderer.sharedMaterials;
- Mesh mesh = renderer.GetComponent().sharedMesh;
-
- // Iterate over each submesh.
- for (int submeshIndex = 0; submeshIndex < materials.Length; submeshIndex++)
+ for (int i = 0; i < textures.Count; i++)
{
- // Get the triangles for this submesh.
- int[] submeshTriangles = mesh.GetTriangles(submeshIndex);
-
- // Get the material for this submesh.
- Material material = materials[submeshIndex];
-
- // Get the corresponding texture for this material (assume mainTexture).
- Texture2D texture = material.mainTexture as Texture2D;
- PSXTexture2D psxTexture = null;
-
- if (texture != null)
+ if (textures[i].OriginalTexture == texture)
{
- // Find the corresponding PSX texture based on the Unity texture.
- psxTexture = textures.FirstOrDefault(t => t.OriginalTexture == texture);
- }
-
- if (psxTexture == null)
- {
- continue;
- }
-
- // Get mesh data arrays.
- mesh.RecalculateNormals();
- Vector3[] vertices = mesh.vertices;
- Vector3[] normals = mesh.normals;
- Vector3[] smoothNormals = RecalculateSmoothNormals(mesh);
- Vector2[] uv = mesh.uv;
- PSXVertex convertData(int index)
- {
- // Scale the vertex based on world scale.
- Vector3 v = Vector3.Scale(vertices[index], transform.lossyScale);
- // Transform the vertex to world space.
- Vector3 wv = transform.TransformPoint(vertices[index]);
- // Transform the normals to world space.
- Vector3 wn = transform.TransformDirection(smoothNormals[index]).normalized;
- // Compute lighting for each vertex.
- Color c = PSXLightingBaker.ComputeLighting(wv, wn);
- // Convert vertex to PSX format, including fixed-point conversion and shading.
- return ConvertToPSXVertex(v, GTEScaling, normals[index], uv[index], psxTexture?.Width, psxTexture?.Height, c);
- }
- // Iterate through the triangles of the submesh.
- for (int i = 0; i < submeshTriangles.Length; i += 3)
- {
- int vid0 = submeshTriangles[i];
- int vid1 = submeshTriangles[i + 1];
- int vid2 = submeshTriangles[i + 2];
-
- Vector3 faceNormal = Vector3.Cross(vertices[vid1] - vertices[vid0], vertices[vid2] - vertices[vid0]).normalized;
-
- if (Vector3.Dot(faceNormal, normals[vid0]) < 0)
- {
- (vid1, vid2) = (vid2, vid1);
- }
-
- // Add the constructed triangle to the mesh.
- psxMesh.Triangles.Add(new Tri { v0 = convertData(vid0), v1 = convertData(vid1), v2 = convertData(vid2), Texture = psxTexture });
+ textureIndex = i;
+ break;
}
}
-
- return psxMesh;
}
+ if (textureIndex == -1)
+ {
+ continue;
+ }
+
+ // Get mesh data arrays
+ mesh.RecalculateNormals();
+ Vector3[] vertices = mesh.vertices;
+ Vector3[] normals = mesh.normals;
+ Vector3[] smoothNormals = RecalculateSmoothNormals(mesh);
+ Vector2[] uv = mesh.uv;
+
+ PSXVertex convertData(int index)
+ {
+ Vector3 v = Vector3.Scale(vertices[index], transform.lossyScale);
+ Vector3 wv = transform.TransformPoint(vertices[index]);
+ Vector3 wn = transform.TransformDirection(smoothNormals[index]).normalized;
+ Color c = PSXLightingBaker.ComputeLighting(wv, wn);
+ return ConvertToPSXVertex(v, GTEScaling, normals[index], uv[index], textures[textureIndex]?.Width, textures[textureIndex]?.Height, c);
+ }
+
+ for (int i = 0; i < submeshTriangles.Length; i += 3)
+ {
+ int vid0 = submeshTriangles[i];
+ int vid1 = submeshTriangles[i + 1];
+ int vid2 = submeshTriangles[i + 2];
+
+ Vector3 faceNormal = Vector3.Cross(vertices[vid1] - vertices[vid0], vertices[vid2] - vertices[vid0]).normalized;
+
+ if (Vector3.Dot(faceNormal, normals[vid0]) < 0)
+ {
+ (vid1, vid2) = (vid2, vid1);
+ }
+
+ psxMesh.Triangles.Add(new Tri {
+ v0 = convertData(vid0),
+ v1 = convertData(vid1),
+ v2 = convertData(vid2),
+ TextureIndex = textureIndex
+ });
+ }
+ }
+
+ return psxMesh;
+}
+
///
/// Converts a Unity vertex into a PSXVertex by applying fixed-point conversion, shading, and UV mapping.
///
diff --git a/Runtime/PSXObjectExporter.cs b/Runtime/PSXObjectExporter.cs
index 889fad7..fe080e2 100644
--- a/Runtime/PSXObjectExporter.cs
+++ b/Runtime/PSXObjectExporter.cs
@@ -118,6 +118,16 @@ namespace SplashEdit.RuntimeCode
return texture2D;
}
+
+ public PSXTexture2D GetTexture(int index)
+ {
+ if (index >= 0 && index < Textures.Count)
+ {
+ return Textures[index];
+ }
+ return null;
+ }
+
///
/// Converts the object's mesh into a PlayStation-compatible mesh.
///
diff --git a/Runtime/PSXSceneExporter.cs b/Runtime/PSXSceneExporter.cs
index c7da4aa..ddf0ed0 100644
--- a/Runtime/PSXSceneExporter.cs
+++ b/Runtime/PSXSceneExporter.cs
@@ -320,7 +320,7 @@ namespace SplashEdit.RuntimeCode
foreach (Tri tri in exporter.Mesh.Triangles)
{
- int expander = 16 / ((int)tri.Texture.BitDepth);
+ int expander = 16 / ((int)exporter.GetTexture(tri.TextureIndex).BitDepth);
// Write vertices coordinates
foreachVertexDo(tri, (v) => writeVertexPosition(v));
@@ -331,19 +331,19 @@ namespace SplashEdit.RuntimeCode
foreachVertexDo(tri, (v) => writeVertexColor(v));
// Write UVs for each vertex, adjusting for texture packing
- foreachVertexDo(tri, (v) => writeVertexUV(v, tri.Texture, expander));
+ foreachVertexDo(tri, (v) => writeVertexUV(v, exporter.GetTexture(tri.TextureIndex), expander));
writer.Write((ushort)0); // padding
TPageAttr tpage = new TPageAttr();
- tpage.SetPageX(tri.Texture.TexpageX);
- tpage.SetPageY(tri.Texture.TexpageY);
- tpage.Set(tri.Texture.BitDepth.ToColorMode());
+ tpage.SetPageX(exporter.GetTexture(tri.TextureIndex).TexpageX);
+ tpage.SetPageY(exporter.GetTexture(tri.TextureIndex).TexpageY);
+ tpage.Set(exporter.GetTexture(tri.TextureIndex).BitDepth.ToColorMode());
tpage.SetDithering(true);
writer.Write((ushort)tpage.info);
- writer.Write((ushort)tri.Texture.ClutPackingX);
- writer.Write((ushort)tri.Texture.ClutPackingY);
+ writer.Write((ushort)exporter.GetTexture(tri.TextureIndex).ClutPackingX);
+ writer.Write((ushort)exporter.GetTexture(tri.TextureIndex).ClutPackingY);
writer.Write((ushort)0);
}
}
diff --git a/Runtime/TexturePacker.cs b/Runtime/TexturePacker.cs
index 7772fca..7eb4c41 100644
--- a/Runtime/TexturePacker.cs
+++ b/Runtime/TexturePacker.cs
@@ -74,8 +74,9 @@ namespace SplashEdit.RuntimeCode
allTextures.AddRange(obj.Textures);
}
- // List to track unique textures.
+ // List to track unique textures and their indices
List uniqueTextures = new List();
+ Dictionary<(int, PSXBPP), int> textureToIndexMap = new Dictionary<(int, PSXBPP), int>();
// Group textures by bit depth (highest first).
var texturesByBitDepth = allTextures
@@ -101,10 +102,12 @@ namespace SplashEdit.RuntimeCode
// Process each texture in descending order of area.
foreach (var texture in group.OrderByDescending(tex => tex.QuantizedWidth * tex.Height))
{
- // Remove duplicate textures
- if (uniqueTextures.Any(tex => tex.OriginalTexture.GetInstanceID() == texture.OriginalTexture.GetInstanceID() && tex.BitDepth == texture.BitDepth))
+ var textureKey = (texture.OriginalTexture.GetInstanceID(), texture.BitDepth);
+
+ // Check if we've already processed this texture
+ if (textureToIndexMap.TryGetValue(textureKey, out int existingIndex))
{
- // Skip packing this texture – it will be replaced later.
+ // This texture is a duplicate, skip packing but remember the mapping
continue;
}
@@ -120,20 +123,64 @@ namespace SplashEdit.RuntimeCode
continue;
}
}
+
+ // Add to unique textures and map
+ int newIndex = uniqueTextures.Count;
uniqueTextures.Add(texture);
+ textureToIndexMap[textureKey] = newIndex;
}
}
- // Now update every exporter so that duplicate textures reference the unique instance.
+ // Now update every exporter and their meshes to use the correct texture indices
foreach (var obj in objects)
{
+ // Create a mapping from old texture indices to new indices for this object
+ Dictionary oldToNewIndexMap = new Dictionary();
+ List newTextures = new List();
+
for (int i = 0; i < obj.Textures.Count; i++)
{
- var unique = uniqueTextures.FirstOrDefault(tex => tex.OriginalTexture.GetInstanceID() == obj.Textures[i].OriginalTexture.GetInstanceID() &&
- tex.BitDepth == obj.Textures[i].BitDepth);
- if (unique != null)
+ var textureKey = (obj.Textures[i].OriginalTexture.GetInstanceID(), obj.Textures[i].BitDepth);
+ if (textureToIndexMap.TryGetValue(textureKey, out int newIndex))
{
- obj.Textures[i] = unique;
+ oldToNewIndexMap[i] = newIndex;
+
+ // Only add to new textures list if not already present
+ var texture = uniqueTextures[newIndex];
+ if (!newTextures.Contains(texture))
+ {
+ newTextures.Add(texture);
+ }
+ }
+ }
+
+ // Replace the exporter's texture list with the deduplicated list
+ obj.Textures = newTextures;
+
+ // Update all triangles in the mesh to use the new texture indices
+ if (obj.Mesh != null && obj.Mesh.Triangles != null)
+ {
+ for (int i = 0; i < obj.Mesh.Triangles.Count; i++)
+ {
+ var tri = obj.Mesh.Triangles[i];
+ if (oldToNewIndexMap.TryGetValue(tri.TextureIndex, out int newGlobalIndex))
+ {
+ // Find the index in the new texture list
+ var texture = uniqueTextures[newGlobalIndex];
+ int finalIndex = newTextures.IndexOf(texture);
+
+ // Create a new Tri with the updated TextureIndex
+ var updatedTri = new Tri
+ {
+ v0 = tri.v0,
+ v1 = tri.v1,
+ v2 = tri.v2,
+ TextureIndex = finalIndex
+ };
+
+ // Replace the tri in the list
+ obj.Mesh.Triangles[i] = updatedTri;
+ }
}
}
}