Skip to content

Commit

Permalink
Refactor object and animated textures (LostArtefacts#648)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahm86 authored May 4, 2024
1 parent acce0b2 commit d14cf45
Show file tree
Hide file tree
Showing 45 changed files with 2,068 additions and 913 deletions.
22 changes: 1 addition & 21 deletions TREnvironmentEditor/Model/Types/Mirroring/EMMirrorFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using TRLevelControl;
using TRLevelControl.Helpers;
using TRLevelControl.Model;
using TRModelTransporter.Model.Textures;

namespace TREnvironmentEditor.Model.Types;

Expand Down Expand Up @@ -109,11 +108,6 @@ private int FlipWorldX(int x)
return x;
}

private static void Swap<T>(T[] arr, int pos1, int pos2)
{
(arr[pos2], arr[pos1]) = (arr[pos1], arr[pos2]);
}

private static void MirrorFloorData(TR1Level level)
{
foreach (TR1Room room in level.Rooms)
Expand Down Expand Up @@ -1039,23 +1033,9 @@ private static void MirrorMeshTextures(TRMesh mesh, ISet<ushort> textureReferenc

private static void MirrorObjectTextures(ISet<ushort> textureReferences, List<TRObjectTexture> objectTextures)
{
// Flip the object texture vertices in the same way as done for faces
foreach (ushort textureRef in textureReferences)
{
IndexedTRObjectTexture texture = new()
{
Texture = objectTextures[textureRef]
};

if (texture.IsTriangle)
{
Swap(texture.Texture.Vertices, 0, 2);
}
else
{
Swap(texture.Texture.Vertices, 0, 3);
Swap(texture.Texture.Vertices, 1, 2);
}
objectTextures[textureRef].FlipHorizontal();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using TRLevelControl.Model;
using TRModelTransporter.Model.Textures;

namespace TREnvironmentEditor.Model.Types;

Expand Down Expand Up @@ -82,25 +81,7 @@ private static void MirrorObjectTextures(ISet<ushort> textureReferences, List<TR
{
foreach (ushort textureRef in textureReferences)
{
IndexedTRObjectTexture texture = new()
{
Texture = objectTextures[textureRef]
};

if (texture.IsTriangle)
{
Swap(texture.Texture.Vertices, 0, 2);
}
else
{
Swap(texture.Texture.Vertices, 0, 3);
Swap(texture.Texture.Vertices, 1, 2);
}
objectTextures[textureRef].FlipHorizontal();
}
}

private static void Swap<T>(T[] arr, int pos1, int pos2)
{
(arr[pos2], arr[pos1]) = (arr[pos1], arr[pos2]);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using TRLevelControl.Model;
using TRModelTransporter.Model.Textures;

namespace TREnvironmentEditor.Model.Types;

Expand All @@ -26,25 +25,7 @@ private void MirrorObjectTextures(List<TRObjectTexture> levelTextures)
{
foreach (ushort textureRef in Textures)
{
IndexedTRObjectTexture texture = new()
{
Texture = levelTextures[textureRef]
};

if (texture.IsTriangle)
{
Swap(texture.Texture.Vertices, 0, 2);
}
else
{
Swap(texture.Texture.Vertices, 0, 3);
Swap(texture.Texture.Vertices, 1, 2);
}
levelTextures[textureRef].FlipHorizontal();
}
}

private static void Swap<T>(T[] arr, int pos1, int pos2)
{
(arr[pos2], arr[pos1]) = (arr[pos1], arr[pos2]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private List<EMTextureMap> BuildAndPackTextures<E, L>(AbstractTexturePacker<E, L
// must remain as-is.
if (data.RetainInWireframe)
{
texture.Texture.Attribute = (ushort)TRBlendingMode.Unused01;
texture.Texture.BlendingMode = TRBlendingMode.Unused01;
}
}

Expand All @@ -91,33 +91,7 @@ private static IndexedTRObjectTexture CreateTexture(Size size)
{
return new()
{
Texture = new()
{
Vertices = new TRObjectTextureVert[]
{
CreatePoint(0, 0),
CreatePoint(size.Width, 0),
CreatePoint(size.Width, size.Height),
CreatePoint(0, size.Height)
}
}
};
}

private static TRObjectTextureVert CreatePoint(int x, int y)
{
return new()
{
XCoordinate = new()
{
Whole = (byte)(x == 0 ? 1 : 255),
Fraction = (byte)(x == 0 ? 0 : x - 1)
},
YCoordinate = new()
{
Whole = (byte)(y == 0 ? 1 : 255),
Fraction = (byte)(y == 0 ? 0 : y - 1)
}
Texture = new(0, 0, size.Width, size.Height)
};
}
}
Expand Down
229 changes: 229 additions & 0 deletions TRLevelControl/Build/TRTextureBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
using System.Diagnostics;
using System.Drawing;
using TRLevelControl.Model;

namespace TRLevelControl.Build;

public class TRTextureBuilder
{
private static readonly string _texMarker = "TEX";

private readonly TRGameVersion _version;
private readonly ITRLevelObserver _observer;

public TRTextureBuilder(TRGameVersion version, ITRLevelObserver observer = null)
{
_version = version;
_observer = observer;
}

public List<TRObjectTexture> ReadObjectTextures(TRLevelReader reader)
{
if (_version >= TRGameVersion.TR4)
{
string texMarker = new(reader.ReadChars(_texMarker.Length));
Debug.Assert(texMarker == _texMarker);
if (_version == TRGameVersion.TR5)
{
Debug.Assert(reader.ReadByte() == 0);
}
}

List<TRObjectTexture> textures = new();
uint numTextures = reader.ReadUInt32();

for (int i = 0; i < numTextures; i++)
{
textures.Add(Read(reader, i));
}

return textures;
}

public List<TRAnimatedTexture> ReadAnimatedTextures(TRLevelReader reader)
{
uint textureLength = reader.ReadUInt32();
long endPosition = reader.BaseStream.Position + textureLength * sizeof(ushort);

ushort numGroups = reader.ReadUInt16();
List<TRAnimatedTexture> textures = reader.ReadAnimatedTextures(numGroups);

// Old TRLEs can contain unreferenced texture data, so ensure to seek to the
// end of the data before finishing.
reader.BaseStream.Position = Math.Max(endPosition, reader.BaseStream.Position);

if (_version >= TRGameVersion.TR4)
{
// The first sets in the group are UVRotate mode, else classic.
byte uvRotates = reader.ReadByte();
for (int i = 0; i < uvRotates && i < numGroups; i++)
{
textures[i].Mode = TRAnimatedTextureMode.UVRotate;
}
}

return textures;
}

public void Write(TRLevelWriter writer, List<TRObjectTexture> textures)
{
if (_version >= TRGameVersion.TR4)
{
writer.Write(_texMarker.ToCharArray());
if (_version == TRGameVersion.TR5)
{
writer.Write((byte)0);
}
}

writer.Write((uint)textures.Count);

for (int i = 0; i < textures.Count; i++)
{
Write(writer, textures[i], i);
}
}

public void Write(TRLevelWriter writer, List<TRAnimatedTexture> textures)
{
List<ushort> data = new()
{
(ushort)textures.Count
};

if (_version >= TRGameVersion.TR4)
{
// Make sure to put UVRotate mode first.
textures = new(textures.OrderByDescending(t => t.Mode));
}

byte uvRotate = 0;
foreach (TRAnimatedTexture texture in textures)
{
data.Add((ushort)(texture.Textures.Count - 1));
data.AddRange(texture.Textures);
if (texture.Mode == TRAnimatedTextureMode.UVRotate)
{
uvRotate++;
}
}

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

if (_version >= TRGameVersion.TR4)
{
writer.Write(uvRotate);
}
}

private TRObjectTexture Read(TRLevelReader reader, int index)
{
TRObjectTexture texture = new()
{
BlendingMode = (TRBlendingMode)reader.ReadUInt16()
};

ushort flags = reader.ReadUInt16();
texture.Atlas = (ushort)(flags & 0x7FFF);

if (_version >= TRGameVersion.TR4)
{
texture.IsTriangle = (flags & 0x8000) > 0;

ushort newFlags = reader.ReadUInt16();
texture.Target = (newFlags & 0x8000) > 0 ? TRObjectTextureMode.Room : TRObjectTextureMode.Model;
texture.BumpLevel = (byte)((newFlags & 0x600) >> 9);
texture.MappingCorrection = (byte)(newFlags & 0x7);
texture.UnknownFlag = (newFlags & 0x0800) > 0;
}
else
{
texture.Target = TRObjectTextureMode.Classic;
}

texture.Vertices = reader.ReadObectTextureVertices(4);

if (_version >= TRGameVersion.TR4)
{
uint originalU = reader.ReadUInt32();
uint originalV = reader.ReadUInt32();
_observer?.OnOrignalUVRead(index, new(originalU, originalV));

// Width-1 and Height-1
reader.ReadUInt32();
reader.ReadUInt32();

if (_version == TRGameVersion.TR5)
{
// Padding
reader.ReadUInt16();
}
}

return texture;
}

private void Write(TRLevelWriter writer, TRObjectTexture texture, int index)
{
TRBlendingMode blendingMode = texture.BlendingMode;
if ((blendingMode == TRBlendingMode.AlphaBlending && _version < TRGameVersion.TR3)
|| (blendingMode == TRBlendingMode.ForcedAlpha && _version < TRGameVersion.TR4))
{
blendingMode = TRBlendingMode.AlphaTesting;
}

writer.Write((ushort)blendingMode);

ushort flags = texture.Atlas;
if (_version >= TRGameVersion.TR4)
{
if (texture.IsTriangle)
{
flags |= 0x8000;
}
writer.Write(flags);

ushort newFlags = (ushort)(texture.MappingCorrection & 0x7);
newFlags |= (ushort)((texture.BumpLevel << 9) & 0x600);
if (texture.Target == TRObjectTextureMode.Room)
{
newFlags |= 0x8000;
}
if (texture.UnknownFlag)
{
newFlags |= 0x0800;
}
writer.Write(newFlags);
}
else
{
writer.Write(flags);
}

writer.Write(texture.Vertices);

if (_version >= TRGameVersion.TR4)
{
// Discarded in game, so only written back as OG for tests.
Tuple<uint, uint> originalUV = _observer?.GetOrignalUV(index) ?? new(0, 0);
writer.Write(originalUV.Item1);
writer.Write(originalUV.Item2);

Size size = texture.Size;
if (_version == TRGameVersion.TR4)
{
writer.Write((uint)(size.Width - 1));
writer.Write((uint)(size.Height - 1));
}
else
{
writer.Write((uint)((size.Width - 1) << 16));
writer.Write((uint)((size.Height - 1) << 16));

// Padding
writer.Write((ushort)0);
}
}
}
}
Loading

0 comments on commit d14cf45

Please sign in to comment.