2024-09-20 15:28:44 +00:00
|
|
|
using System.Numerics;
|
|
|
|
|
|
|
|
namespace KeepersCompound.LGS.Database.Chunks;
|
|
|
|
|
|
|
|
public class WorldRep : IChunk
|
|
|
|
{
|
|
|
|
public struct WrHeader
|
|
|
|
{
|
|
|
|
// Extended header content
|
|
|
|
public int Size { get; set; }
|
|
|
|
public int Version { get; set; }
|
|
|
|
public int Flags { get; set; }
|
|
|
|
public uint LightmapFormat { get; set; }
|
|
|
|
public int LightmapScale { get; set; }
|
|
|
|
|
|
|
|
// Standard header
|
|
|
|
public uint DataSize { get; set; }
|
|
|
|
public uint CellCount { get; set; }
|
|
|
|
|
|
|
|
public WrHeader(BinaryReader reader)
|
|
|
|
{
|
|
|
|
Size = reader.ReadInt32();
|
|
|
|
Version = reader.ReadInt32();
|
|
|
|
Flags = reader.ReadInt32();
|
|
|
|
LightmapFormat = reader.ReadUInt32();
|
|
|
|
LightmapScale = reader.ReadInt32();
|
|
|
|
DataSize = reader.ReadUInt32();
|
|
|
|
CellCount = reader.ReadUInt32();
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly float LightmapScaleMultiplier()
|
|
|
|
{
|
|
|
|
return Math.Sign(LightmapScale) switch
|
|
|
|
{
|
|
|
|
1 => LightmapScale,
|
|
|
|
-1 => 1.0f / LightmapScale,
|
|
|
|
_ => 1.0f,
|
|
|
|
};
|
|
|
|
}
|
2024-09-22 10:14:34 +00:00
|
|
|
|
|
|
|
public readonly void Write(BinaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.Write(Size);
|
|
|
|
writer.Write(Version);
|
|
|
|
writer.Write(Flags);
|
|
|
|
writer.Write(LightmapFormat);
|
|
|
|
writer.Write(LightmapScale);
|
|
|
|
writer.Write(DataSize);
|
|
|
|
writer.Write(CellCount);
|
|
|
|
}
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
|
2024-10-05 13:35:07 +00:00
|
|
|
public class Cell
|
2024-09-20 15:28:44 +00:00
|
|
|
{
|
|
|
|
public struct Poly
|
|
|
|
{
|
|
|
|
public byte Flags { get; set; }
|
|
|
|
public byte VertexCount { get; set; }
|
|
|
|
public byte PlaneId { get; set; }
|
|
|
|
public byte ClutId { get; set; }
|
|
|
|
public ushort Destination { get; set; }
|
|
|
|
public byte MotionIndex { get; set; }
|
|
|
|
|
|
|
|
public Poly(BinaryReader reader)
|
|
|
|
{
|
|
|
|
Flags = reader.ReadByte();
|
|
|
|
VertexCount = reader.ReadByte();
|
|
|
|
PlaneId = reader.ReadByte();
|
|
|
|
ClutId = reader.ReadByte();
|
|
|
|
Destination = reader.ReadUInt16();
|
|
|
|
MotionIndex = reader.ReadByte();
|
|
|
|
reader.ReadByte();
|
|
|
|
}
|
2024-09-22 10:14:34 +00:00
|
|
|
|
|
|
|
public readonly void Write(BinaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.Write(Flags);
|
|
|
|
writer.Write(VertexCount);
|
|
|
|
writer.Write(PlaneId);
|
|
|
|
writer.Write(ClutId);
|
|
|
|
writer.Write(Destination);
|
|
|
|
writer.Write(MotionIndex);
|
|
|
|
writer.Write((byte)0);
|
|
|
|
}
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public struct RenderPoly
|
|
|
|
{
|
|
|
|
public (Vector3, Vector3) TextureVectors { get; set; }
|
|
|
|
public (float, float) TextureBases { get; set; }
|
|
|
|
public ushort TextureId { get; set; }
|
|
|
|
public ushort CachedSurface { get; set; }
|
|
|
|
public float TextureMagnitude { get; set; }
|
|
|
|
public Vector3 Center { get; set; }
|
|
|
|
|
|
|
|
public RenderPoly(BinaryReader reader)
|
|
|
|
{
|
|
|
|
TextureVectors = (reader.ReadVec3(), reader.ReadVec3());
|
|
|
|
TextureBases = (reader.ReadSingle(), reader.ReadSingle());
|
|
|
|
TextureId = reader.ReadUInt16();
|
|
|
|
CachedSurface = reader.ReadUInt16();
|
|
|
|
TextureMagnitude = reader.ReadSingle();
|
|
|
|
Center = reader.ReadVec3();
|
|
|
|
}
|
2024-09-22 10:14:34 +00:00
|
|
|
|
|
|
|
public readonly void Write(BinaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.WriteVec3(TextureVectors.Item1);
|
|
|
|
writer.WriteVec3(TextureVectors.Item2);
|
|
|
|
writer.Write(TextureBases.Item1);
|
|
|
|
writer.Write(TextureBases.Item2);
|
|
|
|
writer.Write(TextureId);
|
|
|
|
writer.Write(CachedSurface);
|
|
|
|
writer.Write(TextureMagnitude);
|
|
|
|
writer.WriteVec3(Center);
|
|
|
|
}
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
|
2024-10-05 13:51:31 +00:00
|
|
|
public class LightmapInfo
|
2024-09-20 15:28:44 +00:00
|
|
|
{
|
|
|
|
public (short, short) Bases { get; set; }
|
|
|
|
public short PaddedWidth { get; set; }
|
|
|
|
public byte Height { get; set; }
|
|
|
|
public byte Width { get; set; }
|
|
|
|
public uint DataPtr { get; set; }
|
|
|
|
public uint DynamicLightPtr { get; set; }
|
|
|
|
public uint AnimLightBitmask { get; set; }
|
|
|
|
|
|
|
|
public LightmapInfo(BinaryReader reader)
|
|
|
|
{
|
|
|
|
Bases = (reader.ReadInt16(), reader.ReadInt16());
|
|
|
|
PaddedWidth = reader.ReadInt16();
|
|
|
|
Height = reader.ReadByte();
|
|
|
|
Width = reader.ReadByte();
|
|
|
|
DataPtr = reader.ReadUInt32();
|
|
|
|
DynamicLightPtr = reader.ReadUInt32();
|
|
|
|
AnimLightBitmask = reader.ReadUInt32();
|
|
|
|
}
|
2024-09-22 10:14:34 +00:00
|
|
|
|
2024-10-05 13:51:31 +00:00
|
|
|
public void Write(BinaryWriter writer)
|
2024-09-22 10:14:34 +00:00
|
|
|
{
|
|
|
|
writer.Write(Bases.Item1);
|
|
|
|
writer.Write(Bases.Item2);
|
|
|
|
writer.Write(PaddedWidth);
|
|
|
|
writer.Write(Height);
|
|
|
|
writer.Write(Width);
|
|
|
|
writer.Write(DataPtr);
|
|
|
|
writer.Write(DynamicLightPtr);
|
|
|
|
writer.Write(AnimLightBitmask);
|
|
|
|
}
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
|
2024-10-05 17:13:30 +00:00
|
|
|
public class Lightmap
|
2024-09-20 15:28:44 +00:00
|
|
|
{
|
2024-10-27 09:21:19 +00:00
|
|
|
private readonly bool[] _litLayers;
|
2024-10-04 17:49:37 +00:00
|
|
|
public List<byte[]> Pixels { get; set; }
|
2024-09-20 15:28:44 +00:00
|
|
|
|
|
|
|
public int Layers;
|
|
|
|
public int Width;
|
|
|
|
public int Height;
|
|
|
|
public int Bpp;
|
|
|
|
|
|
|
|
public Lightmap(BinaryReader reader, byte width, byte height, uint bitmask, int bytesPerPixel)
|
|
|
|
{
|
2024-10-04 17:49:37 +00:00
|
|
|
var layers = 1 + BitOperations.PopCount(bitmask);
|
|
|
|
var length = bytesPerPixel * width * height;
|
2024-10-27 09:21:19 +00:00
|
|
|
_litLayers = new bool[33];
|
2024-10-04 17:49:37 +00:00
|
|
|
Pixels = new List<byte[]>();
|
|
|
|
for (var i = 0; i < layers; i++)
|
|
|
|
{
|
|
|
|
Pixels.Add(reader.ReadBytes(length));
|
2024-10-27 09:21:19 +00:00
|
|
|
_litLayers[i] = true;
|
2024-10-04 17:49:37 +00:00
|
|
|
}
|
|
|
|
Layers = layers;
|
2024-09-20 15:28:44 +00:00
|
|
|
Width = width;
|
|
|
|
Height = height;
|
|
|
|
Bpp = bytesPerPixel;
|
|
|
|
}
|
|
|
|
|
2024-10-05 17:13:30 +00:00
|
|
|
public Vector4 GetPixel(uint layer, uint x, uint y)
|
2024-09-20 15:28:44 +00:00
|
|
|
{
|
|
|
|
if (layer >= Layers || x >= Width || y >= Height)
|
|
|
|
{
|
|
|
|
return Vector4.Zero;
|
|
|
|
}
|
|
|
|
|
2024-10-04 17:49:37 +00:00
|
|
|
var pLayer = Pixels[(int)layer];
|
|
|
|
var idx = x * Bpp + y * Bpp * Width;
|
2024-09-20 15:28:44 +00:00
|
|
|
switch (Bpp)
|
|
|
|
{
|
|
|
|
case 1:
|
2024-10-04 17:49:37 +00:00
|
|
|
var raw1 = pLayer[idx];
|
2024-09-20 15:28:44 +00:00
|
|
|
return new Vector4(raw1, raw1, raw1, 255) / 255.0f;
|
|
|
|
case 2:
|
2024-10-04 17:49:37 +00:00
|
|
|
var raw2 = pLayer[idx] + (pLayer[idx + 1] << 8);
|
2024-09-20 15:28:44 +00:00
|
|
|
return new Vector4(raw2 & 31, (raw2 >> 5) & 31, (raw2 >> 10) & 31, 31) / 31.0f;
|
|
|
|
case 4:
|
2024-10-04 17:49:37 +00:00
|
|
|
return new Vector4(pLayer[idx + 2], pLayer[idx + 1], pLayer[idx], pLayer[idx + 3]) / 255.0f;
|
2024-09-20 15:28:44 +00:00
|
|
|
default:
|
|
|
|
return Vector4.Zero;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-05 17:13:30 +00:00
|
|
|
public byte[] AsBytesRgba(int layer)
|
2024-09-20 15:28:44 +00:00
|
|
|
{
|
|
|
|
ArgumentOutOfRangeException.ThrowIfLessThan(layer, 0, nameof(layer));
|
|
|
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(layer, Layers, nameof(layer));
|
|
|
|
|
2024-10-04 17:49:37 +00:00
|
|
|
var pLayer = Pixels[layer];
|
|
|
|
var pIdx = 0;
|
2024-09-20 15:28:44 +00:00
|
|
|
var length = 4 * Width * Height;
|
|
|
|
var bytes = new byte[length];
|
|
|
|
for (var i = 0; i < length; i += 4, pIdx += Bpp)
|
|
|
|
{
|
|
|
|
switch (Bpp)
|
|
|
|
{
|
|
|
|
case 1:
|
2024-10-04 17:49:37 +00:00
|
|
|
var raw1 = pLayer[pIdx];
|
2024-09-20 15:28:44 +00:00
|
|
|
bytes[i] = raw1;
|
|
|
|
bytes[i + 1] = raw1;
|
|
|
|
bytes[i + 2] = raw1;
|
|
|
|
bytes[i + 3] = 255;
|
|
|
|
break;
|
|
|
|
case 2:
|
2024-10-04 17:49:37 +00:00
|
|
|
var raw2 = pLayer[pIdx] + (pLayer[pIdx + 1] << 8);
|
2024-09-20 15:28:44 +00:00
|
|
|
bytes[i] = (byte)(255 * (raw2 & 31) / 31.0f);
|
|
|
|
bytes[i + 1] = (byte)(255 * ((raw2 >> 5) & 31) / 31.0f);
|
|
|
|
bytes[i + 2] = (byte)(255 * ((raw2 >> 10) & 31) / 31.0f);
|
|
|
|
bytes[i + 3] = 255;
|
|
|
|
break;
|
|
|
|
case 4:
|
2024-10-04 17:49:37 +00:00
|
|
|
bytes[i] = pLayer[pIdx + 2];
|
|
|
|
bytes[i + 1] = pLayer[pIdx + 1];
|
|
|
|
bytes[i + 2] = pLayer[pIdx];
|
|
|
|
bytes[i + 3] = pLayer[pIdx + 3];
|
2024-09-20 15:28:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytes;
|
|
|
|
}
|
2024-09-22 10:14:34 +00:00
|
|
|
|
2024-10-05 17:13:30 +00:00
|
|
|
public void AddLight(int layer, int x, int y, float r, float g, float b)
|
2024-09-22 10:14:34 +00:00
|
|
|
{
|
2024-12-23 17:42:03 +00:00
|
|
|
ArgumentOutOfRangeException.ThrowIfLessThan(layer, 0, nameof(layer));
|
2025-01-12 11:49:26 +00:00
|
|
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(layer, 32, nameof(layer));
|
|
|
|
|
|
|
|
while (layer >= Layers)
|
|
|
|
{
|
|
|
|
AddLayer();
|
|
|
|
}
|
2024-12-23 17:42:03 +00:00
|
|
|
|
2024-10-04 17:49:37 +00:00
|
|
|
var idx = (x + y * Width) * Bpp;
|
|
|
|
var pLayer = Pixels[layer];
|
2024-12-10 20:06:03 +00:00
|
|
|
switch (Bpp)
|
|
|
|
{
|
|
|
|
// 1bpp isn't really supported (does nd dromed even produce it?)
|
|
|
|
case 2:
|
|
|
|
var raw2 = pLayer[idx] + (pLayer[idx + 1] << 8);
|
2024-12-10 21:00:39 +00:00
|
|
|
var newR = (int)Math.Clamp((raw2 & 31) + r * 32f / 255f, 0, 31);
|
|
|
|
var newG = (int)Math.Clamp(((raw2 >> 5) & 31) + g * 32f / 255f, 0, 31);
|
|
|
|
var newB = (int)Math.Clamp(((raw2 >> 10) & 31) + b * 32f / 255f, 0, 31);
|
2024-12-10 20:06:03 +00:00
|
|
|
raw2 = newR + (newG << 5) + (newB << 10);
|
|
|
|
pLayer[idx] = (byte)(raw2 & 0xff);
|
|
|
|
pLayer[idx + 1] = (byte)((raw2 >> 8) & 0xff);
|
|
|
|
break;
|
|
|
|
case 4:
|
2024-12-10 21:00:39 +00:00
|
|
|
pLayer[idx] = (byte)Math.Clamp(pLayer[idx] + b, 0, 255);
|
2024-12-10 20:06:03 +00:00
|
|
|
pLayer[idx + 1] = (byte)Math.Clamp(pLayer[idx + 1] + g, 0, 255);
|
2024-12-10 21:00:39 +00:00
|
|
|
pLayer[idx + 2] = (byte)Math.Clamp(pLayer[idx + 2] + r, 0, 255);
|
2024-12-10 20:06:03 +00:00
|
|
|
pLayer[idx + 3] = 255;
|
|
|
|
break;
|
|
|
|
}
|
2024-10-27 09:21:19 +00:00
|
|
|
|
|
|
|
_litLayers[layer] = true;
|
2024-09-22 10:14:34 +00:00
|
|
|
}
|
|
|
|
|
2024-10-05 17:13:30 +00:00
|
|
|
public void AddLight(int layer, int x, int y, Vector3 color, float strength, bool hdr)
|
2024-09-26 11:04:58 +00:00
|
|
|
{
|
|
|
|
if (hdr)
|
|
|
|
{
|
|
|
|
strength /= 2.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to make sure we don't go over (255, 255, 255).
|
|
|
|
// If we just do Max(color, (255, 255, 255)) then we change
|
|
|
|
// the hue/saturation of coloured lights. Got to make sure we
|
|
|
|
// maintain the colour ratios.
|
|
|
|
var c = color * strength;
|
2024-10-27 08:54:52 +00:00
|
|
|
var ratio = Math.Max(Math.Max(Math.Max(0.0f, c.X), c.Y), c.Z) / 255.0f;
|
2024-09-26 11:04:58 +00:00
|
|
|
if (ratio > 1.0f)
|
|
|
|
{
|
|
|
|
c /= ratio;
|
|
|
|
}
|
|
|
|
|
2024-12-10 21:00:39 +00:00
|
|
|
AddLight(layer, x, y, c.X, c.Y, c.Z);
|
2024-09-26 11:04:58 +00:00
|
|
|
}
|
|
|
|
|
2024-10-05 13:52:09 +00:00
|
|
|
public void Reset(Vector3 ambientLight, bool hdr)
|
2024-10-04 17:49:37 +00:00
|
|
|
{
|
2025-01-12 11:49:26 +00:00
|
|
|
Layers = 0;
|
2024-10-05 13:52:09 +00:00
|
|
|
Pixels.Clear();
|
2025-01-12 11:49:26 +00:00
|
|
|
AddLayer();
|
|
|
|
|
|
|
|
for (var i = 0; i < _litLayers.Length; i++)
|
2024-10-27 09:21:19 +00:00
|
|
|
{
|
|
|
|
_litLayers[i] = false;
|
|
|
|
}
|
2024-10-04 17:49:37 +00:00
|
|
|
|
|
|
|
for (var y = 0; y < Height; y++)
|
|
|
|
{
|
|
|
|
for (var x = 0; x < Width; x++)
|
|
|
|
{
|
|
|
|
AddLight(0, x, y, ambientLight, 1.0f, hdr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-05 17:13:30 +00:00
|
|
|
public void Write(BinaryWriter writer)
|
2024-09-22 10:14:34 +00:00
|
|
|
{
|
2024-10-27 09:21:19 +00:00
|
|
|
for (var i = 0; i < Layers; i++)
|
2024-10-04 17:49:37 +00:00
|
|
|
{
|
2024-10-27 09:21:19 +00:00
|
|
|
if (_litLayers[i])
|
|
|
|
{
|
|
|
|
writer.Write(Pixels[i]);
|
|
|
|
}
|
2024-10-04 17:49:37 +00:00
|
|
|
}
|
2024-09-22 10:14:34 +00:00
|
|
|
}
|
2025-01-12 11:49:26 +00:00
|
|
|
|
|
|
|
private void AddLayer()
|
|
|
|
{
|
|
|
|
var bytesPerLayer = Width * Height * Bpp;
|
|
|
|
var bytes = new byte[bytesPerLayer];
|
|
|
|
for (var i = 0; i < bytesPerLayer; i++)
|
|
|
|
{
|
|
|
|
bytes[i] = 0;
|
|
|
|
}
|
|
|
|
Pixels.Add(bytes);
|
|
|
|
Layers++;
|
|
|
|
}
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public byte VertexCount { get; set; }
|
|
|
|
public byte PolyCount { get; set; }
|
|
|
|
public byte RenderPolyCount { get; set; }
|
|
|
|
public byte PortalPolyCount { get; set; }
|
|
|
|
public byte PlaneCount { get; set; }
|
|
|
|
public byte Medium { get; set; }
|
2024-12-26 17:39:24 +00:00
|
|
|
public byte Flags { get; set; } // TODO: Make these a [Flags] enum
|
2024-09-20 15:28:44 +00:00
|
|
|
public int PortalVertices { get; set; }
|
|
|
|
public ushort NumVList { get; set; }
|
|
|
|
public byte AnimLightCount { get; set; }
|
|
|
|
public byte MotionIndex { get; set; }
|
|
|
|
public Vector3 SphereCenter { get; set; }
|
|
|
|
public float SphereRadius { get; set; }
|
|
|
|
public Vector3[] Vertices { get; set; }
|
|
|
|
public Poly[] Polys { get; set; }
|
|
|
|
public RenderPoly[] RenderPolys { get; set; }
|
|
|
|
public uint IndexCount { get; set; }
|
|
|
|
public byte[] Indices { get; set; }
|
|
|
|
public Plane[] Planes { get; set; }
|
2024-10-05 15:27:35 +00:00
|
|
|
public List<ushort> AnimLights { get; set; }
|
2024-09-20 15:28:44 +00:00
|
|
|
public LightmapInfo[] LightList { get; set; }
|
|
|
|
public Lightmap[] Lightmaps { get; set; }
|
|
|
|
public int LightIndexCount { get; set; }
|
2024-10-06 09:56:17 +00:00
|
|
|
public List<ushort> LightIndices { get; set; }
|
2024-12-26 17:10:41 +00:00
|
|
|
|
|
|
|
// Bonus data to make parallel iteration of cells easier
|
|
|
|
public CellZone ZoneInfo { get; set; } = new();
|
2024-09-20 15:28:44 +00:00
|
|
|
|
|
|
|
public Cell(BinaryReader reader, int bpp)
|
|
|
|
{
|
|
|
|
VertexCount = reader.ReadByte();
|
|
|
|
PolyCount = reader.ReadByte();
|
|
|
|
RenderPolyCount = reader.ReadByte();
|
|
|
|
PortalPolyCount = reader.ReadByte();
|
|
|
|
PlaneCount = reader.ReadByte();
|
|
|
|
Medium = reader.ReadByte();
|
|
|
|
Flags = reader.ReadByte();
|
|
|
|
PortalVertices = reader.ReadInt32();
|
|
|
|
NumVList = reader.ReadUInt16();
|
|
|
|
AnimLightCount = reader.ReadByte();
|
|
|
|
MotionIndex = reader.ReadByte();
|
|
|
|
SphereCenter = reader.ReadVec3();
|
|
|
|
SphereRadius = reader.ReadSingle();
|
|
|
|
Vertices = new Vector3[VertexCount];
|
|
|
|
for (var i = 0; i < VertexCount; i++)
|
|
|
|
{
|
|
|
|
Vertices[i] = reader.ReadVec3();
|
|
|
|
}
|
|
|
|
Polys = new Poly[PolyCount];
|
|
|
|
for (var i = 0; i < PolyCount; i++)
|
|
|
|
{
|
|
|
|
Polys[i] = new Poly(reader);
|
|
|
|
}
|
|
|
|
RenderPolys = new RenderPoly[RenderPolyCount];
|
|
|
|
for (var i = 0; i < RenderPolyCount; i++)
|
|
|
|
{
|
|
|
|
RenderPolys[i] = new RenderPoly(reader);
|
|
|
|
}
|
|
|
|
IndexCount = reader.ReadUInt32();
|
|
|
|
Indices = new byte[IndexCount];
|
|
|
|
for (var i = 0; i < IndexCount; i++)
|
|
|
|
{
|
|
|
|
Indices[i] = reader.ReadByte();
|
|
|
|
}
|
|
|
|
Planes = new Plane[PlaneCount];
|
|
|
|
for (var i = 0; i < PlaneCount; i++)
|
|
|
|
{
|
|
|
|
Planes[i] = new Plane(reader.ReadVec3(), reader.ReadSingle());
|
|
|
|
}
|
2024-10-05 15:27:35 +00:00
|
|
|
AnimLights = new List<ushort>(AnimLightCount);
|
2024-09-20 15:28:44 +00:00
|
|
|
for (var i = 0; i < AnimLightCount; i++)
|
|
|
|
{
|
2024-10-05 15:27:35 +00:00
|
|
|
AnimLights.Add(reader.ReadUInt16());
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
LightList = new LightmapInfo[RenderPolyCount];
|
|
|
|
for (var i = 0; i < RenderPolyCount; i++)
|
|
|
|
{
|
|
|
|
LightList[i] = new LightmapInfo(reader);
|
|
|
|
}
|
|
|
|
Lightmaps = new Lightmap[RenderPolyCount];
|
|
|
|
for (var i = 0; i < RenderPolyCount; i++)
|
|
|
|
{
|
|
|
|
var info = LightList[i];
|
|
|
|
Lightmaps[i] = new Lightmap(reader, info.Width, info.Height, info.AnimLightBitmask, bpp);
|
|
|
|
}
|
|
|
|
LightIndexCount = reader.ReadInt32();
|
2024-10-06 09:56:17 +00:00
|
|
|
LightIndices = new List<ushort>(LightIndexCount);
|
2024-09-20 15:28:44 +00:00
|
|
|
for (var i = 0; i < LightIndexCount; i++)
|
|
|
|
{
|
2024-10-06 09:56:17 +00:00
|
|
|
LightIndices.Add(reader.ReadUInt16());
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-22 10:14:34 +00:00
|
|
|
|
2024-10-05 13:35:07 +00:00
|
|
|
public void Write(BinaryWriter writer)
|
2024-09-22 10:14:34 +00:00
|
|
|
{
|
|
|
|
writer.Write(VertexCount);
|
|
|
|
writer.Write(PolyCount);
|
|
|
|
writer.Write(RenderPolyCount);
|
|
|
|
writer.Write(PortalPolyCount);
|
|
|
|
writer.Write(PlaneCount);
|
|
|
|
writer.Write(Medium);
|
|
|
|
writer.Write(Flags);
|
|
|
|
writer.Write(PortalVertices);
|
|
|
|
writer.Write(NumVList);
|
|
|
|
writer.Write(AnimLightCount);
|
|
|
|
writer.Write(MotionIndex);
|
|
|
|
writer.WriteVec3(SphereCenter);
|
|
|
|
writer.Write(SphereRadius);
|
|
|
|
foreach (var vertex in Vertices)
|
|
|
|
{
|
|
|
|
writer.WriteVec3(vertex);
|
|
|
|
}
|
|
|
|
foreach (var poly in Polys)
|
|
|
|
{
|
|
|
|
poly.Write(writer);
|
|
|
|
}
|
|
|
|
foreach (var renderPoly in RenderPolys)
|
|
|
|
{
|
|
|
|
renderPoly.Write(writer);
|
|
|
|
}
|
|
|
|
writer.Write(IndexCount);
|
|
|
|
writer.Write(Indices);
|
|
|
|
foreach (var plane in Planes)
|
|
|
|
{
|
|
|
|
writer.WriteVec3(plane.Normal);
|
|
|
|
writer.Write(plane.D);
|
|
|
|
}
|
|
|
|
foreach (var animLight in AnimLights)
|
|
|
|
{
|
|
|
|
writer.Write(animLight);
|
|
|
|
}
|
|
|
|
foreach (var lightmapInfo in LightList)
|
|
|
|
{
|
|
|
|
lightmapInfo.Write(writer);
|
|
|
|
}
|
|
|
|
foreach (var lightmap in Lightmaps)
|
|
|
|
{
|
|
|
|
lightmap.Write(writer);
|
|
|
|
}
|
|
|
|
writer.Write(LightIndexCount);
|
|
|
|
foreach (var lightIndex in LightIndices)
|
|
|
|
{
|
|
|
|
writer.Write(lightIndex);
|
|
|
|
}
|
|
|
|
}
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
|
2024-12-26 17:10:41 +00:00
|
|
|
// Readonly for now
|
|
|
|
public record CellZone
|
|
|
|
{
|
|
|
|
private readonly byte _data;
|
|
|
|
|
|
|
|
public CellZone()
|
|
|
|
{
|
|
|
|
_data = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public CellZone(BinaryReader reader)
|
|
|
|
{
|
|
|
|
_data = reader.ReadByte();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int GetAmbientLightZoneIndex()
|
|
|
|
{
|
|
|
|
return _data >> 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int GetFogZoneIndex()
|
|
|
|
{
|
|
|
|
return _data & 0x0F;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Write(BinaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.Write(_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-27 15:20:19 +00:00
|
|
|
public struct BspTree
|
|
|
|
{
|
|
|
|
public struct Node
|
|
|
|
{
|
|
|
|
int parentIndex; // TODO: Split the flags out of this
|
|
|
|
int cellId;
|
|
|
|
int planeId;
|
|
|
|
uint insideIndex;
|
|
|
|
uint outsideIndex;
|
|
|
|
|
|
|
|
public Node(BinaryReader reader)
|
|
|
|
{
|
|
|
|
parentIndex = reader.ReadInt32();
|
|
|
|
cellId = reader.ReadInt32();
|
|
|
|
planeId = reader.ReadInt32();
|
|
|
|
insideIndex = reader.ReadUInt32();
|
|
|
|
outsideIndex = reader.ReadUInt32();
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly void Write(BinaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.Write(parentIndex);
|
|
|
|
writer.Write(cellId);
|
|
|
|
writer.Write(planeId);
|
|
|
|
writer.Write(insideIndex);
|
|
|
|
writer.Write(outsideIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public uint PlaneCount;
|
|
|
|
public uint NodeCount;
|
|
|
|
public Plane[] Planes;
|
|
|
|
public Node[] Nodes;
|
|
|
|
|
|
|
|
public BspTree(BinaryReader reader)
|
|
|
|
{
|
|
|
|
PlaneCount = reader.ReadUInt32();
|
|
|
|
Planes = new Plane[PlaneCount];
|
|
|
|
for (var i = 0; i < PlaneCount; i++)
|
|
|
|
{
|
|
|
|
Planes[i] = new Plane(reader.ReadVec3(), reader.ReadSingle());
|
|
|
|
}
|
|
|
|
|
|
|
|
NodeCount = reader.ReadUInt32();
|
|
|
|
Nodes = new Node[NodeCount];
|
|
|
|
for (var i = 0; i < NodeCount; i++)
|
|
|
|
{
|
|
|
|
Nodes[i] = new Node(reader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly void Write(BinaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.Write(PlaneCount);
|
|
|
|
foreach (var plane in Planes)
|
|
|
|
{
|
|
|
|
writer.WriteVec3(plane.Normal);
|
|
|
|
writer.Write(plane.D);
|
|
|
|
}
|
|
|
|
writer.Write(NodeCount);
|
|
|
|
foreach (var node in Nodes)
|
|
|
|
{
|
|
|
|
node.Write(writer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-05 13:35:07 +00:00
|
|
|
public class LightTable
|
2024-09-27 15:20:19 +00:00
|
|
|
{
|
|
|
|
public struct LightData
|
|
|
|
{
|
|
|
|
public Vector3 Location;
|
|
|
|
public Vector3 Direction;
|
|
|
|
public Vector3 Color;
|
2024-10-05 13:35:07 +00:00
|
|
|
public float InnerAngle; // I'm pretty sure these are the spotlight angles
|
|
|
|
public float OuterAngle;
|
|
|
|
public float Radius;
|
2024-09-27 15:20:19 +00:00
|
|
|
|
|
|
|
public LightData(BinaryReader reader)
|
|
|
|
{
|
|
|
|
Location = reader.ReadVec3();
|
|
|
|
Direction = reader.ReadVec3();
|
|
|
|
Color = reader.ReadVec3();
|
|
|
|
InnerAngle = reader.ReadSingle();
|
|
|
|
OuterAngle = reader.ReadSingle();
|
|
|
|
Radius = reader.ReadSingle();
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly void Write(BinaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.WriteVec3(Location);
|
|
|
|
writer.WriteVec3(Direction);
|
|
|
|
writer.WriteVec3(Color);
|
|
|
|
writer.Write(InnerAngle);
|
|
|
|
writer.Write(OuterAngle);
|
|
|
|
writer.Write(Radius);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public struct AnimCellMap
|
|
|
|
{
|
|
|
|
public ushort CellIndex;
|
|
|
|
public ushort LightIndex;
|
|
|
|
|
|
|
|
public AnimCellMap(BinaryReader reader)
|
|
|
|
{
|
|
|
|
CellIndex = reader.ReadUInt16();
|
|
|
|
LightIndex = reader.ReadUInt16();
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly void Write(BinaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.Write(CellIndex);
|
|
|
|
writer.Write(LightIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int LightCount;
|
|
|
|
public int DynamicLightCount;
|
|
|
|
public int AnimMapCount;
|
2024-10-05 13:52:09 +00:00
|
|
|
public List<LightData> Lights;
|
2024-09-27 15:20:19 +00:00
|
|
|
public LightData[] ScratchpadLights;
|
2024-10-05 13:52:09 +00:00
|
|
|
public List<AnimCellMap> AnimCellMaps;
|
2024-09-27 15:20:19 +00:00
|
|
|
|
|
|
|
// TODO: Support olddark
|
|
|
|
public LightTable(BinaryReader reader)
|
|
|
|
{
|
|
|
|
LightCount = reader.ReadInt32();
|
|
|
|
DynamicLightCount = reader.ReadInt32();
|
2024-10-05 13:52:09 +00:00
|
|
|
var totalLightCount = LightCount + DynamicLightCount;
|
|
|
|
Lights = new List<LightData>(totalLightCount);
|
|
|
|
for (var i = 0; i < totalLightCount; i++)
|
2024-09-27 15:20:19 +00:00
|
|
|
{
|
2024-10-05 13:52:09 +00:00
|
|
|
Lights.Add(new LightData(reader));
|
2024-09-27 15:20:19 +00:00
|
|
|
}
|
|
|
|
ScratchpadLights = new LightData[32];
|
2024-10-05 13:52:09 +00:00
|
|
|
for (var i = 0; i < 32; i++)
|
2024-09-27 15:20:19 +00:00
|
|
|
{
|
|
|
|
ScratchpadLights[i] = new LightData(reader);
|
|
|
|
}
|
|
|
|
AnimMapCount = reader.ReadInt32();
|
2024-10-05 13:52:09 +00:00
|
|
|
AnimCellMaps = new List<AnimCellMap>(AnimMapCount);
|
|
|
|
for (var i = 0; i < AnimMapCount; i++)
|
2024-09-27 15:20:19 +00:00
|
|
|
{
|
2024-10-05 13:52:09 +00:00
|
|
|
AnimCellMaps.Add(new AnimCellMap(reader));
|
2024-09-27 15:20:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-05 13:35:07 +00:00
|
|
|
public void Write(BinaryWriter writer)
|
2024-09-27 15:20:19 +00:00
|
|
|
{
|
|
|
|
writer.Write(LightCount);
|
|
|
|
writer.Write(DynamicLightCount);
|
|
|
|
foreach (var light in Lights)
|
|
|
|
{
|
|
|
|
light.Write(writer);
|
|
|
|
}
|
|
|
|
foreach (var light in ScratchpadLights)
|
|
|
|
{
|
|
|
|
light.Write(writer);
|
|
|
|
}
|
|
|
|
writer.Write(AnimMapCount);
|
|
|
|
foreach (var map in AnimCellMaps)
|
|
|
|
{
|
|
|
|
map.Write(writer);
|
|
|
|
}
|
|
|
|
}
|
2024-10-05 13:52:09 +00:00
|
|
|
|
|
|
|
public void Reset()
|
|
|
|
{
|
2024-10-05 16:36:12 +00:00
|
|
|
// Seems light we store something for sunlight at index 0 even if sunlight isn't being used
|
|
|
|
// TODO: Work out what to actually *put* here
|
|
|
|
LightCount = 1;
|
|
|
|
Lights.Clear();
|
|
|
|
Lights.Add(new LightData());
|
|
|
|
|
2024-10-05 13:52:09 +00:00
|
|
|
DynamicLightCount = 0;
|
|
|
|
AnimMapCount = 0;
|
|
|
|
AnimCellMaps.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void AddLight(LightData data, bool dynamicLight = false)
|
|
|
|
{
|
|
|
|
if (dynamicLight)
|
|
|
|
{
|
|
|
|
DynamicLightCount++;
|
2025-01-11 17:57:52 +00:00
|
|
|
Lights.Add(data);
|
2024-10-05 13:52:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-01-11 17:57:52 +00:00
|
|
|
Lights.Insert(LightCount, data);
|
2024-10-05 13:52:09 +00:00
|
|
|
LightCount++;
|
|
|
|
}
|
|
|
|
}
|
2024-09-27 15:20:19 +00:00
|
|
|
}
|
|
|
|
|
2024-09-20 15:28:44 +00:00
|
|
|
public ChunkHeader Header { get; set; }
|
|
|
|
public WrHeader DataHeader { get; set; }
|
|
|
|
public Cell[] Cells { get; set; }
|
2024-09-27 15:20:19 +00:00
|
|
|
public BspTree Bsp { get; set; }
|
2024-12-26 17:10:41 +00:00
|
|
|
public CellZone[] CellZones { get; set; }
|
2024-09-27 15:20:19 +00:00
|
|
|
public LightTable LightingTable { get; set; }
|
2024-09-22 10:14:34 +00:00
|
|
|
private byte[] _unreadData;
|
2024-09-20 15:28:44 +00:00
|
|
|
|
|
|
|
public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry)
|
|
|
|
{
|
|
|
|
DataHeader = new(reader);
|
|
|
|
var bpp = (DataHeader.LightmapFormat == 0) ? 2 : 4;
|
|
|
|
|
|
|
|
Cells = new Cell[DataHeader.CellCount];
|
|
|
|
for (var i = 0; i < DataHeader.CellCount; i++)
|
|
|
|
{
|
|
|
|
Cells[i] = new Cell(reader, bpp);
|
|
|
|
}
|
|
|
|
|
2024-09-27 15:20:19 +00:00
|
|
|
Bsp = new BspTree(reader);
|
2024-12-26 17:10:41 +00:00
|
|
|
CellZones = new CellZone[Cells.Length];
|
|
|
|
for (var i = 0; i < Cells.Length; i++)
|
|
|
|
{
|
|
|
|
var zone = new CellZone(reader);
|
|
|
|
CellZones[i] = zone;
|
|
|
|
Cells[i].ZoneInfo = zone;
|
|
|
|
}
|
|
|
|
|
2024-09-27 15:20:19 +00:00
|
|
|
LightingTable = new LightTable(reader);
|
|
|
|
|
2024-09-20 15:28:44 +00:00
|
|
|
// TODO: All the other info lol
|
2024-09-22 10:14:34 +00:00
|
|
|
var length = entry.Offset + entry.Size + 24 - reader.BaseStream.Position;
|
|
|
|
_unreadData = reader.ReadBytes((int)length);
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void WriteData(BinaryWriter writer)
|
|
|
|
{
|
2024-09-22 10:14:34 +00:00
|
|
|
DataHeader.Write(writer);
|
|
|
|
foreach (var cell in Cells)
|
|
|
|
{
|
|
|
|
cell.Write(writer);
|
|
|
|
}
|
2024-09-27 15:20:19 +00:00
|
|
|
Bsp.Write(writer);
|
2024-12-26 17:10:41 +00:00
|
|
|
foreach (var cellZone in CellZones)
|
|
|
|
{
|
|
|
|
cellZone.Write(writer);
|
|
|
|
}
|
2024-09-27 15:20:19 +00:00
|
|
|
LightingTable.Write(writer);
|
2024-09-22 10:14:34 +00:00
|
|
|
writer.Write(_unreadData);
|
2024-09-20 15:28:44 +00:00
|
|
|
}
|
|
|
|
}
|