Exporting is working and rendering correctly in pcsx-redux

This commit is contained in:
2025-03-14 00:15:35 +01:00
parent 1e480f6c15
commit 8a6679dff6
10 changed files with 241 additions and 55 deletions

12
Runtime/PSXData.cs Normal file
View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using PSXSplash.RuntimeCode;
using UnityEngine;
[CreateAssetMenu(fileName = "PSXData", menuName = "Scriptable Objects/PSXData")]
public class PSXData : ScriptableObject
{
public Vector2 OutputResolution = new Vector2(320, 240);
public bool DualBuffering = true;
public bool VerticalBuffering = true;
public List<ProhibitedArea> ProhibitedAreas = new List<ProhibitedArea>();
}

2
Runtime/PSXData.cs.meta Normal file
View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cbd8c66199e036896848ce1569567dd6

View File

@@ -3,7 +3,6 @@ using UnityEngine;
namespace PSXSplash.RuntimeCode
{
public struct PSXVertex
{
public short vx, vy, vz;
@@ -22,7 +21,7 @@ namespace PSXSplash.RuntimeCode
{
public List<Tri> Triangles;
public static PSXMesh CreateFromUnityMesh(Mesh mesh, int textureWidth, int textureHeight)
public static PSXMesh CreateFromUnityMesh(Mesh mesh, int textureWidth = 256, int textureHeight = 256, Transform transform = null)
{
PSXMesh psxMesh = new PSXMesh { Triangles = new List<Tri>() };
@@ -36,11 +35,16 @@ namespace PSXSplash.RuntimeCode
int vid1 = indices[i + 1];
int vid2 = indices[i + 2];
PSXVertex v0 = ConvertToPSXVertex(vertices[vid0], uv[vid0], textureWidth, textureHeight);
PSXVertex v1 = ConvertToPSXVertex(vertices[vid1], uv[vid1], textureWidth, textureHeight);
PSXVertex v2 = ConvertToPSXVertex(vertices[vid2], uv[vid2], textureWidth, textureHeight);
// Convert to world space only if a transform is provided
Vector3 v0 = transform ? transform.TransformPoint(vertices[vid0]) : vertices[vid0];
Vector3 v1 = transform ? transform.TransformPoint(vertices[vid1]) : vertices[vid1];
Vector3 v2 = transform ? transform.TransformPoint(vertices[vid2]) : vertices[vid2];
psxMesh.Triangles.Add(new Tri { v0 = v0, v1 = v1, v2 = v2 });
PSXVertex psxV0 = ConvertToPSXVertex(v0, uv[vid0], textureWidth, textureHeight);
PSXVertex psxV1 = ConvertToPSXVertex(v1, uv[vid1], textureWidth, textureHeight);
PSXVertex psxV2 = ConvertToPSXVertex(v2, uv[vid2], textureWidth, textureHeight);
psxMesh.Triangles.Add(new Tri { v0 = psxV0, v1 = psxV1, v2 = psxV2 });
}
return psxMesh;
@@ -51,12 +55,12 @@ namespace PSXSplash.RuntimeCode
PSXVertex psxVertex = new PSXVertex
{
vx = (short)(Mathf.Clamp(vertex.x, -4f, 3.999f) * 4096),
vy = (short)(Mathf.Clamp(vertex.y, -4f, 3.999f) * 4096),
vy = (short)(Mathf.Clamp(-vertex.y, -4f, 3.999f) * 4096),
vz = (short)(Mathf.Clamp(vertex.z, -4f, 3.999f) * 4096),
u = (byte)(Mathf.Clamp(uv.x * textureWidth, 0, 255)),
v = (byte)(Mathf.Clamp((1.0f - uv.y) * textureHeight, 0, 255))
u = (byte)(Mathf.Clamp((uv.x * (textureWidth-1)), 0, 255)),
v = (byte)(Mathf.Clamp(((1.0f - uv.y) * (textureHeight-1)), 0, 255))
};
return psxVertex;
}
}
}
}

View File

@@ -5,6 +5,7 @@ namespace PSXSplash.RuntimeCode
public class PSXObjectExporter : MonoBehaviour
{
public PSXBPP BitDepth;
public bool MeshIsStatic = true;
[HideInInspector]
public PSXTexture2D Texture;
@@ -27,7 +28,12 @@ namespace PSXSplash.RuntimeCode
MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if (meshFilter != null)
{
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.mesh, Texture.Width, Texture.Height);
if(MeshIsStatic) {
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height, transform);
}
else {
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, Texture.Width, Texture.Height);
}
}
}
}

View File

