diff --git a/Editor/PSXObjectExporterEditor.cs b/Editor/PSXObjectExporterEditor.cs index 29ff5c1..d541c5b 100644 --- a/Editor/PSXObjectExporterEditor.cs +++ b/Editor/PSXObjectExporterEditor.cs @@ -1,6 +1,7 @@ using UnityEngine; using UnityEditor; using PSXSplash.RuntimeCode; +using System.IO; namespace PSXSplash.EditorCode { @@ -18,6 +19,7 @@ namespace PSXSplash.EditorCode if (GUILayout.Button("Export mesh")) { comp.Mesh.Export(comp.gameObject); + GUIUtility.ExitGUI(); } EditorGUILayout.EndVertical(); @@ -26,7 +28,53 @@ namespace PSXSplash.EditorCode EditorGUILayout.PropertyField(serializedObject.FindProperty("Texture")); if (GUILayout.Button("Export texture")) { - comp.Texture.Export(comp.gameObject); + ushort[] textureData = comp.Texture.ExportTexture(comp.gameObject); + string path = EditorUtility.SaveFilePanel( + "Save texture data", + "", + "texture_data", + "bin" + ); + + if (!string.IsNullOrEmpty(path)) + { + using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)) + using (BinaryWriter writer = new BinaryWriter(fileStream)) + { + foreach (ushort value in textureData) + { + writer.Write(value); + } + } + } + GUIUtility.ExitGUI(); + } + + if (comp.Texture.TextureType != PSXTextureType.TEX16_BPP) + { + if (GUILayout.Button("Export clut")) + { + ushort[] clutData = comp.Texture.ExportClut(comp.gameObject); + string path = EditorUtility.SaveFilePanel( + "Save clut data", + "", + "clut_data", + "bin" + ); + + if (!string.IsNullOrEmpty(path)) + { + using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)) + using (BinaryWriter writer = new BinaryWriter(fileStream)) + { + foreach (ushort value in clutData) + { + writer.Write(value); + } + } + } + GUIUtility.ExitGUI(); + } } EditorGUILayout.EndVertical(); diff --git a/Editor/QuantizedPreviewWindow.cs b/Editor/QuantizedPreviewWindow.cs index 283d890..1d56d23 100644 --- a/Editor/QuantizedPreviewWindow.cs +++ b/Editor/QuantizedPreviewWindow.cs @@ -1,4 +1,5 @@ using System.IO; +using PSXSplash.RuntimeCode; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; @@ -78,10 +79,10 @@ public class QuantizedPreviewWindow : EditorWindow if (indexedPixelData != null) { - if (GUILayout.Button("Export pixel data")) + if (GUILayout.Button("Export texute data")) { string path = EditorUtility.SaveFilePanel( - "Save pixel data", + "Save texture data", "", "pixel_data", "bin" @@ -129,13 +130,14 @@ public class QuantizedPreviewWindow : EditorWindow private void GenerateQuantizedPreview() { - Texture2D resizedTexture = ResizeTexture(originalTexture, targetWidth, targetHeight); + Texture2D resizedTexture = PSXTexture.ResizeTexture(originalTexture, targetWidth, targetHeight); if (bpp == 16) { - quantizedTexture = ConvertTo16Bpp(resizedTexture); + quantizedTexture = null; + indexedPixelData = PSXTexture.ConvertTo16Bpp(resizedTexture); clut = null; - vramTexture = resizedTexture; + vramTexture = ConvertTo16BppTexture2D(resizedTexture); } else { @@ -153,7 +155,7 @@ public class QuantizedPreviewWindow : EditorWindow { for (int x = 0; x < resizedTexture.width; x++) { - int index = 0; + int index; if (pixelSize == 4) { @@ -231,34 +233,6 @@ public class QuantizedPreviewWindow : EditorWindow return vramTexture; } - - - private Texture2D ConvertTo16Bpp(Texture2D source) - { - int width = source.width; - int height = source.height; - Texture2D convertedTexture = new Texture2D(width, height); - - Color[] originalPixels = source.GetPixels(); - Color[] convertedPixels = new Color[originalPixels.Length]; - - for (int i = 0; i < originalPixels.Length; i++) - { - Color pixel = originalPixels[i]; - - float r = Mathf.Floor(pixel.r * 31) / 31.0f; // 5 bits for red - float g = Mathf.Floor(pixel.g * 31) / 31.0f; // 5 bits for green - float b = Mathf.Floor(pixel.b * 31) / 31.0f; // 5 bits for blue - - convertedPixels[i] = new Color(r, g, b, pixel.a); - } - - convertedTexture.SetPixels(convertedPixels); - convertedTexture.Apply(); - - return convertedTexture; - } - private void DrawTexturePreview(Texture2D texture, int size, bool flipY = true) { Rect rect = GUILayoutUtility.GetRect(size, size, GUILayout.ExpandWidth(false)); @@ -328,21 +302,35 @@ public class QuantizedPreviewWindow : EditorWindow } } + private Texture2D ConvertTo16BppTexture2D(Texture2D source) +{ + int width = source.width; + int height = source.height; + Texture2D convertedTexture = new Texture2D(width, height); - private Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight) + Color[] originalPixels = source.GetPixels(); + Color[] convertedPixels = new Color[originalPixels.Length]; + + for (int y = 0; y < height; y++) { - RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight); - rt.antiAliasing = 1; - Graphics.Blit(source, rt); + for (int x = 0; x < width; x++) + { + int flippedY = height - y - 1; - Texture2D resizedTexture = new Texture2D(newWidth, newHeight); - RenderTexture.active = rt; - resizedTexture.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0); - resizedTexture.Apply(); + Color pixel = originalPixels[flippedY * width + x]; - RenderTexture.active = null; - RenderTexture.ReleaseTemporary(rt); + float r = Mathf.Floor(pixel.r * 31) / 31.0f; // 5 bits for red + float g = Mathf.Floor(pixel.g * 31) / 31.0f; // 5 bits for green + float b = Mathf.Floor(pixel.b * 31) / 31.0f; // 5 bits for blue - return resizedTexture; + convertedPixels[y * width + x] = new Color(r, g, b, pixel.a); + } } + + convertedTexture.SetPixels(convertedPixels); + convertedTexture.Apply(); + + return convertedTexture; +} + } diff --git a/Runtime/PSXTexture.cs b/Runtime/PSXTexture.cs index 0368421..ca60bf0 100644 --- a/Runtime/PSXTexture.cs +++ b/Runtime/PSXTexture.cs @@ -1,3 +1,5 @@ +using System.IO; +using UnityEditor; using UnityEngine; namespace PSXSplash.RuntimeCode @@ -26,9 +28,8 @@ namespace PSXSplash.RuntimeCode public int Height = 128; - // TODO: This just uses the quantization and doesn't store the result anywhere - // Maybe it should return the image and the clut back to the SceneExporter / The Editor code for only-texture export? - public void Export(GameObject gameObject) + + public ushort[] ExportTexture(GameObject gameObject) { Debug.Log($"Export: {this}"); @@ -40,15 +41,101 @@ namespace PSXSplash.RuntimeCode { Texture2D originalTexture = (Texture2D)texture; - Texture2D newTexture = new Texture2D(originalTexture.width, originalTexture.height, originalTexture.format, false); - newTexture.SetPixels(originalTexture.GetPixels()); - newTexture.Apply(); - Debug.Log((int)TextureType); - newTexture.Reinitialize(Width, Height, UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8_UInt, false); - var (quantizedPixels, clut) = ImageQuantizer.Quantize(originalTexture, (int)TextureType); + Texture2D newTexture = ResizeTexture(originalTexture, Width, Height); + if (TextureType == PSXTextureType.TEX16_BPP) + { + ushort[] converted = ConvertTo16Bpp(newTexture); + return converted; + } + else + { + var (indexedPixels, _) = ImageQuantizer.Quantize(newTexture, (int)TextureType, 100); + return indexedPixels; + } } } + return null; } + + public ushort[] ExportClut(GameObject gameObject) + { + Debug.Log($"Export: {this}"); + + MeshRenderer meshRenderer = gameObject.GetComponent(); + if (meshRenderer != null) + { + Texture texture = meshRenderer.material.mainTexture; + if (texture is Texture2D) + { + Texture2D originalTexture = (Texture2D)texture; + + Texture2D newTexture = ResizeTexture(originalTexture, Width, Height); + if (TextureType == PSXTextureType.TEX16_BPP) + { + return null; + } + else + { + var (_, generatedClut) = ImageQuantizer.Quantize(newTexture, (int)TextureType, 100); + return generatedClut; + } + + } + } + return null; + } + + public static Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight) + { + RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight); + rt.antiAliasing = 1; + Graphics.Blit(source, rt); + + Texture2D resizedTexture = new Texture2D(newWidth, newHeight); + RenderTexture.active = rt; + resizedTexture.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0); + resizedTexture.Apply(); + + RenderTexture.active = null; + RenderTexture.ReleaseTemporary(rt); + + return resizedTexture; + } + + public static ushort[] ConvertTo16Bpp(Texture2D source) + { + int width = source.width; + int height = source.height; + ushort[] packedData = new ushort[width * height]; + + Color[] originalPixels = source.GetPixels(); + + // Flip the image on the Y-axis + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int flippedY = height - y - 1; + + int index = flippedY * width + x; + + // Retrieve the pixel color + Color pixel = originalPixels[index]; + + // Convert to 5-bit components + int r = Mathf.Clamp(Mathf.RoundToInt(pixel.r * 31), 0, 31); // 5 bits for red + int g = Mathf.Clamp(Mathf.RoundToInt(pixel.g * 31), 0, 31); // 5 bits for green + int b = Mathf.Clamp(Mathf.RoundToInt(pixel.b * 31), 0, 31); // 5 bits for blue + + // Pack into a ushort: R(0..4), G(5..9), B(10..14), Padding(15) + packedData[y * width + x] = (ushort)((b << 10) | (g << 5) | r); + } + } + + return packedData; + } + + } }