Skip to content

Commit

Permalink
Add TRR texture support (LostArtefacts#703)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahm86 authored Jun 9, 2024
1 parent 1086754 commit 7b9b65d
Show file tree
Hide file tree
Showing 42 changed files with 1,909 additions and 38 deletions.
10 changes: 10 additions & 0 deletions TRLevelControl/IO/TRLevelReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,16 @@ public TRVertex ReadVertex(bool reverse = false)
return vertex;
}

public TRVertex8 ReadVertex8()
{
return new()
{
X = ReadByte(),
Y = ReadByte(),
Z = ReadByte()
};
}

public TRVertex32 ReadVertex32()
{
return new()
Expand Down
7 changes: 7 additions & 0 deletions TRLevelControl/IO/TRLevelWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,13 @@ public void Write(TRVertex vertex, bool reverse = false)
Write(reverse ? vertex.X : vertex.Z);
}

public void Write(TRVertex8 vertex)
{
Write(vertex.X);
Write(vertex.Y);
Write(vertex.Z);
}

public void Write(TRVertex32 vertex)
{
Write(vertex.X);
Expand Down
20 changes: 20 additions & 0 deletions TRLevelControl/Model/Common/TRVertex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,23 @@ public TRVertex32 Clone()
object ICloneable.Clone()
=> Clone();
}

public class TRVertex8 : ICloneable
{
public byte X { get; set; }
public byte Y { get; set; }
public byte Z { get; set; }

public TRVertex8 Clone()
{
return new()
{
X = X,
Y = Y,
Z = Z
};
}

object ICloneable.Clone()
=> Clone();
}
15 changes: 15 additions & 0 deletions TRLevelControl/Model/TRG/TRGData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace TRLevelControl.Model;

public class TRGData
{
public List<TRColour4[]> AmbientCubes { get; set; }
public byte[] Unknown1 { get; set; }
public byte Unknown2 { get; set; }
public byte Unknown3 { get; set; }
public List<byte[]> Unknown4 { get; set; }
public List<byte[]> Unknown5 { get; set; }
public List<TRGMesh> Meshes { get; set; }
public List<ushort> Textures { get; set; }
public List<uint> Indices { get; set; }
public List<TRGVertex> Vertices { get; set; }
}
8 changes: 8 additions & 0 deletions TRLevelControl/Model/TRG/TRGMesh.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace TRLevelControl.Model;

public class TRGMesh
{
public List<TRGShader> Shaders { get; set; }
public uint Unknown1 { get; set; }
public List<byte[]> Unknown2 { get; set; }
}
11 changes: 11 additions & 0 deletions TRLevelControl/Model/TRG/TRGShader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace TRLevelControl.Model;

public class TRGShader
{
public uint Type { get; set; }
public uint Unknown1 { get; set; }
public uint Unknown2 { get; set; }
public uint Unknown3 { get; set; }
public uint Unknown4 { get; set; }
public Tuple<uint, uint>[] IndexInfo { get; set; }
}
13 changes: 13 additions & 0 deletions TRLevelControl/Model/TRG/TRGVertex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace TRLevelControl.Model;

public class TRGVertex
{
public TRVertex Vertex { get; set; }
public short Unknown { get; set; }
public TRVertex8 Normal { get; set; }
public byte Texture { get; set; }
public TRColour Ambient1 { get; set; }
public TRColour Ambient2 { get; set; }
public byte U { get; set; }
public byte V { get; set; }
}
187 changes: 187 additions & 0 deletions TRLevelControl/TRGControlBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
using System.Diagnostics;
using TRLevelControl.Model;

namespace TRLevelControl;

