Added texture export options
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
using System.IO;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Rendering;
|
using UnityEngine.Rendering;
|
||||||
@@ -7,7 +8,7 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
private Texture2D originalTexture;
|
private Texture2D originalTexture;
|
||||||
private Texture2D quantizedTexture;
|
private Texture2D quantizedTexture;
|
||||||
private Texture2D vramTexture; // New VRAM Texture
|
private Texture2D vramTexture; // New VRAM Texture
|
||||||
private float[] clut; // Changed to 1D array
|
private ushort[] clut; // Changed to 1D array
|
||||||
private ushort[] indexedPixelData; // New field for indexed pixel data
|
private ushort[] indexedPixelData; // New field for indexed pixel data
|
||||||
private int bpp = 4;
|
private int bpp = 4;
|
||||||
private int targetWidth = 128;
|
private int targetWidth = 128;
|
||||||
@@ -45,7 +46,7 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
{
|
{
|
||||||
GUILayout.BeginVertical();
|
GUILayout.BeginVertical();
|
||||||
GUILayout.Label("Original Texture");
|
GUILayout.Label("Original Texture");
|
||||||
DrawTexturePreview(originalTexture, previewSize);
|
DrawTexturePreview(originalTexture, previewSize, false);
|
||||||
GUILayout.EndVertical();
|
GUILayout.EndVertical();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +73,58 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
GUILayout.Label("Color Lookup Table (CLUT)");
|
GUILayout.Label("Color Lookup Table (CLUT)");
|
||||||
DrawCLUT();
|
DrawCLUT();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GUILayout.Space(10);
|
||||||
|
|
||||||
|
if (indexedPixelData != null)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("Export pixel data"))
|
||||||
|
{
|
||||||
|
string path = EditorUtility.SaveFilePanel(
|
||||||
|
"Save pixel data",
|
||||||
|
"",
|
||||||
|
"pixel_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 indexedPixelData)
|
||||||
|
{
|
||||||
|
writer.Write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clut != null)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("Export clut data"))
|
||||||
|
{
|
||||||
|
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 clut)
|
||||||
|
{
|
||||||
|
writer.Write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateQuantizedPreview()
|
private void GenerateQuantizedPreview()
|
||||||
@@ -119,9 +172,14 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Vector3 color = new Vector3(clut[index * 3], clut[index * 3 + 1], clut[index * 3 + 2]);
|
Vector3 color = new Vector3(
|
||||||
|
(clut[index] & 31) / 31.0f, // Red: bits 0–4
|
||||||
|
((clut[index] >> 5) & 31) / 31.0f, // Green: bits 5–9
|
||||||
|
((clut[index] >> 10) & 31) / 31.0f // Blue: bits 10–14
|
||||||
|
);
|
||||||
quantizedColors[y * resizedTexture.width + x] = new Color(color.x, color.y, color.z);
|
quantizedColors[y * resizedTexture.width + x] = new Color(color.x, color.y, color.z);
|
||||||
|
|
||||||
|
|
||||||
if ((x % pixelSize) == (pixelSize - 1))
|
if ((x % pixelSize) == (pixelSize - 1))
|
||||||
{
|
{
|
||||||
pixelIndex++;
|
pixelIndex++;
|
||||||
@@ -160,9 +218,9 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
{
|
{
|
||||||
int index = indexedData[i];
|
int index = indexedData[i];
|
||||||
|
|
||||||
float r = Mathf.Floor((index >> 11) & 31) / 31.0f;
|
float r = (index & 31) / 31.0f; // Red: bits 0–4
|
||||||
float g = Mathf.Floor((index >> 5) & 31) / 31.0f;
|
float g = ((index >> 5) & 31) / 31.0f; // Green: bits 5–9
|
||||||
float b = Mathf.Floor(index & 31) / 31.0f;
|
float b = ((index >> 10) & 31) / 31.0f; // Blue: bits 10–14
|
||||||
|
|
||||||
vramColors[i] = new Color(r, g, b);
|
vramColors[i] = new Color(r, g, b);
|
||||||
}
|
}
|
||||||
@@ -174,6 +232,7 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private Texture2D ConvertTo16Bpp(Texture2D source)
|
private Texture2D ConvertTo16Bpp(Texture2D source)
|
||||||
{
|
{
|
||||||
int width = source.width;
|
int width = source.width;
|
||||||
@@ -187,7 +246,6 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
{
|
{
|
||||||
Color pixel = originalPixels[i];
|
Color pixel = originalPixels[i];
|
||||||
|
|
||||||
// Convert to 5 bits per channel (R5G5B5)
|
|
||||||
float r = Mathf.Floor(pixel.r * 31) / 31.0f; // 5 bits for red
|
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 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
|
float b = Mathf.Floor(pixel.b * 31) / 31.0f; // 5 bits for blue
|
||||||
@@ -201,10 +259,37 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
return convertedTexture;
|
return convertedTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawTexturePreview(Texture2D texture, int size)
|
private void DrawTexturePreview(Texture2D texture, int size, bool flipY = true)
|
||||||
{
|
{
|
||||||
Rect rect = GUILayoutUtility.GetRect(size, size, GUILayout.ExpandWidth(false));
|
Rect rect = GUILayoutUtility.GetRect(size, size, GUILayout.ExpandWidth(false));
|
||||||
EditorGUI.DrawPreviewTexture(rect, texture, null, ScaleMode.ScaleToFit, 0, 0, ColorWriteMask.All);
|
|
||||||
|
// Flip the texture on the Y-axis
|
||||||
|
Texture2D displayedTexture = flipY ? FlipTextureY(texture) : texture;
|
||||||
|
EditorGUI.DrawPreviewTexture(rect, displayedTexture, null, ScaleMode.ScaleToFit, 0, 0, ColorWriteMask.All);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Texture2D FlipTextureY(Texture2D texture)
|
||||||
|
{
|
||||||
|
Color[] originalPixels = texture.GetPixels();
|
||||||
|
Color[] flippedPixels = new Color[originalPixels.Length];
|
||||||
|
|
||||||
|
int width = texture.width;
|
||||||
|
int height = texture.height;
|
||||||
|
|
||||||
|
// Flip the pixels on the Y-axis
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
flippedPixels[(height - y - 1) * width + x] = originalPixels[y * width + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D flippedTexture = new Texture2D(width, height);
|
||||||
|
flippedTexture.SetPixels(flippedPixels);
|
||||||
|
flippedTexture.Apply();
|
||||||
|
|
||||||
|
return flippedTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCLUT()
|
private void DrawCLUT()
|
||||||
@@ -216,7 +301,7 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
|
|
||||||
GUILayout.Space(10);
|
GUILayout.Space(10);
|
||||||
|
|
||||||
int totalColors = clut.Length / 3;
|
int totalColors = clut.Length;
|
||||||
int totalRows = Mathf.CeilToInt((float)totalColors / maxColorsPerRow);
|
int totalRows = Mathf.CeilToInt((float)totalColors / maxColorsPerRow);
|
||||||
|
|
||||||
for (int row = 0; row < totalRows; row++)
|
for (int row = 0; row < totalRows; row++)
|
||||||
@@ -228,7 +313,13 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
for (int col = 0; col < colorsInRow; col++)
|
for (int col = 0; col < colorsInRow; col++)
|
||||||
{
|
{
|
||||||
int index = row * maxColorsPerRow + col;
|
int index = row * maxColorsPerRow + col;
|
||||||
Vector3 color = new Vector3(clut[index * 3], clut[index * 3 + 1], clut[index * 3 + 2]);
|
|
||||||
|
Vector3 color = new Vector3(
|
||||||
|
(clut[index] & 31) / 31.0f, // Red: bits 0–4
|
||||||
|
((clut[index] >> 5) & 31) / 31.0f, // Green: bits 5–9
|
||||||
|
((clut[index] >> 10) & 31) / 31.0f // Blue: bits 10–14
|
||||||
|
);
|
||||||
|
|
||||||
Rect rect = GUILayoutUtility.GetRect(swatchSize, swatchSize, GUILayout.ExpandWidth(false));
|
Rect rect = GUILayoutUtility.GetRect(swatchSize, swatchSize, GUILayout.ExpandWidth(false));
|
||||||
EditorGUI.DrawRect(rect, new Color(color.x, color.y, color.z));
|
EditorGUI.DrawRect(rect, new Color(color.x, color.y, color.z));
|
||||||
}
|
}
|
||||||
@@ -237,6 +328,7 @@ public class QuantizedPreviewWindow : EditorWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight)
|
private Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight)
|
||||||
{
|
{
|
||||||
RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight);
|
RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using Random = UnityEngine.Random;
|
|||||||
public class ImageQuantizer
|
public class ImageQuantizer
|
||||||
{
|
{
|
||||||
|
|
||||||
public static (ushort[], float[]) Quantize(Texture2D image, int bpp, int maxIterations = 10)
|
public static (ushort[], ushort[]) Quantize(Texture2D image, int bpp, int maxIterations = 10)
|
||||||
{
|
{
|
||||||
int width = image.width;
|
int width = image.width;
|
||||||
int height = image.height;
|
int height = image.height;
|
||||||
@@ -73,17 +73,20 @@ public class ImageQuantizer
|
|||||||
ushort packIndex = 0;
|
ushort packIndex = 0;
|
||||||
int bitShift = 0;
|
int bitShift = 0;
|
||||||
|
|
||||||
for (int i = 0; i < pixelColors.Length; i++)
|
// Loop through pixels and pack the data, flipping along the Y-axis
|
||||||
|
for (int y = height - 1; y >= 0; y--)
|
||||||
{
|
{
|
||||||
ushort centroidIndex = assignments[i];
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
int pixelIndex = y * width + x;
|
||||||
|
ushort centroidIndex = assignments[pixelIndex];
|
||||||
|
|
||||||
// For 4bpp, we need to pack 4 indices into a single integer
|
// For 4bpp, we need to pack 4 indices into a single integer
|
||||||
if (bpp == 4)
|
if (bpp == 4)
|
||||||
{
|
{
|
||||||
pixelArray[packIndex] |= (ushort)(centroidIndex << (bitShift * 4)); // Shift by 4 bits for each index
|
pixelArray[packIndex] |= (ushort)(centroidIndex << (bitShift * 4));
|
||||||
bitShift++;
|
bitShift++;
|
||||||
|
|
||||||
// Every 4 indices, move to the next position in the pixelArray
|
|
||||||
if (bitShift == 4)
|
if (bitShift == 4)
|
||||||
{
|
{
|
||||||
bitShift = 0;
|
bitShift = 0;
|
||||||
@@ -93,10 +96,9 @@ public class ImageQuantizer
|
|||||||
// For 8bpp, we need to pack 2 indices into a single integer
|
// For 8bpp, we need to pack 2 indices into a single integer
|
||||||
else if (bpp == 8)
|
else if (bpp == 8)
|
||||||
{
|
{
|
||||||
pixelArray[packIndex] |= (ushort)(centroidIndex << (bitShift * 8)); // Shift by 8 bits for each index
|
pixelArray[packIndex] |= (ushort)(centroidIndex << (bitShift * 8));
|
||||||
bitShift++;
|
bitShift++;
|
||||||
|
|
||||||
// Every 2 indices, move to the next position in the pixelArray
|
|
||||||
if (bitShift == 2)
|
if (bitShift == 2)
|
||||||
{
|
{
|
||||||
bitShift = 0;
|
bitShift = 0;
|
||||||
@@ -110,20 +112,23 @@ public class ImageQuantizer
|
|||||||
packIndex++;
|
packIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create the CLUT as a 1D array of RGB values
|
|
||||||
int actualColors = centroids.Count;
|
int actualColors = centroids.Count;
|
||||||
float[] clut = new float[actualColors * 3];
|
ushort[] clut = new ushort[actualColors];
|
||||||
for (int i = 0; i < actualColors; i++)
|
for (int i = 0; i < actualColors; i++)
|
||||||
{
|
{
|
||||||
clut[i * 3 + 0] = centroids[i].x; // Red
|
int red = Mathf.Clamp(Mathf.RoundToInt(centroids[i].x * 31), 0, 31); // 5 bits
|
||||||
clut[i * 3 + 1] = centroids[i].y; // Green
|
int green = Mathf.Clamp(Mathf.RoundToInt(centroids[i].y * 31), 0, 31); // 5 bits
|
||||||
clut[i * 3 + 2] = centroids[i].z; // Blue
|
int blue = Mathf.Clamp(Mathf.RoundToInt(centroids[i].z * 31), 0, 31); // 5 bits
|
||||||
|
|
||||||
|
clut[i] = (ushort)((blue << 10) | (green << 5) | red);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (pixelArray, clut);
|
return (pixelArray, clut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static List<Vector3> InitializeCentroids(Texture2D image, int maxColors)
|
private static List<Vector3> InitializeCentroids(Texture2D image, int maxColors)
|
||||||
{
|
{
|
||||||
List<Vector3> centroids = new List<Vector3>();
|
List<Vector3> centroids = new List<Vector3>();
|
||||||
|
|||||||
Reference in New Issue
Block a user