diff --git a/Runtime/PSXMesh.cs b/Runtime/PSXMesh.cs index 618864f..d1af672 100644 --- a/Runtime/PSXMesh.cs +++ b/Runtime/PSXMesh.cs @@ -104,26 +104,28 @@ namespace SplashEdit.RuntimeCode // Compute the final shaded color by multiplying the light color by the intensity. Color shadedColor = lightColor * lightIntensity; + static short clampPosition(float v) => (short)(Mathf.Clamp(v, -4f, 3.999f) * 4096); + static byte clamp0255(float v) => (byte)(Mathf.Clamp(v, 0, 255)); PSXVertex psxVertex = new PSXVertex { // Convert position to fixed-point, clamping values to a defined range. - vx = (short)(Mathf.Clamp(vertex.x, -4f, 3.999f) * 4096), - vy = (short)(Mathf.Clamp(-vertex.y, -4f, 3.999f) * 4096), - vz = (short)(Mathf.Clamp(vertex.z, -4f, 3.999f) * 4096), + vx = clampPosition(vertex.x), + vy = clampPosition(-vertex.y), + vz = clampPosition(vertex.z), // Convert normals to fixed-point. - nx = (short)(Mathf.Clamp(normal.x, -4f, 3.999f) * 4096), - ny = (short)(Mathf.Clamp(-normal.y, -4f, 3.999f) * 4096), - nz = (short)(Mathf.Clamp(normal.z, -4f, 3.999f) * 4096), + nx = clampPosition(normal.x), + ny = clampPosition(-normal.y), + nz = clampPosition(normal.z), // Map UV coordinates to a byte range after scaling based on texture dimensions. - u = (byte)(Mathf.Clamp((uv.x * (textureWidth - 1)), 0, 255)), - v = (byte)(Mathf.Clamp(((1.0f - uv.y) * (textureHeight - 1)), 0, 255)), + u = clamp0255(uv.x * (textureWidth - 1)), + v = clamp0255((1.0f - uv.y) * (textureHeight - 1)), // Convert the computed color to a byte range. - r = (byte)(Mathf.Clamp(shadedColor.r * 255, 0, 255)), - g = (byte)(Mathf.Clamp(shadedColor.g * 255, 0, 255)), - b = (byte)(Mathf.Clamp(shadedColor.b * 255, 0, 255)) + r = clamp0255(shadedColor.r * 255), + g = clamp0255(shadedColor.g * 255), + b = clamp0255(shadedColor.b * 255) }; return psxVertex; diff --git a/Runtime/PSXObjectExporter.cs b/Runtime/PSXObjectExporter.cs index 38d7dc7..371c973 100644 --- a/Runtime/PSXObjectExporter.cs +++ b/Runtime/PSXObjectExporter.cs @@ -2,16 +2,14 @@ using UnityEngine; namespace SplashEdit.RuntimeCode { + [RequireComponent(typeof(MeshFilter))] + [RequireComponent(typeof(Renderer))] public class PSXObjectExporter : MonoBehaviour { public PSXBPP BitDepth = PSXBPP.TEX_8BIT; // Defines the bit depth of the texture (e.g., 4BPP, 8BPP) - public bool MeshIsStatic = true; // Determines if the mesh is static, affecting how it's processed. Non-static meshes don't export correctly as of now. - [HideInInspector] - public PSXTexture2D Texture; // Stores the converted PlayStation-style texture - - [HideInInspector] - public PSXMesh Mesh; // Stores the converted PlayStation-style mesh + public PSXTexture2D Texture { get; set; } // Stores the converted PlayStation-style texture + public PSXMesh Mesh { get; set; } // Stores the converted PlayStation-style mesh /// /// Converts the object's material texture into a PlayStation-compatible texture. @@ -19,11 +17,22 @@ namespace SplashEdit.RuntimeCode public void CreatePSXTexture2D() { Renderer renderer = GetComponent(); - if (renderer != null && renderer.sharedMaterial != null && renderer.sharedMaterial.mainTexture is Texture2D texture) + if (renderer.sharedMaterial != null && renderer.sharedMaterial.mainTexture is Texture2D texture) { Texture = PSXTexture2D.CreateFromTexture2D(texture, BitDepth); Texture.OriginalTexture = texture; // Stores reference to the original texture } + else + { + //TODO: Better handle object with default texture + Texture = new PSXTexture2D() + { + BitDepth = BitDepth, + Width = 0, + Height = 0, + }; + Texture.OriginalTexture = null; + } } /// @@ -31,19 +40,16 @@ namespace SplashEdit.RuntimeCode /// public void CreatePSXMesh() { - MeshFilter meshFilter = gameObject.GetComponent(); - if (meshFilter != null) + MeshFilter meshFilter = GetComponent(); + if (gameObject.isStatic) { - if (MeshIsStatic) - { - // Static meshes take object transformation into account - Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height, transform); - } - else - { - // Dynamic meshes do not consider object transformation - Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height); - } + // Static meshes take object transformation into account + Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height, transform); + } + else + { + // Dynamic meshes do not consider object transformation + Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height); } } } diff --git a/Runtime/PSXSceneExporter.cs b/Runtime/PSXSceneExporter.cs index 5799463..f0c7d9b 100644 --- a/Runtime/PSXSceneExporter.cs +++ b/Runtime/PSXSceneExporter.cs @@ -8,151 +8,130 @@ using UnityEngine.SceneManagement; namespace SplashEdit.RuntimeCode { - [ExecuteInEditMode] - public class PSXSceneExporter : MonoBehaviour - { - private PSXObjectExporter[] _exporters; - - private PSXData _psxData; - private readonly string _psxDataPath = "Assets/PSXData.asset"; - - private Vector2 selectedResolution; - private bool dualBuffering; - private bool verticalLayout; - private List prohibitedAreas; - private VRAMPixel[,] vramPixels; - - - - public void Export() + [ExecuteInEditMode] + public class PSXSceneExporter : MonoBehaviour { - LoadData(); - _exporters = FindObjectsByType(FindObjectsSortMode.None); - foreach (PSXObjectExporter exp in _exporters) - { - exp.CreatePSXTexture2D(); - exp.CreatePSXMesh(); - } - PackTextures(); - ExportFile(); - } + private PSXObjectExporter[] _exporters; - void PackTextures() - { + private PSXData _psxData; + private readonly string _psxDataPath = "Assets/PSXData.asset"; - Rect buffer1 = new Rect(0, 0, selectedResolution.x, selectedResolution.y); - Rect buffer2 = verticalLayout ? new Rect(0, 256, selectedResolution.x, selectedResolution.y) - : new Rect(selectedResolution.x, 0, selectedResolution.x, selectedResolution.y); + private Vector2 selectedResolution; + private bool dualBuffering; + private bool verticalLayout; + private List prohibitedAreas; + private VRAMPixel[,] vramPixels; - List framebuffers = new List { buffer1 }; - if (dualBuffering) - { - framebuffers.Add(buffer2); - } - VRAMPacker tp = new VRAMPacker(framebuffers, prohibitedAreas); - var packed = tp.PackTexturesIntoVRAM(_exporters); - _exporters = packed.processedObjects; - vramPixels = packed._vramPixels; - } - - void ExportFile() - { - string path = EditorUtility.SaveFilePanel("Select Output File", "", "output", "bin"); - int totalFaces = 0; - using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create))) - { - // VramPixels are always 1MB - for (int y = 0; y < vramPixels.GetLength(1); y++) + public void Export() { - for (int x = 0; x < vramPixels.GetLength(0); x++) - { - writer.Write(vramPixels[x, y].Pack()); - } + LoadData(); + _exporters = FindObjectsByType(FindObjectsSortMode.None); + foreach (PSXObjectExporter exp in _exporters) + { + exp.CreatePSXTexture2D(); + exp.CreatePSXMesh(); + } + PackTextures(); + ExportFile(); } - writer.Write((ushort)_exporters.Length); - foreach (PSXObjectExporter exporter in _exporters) + + void PackTextures() { - int expander = 16 / ((int)exporter.Texture.BitDepth); + Rect buffer1 = new Rect(0, 0, selectedResolution.x, selectedResolution.y); + Rect buffer2 = verticalLayout ? new Rect(0, 256, selectedResolution.x, selectedResolution.y) + : new Rect(selectedResolution.x, 0, selectedResolution.x, selectedResolution.y); - totalFaces += exporter.Mesh.Triangles.Count; - writer.Write((ushort)exporter.Mesh.Triangles.Count); - writer.Write((byte)exporter.Texture.BitDepth); - writer.Write((byte)exporter.Texture.TexpageX); - writer.Write((byte)exporter.Texture.TexpageY); - writer.Write((ushort)exporter.Texture.ClutPackingX); - writer.Write((ushort)exporter.Texture.ClutPackingY); - writer.Write((byte)0); - foreach (Tri tri in exporter.Mesh.Triangles) - { - writer.Write((short)tri.v0.vx); - writer.Write((short)tri.v0.vy); - writer.Write((short)tri.v0.vz); - writer.Write((short)tri.v0.nx); - writer.Write((short)tri.v0.ny); - writer.Write((short)tri.v0.nz); - writer.Write((byte)(tri.v0.u + exporter.Texture.PackingX * expander)); - writer.Write((byte)(tri.v0.v + exporter.Texture.PackingY)); - writer.Write((byte) tri.v0.r); - writer.Write((byte) tri.v0.g); - writer.Write((byte) tri.v0.b); - for(int i = 0; i < 7; i ++) writer.Write((byte) 0); + List framebuffers = new List { buffer1 }; + if (dualBuffering) + { + framebuffers.Add(buffer2); + } - writer.Write((short)tri.v1.vx); - writer.Write((short)tri.v1.vy); - writer.Write((short)tri.v1.vz); - writer.Write((short)tri.v1.nx); - writer.Write((short)tri.v1.ny); - writer.Write((short)tri.v1.nz); - writer.Write((byte)(tri.v1.u + exporter.Texture.PackingX * expander)); - writer.Write((byte)(tri.v1.v + exporter.Texture.PackingY)); - writer.Write((byte) tri.v1.r); - writer.Write((byte) tri.v1.g); - writer.Write((byte) tri.v1.b); - for(int i = 0; i < 7; i ++) writer.Write((byte) 0); + VRAMPacker tp = new VRAMPacker(framebuffers, prohibitedAreas); + var packed = tp.PackTexturesIntoVRAM(_exporters); + _exporters = packed.processedObjects; + vramPixels = packed._vramPixels; - writer.Write((short)tri.v2.vx); - writer.Write((short)tri.v2.vy); - writer.Write((short)tri.v2.vz); - writer.Write((short)tri.v2.nx); - writer.Write((short)tri.v2.ny); - writer.Write((short)tri.v2.nz); - writer.Write((byte)(tri.v2.u + exporter.Texture.PackingX * expander)); - writer.Write((byte)(tri.v2.v + exporter.Texture.PackingY)); - writer.Write((byte) tri.v2.r); - writer.Write((byte) tri.v2.g); - writer.Write((byte) tri.v2.b); - for(int i = 0; i < 7; i ++) writer.Write((byte) 0); - - } } - } - Debug.Log(totalFaces); + + void ExportFile() + { + string path = EditorUtility.SaveFilePanel("Select Output File", "", "output", "bin"); + int totalFaces = 0; + using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create))) + { + // VramPixels are always 1MB + for (int y = 0; y < vramPixels.GetLength(1); y++) + { + for (int x = 0; x < vramPixels.GetLength(0); x++) + { + writer.Write(vramPixels[x, y].Pack()); + } + } + writer.Write((ushort)_exporters.Length); + foreach (PSXObjectExporter exporter in _exporters) + { + + int expander = 16 / ((int)exporter.Texture.BitDepth); + + totalFaces += exporter.Mesh.Triangles.Count; + writer.Write((ushort)exporter.Mesh.Triangles.Count); + writer.Write((byte)exporter.Texture.BitDepth); + writer.Write((byte)exporter.Texture.TexpageX); + writer.Write((byte)exporter.Texture.TexpageY); + writer.Write((ushort)exporter.Texture.ClutPackingX); + writer.Write((ushort)exporter.Texture.ClutPackingY); + writer.Write((byte)0); + void writePSXVertex(PSXVertex vertex) + { + writer.Write((short)vertex.vx); + writer.Write((short)vertex.vy); + writer.Write((short)vertex.vz); + writer.Write((short)vertex.nx); + writer.Write((short)vertex.ny); + writer.Write((short)vertex.nz); + writer.Write((byte)(vertex.u + exporter.Texture.PackingX * expander)); + writer.Write((byte)(vertex.v + exporter.Texture.PackingY)); + writer.Write((byte)vertex.r); + writer.Write((byte)vertex.g); + writer.Write((byte)vertex.b); + for (int i = 0; i < 7; i++) writer.Write((byte)0); + } + foreach (Tri tri in exporter.Mesh.Triangles) + { + writePSXVertex(tri.v0); + writePSXVertex(tri.v1); + writePSXVertex(tri.v2); + } + } + } + Debug.Log(totalFaces); + } + + public void LoadData() + { + _psxData = AssetDatabase.LoadAssetAtPath(_psxDataPath); + + if (!_psxData) + { + _psxData = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(_psxData, _psxDataPath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + selectedResolution = _psxData.OutputResolution; + dualBuffering = _psxData.DualBuffering; + verticalLayout = _psxData.VerticalBuffering; + prohibitedAreas = _psxData.ProhibitedAreas; + } + + void OnDrawGizmos() + { + Gizmos.DrawIcon(transform.position, "Packages/net.psxsplash.splashedit/Icons/PSXSceneExporter.png", true); + } } - - public void LoadData() - { - _psxData = AssetDatabase.LoadAssetAtPath(_psxDataPath); - - if (!_psxData) - { - _psxData = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(_psxData, _psxDataPath); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } - - selectedResolution = _psxData.OutputResolution; - dualBuffering = _psxData.DualBuffering; - verticalLayout = _psxData.VerticalBuffering; - prohibitedAreas = _psxData.ProhibitedAreas; - } - - void OnDrawGizmos() - { - Gizmos.DrawIcon(transform.position, "Packages/net.psxsplash.splashedit/Icons/PSXSceneExporter.png", true); - } - } } diff --git a/Runtime/TexturePacker.cs b/Runtime/TexturePacker.cs index fd2a9c8..dc50969 100644 --- a/Runtime/TexturePacker.cs +++ b/Runtime/TexturePacker.cs @@ -74,9 +74,9 @@ namespace SplashEdit.RuntimeCode int atlasWidth = group.Key switch { PSXBPP.TEX_16BIT => 256, - PSXBPP.TEX_8BIT => 128, - PSXBPP.TEX_4BIT => 64, - _ => 256 + PSXBPP.TEX_8BIT => 128, + PSXBPP.TEX_4BIT => 64, + _ => 256 }; // Create a new atlas for this group. @@ -87,7 +87,7 @@ namespace SplashEdit.RuntimeCode foreach (var obj in group.OrderByDescending(obj => obj.Texture.QuantizedWidth * obj.Texture.Height)) { // Remove duplicate textures - if (uniqueTextures.Any(tex => tex.OriginalTexture.GetInstanceID() == obj.Texture.OriginalTexture.GetInstanceID() && tex.BitDepth == obj.Texture.BitDepth)) + if (uniqueTextures.Where(tex => tex.OriginalTexture != null).Any(tex => tex.OriginalTexture.GetInstanceID() == obj.Texture.OriginalTexture.GetInstanceID() && tex.BitDepth == obj.Texture.BitDepth)) { obj.Texture = uniqueTextures.First(tex => tex.OriginalTexture.GetInstanceID() == obj.Texture.OriginalTexture.GetInstanceID()); continue;