Exporting is working and rendering correctly in pcsx-redux
This commit is contained in:
@@ -8,13 +8,6 @@ using UnityEngine.Rendering;
|
||||
|
||||
public class VRAMEditorWindow : EditorWindow
|
||||
{
|
||||
class ProhibitedArea
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
public int Width;
|
||||
public int Height;
|
||||
}
|
||||
|
||||
private const int VramWidth = 1024;
|
||||
private const int VramHeight = 512;
|
||||
@@ -28,6 +21,9 @@ public class VRAMEditorWindow : EditorWindow
|
||||
private Color bufferColor2 = new Color(0, 1, 0, 0.5f);
|
||||
private Color prohibitedColor = new Color(1, 0, 0, 0.3f);
|
||||
|
||||
private static string _psxDataPath = "Assets/PSXData.asset";
|
||||
private PSXData _psxData;
|
||||
|
||||
private static readonly Vector2[] resolutions =
|
||||
{
|
||||
new Vector2(256, 240), new Vector2(256, 480),
|
||||
@@ -50,6 +46,8 @@ public class VRAMEditorWindow : EditorWindow
|
||||
vramImage.SetPixelData(blackPixels, 0);
|
||||
vramImage.Apply();
|
||||
blackPixels.Dispose();
|
||||
|
||||
LoadData();
|
||||
}
|
||||
|
||||
public static void PasteTexture(Texture2D baseTexture, Texture2D overlayTexture, int posX, int posY)
|
||||
@@ -73,7 +71,7 @@ public class VRAMEditorWindow : EditorWindow
|
||||
for (int x = 0; x < overlayWidth; x++)
|
||||
{
|
||||
int baseX = posX + x;
|
||||
int baseY = posY + y;
|
||||
int baseY = posY + y;
|
||||
if (baseX >= 0 && baseX < baseWidth && baseY >= 0 && baseY < baseHeight)
|
||||
{
|
||||
int baseIndex = baseY * baseWidth + baseX;
|
||||
@@ -102,26 +100,21 @@ public class VRAMEditorWindow : EditorWindow
|
||||
exp.CreatePSXTexture2D();
|
||||
}
|
||||
|
||||
List<Rect> dontPackAreas = new List<Rect>();
|
||||
foreach (ProhibitedArea area in prohibitedAreas)
|
||||
{
|
||||
dontPackAreas.Add(new Rect(area.X, area.Y, area.Width, area.Height));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
dontPackAreas.Add(buffer1);
|
||||
List<Rect> framebuffers = new List<Rect> { buffer1 };
|
||||
if (dualBuffering)
|
||||
{
|
||||
dontPackAreas.Add(buffer2);
|
||||
framebuffers.Add(buffer2);
|
||||
}
|
||||
|
||||
VRAMPacker tp = new VRAMPacker(dontPackAreas);
|
||||
VRAMPacker tp = new VRAMPacker(framebuffers, prohibitedAreas);
|
||||
var packed = tp.PackTexturesIntoVRAM(objects);
|
||||
|
||||
|
||||
for (int y = 0; y < VramHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < VramWidth; x++)
|
||||
@@ -190,10 +183,10 @@ public class VRAMEditorWindow : EditorWindow
|
||||
if (GUILayout.Button("Remove"))
|
||||
{
|
||||
prohibitedAreas.RemoveAt(i);
|
||||
break; // Avoid out-of-bounds errors after removal
|
||||
break;
|
||||
}
|
||||
|
||||
prohibitedAreas[i] = area; // Update the list with edited values
|
||||
prohibitedAreas[i] = area;
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
|
||||
@@ -204,7 +197,6 @@ public class VRAMEditorWindow : EditorWindow
|
||||
prohibitedAreas.Add(new ProhibitedArea());
|
||||
}
|
||||
|
||||
// New "Pack Textures" Button
|
||||
if (GUILayout.Button("Pack Textures"))
|
||||
{
|
||||
PackTextures();
|
||||
@@ -235,5 +227,38 @@ public class VRAMEditorWindow : EditorWindow
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
StoreData();
|
||||
}
|
||||
|
||||
private void LoadData()
|
||||
{
|
||||
_psxData = AssetDatabase.LoadAssetAtPath<PSXData>(_psxDataPath);
|
||||
|
||||
if (!_psxData)
|
||||
{
|
||||
_psxData = CreateInstance<PSXData>();
|
||||
AssetDatabase.CreateAsset(_psxData, _psxDataPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
selectedResolution = _psxData.OutputResolution;
|
||||
dualBuffering = _psxData.DualBuffering;
|
||||
verticalLayout = _psxData.VerticalBuffering;
|
||||
prohibitedAreas = _psxData.ProhibitedAreas;
|
||||
}
|
||||
|
||||
private void StoreData()
|
||||
{
|
||||
if (_psxData != null)
|
||||
{
|
||||
_psxData.OutputResolution = selectedResolution;
|
||||
_psxData.DualBuffering = dualBuffering;
|
||||
_psxData.VerticalBuffering = verticalLayout;
|
||||
_psxData.ProhibitedAreas = prohibitedAreas;
|
||||
|
||||
EditorUtility.SetDirty(_psxData);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Runtime/PSXData.cs
Normal file
12
Runtime/PSXData.cs
Normal 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
2
Runtime/PSXData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbd8c66199e036896848ce1569567dd6
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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
28
Runtime/Utils.cs
Normal 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
2
Runtime/Utils.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c9b0581c1e4eeb6296f4c162359043f
|
||||
Reference in New Issue
Block a user