@@ -1,34 +1,128 @@
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Overlays;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace PSXSplash.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<ProhibitedArea> prohibitedAreas;
private VRAMPixel[,] vramPixels;
public void Export()
{
Debug.Log($"Exporting scene: {SceneManager.GetActiveScene().name}");
Scene activeScene = SceneManager.GetActiveScene();
foreach (GameObject obj in activeScene.GetRootGameObjects())
LoadData();
_exporters = FindObjectsByType<PSXObjectExporter>(FindObjectsSortMode.None);
foreach (PSXObjectExporter exp in _exporters)
{
ExportAllPSXExporters(obj.transform);
exp.CreatePSXTexture2D();
exp.CreatePSXMesh();
}
PackTextures();
ExportFile();
}
void ExportAllPSXExporters(Transform parentTransform)
void PackTextures()
{
PSXObjectExporter exporter = parentTransform.GetComponent<PSXObjectExporter>();
if (exporter != null)
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);
List<Rect> framebuffers = new List<Rect> { buffer1 };
if (dualBuffering)
{
//exporter.Export();
framebuffers.Add(buffer2);
}
foreach (Transform child in parentTransform)
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++)
{
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);
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((byte)(tri.v0.u + exporter.Texture.PackingX * expander));
writer.Write((byte)(tri.v0.v + exporter.Texture.PackingY));
writer.Write((short)tri.v1.vx);
writer.Write((short)tri.v1.vy);
writer.Write((short)tri.v1.vz);
writer.Write((byte)(tri.v1.u + exporter.Texture.PackingX * expander));
writer.Write((byte)(tri.v1.v + exporter.Texture.PackingY));
writer.Write((short)tri.v2.vx);
writer.Write((short)tri.v2.vy);
writer.Write((short)tri.v2.vz);
writer.Write((byte)(tri.v2.u + exporter.Texture.PackingX * expander));
writer.Write((byte)(tri.v2.v + exporter.Texture.PackingY));
}
}
}
Debug.Log(totalFaces);
}
public void LoadData()
{
_psxData = AssetDatabase.LoadAssetAtPath<PSXData>(_psxDataPath);
if (!_psxData)
{
ExportAllPSXExporters(child);
_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()

View File

@@ -102,14 +102,15 @@ namespace PSXSplash.RuntimeCode
public Texture2D OriginalTexture;
// Within supertexture
public int PackingX;
public int PackingY;
public byte PackingX;
public byte PackingY;
public int TexpageNum;
public byte TexpageX;
public byte TexpageY;
// Absolute positioning
public int ClutPackingX;
public int ClutPackingY;
public ushort ClutPackingX;
public ushort ClutPackingY;
private int _maxColors;
@@ -263,7 +264,6 @@ namespace PSXSplash.RuntimeCode
Texture2D vramTexture = new Texture2D(QuantizedWidth, Height);
List<Color> colors = new List<Color>();
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < QuantizedWidth; x++)

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using UnityEngine;
@@ -29,9 +30,16 @@ namespace PSXSplash.RuntimeCode
private VRAMPixel[,] _vramPixels;
public VRAMPacker(List<Rect> reservedAreas)
public VRAMPacker(List<Rect> framebuffers, List<ProhibitedArea> reservedAreas)
{
_reservedAreas = reservedAreas;
List<Rect> areasConvertedToRect = new List<Rect>();
foreach (ProhibitedArea area in reservedAreas)
{
areasConvertedToRect.Add(new Rect(area.X, area.Y, area.Width, area.Height));
}
_reservedAreas = areasConvertedToRect;
_reservedAreas.Add(framebuffers[0]);
_reservedAreas.Add(framebuffers[1]);
_vramPixels = new VRAMPixel[VRAM_WIDTH, VRAM_HEIGHT];
}
@@ -77,15 +85,16 @@ namespace PSXSplash.RuntimeCode
ArrangeAtlasesInVRAM();
AllocateCLUTs();
BuildVram();
return (objects, _vramPixels);
}
private bool TryPlaceTextureInAtlas(TextureAtlas atlas, PSXTexture2D texture)
{
for (int y = 0; y <= TextureAtlas.Height - texture.Height; y++)
for (byte y = 0; y <= TextureAtlas.Height - texture.Height; y++)
{
for (int x = 0; x <= atlas.Width - texture.QuantizedWidth; x++)
for (byte x = 0; x <= atlas.Width - texture.QuantizedWidth; x++)
{
var candidateRect = new Rect(x, y, texture.QuantizedWidth, texture.Height);
if (!atlas.ContainedTextures.Any(tex => new Rect(tex.PackingX, tex.PackingY, tex.QuantizedWidth, tex.Height).Overlaps(candidateRect)))
@@ -129,7 +138,11 @@ namespace PSXSplash.RuntimeCode
{
foreach (PSXTexture2D texture in atlas.ContainedTextures)
{
texture.TexpageNum = CalculateTexpage(atlas.PositionX, atlas.PositionY);
int colIndex = atlas.PositionX / 64;
int rowIndex = atlas.PositionY / 256;
texture.TexpageX = (byte)colIndex;
texture.TexpageY = (byte)rowIndex;
}
break;
}
@@ -153,9 +166,9 @@ namespace PSXSplash.RuntimeCode
int clutHeight = 1;
bool placed = false;
for (int x = 0; x < VRAM_WIDTH; x+=16)
for (ushort x = 0; x < VRAM_WIDTH; x += 16)
{
for (int y = 0; y <= VRAM_HEIGHT; y++)
for (ushort y = 0; y <= VRAM_HEIGHT; y++)
{
var candidate = new Rect(x, y, clutWidth, clutHeight);
if (IsPlacementValid(candidate))

28
Runtime/Utils.cs Normal file
View File

@@ -0,0 +1,28 @@
using UnityEngine;
namespace PSXSplash.RuntimeCode
{
public class ProhibitedArea
{
public int X;
public int Y;
public int Width;
public int Height;
public static ProhibitedArea FromUnityRect(Rect rect)
{
return new ProhibitedArea
{
X = Mathf.RoundToInt(rect.x),
Y = Mathf.RoundToInt(rect.y),
Width = Mathf.RoundToInt(rect.width),
Height = Mathf.RoundToInt(rect.height)
};
}
public Rect ToUnityRect()
{
return new Rect(X, Y, Width, Height);
}
}
}

2
Runtime/Utils.cs.meta Normal file
View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8c9b0581c1e4eeb6296f4c162359043f