public class TRGControlBase
{
private const uint _trgMagic = 'T' | ('R' << 8) | ('G' << 16) | (3 << 24);

public static TRGData Read(string filePath)
=> Read(File.OpenRead(filePath));

public static TRGData Read(Stream stream)
{
using TRLevelReader reader = new(stream);
Debug.Assert(reader.ReadUInt32() == _trgMagic);

TRGData data = new();

uint numAmbientCubes = reader.ReadUInt32();
data.AmbientCubes = new();
for (int i = 0; i < numAmbientCubes; i++)
{
TRColour4[] cube = new TRColour4[6];
data.AmbientCubes.Add(cube);
for (int j = 0; j < cube.Length; j++)
{
cube[j] = new(reader.ReadUInt32());
}
}

data.Unknown1 = reader.ReadBytes(24);
data.Unknown2 = reader.ReadByte();
data.Unknown3 = reader.ReadByte();

byte numUnknown4 = reader.ReadByte();
byte numUnknown5 = reader.ReadByte();

data.Unknown4 = new();
for (int i = 0; i < numUnknown4; i++)
{
data.Unknown4.Add(reader.ReadBytes(40));
}

data.Unknown5 = new();
for (int i = 0; i < numUnknown5; i++)
{
data.Unknown5.Add(reader.ReadBytes(28));
}

uint numMeshes = reader.ReadUInt32();
data.Meshes = new();
for (int i = 0; i < numMeshes; i++)
{
TRGMesh mesh = new();
data.Meshes.Add(mesh);

uint numShaders = reader.ReadUInt32();
mesh.Shaders = new();
for (int j = 0; j < numShaders; j++)
{
TRGShader shader = new()
{
Type = reader.ReadUInt32(),
Unknown1 = reader.ReadUInt32(),
Unknown2 = reader.ReadUInt32(),
Unknown3 = reader.ReadUInt32(),
Unknown4 = reader.ReadUInt32(),
IndexInfo = new Tuple<uint, uint>[3]
};
mesh.Shaders.Add(shader);

for (int k = 0; k < 3; k++)
{
shader.IndexInfo[k] = new(reader.ReadUInt32(), reader.ReadUInt32());
}
}

mesh.Unknown1 = reader.ReadUInt32();
uint numUnknowns = reader.ReadUInt32();
mesh.Unknown2 = new();
for (int j = 0; j < numUnknowns; j++)
{
mesh.Unknown2.Add(reader.ReadUInt8s(28));
}
}

uint numTextures = reader.ReadUInt32();
data.Textures = new(reader.ReadUInt16s(numTextures));

while (reader.BaseStream.Position % sizeof(uint) != 0)
{
reader.ReadByte();
}

uint numIndices = reader.ReadUInt32();
uint numVertices = reader.ReadUInt32();
data.Indices = new(reader.ReadUInt32s(numIndices));
data.Vertices = new();
for (int i = 0; i < numVertices; i++)
{
data.Vertices.Add(new()
{
Vertex = reader.ReadVertex(),
Unknown = reader.ReadInt16(),
Normal = reader.ReadVertex8(),
Texture = reader.ReadByte(),
Ambient1 = reader.ReadColour(),
U = reader.ReadByte(),
Ambient2 = reader.ReadColour(),
V = reader.ReadByte(),
});
}

Debug.Assert(reader.BaseStream.Position == reader.BaseStream.Length);

return data;
}

public static void Write(TRGData data, string filePath)
=> Write(data, File.Create(filePath));

public static void Write(TRGData data, Stream outputStream)
{
using TRLevelWriter writer = new(outputStream);
writer.Write(_trgMagic);

writer.Write((uint)data.AmbientCubes.Count);
writer.Write(data.AmbientCubes.SelectMany(c => c.Select(a => a.ToARGB())));

writer.Write(data.Unknown1);
writer.Write(data.Unknown2);
writer.Write(data.Unknown3);

writer.Write((byte)data.Unknown4.Count);
writer.Write((byte)data.Unknown5.Count);
writer.Write(data.Unknown4.SelectMany(u => u));
writer.Write(data.Unknown5.SelectMany(u => u));

writer.Write((uint)data.Meshes.Count);
foreach (TRGMesh mesh in data.Meshes)
{
writer.Write((uint)mesh.Shaders.Count);
foreach (TRGShader shader in mesh.Shaders)
{
writer.Write(shader.Type);
writer.Write(shader.Unknown1);
writer.Write(shader.Unknown2);
writer.Write(shader.Unknown3);
writer.Write(shader.Unknown4);
foreach (var (index, offset) in shader.IndexInfo)
{
writer.Write(index);
writer.Write(offset);
}
}

writer.Write(mesh.Unknown1);
writer.Write((uint)mesh.Unknown2.Count);
writer.Write(mesh.Unknown2.SelectMany(u => u));
}

writer.Write((uint)data.Textures.Count);
writer.Write(data.Textures);

while (writer.BaseStream.Position % sizeof(uint) != 0)
{
writer.Write((byte)0);
}

writer.Write((uint)data.Indices.Count);
writer.Write((uint)data.Vertices.Count);
writer.Write(data.Indices);

foreach (TRGVertex vertex in data.Vertices)
{
writer.Write(vertex.Vertex);
writer.Write(vertex.Unknown);
writer.Write(vertex.Normal);
writer.Write(vertex.Texture);
writer.Write(vertex.Ambient1);
writer.Write(vertex.U);
writer.Write(vertex.Ambient2);
writer.Write(vertex.V);
}
}
}
20 changes: 20 additions & 0 deletions TRLevelControlTests/Base/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,26 @@ public static void ReadWriteMAP(string levelName, TRGameVersion version)
CollectionAssert.AreEqual(originalLines.Distinct().ToList(), outputLines);
}

