code refactoring and improvement
add more "Unity" way of handling static object add RequireComponent to ensure that object have mesh and texture add some handling of empty texture on objects removed repetition from code set all formatting to be the same
This commit is contained in:
@@ -104,26 +104,28 @@ namespace SplashEdit.RuntimeCode
|
|||||||
// Compute the final shaded color by multiplying the light color by the intensity.
|
// Compute the final shaded color by multiplying the light color by the intensity.
|
||||||
Color shadedColor = lightColor * lightIntensity;
|
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
|
PSXVertex psxVertex = new PSXVertex
|
||||||
{
|
{
|
||||||
// Convert position to fixed-point, clamping values to a defined range.
|
// Convert position to fixed-point, clamping values to a defined range.
|
||||||
vx = (short)(Mathf.Clamp(vertex.x, -4f, 3.999f) * 4096),
|
vx = clampPosition(vertex.x),
|
||||||
vy = (short)(Mathf.Clamp(-vertex.y, -4f, 3.999f) * 4096),
|
vy = clampPosition(-vertex.y),
|
||||||
vz = (short)(Mathf.Clamp(vertex.z, -4f, 3.999f) * 4096),
|
vz = clampPosition(vertex.z),
|
||||||
|
|
||||||
// Convert normals to fixed-point.
|
// Convert normals to fixed-point.
|
||||||
nx = (short)(Mathf.Clamp(normal.x, -4f, 3.999f) * 4096),
|
nx = clampPosition(normal.x),
|
||||||
ny = (short)(Mathf.Clamp(-normal.y, -4f, 3.999f) * 4096),
|
ny = clampPosition(-normal.y),
|
||||||
nz = (short)(Mathf.Clamp(normal.z, -4f, 3.999f) * 4096),
|
nz = clampPosition(normal.z),
|
||||||
|
|
||||||
// Map UV coordinates to a byte range after scaling based on texture dimensions.
|
// Map UV coordinates to a byte range after scaling based on texture dimensions.
|
||||||
u = (byte)(Mathf.Clamp((uv.x * (textureWidth - 1)), 0, 255)),
|
u = clamp0255(uv.x * (textureWidth - 1)),
|
||||||
v = (byte)(Mathf.Clamp(((1.0f - uv.y) * (textureHeight - 1)), 0, 255)),
|
v = clamp0255((1.0f - uv.y) * (textureHeight - 1)),
|
||||||
|
|
||||||
// Convert the computed color to a byte range.
|
// Convert the computed color to a byte range.
|
||||||
r = (byte)(Mathf.Clamp(shadedColor.r * 255, 0, 255)),
|
r = clamp0255(shadedColor.r * 255),
|
||||||
g = (byte)(Mathf.Clamp(shadedColor.g * 255, 0, 255)),
|
g = clamp0255(shadedColor.g * 255),
|
||||||
b = (byte)(Mathf.Clamp(shadedColor.b * 255, 0, 255))
|
b = clamp0255(shadedColor.b * 255)
|
||||||
};
|
};
|
||||||
|
|
||||||
return psxVertex;
|
return psxVertex;
|
||||||
|
|||||||
@@ -2,16 +2,14 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace SplashEdit.RuntimeCode
|
namespace SplashEdit.RuntimeCode
|
||||||
{
|
{
|
||||||
|
[RequireComponent(typeof(MeshFilter))]
|
||||||
|
[RequireComponent(typeof(Renderer))]
|
||||||
public class PSXObjectExporter : MonoBehaviour
|
public class PSXObjectExporter : MonoBehaviour
|
||||||
{
|
{
|
||||||
public PSXBPP BitDepth = PSXBPP.TEX_8BIT; // Defines the bit depth of the texture (e.g., 4BPP, 8BPP)
|
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 { get; set; } // Stores the converted PlayStation-style texture
|
||||||
public PSXTexture2D Texture; // Stores the converted PlayStation-style texture
|
public PSXMesh Mesh { get; set; } // Stores the converted PlayStation-style mesh
|
||||||
|
|
||||||
[HideInInspector]
|
|
||||||
public PSXMesh Mesh; // Stores the converted PlayStation-style mesh
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts the object's material texture into a PlayStation-compatible texture.
|
/// Converts the object's material texture into a PlayStation-compatible texture.
|
||||||
@@ -19,11 +17,22 @@ namespace SplashEdit.RuntimeCode
|
|||||||
public void CreatePSXTexture2D()
|
public void CreatePSXTexture2D()
|
||||||
{
|
{
|
||||||
Renderer renderer = GetComponent<Renderer>();
|
Renderer renderer = GetComponent<Renderer>();
|
||||||
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 = PSXTexture2D.CreateFromTexture2D(texture, BitDepth);
|
||||||
Texture.OriginalTexture = texture; // Stores reference to the original texture
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,19 +40,16 @@ namespace SplashEdit.RuntimeCode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void CreatePSXMesh()
|
public void CreatePSXMesh()
|
||||||
{
|
{
|
||||||
MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
|
MeshFilter meshFilter = GetComponent<MeshFilter>();
|
||||||
if (meshFilter != null)
|
if (gameObject.isStatic)
|
||||||
{
|
{
|
||||||
if (MeshIsStatic)
|
// Static meshes take object transformation into account
|
||||||
{
|
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height, transform);
|
||||||
// Static meshes take object transformation into account
|
}
|
||||||
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height, transform);
|
else
|
||||||
}
|
{
|
||||||
else
|
// Dynamic meshes do not consider object transformation
|
||||||
{
|
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height);
|
||||||
// Dynamic meshes do not consider object transformation
|
|
||||||
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,151 +8,130 @@ using UnityEngine.SceneManagement;
|
|||||||
namespace SplashEdit.RuntimeCode
|
namespace SplashEdit.RuntimeCode
|
||||||
{
|
{
|
||||||
|
|
||||||
[ExecuteInEditMode]
|
[ExecuteInEditMode]
|
||||||
public class PSXSceneExporter : MonoBehaviour
|
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<ProhibitedArea> prohibitedAreas;
|
|
||||||
private VRAMPixel[,] vramPixels;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void Export()
|
|
||||||
{
|
{
|
||||||
LoadData();
|
private PSXObjectExporter[] _exporters;
|
||||||
_exporters = FindObjectsByType<PSXObjectExporter>(FindObjectsSortMode.None);
|
|
||||||
foreach (PSXObjectExporter exp in _exporters)
|
|
||||||
{
|
|
||||||
exp.CreatePSXTexture2D();
|
|
||||||
exp.CreatePSXMesh();
|
|
||||||
}
|
|
||||||
PackTextures();
|
|
||||||
ExportFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackTextures()
|
private PSXData _psxData;
|
||||||
{
|
private readonly string _psxDataPath = "Assets/PSXData.asset";
|
||||||
|
|
||||||
Rect buffer1 = new Rect(0, 0, selectedResolution.x, selectedResolution.y);
|
private Vector2 selectedResolution;
|
||||||
Rect buffer2 = verticalLayout ? new Rect(0, 256, selectedResolution.x, selectedResolution.y)
|
private bool dualBuffering;
|
||||||
: new Rect(selectedResolution.x, 0, selectedResolution.x, selectedResolution.y);
|
private bool verticalLayout;
|
||||||
|
private List<ProhibitedArea> prohibitedAreas;
|
||||||
|
private VRAMPixel[,] vramPixels;
|
||||||
|
|
||||||
List<Rect> framebuffers = new List<Rect> { buffer1 };
|
|
||||||
if (dualBuffering)
|
|
||||||
{
|
|
||||||
framebuffers.Add(buffer2);
|
|
||||||
}
|
|
||||||
|
|
||||||
VRAMPacker tp = new VRAMPacker(framebuffers, prohibitedAreas);
|
|
||||||
var packed = tp.PackTexturesIntoVRAM(_exporters);
|
|
||||||
_exporters = packed.processedObjects;
|
|
||||||
vramPixels = packed._vramPixels;
|
|
||||||
|
|
||||||
}
|
public void Export()
|
||||||
|
|
||||||
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++)
|
LoadData();
|
||||||
{
|
_exporters = FindObjectsByType<PSXObjectExporter>(FindObjectsSortMode.None);
|
||||||
writer.Write(vramPixels[x, y].Pack());
|
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;
|
List<Rect> framebuffers = new List<Rect> { buffer1 };
|
||||||
writer.Write((ushort)exporter.Mesh.Triangles.Count);
|
if (dualBuffering)
|
||||||
writer.Write((byte)exporter.Texture.BitDepth);
|
{
|
||||||
writer.Write((byte)exporter.Texture.TexpageX);
|
framebuffers.Add(buffer2);
|
||||||
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);
|
|
||||||
|
|
||||||
writer.Write((short)tri.v1.vx);
|
VRAMPacker tp = new VRAMPacker(framebuffers, prohibitedAreas);
|
||||||
writer.Write((short)tri.v1.vy);
|
var packed = tp.PackTexturesIntoVRAM(_exporters);
|
||||||
writer.Write((short)tri.v1.vz);
|
_exporters = packed.processedObjects;
|
||||||
writer.Write((short)tri.v1.nx);
|
vramPixels = packed._vramPixels;
|
||||||
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);
|
|
||||||
|
|
||||||
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<PSXData>(_psxDataPath);
|
||||||
|
|
||||||
|
if (!_psxData)
|
||||||
|
{
|
||||||
|
_psxData = ScriptableObject.CreateInstance<PSXData>();
|
||||||
|
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<PSXData>(_psxDataPath);
|
|
||||||
|
|
||||||
if (!_psxData)
|
|
||||||
{
|
|
||||||
_psxData = ScriptableObject.CreateInstance<PSXData>();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,9 +74,9 @@ namespace SplashEdit.RuntimeCode
|
|||||||
int atlasWidth = group.Key switch
|
int atlasWidth = group.Key switch
|
||||||
{
|
{
|
||||||
PSXBPP.TEX_16BIT => 256,
|
PSXBPP.TEX_16BIT => 256,
|
||||||
PSXBPP.TEX_8BIT => 128,
|
PSXBPP.TEX_8BIT => 128,
|
||||||
PSXBPP.TEX_4BIT => 64,
|
PSXBPP.TEX_4BIT => 64,
|
||||||
_ => 256
|
_ => 256
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new atlas for this group.
|
// 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))
|
foreach (var obj in group.OrderByDescending(obj => obj.Texture.QuantizedWidth * obj.Texture.Height))
|
||||||
{
|
{
|
||||||
// Remove duplicate textures
|
// 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());
|
obj.Texture = uniqueTextures.First(tex => tex.OriginalTexture.GetInstanceID() == obj.Texture.OriginalTexture.GetInstanceID());
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user