Minor refactor and QoL features, updated splashpack
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SplashEdit.RuntimeCode;
|
||||
using Unity.Collections;
|
||||
using UnityEditor;
|
||||
@@ -22,8 +23,6 @@ namespace SplashEdit.EditorCode
|
||||
private Color bufferColor1 = new Color(1, 0, 0, 0.5f);
|
||||
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 =
|
||||
@@ -55,7 +54,11 @@ namespace SplashEdit.EditorCode
|
||||
// Ensure minimum window size is applied.
|
||||
this.minSize = new Vector2(800, 600);
|
||||
|
||||
LoadData();
|
||||
_psxData = DataStorage.LoadData();
|
||||
selectedResolution = _psxData.OutputResolution;
|
||||
dualBuffering = _psxData.DualBuffering;
|
||||
verticalLayout = _psxData.VerticalBuffering;
|
||||
prohibitedAreas = _psxData.ProhibitedAreas;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -143,6 +146,9 @@ namespace SplashEdit.EditorCode
|
||||
|
||||
// Prompt the user to select a file location and save the VRAM data.
|
||||
string path = EditorUtility.SaveFilePanel("Select Output File", "", "output", "bin");
|
||||
|
||||
if (path != string.Empty)
|
||||
{
|
||||
using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)))
|
||||
{
|
||||
for (int y = 0; y < VramHeight; y++)
|
||||
@@ -154,6 +160,7 @@ namespace SplashEdit.EditorCode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
@@ -192,30 +199,51 @@ namespace SplashEdit.EditorCode
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label("Prohibited areas", EditorStyles.boldLabel);
|
||||
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.MaxHeight(150f));
|
||||
GUILayout.Label("Prohibited Areas", EditorStyles.boldLabel);
|
||||
GUILayout.Space(10);
|
||||
|
||||
scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, true, GUILayout.MinHeight(300f), GUILayout.ExpandWidth(true));
|
||||
|
||||
// List and edit each prohibited area.
|
||||
List<int> toRemove = new List<int>();
|
||||
|
||||
for (int i = 0; i < prohibitedAreas.Count; i++)
|
||||
{
|
||||
var area = prohibitedAreas[i];
|
||||
|
||||
area.X = EditorGUILayout.IntField("X", area.X);
|
||||
area.Y = EditorGUILayout.IntField("Y", area.Y);
|
||||
GUI.backgroundColor = new Color(0.95f, 0.95f, 0.95f);
|
||||
GUILayout.BeginVertical("box");
|
||||
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
// Display fields for editing the area
|
||||
area.X = EditorGUILayout.IntField("X Coordinate", area.X);
|
||||
area.Y = EditorGUILayout.IntField("Y Coordinate", area.Y);
|
||||
area.Width = EditorGUILayout.IntField("Width", area.Width);
|
||||
area.Height = EditorGUILayout.IntField("Height", area.Height);
|
||||
|
||||
if (GUILayout.Button("Remove"))
|
||||
|
||||
if (GUILayout.Button("Remove", GUILayout.Height(30)))
|
||||
{
|
||||
prohibitedAreas.RemoveAt(i);
|
||||
break;
|
||||
toRemove.Add(i); // Mark for removal
|
||||
}
|
||||
|
||||
|
||||
prohibitedAreas[i] = area;
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
|
||||
// Remove the areas marked for deletion outside the loop to avoid skipping elements
|
||||
foreach (var index in toRemove.OrderByDescending(x => x))
|
||||
{
|
||||
prohibitedAreas.RemoveAt(index);
|
||||
}
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
GUILayout.Space(10);
|
||||
|
||||
if (GUILayout.Button("Add Prohibited Area"))
|
||||
{
|
||||
prohibitedAreas.Add(new ProhibitedArea());
|
||||
@@ -230,7 +258,13 @@ namespace SplashEdit.EditorCode
|
||||
// Button to save settings; saving now occurs only on button press.
|
||||
if (GUILayout.Button("Save Settings"))
|
||||
{
|
||||
StoreData();
|
||||
_psxData.OutputResolution = selectedResolution;
|
||||
_psxData.DualBuffering = dualBuffering;
|
||||
_psxData.VerticalBuffering = verticalLayout;
|
||||
_psxData.ProhibitedAreas = prohibitedAreas;
|
||||
|
||||
DataStorage.StoreData(_psxData);
|
||||
EditorUtility.DisplayDialog("splashedit", "Vram configuration saved", "OK");
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
@@ -263,42 +297,10 @@ namespace SplashEdit.EditorCode
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads stored PSX data from the asset.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores current configuration to the PSX data asset.
|
||||
/// This is now triggered manually via the "Save Settings" button.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ namespace SplashEdit.RuntimeCode
|
||||
/// <param name="textureHeight">Height of the texture (default is 256).</param>
|
||||
/// <param name="transform">Optional transform to convert vertices to world space.</param>
|
||||
/// <returns>A new PSXMesh containing the converted triangles.</returns>
|
||||
public static PSXMesh CreateFromUnityMesh(Mesh mesh, float GTEScaling, int textureWidth = 256, int textureHeight = 256, Transform transform = null)
|
||||
public static PSXMesh CreateFromUnityMesh(Mesh mesh, float GTEScaling, Transform transform, bool isStatic, int textureWidth = 256, int textureHeight = 256)
|
||||
{
|
||||
PSXMesh psxMesh = new PSXMesh { Triangles = new List<Tri>() };
|
||||
|
||||
@@ -67,10 +67,27 @@ namespace SplashEdit.RuntimeCode
|
||||
int vid1 = indices[i + 1];
|
||||
int vid2 = indices[i + 2];
|
||||
|
||||
Vector3 v0, v1, v2;
|
||||
|
||||
// Transform vertices to world space 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];
|
||||
|
||||
if (isStatic)
|
||||
{
|
||||
v0 = transform.TransformPoint(vertices[vid0]);
|
||||
v1 = transform.TransformPoint(vertices[vid1]);
|
||||
v2 = transform.TransformPoint(vertices[vid2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Extract ONLY world scale
|
||||
Vector3 worldScale = transform.lossyScale;
|
||||
|
||||
// Apply scale *before* transformation, ensuring rotation isn’t affected
|
||||
v0 = Vector3.Scale(vertices[vid0], worldScale);
|
||||
v1 = Vector3.Scale(vertices[vid1], worldScale);
|
||||
v2 = Vector3.Scale(vertices[vid2], worldScale);
|
||||
|
||||
}
|
||||
|
||||
// Convert vertices to PSX format including fixed-point conversion and shading.
|
||||
PSXVertex psxV0 = ConvertToPSXVertex(v0, GTEScaling, normals[vid0], uv[vid0], lightDir, lightColor, textureWidth, textureHeight);
|
||||
@@ -116,13 +133,13 @@ namespace SplashEdit.RuntimeCode
|
||||
nz = (short)PSXTrig.ConvertCoordinateToPSX(normal.z),
|
||||
|
||||
// Map UV coordinates to a byte range after scaling based on texture dimensions.
|
||||
u = (byte)(Mathf.Clamp((uv.x * (textureWidth - 1)), 0, 255)),
|
||||
v = (byte)(Mathf.Clamp(((1.0f - uv.y) * (textureHeight - 1)), 0, 255)),
|
||||
u = (byte)Mathf.Clamp(uv.x * (textureWidth - 1), 0, 255),
|
||||
v = (byte)Mathf.Clamp((1.0f - uv.y) * (textureHeight - 1), 0, 255),
|
||||
|
||||
// Convert the computed color to a byte range.
|
||||
r = (byte)(Mathf.Clamp(shadedColor.r * 255, 0, 255)),
|
||||
g = (byte)(Mathf.Clamp(shadedColor.g * 255, 0, 255)),
|
||||
b = (byte)(Mathf.Clamp(shadedColor.b * 255, 0, 255))
|
||||
r = (byte)Mathf.Clamp(shadedColor.r * 255, 0, 255),
|
||||
g = (byte)Mathf.Clamp(shadedColor.g * 255, 0, 255),
|
||||
b = (byte)Mathf.Clamp(shadedColor.b * 255, 0, 255)
|
||||
};
|
||||
|
||||
return psxVertex;
|
||||
|
||||
@@ -34,16 +34,7 @@ namespace SplashEdit.RuntimeCode
|
||||
MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
|
||||
if (meshFilter != null)
|
||||
{
|
||||
if (MeshIsStatic)
|
||||
{
|
||||
// Static meshes take object transformation into account
|
||||
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, GTEScaling, Texture.Width, Texture.Height, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dynamic meshes do not consider object transformation
|
||||
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, GTEScaling, Texture.Width, Texture.Height);
|
||||
}
|
||||
Mesh = PSXMesh.CreateFromUnityMesh(meshFilter.sharedMesh, GTEScaling, transform, MeshIsStatic, Texture.Width, Texture.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,20 @@ namespace SplashEdit.RuntimeCode
|
||||
private TextureAtlas[] _atlases;
|
||||
|
||||
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();
|
||||
_psxData = DataStorage.LoadData();
|
||||
selectedResolution = _psxData.OutputResolution;
|
||||
dualBuffering = _psxData.DualBuffering;
|
||||
verticalLayout = _psxData.VerticalBuffering;
|
||||
prohibitedAreas = _psxData.ProhibitedAreas;
|
||||
|
||||
_exporters = FindObjectsByType<PSXObjectExporter>(FindObjectsSortMode.None);
|
||||
foreach (PSXObjectExporter exp in _exporters)
|
||||
{
|
||||
@@ -59,8 +60,37 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
}
|
||||
|
||||
public static string PSXMatrixToStringMultiline(int[,] matrix)
|
||||
{
|
||||
return $@"
|
||||
RT11={matrix[0, 0],6} RT12={matrix[0, 1],6} RT13={matrix[0, 2],6}
|
||||
RT21={matrix[1, 0],6} RT22={matrix[1, 1],6} RT23={matrix[1, 2],6}
|
||||
RT31={matrix[2, 0],6} RT32={matrix[2, 1],6} RT33={matrix[2, 2],6}";
|
||||
}
|
||||
|
||||
public static Vector3 ConvertPSXMatrixToEulerAngles(int[,] psxMatrix)
|
||||
{
|
||||
// Convert PSX fixed-point (s3.12) to float
|
||||
float r00 = psxMatrix[0, 0] / 4096.0f;
|
||||
float r01 = psxMatrix[0, 1] / 4096.0f;
|
||||
float r02 = psxMatrix[0, 2] / 4096.0f;
|
||||
float r10 = psxMatrix[1, 0] / 4096.0f;
|
||||
float r11 = psxMatrix[1, 1] / 4096.0f;
|
||||
float r12 = psxMatrix[1, 2] / 4096.0f;
|
||||
float r20 = psxMatrix[2, 0] / 4096.0f;
|
||||
float r21 = psxMatrix[2, 1] / 4096.0f;
|
||||
float r22 = psxMatrix[2, 2] / 4096.0f;
|
||||
|
||||
// Compute Euler angles (YXZ order for Unity)
|
||||
float thetaX = Mathf.Asin(-r21) * Mathf.Rad2Deg; // X Rotation
|
||||
float thetaY = Mathf.Atan2(r20, r22) * Mathf.Rad2Deg; // Y Rotation
|
||||
float thetaZ = Mathf.Atan2(r01, r11) * Mathf.Rad2Deg; // Z Rotation
|
||||
|
||||
return new Vector3(thetaX, thetaY, thetaZ);
|
||||
}
|
||||
void ExportFile()
|
||||
{
|
||||
|
||||
string path = EditorUtility.SaveFilePanel("Select Output File", "", "output", "bin");
|
||||
int totalFaces = 0;
|
||||
|
||||
@@ -86,12 +116,12 @@ namespace SplashEdit.RuntimeCode
|
||||
// GameObject section (exporters)
|
||||
foreach (PSXObjectExporter exporter in _exporters)
|
||||
{
|
||||
// Write object's position
|
||||
writer.Write((int)PSXTrig.ConvertCoordinateToPSX(transform.position.x));
|
||||
writer.Write((int)PSXTrig.ConvertCoordinateToPSX(transform.position.y));
|
||||
writer.Write((int)PSXTrig.ConvertCoordinateToPSX(transform.position.z));
|
||||
|
||||
// Write object's transform
|
||||
writer.Write((int)PSXTrig.ConvertCoordinateToPSX(exporter.transform.localToWorldMatrix.GetPosition().x, GTEScaling));
|
||||
writer.Write((int)PSXTrig.ConvertCoordinateToPSX(-exporter.transform.localToWorldMatrix.GetPosition().y, GTEScaling));
|
||||
writer.Write((int)PSXTrig.ConvertCoordinateToPSX(exporter.transform.localToWorldMatrix.GetPosition().z, GTEScaling));
|
||||
int[,] rotationMatrix = PSXTrig.ConvertRotationToPSXMatrix(exporter.transform.rotation);
|
||||
|
||||
writer.Write((int)rotationMatrix[0, 0]);
|
||||
writer.Write((int)rotationMatrix[0, 1]);
|
||||
writer.Write((int)rotationMatrix[0, 2]);
|
||||
@@ -277,35 +307,7 @@ namespace SplashEdit.RuntimeCode
|
||||
{
|
||||
long position = writer.BaseStream.Position;
|
||||
int padding = (int)(4 - (position % 4)) % 4; // Compute needed padding
|
||||
Debug.Log($"aligned {padding} bytes");
|
||||
writer.Write(new byte[padding]); // Write zero padding
|
||||
}
|
||||
|
||||
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);
|
||||
Vector3 sceneOrigin = new Vector3(0, 0, 0);
|
||||
Vector3 cubeSize = new Vector3(8.0f * GTEScaling, 8.0f * GTEScaling, 8.0f * GTEScaling);
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireCube(sceneOrigin, cubeSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
160
Runtime/Utils.cs
160
Runtime/Utils.cs
@@ -1,8 +1,39 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SplashEdit.RuntimeCode
|
||||
{
|
||||
|
||||
public static class DataStorage
|
||||
{
|
||||
private static readonly string psxDataPath = "Assets/PSXData.asset";
|
||||
public static PSXData LoadData()
|
||||
{
|
||||
PSXData psxData = AssetDatabase.LoadAssetAtPath<PSXData>(psxDataPath);
|
||||
|
||||
if (!psxData)
|
||||
{
|
||||
psxData = ScriptableObject.CreateInstance<PSXData>();
|
||||
AssetDatabase.CreateAsset(psxData, psxDataPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
return psxData;
|
||||
}
|
||||
|
||||
public static void StoreData(PSXData psxData)
|
||||
{
|
||||
if (psxData != null)
|
||||
{
|
||||
EditorUtility.SetDirty(psxData);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a prohibited area in PlayStation 2D VRAM where textures should not be packed.
|
||||
/// This class provides conversion methods to and from Unity's Rect structure.
|
||||
@@ -42,48 +73,83 @@ namespace SplashEdit.RuntimeCode
|
||||
return new Rect(X, Y, Width, Height);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A utility class containing methods for converting Unity-specific data formats to PSX-compatible formats.
|
||||
/// This includes converting coordinates and rotations to PSX's 3.12 fixed-point format.
|
||||
/// </summary>
|
||||
public static class PSXTrig
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Converts a floating-point coordinate to a PSX-compatible 3.12 fixed-point format.
|
||||
/// The value is clamped to the range [-4, 3.999] and scaled by the provided GTEScaling factor.
|
||||
/// </summary>
|
||||
/// <param name="value">The coordinate value to convert.</param>
|
||||
/// <param name="GTEScaling">A scaling factor for the value (default is 1.0f).</param>
|
||||
/// <returns>The converted coordinate in 3.12 fixed-point format.</returns>
|
||||
public static short ConvertCoordinateToPSX(float value, float GTEScaling = 1.0f)
|
||||
{
|
||||
return (short)(Mathf.Clamp(value/GTEScaling, -4f, 3.999f) * 4096);
|
||||
return (short)(Mathf.Clamp(value / GTEScaling, -4f, 3.999f) * 4096);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts a quaternion rotation to a PSX-compatible 3x3 rotation matrix.
|
||||
/// The matrix is adjusted for the difference in the Y-axis orientation between Unity (Y-up) and PSX (Y-down).
|
||||
/// Each matrix element is converted to a 3.12 fixed-point format.
|
||||
/// </summary>
|
||||
/// <param name="rotation">The quaternion representing the rotation to convert.</param>
|
||||
/// <returns>A 3x3 matrix representing the PSX-compatible rotation.</returns>
|
||||
public static int[,] ConvertRotationToPSXMatrix(Quaternion rotation)
|
||||
{
|
||||
float xx = rotation.x * rotation.x;
|
||||
float yy = rotation.y * rotation.y;
|
||||
float zz = rotation.z * rotation.z;
|
||||
float xy = rotation.x * rotation.y;
|
||||
float xz = rotation.x * rotation.z;
|
||||
float yz = rotation.y * rotation.z;
|
||||
float wx = rotation.w * rotation.x;
|
||||
float wy = rotation.w * rotation.y;
|
||||
float wz = rotation.w * rotation.z;
|
||||
// Convert the quaternion to a Unity rotation matrix.
|
||||
Matrix4x4 unityMatrix = Matrix4x4.Rotate(rotation);
|
||||
|
||||
// Create the 3x3 rotation matrix
|
||||
int[,] psxMatrix = new int[3, 3]
|
||||
// Flip the Y-axis to match PSX's Y-down convention.
|
||||
float[,] fixedMatrix = new float[3, 3]
|
||||
{
|
||||
{ ConvertToFixed12(1.0f - 2.0f * (yy + zz)), ConvertToFixed12(2.0f * (xy - wz)), ConvertToFixed12(2.0f * (xz + wy)) },
|
||||
{ ConvertToFixed12(2.0f * (xy + wz)), ConvertToFixed12(1.0f - 2.0f * (xx + zz)), ConvertToFixed12(2.0f * (yz - wx)) },
|
||||
{ ConvertToFixed12(2.0f * (xz - wy)), ConvertToFixed12(2.0f * (yz + wx)), ConvertToFixed12(1.0f - 2.0f * (xx + yy)) }
|
||||
{ unityMatrix.m00, -unityMatrix.m01, unityMatrix.m02 }, // Flip Y
|
||||
{ -unityMatrix.m10, unityMatrix.m11, -unityMatrix.m12 }, // Flip Y
|
||||
{ unityMatrix.m20, -unityMatrix.m21, unityMatrix.m22 } // Flip Y
|
||||
};
|
||||
|
||||
// Convert the Unity matrix to PSX fixed-point format.
|
||||
int[,] psxMatrix = new int[3, 3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
psxMatrix[i, j] = ConvertToFixed12(fixedMatrix[i, j]);
|
||||
}
|
||||
}
|
||||
|
||||
return psxMatrix;
|
||||
}
|
||||
|
||||
private static int ConvertToFixed12(float value)
|
||||
/// <summary>
|
||||
/// Converts a floating-point value to a 3.12 fixed-point format (PSX format).
|
||||
/// The value is scaled by a factor of 4096 and clamped to the range of a signed 16-bit integer.
|
||||
/// </summary>
|
||||
/// <param name="value">The floating-point value to convert.</param>
|
||||
/// <returns>The converted value in 3.12 fixed-point format as a 16-bit signed integer.</returns>
|
||||
public static short ConvertToFixed12(float value)
|
||||
{
|
||||
return (int)(value * 4096.0f); // 2^12 = 4096
|
||||
int fixedValue = Mathf.RoundToInt(value * 4096.0f); // Scale to 3.12 format
|
||||
return (short)Mathf.Clamp(fixedValue, -32768, 32767); // Clamp to signed 16-bit
|
||||
}
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
/// <summary>
|
||||
/// Represents the attributes of a texture page in the PSX graphics system.
|
||||
/// Provides methods for setting various properties such as the page coordinates, transparency type, color mode, dithering, and display area.
|
||||
/// </summary>
|
||||
public struct TPageAttr
|
||||
{
|
||||
public ushort info;
|
||||
public ushort info; // Stores the packed attribute information as a 16-bit unsigned integer.
|
||||
|
||||
/// <summary>
|
||||
/// Sets the X-coordinate of the texture page.
|
||||
/// The lower 4 bits of the 'info' field are used to store the X value.
|
||||
/// </summary>
|
||||
/// <param name="x">The X-coordinate value (0 to 15).</param>
|
||||
/// <returns>The updated TPageAttr instance.</returns>
|
||||
public TPageAttr SetPageX(byte x)
|
||||
{
|
||||
info &= 0xFFF0; // Clear lower 4 bits
|
||||
@@ -92,6 +158,12 @@ namespace SplashEdit.RuntimeCode
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Y-coordinate of the texture page.
|
||||
/// The 4th bit of the 'info' field is used to store the Y value (0 or 1).
|
||||
/// </summary>
|
||||
/// <param name="y">The Y-coordinate value (0 or 1).</param>
|
||||
/// <returns>The updated TPageAttr instance.</returns>
|
||||
public TPageAttr SetPageY(byte y)
|
||||
{
|
||||
info &= 0xFFEF; // Clear bit 4
|
||||
@@ -100,6 +172,12 @@ namespace SplashEdit.RuntimeCode
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the transparency type of the texture page.
|
||||
/// The transparency type is stored in bits 5 and 6 of the 'info' field.
|
||||
/// </summary>
|
||||
/// <param name="trans">The transparency type to set.</param>
|
||||
/// <returns>The updated TPageAttr instance.</returns>
|
||||
public TPageAttr Set(SemiTrans trans)
|
||||
{
|
||||
info &= 0xFF9F; // Clear bits 5 and 6
|
||||
@@ -108,6 +186,12 @@ namespace SplashEdit.RuntimeCode
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the color mode of the texture page.
|
||||
/// The color mode is stored in bits 7 and 8 of the 'info' field.
|
||||
/// </summary>
|
||||
/// <param name="mode">The color mode to set (4-bit, 8-bit, or 16-bit).</param>
|
||||
/// <returns>The updated TPageAttr instance.</returns>
|
||||
public TPageAttr Set(ColorMode mode)
|
||||
{
|
||||
info &= 0xFE7F; // Clear bits 7 and 8
|
||||
@@ -116,31 +200,54 @@ namespace SplashEdit.RuntimeCode
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables dithering for the texture page.
|
||||
/// Dithering is stored in bit 9 of the 'info' field.
|
||||
/// </summary>
|
||||
/// <param name="dithering">True to enable dithering, false to disable it.</param>
|
||||
/// <returns>The updated TPageAttr instance.</returns>
|
||||
public TPageAttr SetDithering(bool dithering)
|
||||
{
|
||||
if (dithering)
|
||||
info |= 0x0200;
|
||||
info |= 0x0200; // Set bit 9 to enable dithering
|
||||
else
|
||||
info &= 0xFDFF;
|
||||
info &= 0xFDFF; // Clear bit 9 to disable dithering
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the display area for the texture page.
|
||||
/// This will clear bit 10 of the 'info' field.
|
||||
/// </summary>
|
||||
/// <returns>The updated TPageAttr instance.</returns>
|
||||
public TPageAttr DisableDisplayArea()
|
||||
{
|
||||
info &= 0xFBFF; // Clear bit 10
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the display area for the texture page.
|
||||
/// This will set bit 10 of the 'info' field.
|
||||
/// </summary>
|
||||
/// <returns>The updated TPageAttr instance.</returns>
|
||||
public TPageAttr EnableDisplayArea()
|
||||
{
|
||||
info |= 0x0400; // Set bit 10
|
||||
info |= 0x0400; // Set bit 10 to enable display area
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the TPageAttr instance, showing the 'info' value in hexadecimal.
|
||||
/// </summary>
|
||||
/// <returns>A string representing the 'info' value in hexadecimal format.</returns>
|
||||
public override string ToString() => $"Info: 0x{info:X4}";
|
||||
|
||||
|
||||
// Define the enums for SemiTrans and ColorMode (assuming their values)
|
||||
|
||||
/// <summary>
|
||||
/// Defines the transparency types for a texture page.
|
||||
/// </summary>
|
||||
public enum SemiTrans : uint
|
||||
{
|
||||
None = 0,
|
||||
@@ -149,6 +256,9 @@ namespace SplashEdit.RuntimeCode
|
||||
Type3 = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the color modes for a texture page.
|
||||
/// </summary>
|
||||
public enum ColorMode : uint
|
||||
{
|
||||
Mode4Bit = 0,
|
||||
|
||||
Reference in New Issue
Block a user