public static void ReadWriteTRG(string levelName, TRGameVersion version)
{
levelName = Path.GetFileNameWithoutExtension(levelName) + ".TRG";
string pathI = GetReadPath(levelName, version, true);

using FileStream dataStream = File.OpenRead(pathI);
using MemoryStream inputStream = new();
using MemoryStream outputStream = new();

dataStream.CopyTo(inputStream);
byte[] inputData = inputStream.ToArray();
inputStream.Position = 0;

ObserverBase observer = new();
TRGData data = TRGControlBase.Read(inputStream);
TRGControlBase.Write(data, outputStream);

observer.TestOutput(inputData, outputStream.ToArray());
}

public static IEnumerable<object[]> GetLevelNames(IEnumerable<string> names)
{
foreach (string lvl in names)
Expand Down
7 changes: 7 additions & 0 deletions TRLevelControlTests/TR1/IOTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public void TestMAPReadWrite(string levelName)
ReadWriteMAP(levelName, TRGameVersion.TR1);
}

[TestMethod]
[DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)]
public void TestTRGReadWrite(string levelName)
{
ReadWriteTRG(levelName, TRGameVersion.TR1);
}

[TestMethod]
[DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)]
public void TestAgressiveFloorData(string levelName)
Expand Down
7 changes: 7 additions & 0 deletions TRLevelControlTests/TR2/IOTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ public void TestMAPReadWrite(string levelName)
ReadWriteMAP(levelName, TRGameVersion.TR2);
}

[TestMethod]
[DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)]
public void TestTRGReadWrite(string levelName)
{
ReadWriteTRG(levelName, TRGameVersion.TR2);
}

[TestMethod]
[DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)]
public void TestAgressiveFloorData(string levelName)
Expand Down
7 changes: 7 additions & 0 deletions TRLevelControlTests/TR3/IOTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ public void TestMAPReadWrite(string levelName)
ReadWriteMAP(levelName, TRGameVersion.TR3);
}

[TestMethod]
[DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)]
public void TestTRGReadWrite(string levelName)
{
ReadWriteTRG(levelName, TRGameVersion.TR3);
}

[TestMethod]
[DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)]
public void TestAgressiveFloorData(string levelName)
Expand Down
9 changes: 9 additions & 0 deletions TRRandomizerCore/Editors/RandomizerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public class RandomizerSettings
public bool IncludeExtraPickups { get; set; }
public bool DevelopmentMode { get; set; }
public ItemDifficulty RandoItemDifficulty { get; set; }
public TextureMode TextureMode { get; set; }
public bool MatchTextureTypes { get; set; }
public bool MatchTextureItems { get; set; }
public bool PersistTextureVariants { get; set; }
public bool RandomizeWaterColour { get; set; }
public bool RetainMainLevelTextures { get; set; }
Expand Down Expand Up @@ -251,6 +254,9 @@ public void ApplyConfig(Config config)

RandomizeTextures = config.GetBool(nameof(RandomizeTextures));
TextureSeed = config.GetInt(nameof(TextureSeed), defaultSeed);
TextureMode = (TextureMode)config.GetEnum(nameof(TextureMode), typeof(TextureMode), TextureMode.Game);
MatchTextureTypes = config.GetBool(nameof(MatchTextureTypes), true);
MatchTextureItems = config.GetBool(nameof(MatchTextureItems), true);
PersistTextureVariants = config.GetBool(nameof(PersistTextureVariants));
RandomizeWaterColour = config.GetBool(nameof(RandomizeWaterColour), true);
RetainMainLevelTextures = config.GetBool(nameof(RetainMainLevelTextures));
Expand Down Expand Up @@ -416,6 +422,9 @@ public void StoreConfig(Config config)

config[nameof(RandomizeTextures)] = RandomizeTextures;
config[nameof(TextureSeed)] = TextureSeed;
config[nameof(TextureMode)] = TextureMode;
config[nameof(MatchTextureTypes)] = MatchTextureTypes;
config[nameof(MatchTextureItems)] = MatchTextureItems;
config[nameof(PersistTextureVariants)] = PersistTextureVariants;
config[nameof(RandomizeWaterColour)] = RandomizeWaterColour;
config[nameof(RetainMainLevelTextures)] = RetainMainLevelTextures;
Expand Down
Loading

0 comments on commit 7b9b65d

Please sign in to comment.