Fixed texture deduping
This commit is contained in:
@@ -28,7 +28,7 @@ namespace SplashEdit.RuntimeCode
|
|||||||
public PSXVertex v1;
|
public PSXVertex v1;
|
||||||
public PSXVertex v2;
|
public PSXVertex v2;
|
||||||
|
|
||||||
public PSXTexture2D Texture;
|
public int TextureIndex;
|
||||||
public readonly PSXVertex[] Vertexes => new PSXVertex[] { v0, v1, v2 };
|
public readonly PSXVertex[] Vertexes => new PSXVertex[] { v0, v1, v2 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,57 +83,52 @@ namespace SplashEdit.RuntimeCode
|
|||||||
/// <param name="transform">Optional transform to convert vertices to world space.</param>
|
/// <param name="transform">Optional transform to convert vertices to world space.</param>
|
||||||
/// <returns>A new PSXMesh containing the converted triangles.</returns>
|
/// <returns>A new PSXMesh containing the converted triangles.</returns>
|
||||||
public static PSXMesh CreateFromUnityRenderer(Renderer renderer, float GTEScaling, Transform transform, List<PSXTexture2D> textures)
|
public static PSXMesh CreateFromUnityRenderer(Renderer renderer, float GTEScaling, Transform transform, List<PSXTexture2D> textures)
|
||||||
{
|
{
|
||||||
PSXMesh psxMesh = new PSXMesh { Triangles = new List<Tri>() };
|
PSXMesh psxMesh = new PSXMesh { Triangles = new List<Tri>() };
|
||||||
|
|
||||||
// Get materials and mesh.
|
|
||||||
Material[] materials = renderer.sharedMaterials;
|
Material[] materials = renderer.sharedMaterials;
|
||||||
Mesh mesh = renderer.GetComponent<MeshFilter>().sharedMesh;
|
Mesh mesh = renderer.GetComponent<MeshFilter>().sharedMesh;
|
||||||
|
|
||||||
// Iterate over each submesh.
|
|
||||||
for (int submeshIndex = 0; submeshIndex < materials.Length; submeshIndex++)
|
for (int submeshIndex = 0; submeshIndex < materials.Length; submeshIndex++)
|
||||||
{
|
{
|
||||||
// Get the triangles for this submesh.
|
|
||||||
int[] submeshTriangles = mesh.GetTriangles(submeshIndex);
|
int[] submeshTriangles = mesh.GetTriangles(submeshIndex);
|
||||||
|
|
||||||
// Get the material for this submesh.
|
|
||||||
Material material = materials[submeshIndex];
|
Material material = materials[submeshIndex];
|
||||||
|
|
||||||
// Get the corresponding texture for this material (assume mainTexture).
|
|
||||||
Texture2D texture = material.mainTexture as Texture2D;
|
Texture2D texture = material.mainTexture as Texture2D;
|
||||||
PSXTexture2D psxTexture = null;
|
|
||||||
|
|
||||||
|
// Find texture index instead of the texture itself
|
||||||
|
int textureIndex = -1;
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
// Find the corresponding PSX texture based on the Unity texture.
|
for (int i = 0; i < textures.Count; i++)
|
||||||
psxTexture = textures.FirstOrDefault(t => t.OriginalTexture == texture);
|
{
|
||||||
|
if (textures[i].OriginalTexture == texture)
|
||||||
|
{
|
||||||
|
textureIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (psxTexture == null)
|
if (textureIndex == -1)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get mesh data arrays.
|
// Get mesh data arrays
|
||||||
mesh.RecalculateNormals();
|
mesh.RecalculateNormals();
|
||||||
Vector3[] vertices = mesh.vertices;
|
Vector3[] vertices = mesh.vertices;
|
||||||
Vector3[] normals = mesh.normals;
|
Vector3[] normals = mesh.normals;
|
||||||
Vector3[] smoothNormals = RecalculateSmoothNormals(mesh);
|
Vector3[] smoothNormals = RecalculateSmoothNormals(mesh);
|
||||||
Vector2[] uv = mesh.uv;
|
Vector2[] uv = mesh.uv;
|
||||||
|
|
||||||
PSXVertex convertData(int index)
|
PSXVertex convertData(int index)
|
||||||
{
|
{
|
||||||
// Scale the vertex based on world scale.
|
|
||||||
Vector3 v = Vector3.Scale(vertices[index], transform.lossyScale);
|
Vector3 v = Vector3.Scale(vertices[index], transform.lossyScale);
|
||||||
// Transform the vertex to world space.
|
|
||||||
Vector3 wv = transform.TransformPoint(vertices[index]);
|
Vector3 wv = transform.TransformPoint(vertices[index]);
|
||||||
// Transform the normals to world space.
|
|
||||||
Vector3 wn = transform.TransformDirection(smoothNormals[index]).normalized;
|
Vector3 wn = transform.TransformDirection(smoothNormals[index]).normalized;
|
||||||
// Compute lighting for each vertex.
|
|
||||||
Color c = PSXLightingBaker.ComputeLighting(wv, wn);
|
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], textures[textureIndex]?.Width, textures[textureIndex]?.Height, c);
|
||||||
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)
|
for (int i = 0; i < submeshTriangles.Length; i += 3)
|
||||||
{
|
{
|
||||||
int vid0 = submeshTriangles[i];
|
int vid0 = submeshTriangles[i];
|
||||||
@@ -147,13 +142,17 @@ namespace SplashEdit.RuntimeCode
|
|||||||
(vid1, vid2) = (vid2, vid1);
|
(vid1, vid2) = (vid2, vid1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the constructed triangle to the mesh.
|
psxMesh.Triangles.Add(new Tri {
|
||||||
psxMesh.Triangles.Add(new Tri { v0 = convertData(vid0), v1 = convertData(vid1), v2 = convertData(vid2), Texture = psxTexture });
|
v0 = convertData(vid0),
|
||||||
|
v1 = convertData(vid1),
|
||||||
|
v2 = convertData(vid2),
|
||||||
|
TextureIndex = textureIndex
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return psxMesh;
|
return psxMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a Unity vertex into a PSXVertex by applying fixed-point conversion, shading, and UV mapping.
|
/// Converts a Unity vertex into a PSXVertex by applying fixed-point conversion, shading, and UV mapping.
|
||||||
|
|||||||
@@ -118,6 +118,16 @@ namespace SplashEdit.RuntimeCode
|
|||||||
|
|
||||||
return texture2D;
|
return texture2D;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PSXTexture2D GetTexture(int index)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < Textures.Count)
|
||||||
|
{
|
||||||
|
return Textures[index];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts the object's mesh into a PlayStation-compatible mesh.
|
/// Converts the object's mesh into a PlayStation-compatible mesh.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ namespace SplashEdit.RuntimeCode
|
|||||||
|
|
||||||
foreach (Tri tri in exporter.Mesh.Triangles)
|
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
|
// Write vertices coordinates
|
||||||
foreachVertexDo(tri, (v) => writeVertexPosition(v));
|
foreachVertexDo(tri, (v) => writeVertexPosition(v));
|
||||||
|
|
||||||
@@ -331,19 +331,19 @@ namespace SplashEdit.RuntimeCode
|
|||||||
foreachVertexDo(tri, (v) => writeVertexColor(v));
|
foreachVertexDo(tri, (v) => writeVertexColor(v));
|
||||||
|
|
||||||
// Write UVs for each vertex, adjusting for texture packing
|
// 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
|
writer.Write((ushort)0); // padding
|
||||||
|
|
||||||
|
|
||||||
TPageAttr tpage = new TPageAttr();
|
TPageAttr tpage = new TPageAttr();
|
||||||
tpage.SetPageX(tri.Texture.TexpageX);
|
tpage.SetPageX(exporter.GetTexture(tri.TextureIndex).TexpageX);
|
||||||
tpage.SetPageY(tri.Texture.TexpageY);
|
tpage.SetPageY(exporter.GetTexture(tri.TextureIndex).TexpageY);
|
||||||
tpage.Set(tri.Texture.BitDepth.ToColorMode());
|
tpage.Set(exporter.GetTexture(tri.TextureIndex).BitDepth.ToColorMode());
|
||||||
tpage.SetDithering(true);
|
tpage.SetDithering(true);
|
||||||
writer.Write((ushort)tpage.info);
|
writer.Write((ushort)tpage.info);
|
||||||
writer.Write((ushort)tri.Texture.ClutPackingX);
|
writer.Write((ushort)exporter.GetTexture(tri.TextureIndex).ClutPackingX);
|
||||||
writer.Write((ushort)tri.Texture.ClutPackingY);
|
writer.Write((ushort)exporter.GetTexture(tri.TextureIndex).ClutPackingY);
|
||||||
writer.Write((ushort)0);
|
writer.Write((ushort)0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,8 +74,9 @@ namespace SplashEdit.RuntimeCode
|
|||||||
allTextures.AddRange(obj.Textures);
|
allTextures.AddRange(obj.Textures);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List to track unique textures.
|
// List to track unique textures and their indices
|
||||||
List<PSXTexture2D> uniqueTextures = new List<PSXTexture2D>();
|
List<PSXTexture2D> uniqueTextures = new List<PSXTexture2D>();
|
||||||
|
Dictionary<(int, PSXBPP), int> textureToIndexMap = new Dictionary<(int, PSXBPP), int>();
|
||||||
|
|
||||||
// Group textures by bit depth (highest first).
|
// Group textures by bit depth (highest first).
|
||||||
var texturesByBitDepth = allTextures
|
var texturesByBitDepth = allTextures
|
||||||
@@ -101,10 +102,12 @@ namespace SplashEdit.RuntimeCode
|
|||||||
// Process each texture in descending order of area.
|
// Process each texture in descending order of area.
|
||||||
foreach (var texture in group.OrderByDescending(tex => tex.QuantizedWidth * tex.Height))
|
foreach (var texture in group.OrderByDescending(tex => tex.QuantizedWidth * tex.Height))
|
||||||
{
|
{
|
||||||
// Remove duplicate textures
|
var textureKey = (texture.OriginalTexture.GetInstanceID(), texture.BitDepth);
|
||||||
if (uniqueTextures.Any(tex => tex.OriginalTexture.GetInstanceID() == texture.OriginalTexture.GetInstanceID() && tex.BitDepth == 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,20 +123,64 @@ namespace SplashEdit.RuntimeCode
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add to unique textures and map
|
||||||
|
int newIndex = uniqueTextures.Count;
|
||||||
uniqueTextures.Add(texture);
|
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)
|
foreach (var obj in objects)
|
||||||
{
|
{
|
||||||
|
// Create a mapping from old texture indices to new indices for this object
|
||||||
|
Dictionary<int, int> oldToNewIndexMap = new Dictionary<int, int>();
|
||||||
|
List<PSXTexture2D> newTextures = new List<PSXTexture2D>();
|
||||||
|
|
||||||
for (int i = 0; i < obj.Textures.Count; i++)
|
for (int i = 0; i < obj.Textures.Count; i++)
|
||||||
{
|
{
|
||||||
var unique = uniqueTextures.FirstOrDefault(tex => tex.OriginalTexture.GetInstanceID() == obj.Textures[i].OriginalTexture.GetInstanceID() &&
|
var textureKey = (obj.Textures[i].OriginalTexture.GetInstanceID(), obj.Textures[i].BitDepth);
|
||||||
tex.BitDepth == obj.Textures[i].BitDepth);
|
if (textureToIndexMap.TryGetValue(textureKey, out int newIndex))
|
||||||
if (unique != null)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user