Add mesh reading and conversion support
This commit is contained in:
parent
65dc96c4d6
commit
aa1277a4e1
25 changed files with 2212 additions and 9 deletions
207
LibDgf/Ps2/Gs/GsMemoryUtils.cs
Normal file
207
LibDgf/Ps2/Gs/GsMemoryUtils.cs
Normal file
|
@ -0,0 +1,207 @@
|
|||
using LibDgf.Txm;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Gs
|
||||
{
|
||||
public static class GsMemoryUtils
|
||||
{
|
||||
public const int BYTES_PER_COLUMN = 64;
|
||||
public const int COLUMNS_PER_BLOCK = 4;
|
||||
public const int BYTES_PER_BLOCK = BYTES_PER_COLUMN * COLUMNS_PER_BLOCK;
|
||||
public const int BLOCKS_PER_PAGE = 32;
|
||||
public const int BYTES_PER_PAGE = BYTES_PER_BLOCK * BLOCKS_PER_PAGE;
|
||||
public const int TOTAL_PAGES = 512;
|
||||
public const int TOTAL_BYTES = BYTES_PER_PAGE * TOTAL_PAGES;
|
||||
|
||||
public class ColumnParams
|
||||
{
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public int BitsPerPixel { get; }
|
||||
public int ColsPerPage { get; }
|
||||
public int RowsPerPage { get; }
|
||||
|
||||
internal ColumnParams(int width, int height, int bitsPerPixel, int colsPerPage, int rowsPerPage)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
BitsPerPixel = bitsPerPixel;
|
||||
ColsPerPage = colsPerPage;
|
||||
RowsPerPage = rowsPerPage;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly ColumnParams PSMCT32_COLUMN_PARAMS = new ColumnParams(8, 2, 32, 8, 4);
|
||||
static readonly ColumnParams PSMT8_COLUMN_PARAMS = new ColumnParams(16, 4, 8, 8, 4);
|
||||
static readonly ColumnParams PSMT4_COLUMN_PARAMS = new ColumnParams(32, 4, 4, 4, 8);
|
||||
|
||||
public static ColumnParams GetColumnParams(TxmPixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case TxmPixelFormat.PSMCT32:
|
||||
case TxmPixelFormat.PSMCT24:
|
||||
return PSMCT32_COLUMN_PARAMS;
|
||||
case TxmPixelFormat.PSMT8:
|
||||
return PSMT8_COLUMN_PARAMS;
|
||||
case TxmPixelFormat.PSMT4:
|
||||
return PSMT4_COLUMN_PARAMS;
|
||||
default:
|
||||
throw new NotSupportedException($"{format} not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ReadColumn(BinaryReader br, TxmPixelFormat format, int bytesPerLine)
|
||||
{
|
||||
ColumnParams colParams = GetColumnParams(format);
|
||||
byte[] data = new byte[colParams.Width * colParams.Height * colParams.BitsPerPixel / 8];
|
||||
int bytesPerDestLine = colParams.Width * colParams.BitsPerPixel / 8;
|
||||
int skipLength = bytesPerLine - bytesPerDestLine;
|
||||
for (int i = 0; i < colParams.Height; ++i)
|
||||
{
|
||||
byte[] lineData = br.ReadBytes(bytesPerDestLine);
|
||||
br.BaseStream.Seek(skipLength, SeekOrigin.Current);
|
||||
Buffer.BlockCopy(lineData, 0, data, i * bytesPerDestLine, lineData.Length);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void WriteColumn(BinaryWriter bw, TxmPixelFormat format, int bytesPerLine, byte[] data)
|
||||
{
|
||||
ColumnParams colParams = GetColumnParams(format);
|
||||
int bytesPerDestLine = colParams.Width * colParams.BitsPerPixel / 8;
|
||||
int skipLength = bytesPerLine - bytesPerDestLine;
|
||||
for (int i = 0; i < colParams.Height; ++i)
|
||||
{
|
||||
bw.Write(data, i * bytesPerDestLine, bytesPerDestLine);
|
||||
bw.Seek(skipLength, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// Block address to linear offset lookup for PSMCT32
|
||||
static readonly int[,] PSMCT32_BLOCK_LOOKUP = new[,]
|
||||
{
|
||||
{ 0, 1, 4, 5, 16, 17, 20, 21 },
|
||||
{ 2, 3, 6, 7, 18, 19, 22, 23 },
|
||||
{ 8, 9, 12, 13, 24, 25, 28, 29 },
|
||||
{ 10, 11, 14, 15, 26, 27, 30, 31 }
|
||||
};
|
||||
|
||||
static readonly int[,] PSMT8_BLOCK_LOOKUP = new[,]
|
||||
{
|
||||
{ 0, 1, 4, 5, 16, 17, 20, 21 },
|
||||
{ 2, 3, 6, 7, 18, 19, 22, 23 },
|
||||
{ 8, 9, 12, 13, 24, 25, 28, 29 },
|
||||
{ 10, 11, 14, 15, 26, 27, 30, 31 }
|
||||
};
|
||||
|
||||
static readonly int[,] PSMT4_BLOCK_LOOKUP = new[,]
|
||||
{
|
||||
{ 0, 2, 8, 10 },
|
||||
{ 1, 3, 9, 11 },
|
||||
{ 4, 6, 12, 14 },
|
||||
{ 5, 7, 13, 15 },
|
||||
{ 16, 18, 24, 26 },
|
||||
{ 17, 19, 25, 27 },
|
||||
{ 20, 22, 28, 30 },
|
||||
{ 21, 23, 29, 31 }
|
||||
};
|
||||
|
||||
static readonly int[] PSMCT32_BLOCK_REVERSE_LOOKUP;
|
||||
static readonly int[] PSMT8_BLOCK_REVERSE_LOOKUP;
|
||||
static readonly int[] PSMT4_BLOCK_REVERSE_LOOKUP;
|
||||
|
||||
static GsMemoryUtils()
|
||||
{
|
||||
PSMCT32_BLOCK_REVERSE_LOOKUP = MakeReverseLookup(PSMCT32_BLOCK_LOOKUP);
|
||||
PSMT8_BLOCK_REVERSE_LOOKUP = MakeReverseLookup(PSMT8_BLOCK_LOOKUP);
|
||||
PSMT4_BLOCK_REVERSE_LOOKUP = MakeReverseLookup(PSMT4_BLOCK_LOOKUP);
|
||||
}
|
||||
|
||||
static int[] MakeReverseLookup(int[,] lut)
|
||||
{
|
||||
int[] reverse = new int[lut.Length];
|
||||
int i = 0;
|
||||
foreach (var n in lut)
|
||||
{
|
||||
reverse[n] = i++;
|
||||
}
|
||||
return reverse;
|
||||
}
|
||||
|
||||
public static int CalcBlockNumber(TxmPixelFormat format, int blockX, int blockY, int texBufWidth)
|
||||
{
|
||||
ColumnParams colParams = GetColumnParams(format);
|
||||
switch (format)
|
||||
{
|
||||
case TxmPixelFormat.PSMCT32:
|
||||
return CalcBlockNumber(colParams, PSMCT32_BLOCK_LOOKUP,blockX, blockY, texBufWidth);
|
||||
case TxmPixelFormat.PSMT8:
|
||||
return CalcBlockNumber(colParams, PSMT8_BLOCK_LOOKUP, blockX, blockY, texBufWidth);
|
||||
case TxmPixelFormat.PSMT4:
|
||||
return CalcBlockNumber(colParams, PSMT4_BLOCK_LOOKUP, blockX, blockY, texBufWidth);
|
||||
default:
|
||||
throw new NotSupportedException($"{format} not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public static int CalcBlockMemoryOffset(TxmPixelFormat format, int index)
|
||||
{
|
||||
ColumnParams colParams = GetColumnParams(format);
|
||||
switch (format)
|
||||
{
|
||||
case TxmPixelFormat.PSMCT32:
|
||||
return CalcBlockMemoryOffset(colParams, PSMCT32_BLOCK_REVERSE_LOOKUP, index);
|
||||
case TxmPixelFormat.PSMT8:
|
||||
return CalcBlockMemoryOffset(colParams, PSMT8_BLOCK_REVERSE_LOOKUP, index);
|
||||
case TxmPixelFormat.PSMT4:
|
||||
return CalcBlockMemoryOffset(colParams, PSMT4_BLOCK_REVERSE_LOOKUP, index);
|
||||
default:
|
||||
throw new NotSupportedException($"{format} not supported");
|
||||
}
|
||||
}
|
||||
|
||||
static int CalcBlockNumber(ColumnParams colParams, int[,] lut, int blockX, int blockY, int texBufWidth)
|
||||
{
|
||||
int pageX = blockX / colParams.ColsPerPage;
|
||||
int pageY = blockY / colParams.RowsPerPage;
|
||||
int blockXInPage = blockX % colParams.ColsPerPage;
|
||||
int blockYInPage = blockY % colParams.RowsPerPage;
|
||||
|
||||
return (pageY * texBufWidth + pageX) * BLOCKS_PER_PAGE + lut[blockYInPage, blockXInPage];
|
||||
}
|
||||
|
||||
static int CalcBlockMemoryOffset(ColumnParams colParams, int[] lut, int index)
|
||||
{
|
||||
int pageIndex = index / BLOCKS_PER_PAGE;
|
||||
int rem = index % BLOCKS_PER_PAGE;
|
||||
int pixelsPerBlock = colParams.Width * colParams.Height * COLUMNS_PER_BLOCK;
|
||||
int memBlockNum = lut[rem];
|
||||
int fullBlockRows = memBlockNum / colParams.ColsPerPage;
|
||||
int partialBlocks = memBlockNum % colParams.ColsPerPage;
|
||||
|
||||
return (
|
||||
pageIndex * BLOCKS_PER_PAGE * pixelsPerBlock + // Full pages
|
||||
fullBlockRows * pixelsPerBlock * colParams.ColsPerPage + // Full row of blocks
|
||||
partialBlocks * colParams.Width // Partial row of blocks
|
||||
) * colParams.BitsPerPixel / 8;
|
||||
}
|
||||
|
||||
public static int CalcTxmImageOffset(ColumnParams colParams, int blockIndex, int imageWidth)
|
||||
{
|
||||
int blocksPerRow = imageWidth / colParams.Width;
|
||||
if (blocksPerRow == 0) blocksPerRow = 1;
|
||||
int fullRows = blockIndex / blocksPerRow;
|
||||
int rowBlockIndex = blockIndex % blocksPerRow;
|
||||
return (
|
||||
fullRows * imageWidth * colParams.Height * 4 +
|
||||
rowBlockIndex * colParams.Width
|
||||
) * colParams.BitsPerPixel / 8;
|
||||
}
|
||||
}
|
||||
}
|
141
LibDgf/Ps2/Gs/PsmtMixer.cs
Normal file
141
LibDgf/Ps2/Gs/PsmtMixer.cs
Normal file
|
@ -0,0 +1,141 @@
|
|||
using LibDgf.Txm;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Gs
|
||||
{
|
||||
public static class PsmtMixer
|
||||
{
|
||||
static int[,] PSMCT32_TO_PSMT4_COL02_LOOKUP = new int[16, 8];
|
||||
static int[,] PSMCT32_TO_PSMT4_COL13_LOOKUP = new int[16, 8];
|
||||
static int[,] PSMCT32_TO_PSMT8_COL02_LOOKUP = new int[16, 4];
|
||||
static int[,] PSMCT32_TO_PSMT8_COL13_LOOKUP = new int[16, 4];
|
||||
|
||||
static PsmtMixer()
|
||||
{
|
||||
FillLookup(PSMCT32_TO_PSMT4_COL02_LOOKUP, false);
|
||||
FillOddLookup(PSMCT32_TO_PSMT4_COL02_LOOKUP, PSMCT32_TO_PSMT4_COL13_LOOKUP);
|
||||
FillLookup(PSMCT32_TO_PSMT8_COL02_LOOKUP, false);
|
||||
FillOddLookup(PSMCT32_TO_PSMT8_COL02_LOOKUP, PSMCT32_TO_PSMT8_COL13_LOOKUP);
|
||||
|
||||
//PrintLookup(PSMCT32_TO_PSMT4_COL02_LOOKUP, nameof(PSMCT32_TO_PSMT4_COL02_LOOKUP));
|
||||
//PrintLookup(PSMCT32_TO_PSMT4_COL13_LOOKUP, nameof(PSMCT32_TO_PSMT4_COL13_LOOKUP));
|
||||
//PrintLookup(PSMCT32_TO_PSMT8_COL02_LOOKUP, nameof(PSMCT32_TO_PSMT8_COL02_LOOKUP));
|
||||
//PrintLookup(PSMCT32_TO_PSMT8_COL13_LOOKUP, nameof(PSMCT32_TO_PSMT8_COL13_LOOKUP));
|
||||
}
|
||||
|
||||
static void PrintLookup(int[,] lookup, string name)
|
||||
{
|
||||
Console.WriteLine(name);
|
||||
int rowNum = lookup.GetLength(0);
|
||||
int colNum = lookup.GetLength(1);
|
||||
for (int row = 0; row < rowNum; ++row)
|
||||
{
|
||||
for (int col = colNum - 1; col >= 0; --col)
|
||||
{
|
||||
Console.Write("{0,3} ", lookup[row, col]);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static void FillLookup(int[,] lookup, bool skipNonConsec)
|
||||
{
|
||||
int numCols = lookup.GetLength(1);
|
||||
int num = 0;
|
||||
|
||||
// Phase 1: consecutive numbers
|
||||
// Fill every second column
|
||||
// Top half then bottom half
|
||||
for (int half = 0; half < 2; ++half)
|
||||
{
|
||||
for (int col = 0; col < numCols; col += skipNonConsec ? 1 : 2)
|
||||
{
|
||||
for (int row = 0; row < 8; ++row)
|
||||
{
|
||||
lookup[half * 8 + row, col] = num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: wrapped numbers
|
||||
if (!skipNonConsec)
|
||||
{
|
||||
for (int half = 0; half < 2; ++half)
|
||||
{
|
||||
for (int col = 1; col < numCols; col += 2)
|
||||
{
|
||||
for (int row = 4; row < 12; ++row)
|
||||
{
|
||||
lookup[half * 8 + (row % 8), col] = num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FillOddLookup(int[,] evenLookup, int[,] oddLookup)
|
||||
{
|
||||
int numCols = evenLookup.GetLength(1);
|
||||
for (int half = 0; half < 2; ++half)
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
for (int j = 0; j < numCols; ++j)
|
||||
{
|
||||
oddLookup[half * 8 + i, j] = evenLookup[half * 8 + ((i + 4) % 8), j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] MixColumn(byte[] column, TxmPixelFormat srcFormat, TxmPixelFormat destFormat, bool isOdd)
|
||||
{
|
||||
if (srcFormat != TxmPixelFormat.PSMCT32)
|
||||
throw new NotSupportedException("Only PSMCT32 supported as source format.");
|
||||
switch (destFormat)
|
||||
{
|
||||
case TxmPixelFormat.PSMT4:
|
||||
return MixColumn32To4(column, isOdd);
|
||||
case TxmPixelFormat.PSMT8:
|
||||
return MixColumn32To8(column, isOdd);
|
||||
default:
|
||||
throw new NotSupportedException($"{destFormat} not supported as destination format.");
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] MixColumn32To4(byte[] column, bool isOdd)
|
||||
{
|
||||
int[,] lookup = isOdd ? PSMCT32_TO_PSMT4_COL13_LOOKUP : PSMCT32_TO_PSMT4_COL02_LOOKUP;
|
||||
int numCol = lookup.GetLength(1);
|
||||
byte[] dest = new byte[column.Length];
|
||||
byte b = 0;
|
||||
for (int i = 0; i < column.Length * 2; ++i)
|
||||
{
|
||||
if (i % 2 == 0)
|
||||
b = column[i / 2];
|
||||
else
|
||||
b >>= 4;
|
||||
|
||||
int index = lookup[i / numCol, i % numCol];
|
||||
dest[index / 2] |= (byte)((b & 0x0f) << (index % 2 * 4));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static byte[] MixColumn32To8(byte[] column, bool isOdd)
|
||||
{
|
||||
int[,] lookup = isOdd ? PSMCT32_TO_PSMT8_COL13_LOOKUP : PSMCT32_TO_PSMT8_COL02_LOOKUP;
|
||||
int numCol = lookup.GetLength(1);
|
||||
byte[] dest = new byte[column.Length];
|
||||
for (int i = 0; i < column.Length; ++i)
|
||||
{
|
||||
int index = lookup[i / numCol, i % numCol];
|
||||
dest[index] = column[i];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
}
|
107
LibDgf/Ps2/Gs/Registers/Tex0.cs
Normal file
107
LibDgf/Ps2/Gs/Registers/Tex0.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Gs.Registers
|
||||
{
|
||||
public struct Tex0
|
||||
{
|
||||
public ulong Packed;
|
||||
|
||||
public ushort Tbp0
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ushort)(Packed & 0x3fff);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Tbw
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 14) & 0x3f);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Psm
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 20) & 0x3f);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Tw
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 26) & 0xf);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Th
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 30) & 0xf);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Tcc
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 34) & 0x1);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Tfx
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 35) & 0x3);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Cbp
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 37) & 0x3ff);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Cpsm
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 51) & 0xf);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Csm
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 55) & 0x1);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Csa
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 56) & 0x1f);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Cld
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)((Packed >> 61) & 0x7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue