Compare commits
16 Commits
bc01e91dbc
...
1d32b3ef7b
Author | SHA1 | Date |
---|---|---|
Jarrod Doyle | 1d32b3ef7b | |
Jarrod Doyle | 5d72ba546e | |
Jarrod Doyle | 55c9c2cd31 | |
Jarrod Doyle | 7c733fc9fd | |
Jarrod Doyle | 6d665302d2 | |
Jarrod Doyle | 1f05663460 | |
Jarrod Doyle | dff2a9da2c | |
Jarrod Doyle | 0a4dcc6de0 | |
Jarrod Doyle | 0bcf24f640 | |
Jarrod Doyle | 3fa86a233f | |
Jarrod Doyle | ab738203d6 | |
Jarrod Doyle | ea72c3af4a | |
Jarrod Doyle | 013a3b845a | |
Jarrod Doyle | 25e8e50f5e | |
Jarrod Doyle | b9eae0e437 | |
Jarrod Doyle | 9251685d26 |
|
@ -1,5 +1,3 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace KeepersCompound.LGS.Database;
|
namespace KeepersCompound.LGS.Database;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace KeepersCompound.LGS.Database.Chunks;
|
namespace KeepersCompound.LGS.Database.Chunks;
|
||||||
|
|
||||||
class AiConverseChunk : IChunk
|
class AiConverseChunk : IChunk
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace KeepersCompound.LGS.Database.Chunks;
|
namespace KeepersCompound.LGS.Database.Chunks;
|
||||||
|
|
||||||
class AiRoomDb : IChunk
|
class AiRoomDb : IChunk
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace KeepersCompound.LGS.Database.Chunks;
|
namespace KeepersCompound.LGS.Database.Chunks;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace KeepersCompound.LGS.Database.Chunks;
|
namespace KeepersCompound.LGS.Database.Chunks;
|
||||||
|
|
||||||
public record LinkId
|
public record LinkId
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace KeepersCompound.LGS.Database.Chunks;
|
namespace KeepersCompound.LGS.Database.Chunks;
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class WorldRep : IChunk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Cell
|
public class Cell
|
||||||
{
|
{
|
||||||
public struct Poly
|
public struct Poly
|
||||||
{
|
{
|
||||||
|
@ -116,7 +116,7 @@ public class WorldRep : IChunk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct LightmapInfo
|
public class LightmapInfo
|
||||||
{
|
{
|
||||||
public (short, short) Bases { get; set; }
|
public (short, short) Bases { get; set; }
|
||||||
public short PaddedWidth { get; set; }
|
public short PaddedWidth { get; set; }
|
||||||
|
@ -137,7 +137,7 @@ public class WorldRep : IChunk
|
||||||
AnimLightBitmask = reader.ReadUInt32();
|
AnimLightBitmask = reader.ReadUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly void Write(BinaryWriter writer)
|
public void Write(BinaryWriter writer)
|
||||||
{
|
{
|
||||||
writer.Write(Bases.Item1);
|
writer.Write(Bases.Item1);
|
||||||
writer.Write(Bases.Item2);
|
writer.Write(Bases.Item2);
|
||||||
|
@ -150,9 +150,9 @@ public class WorldRep : IChunk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Lightmap
|
public class Lightmap
|
||||||
{
|
{
|
||||||
public byte[] Pixels { get; set; }
|
public List<byte[]> Pixels { get; set; }
|
||||||
|
|
||||||
public int Layers;
|
public int Layers;
|
||||||
public int Width;
|
public int Width;
|
||||||
|
@ -161,44 +161,50 @@ public class WorldRep : IChunk
|
||||||
|
|
||||||
public Lightmap(BinaryReader reader, byte width, byte height, uint bitmask, int bytesPerPixel)
|
public Lightmap(BinaryReader reader, byte width, byte height, uint bitmask, int bytesPerPixel)
|
||||||
{
|
{
|
||||||
var count = 1 + BitOperations.PopCount(bitmask);
|
var layers = 1 + BitOperations.PopCount(bitmask);
|
||||||
var length = bytesPerPixel * width * height * count;
|
var length = bytesPerPixel * width * height;
|
||||||
Pixels = reader.ReadBytes(length);
|
Pixels = new List<byte[]>();
|
||||||
Layers = count;
|
for (var i = 0; i < layers; i++)
|
||||||
|
{
|
||||||
|
Pixels.Add(reader.ReadBytes(length));
|
||||||
|
}
|
||||||
|
Layers = layers;
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = height;
|
Height = height;
|
||||||
Bpp = bytesPerPixel;
|
Bpp = bytesPerPixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Vector4 GetPixel(uint layer, uint x, uint y)
|
public Vector4 GetPixel(uint layer, uint x, uint y)
|
||||||
{
|
{
|
||||||
if (layer >= Layers || x >= Width || y >= Height)
|
if (layer >= Layers || x >= Width || y >= Height)
|
||||||
{
|
{
|
||||||
return Vector4.Zero;
|
return Vector4.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
var idx = 0 + x * Bpp + y * Bpp * Width + layer * Bpp * Width * Height;
|
var pLayer = Pixels[(int)layer];
|
||||||
|
var idx = x * Bpp + y * Bpp * Width;
|
||||||
switch (Bpp)
|
switch (Bpp)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
var raw1 = Pixels[idx];
|
var raw1 = pLayer[idx];
|
||||||
return new Vector4(raw1, raw1, raw1, 255) / 255.0f;
|
return new Vector4(raw1, raw1, raw1, 255) / 255.0f;
|
||||||
case 2:
|
case 2:
|
||||||
var raw2 = Pixels[idx] + (Pixels[idx + 1] << 8);
|
var raw2 = pLayer[idx] + (pLayer[idx + 1] << 8);
|
||||||
return new Vector4(raw2 & 31, (raw2 >> 5) & 31, (raw2 >> 10) & 31, 31) / 31.0f;
|
return new Vector4(raw2 & 31, (raw2 >> 5) & 31, (raw2 >> 10) & 31, 31) / 31.0f;
|
||||||
case 4:
|
case 4:
|
||||||
return new Vector4(Pixels[idx + 2], Pixels[idx + 1], Pixels[idx], Pixels[idx + 3]) / 255.0f;
|
return new Vector4(pLayer[idx + 2], pLayer[idx + 1], pLayer[idx], pLayer[idx + 3]) / 255.0f;
|
||||||
default:
|
default:
|
||||||
return Vector4.Zero;
|
return Vector4.Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly byte[] AsBytesRgba(int layer)
|
public byte[] AsBytesRgba(int layer)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfLessThan(layer, 0, nameof(layer));
|
ArgumentOutOfRangeException.ThrowIfLessThan(layer, 0, nameof(layer));
|
||||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(layer, Layers, nameof(layer));
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(layer, Layers, nameof(layer));
|
||||||
|
|
||||||
var pIdx = layer * Bpp * Width * Height;
|
var pLayer = Pixels[layer];
|
||||||
|
var pIdx = 0;
|
||||||
var length = 4 * Width * Height;
|
var length = 4 * Width * Height;
|
||||||
var bytes = new byte[length];
|
var bytes = new byte[length];
|
||||||
for (var i = 0; i < length; i += 4, pIdx += Bpp)
|
for (var i = 0; i < length; i += 4, pIdx += Bpp)
|
||||||
|
@ -206,24 +212,24 @@ public class WorldRep : IChunk
|
||||||
switch (Bpp)
|
switch (Bpp)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
var raw1 = Pixels[pIdx];
|
var raw1 = pLayer[pIdx];
|
||||||
bytes[i] = raw1;
|
bytes[i] = raw1;
|
||||||
bytes[i + 1] = raw1;
|
bytes[i + 1] = raw1;
|
||||||
bytes[i + 2] = raw1;
|
bytes[i + 2] = raw1;
|
||||||
bytes[i + 3] = 255;
|
bytes[i + 3] = 255;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
var raw2 = Pixels[pIdx] + (Pixels[pIdx + 1] << 8);
|
var raw2 = pLayer[pIdx] + (pLayer[pIdx + 1] << 8);
|
||||||
bytes[i] = (byte)(255 * (raw2 & 31) / 31.0f);
|
bytes[i] = (byte)(255 * (raw2 & 31) / 31.0f);
|
||||||
bytes[i + 1] = (byte)(255 * ((raw2 >> 5) & 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 + 2] = (byte)(255 * ((raw2 >> 10) & 31) / 31.0f);
|
||||||
bytes[i + 3] = 255;
|
bytes[i + 3] = 255;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
bytes[i] = Pixels[pIdx + 2];
|
bytes[i] = pLayer[pIdx + 2];
|
||||||
bytes[i + 1] = Pixels[pIdx + 1];
|
bytes[i + 1] = pLayer[pIdx + 1];
|
||||||
bytes[i + 2] = Pixels[pIdx];
|
bytes[i + 2] = pLayer[pIdx];
|
||||||
bytes[i + 3] = Pixels[pIdx + 3];
|
bytes[i + 3] = pLayer[pIdx + 3];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,13 +238,14 @@ public class WorldRep : IChunk
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This ONLY works for rgba (bpp = 4)!!!
|
// TODO: This ONLY works for rgba (bpp = 4)!!!
|
||||||
public void AddLight(int layer, int x, int y, byte r, byte g, byte b)
|
public void AddLight(int layer, int x, int y, float r, float g, float b)
|
||||||
{
|
{
|
||||||
var idx = (x + y * Width + layer * Width * Height) * Bpp;
|
var idx = (x + y * Width) * Bpp;
|
||||||
Pixels[idx] = (byte)Math.Min(Pixels[idx] + b, 255);
|
var pLayer = Pixels[layer];
|
||||||
Pixels[idx + 1] = (byte)Math.Min(Pixels[idx + 1] + g, 255);
|
pLayer[idx] = (byte)Math.Clamp(pLayer[idx] + r, 0, 255);
|
||||||
Pixels[idx + 2] = (byte)Math.Min(Pixels[idx + 2] + r, 255);
|
pLayer[idx + 1] = (byte)Math.Clamp(pLayer[idx + 1] + g, 0, 255);
|
||||||
Pixels[idx + 3] = 255;
|
pLayer[idx + 2] = (byte)Math.Clamp(pLayer[idx + 2] + b, 0, 255);
|
||||||
|
pLayer[idx + 3] = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddLight(int layer, int x, int y, Vector3 color, float strength, bool hdr)
|
public void AddLight(int layer, int x, int y, Vector3 color, float strength, bool hdr)
|
||||||
|
@ -264,12 +271,41 @@ public class WorldRep : IChunk
|
||||||
c /= ratio;
|
c /= ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddLight(layer, x, y, (byte)c.X, (byte)c.Y, (byte)c.Z);
|
AddLight(layer, x, y, c.Z, c.Y, c.X);
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly void Write(BinaryWriter writer)
|
public void Reset(Vector3 ambientLight, bool hdr)
|
||||||
{
|
{
|
||||||
writer.Write(Pixels);
|
Layers = 0;
|
||||||
|
Pixels.Clear();
|
||||||
|
AddLayer();
|
||||||
|
|
||||||
|
for (var y = 0; y < Height; y++)
|
||||||
|
{
|
||||||
|
for (var x = 0; x < Width; x++)
|
||||||
|
{
|
||||||
|
AddLight(0, x, y, ambientLight, 1.0f, hdr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddLayer()
|
||||||
|
{
|
||||||
|
var bytesPerLayer = Width * Height * Bpp;
|
||||||
|
Pixels.Add(new byte[bytesPerLayer]);
|
||||||
|
for (var j = 0; j < bytesPerLayer; j++)
|
||||||
|
{
|
||||||
|
Pixels[Layers][j] = 0;
|
||||||
|
}
|
||||||
|
Layers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(BinaryWriter writer)
|
||||||
|
{
|
||||||
|
foreach (var layer in Pixels)
|
||||||
|
{
|
||||||
|
writer.Write(layer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +328,7 @@ public class WorldRep : IChunk
|
||||||
public uint IndexCount { get; set; }
|
public uint IndexCount { get; set; }
|
||||||
public byte[] Indices { get; set; }
|
public byte[] Indices { get; set; }
|
||||||
public Plane[] Planes { get; set; }
|
public Plane[] Planes { get; set; }
|
||||||
public ushort[] AnimLights { get; set; }
|
public List<ushort> AnimLights { get; set; }
|
||||||
public LightmapInfo[] LightList { get; set; }
|
public LightmapInfo[] LightList { get; set; }
|
||||||
public Lightmap[] Lightmaps { get; set; }
|
public Lightmap[] Lightmaps { get; set; }
|
||||||
public int LightIndexCount { get; set; }
|
public int LightIndexCount { get; set; }
|
||||||
|
@ -339,10 +375,10 @@ public class WorldRep : IChunk
|
||||||
{
|
{
|
||||||
Planes[i] = new Plane(reader.ReadVec3(), reader.ReadSingle());
|
Planes[i] = new Plane(reader.ReadVec3(), reader.ReadSingle());
|
||||||
}
|
}
|
||||||
AnimLights = new ushort[AnimLightCount];
|
AnimLights = new List<ushort>(AnimLightCount);
|
||||||
for (var i = 0; i < AnimLightCount; i++)
|
for (var i = 0; i < AnimLightCount; i++)
|
||||||
{
|
{
|
||||||
AnimLights[i] = reader.ReadUInt16();
|
AnimLights.Add(reader.ReadUInt16());
|
||||||
}
|
}
|
||||||
LightList = new LightmapInfo[RenderPolyCount];
|
LightList = new LightmapInfo[RenderPolyCount];
|
||||||
for (var i = 0; i < RenderPolyCount; i++)
|
for (var i = 0; i < RenderPolyCount; i++)
|
||||||
|
@ -363,7 +399,7 @@ public class WorldRep : IChunk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly void Write(BinaryWriter writer)
|
public void Write(BinaryWriter writer)
|
||||||
{
|
{
|
||||||
writer.Write(VertexCount);
|
writer.Write(VertexCount);
|
||||||
writer.Write(PolyCount);
|
writer.Write(PolyCount);
|
||||||
|
@ -484,16 +520,16 @@ public class WorldRep : IChunk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct LightTable
|
public class LightTable
|
||||||
{
|
{
|
||||||
public struct LightData
|
public struct LightData
|
||||||
{
|
{
|
||||||
public Vector3 Location;
|
public Vector3 Location;
|
||||||
public Vector3 Direction;
|
public Vector3 Direction;
|
||||||
public Vector3 Color;
|
public Vector3 Color;
|
||||||
float InnerAngle; // I'm pretty sure these are the spotlight angles
|
public float InnerAngle; // I'm pretty sure these are the spotlight angles
|
||||||
float OuterAngle;
|
public float OuterAngle;
|
||||||
float Radius;
|
public float Radius;
|
||||||
|
|
||||||
public LightData(BinaryReader reader)
|
public LightData(BinaryReader reader)
|
||||||
{
|
{
|
||||||
|
@ -537,34 +573,35 @@ public class WorldRep : IChunk
|
||||||
public int LightCount;
|
public int LightCount;
|
||||||
public int DynamicLightCount;
|
public int DynamicLightCount;
|
||||||
public int AnimMapCount;
|
public int AnimMapCount;
|
||||||
public LightData[] Lights;
|
public List<LightData> Lights;
|
||||||
public LightData[] ScratchpadLights;
|
public LightData[] ScratchpadLights;
|
||||||
public AnimCellMap[] AnimCellMaps;
|
public List<AnimCellMap> AnimCellMaps;
|
||||||
|
|
||||||
// TODO: Support olddark
|
// TODO: Support olddark
|
||||||
public LightTable(BinaryReader reader)
|
public LightTable(BinaryReader reader)
|
||||||
{
|
{
|
||||||
LightCount = reader.ReadInt32();
|
LightCount = reader.ReadInt32();
|
||||||
DynamicLightCount = reader.ReadInt32();
|
DynamicLightCount = reader.ReadInt32();
|
||||||
Lights = new LightData[LightCount + DynamicLightCount];
|
var totalLightCount = LightCount + DynamicLightCount;
|
||||||
for (var i = 0; i < Lights.Length; i++)
|
Lights = new List<LightData>(totalLightCount);
|
||||||
|
for (var i = 0; i < totalLightCount; i++)
|
||||||
{
|
{
|
||||||
Lights[i] = new LightData(reader);
|
Lights.Add(new LightData(reader));
|
||||||
}
|
}
|
||||||
ScratchpadLights = new LightData[32];
|
ScratchpadLights = new LightData[32];
|
||||||
for (var i = 0; i < ScratchpadLights.Length; i++)
|
for (var i = 0; i < 32; i++)
|
||||||
{
|
{
|
||||||
ScratchpadLights[i] = new LightData(reader);
|
ScratchpadLights[i] = new LightData(reader);
|
||||||
}
|
}
|
||||||
AnimMapCount = reader.ReadInt32();
|
AnimMapCount = reader.ReadInt32();
|
||||||
AnimCellMaps = new AnimCellMap[AnimMapCount];
|
AnimCellMaps = new List<AnimCellMap>(AnimMapCount);
|
||||||
for (var i = 0; i < AnimCellMaps.Length; i++)
|
for (var i = 0; i < AnimMapCount; i++)
|
||||||
{
|
{
|
||||||
AnimCellMaps[i] = new AnimCellMap(reader);
|
AnimCellMaps.Add(new AnimCellMap(reader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly void Write(BinaryWriter writer)
|
public void Write(BinaryWriter writer)
|
||||||
{
|
{
|
||||||
writer.Write(LightCount);
|
writer.Write(LightCount);
|
||||||
writer.Write(DynamicLightCount);
|
writer.Write(DynamicLightCount);
|
||||||
|
@ -582,6 +619,33 @@ public class WorldRep : IChunk
|
||||||
map.Write(writer);
|
map.Write(writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
DynamicLightCount = 0;
|
||||||
|
AnimMapCount = 0;
|
||||||
|
AnimCellMaps.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddLight(LightData data, bool dynamicLight = false)
|
||||||
|
{
|
||||||
|
if (dynamicLight)
|
||||||
|
{
|
||||||
|
DynamicLightCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LightCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lights.Add(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChunkHeader Header { get; set; }
|
public ChunkHeader Header { get; set; }
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using KeepersCompound.LGS.Database.Chunks;
|
using KeepersCompound.LGS.Database.Chunks;
|
||||||
|
|
||||||
|
@ -133,6 +129,17 @@ public class DbFile
|
||||||
writer.Write(tocOffset);
|
writer.Write(tocOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetChunk<T>(string name, out T chunk)
|
||||||
|
{
|
||||||
|
if (Chunks.TryGetValue(name, out var rawChunk))
|
||||||
|
{
|
||||||
|
chunk = (T)rawChunk;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
chunk = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static IChunk NewChunk(string entryName)
|
private static IChunk NewChunk(string entryName)
|
||||||
{
|
{
|
||||||
return entryName switch
|
return entryName switch
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using KeepersCompound.LGS.Database.Chunks;
|
using KeepersCompound.LGS.Database.Chunks;
|
||||||
|
|
||||||
namespace KeepersCompound.LGS.Database;
|
namespace KeepersCompound.LGS.Database;
|
||||||
|
@ -38,19 +35,17 @@ public class ObjectHierarchy
|
||||||
|
|
||||||
T GetMergedChunk<T>(string name) where T : IMergable
|
T GetMergedChunk<T>(string name) where T : IMergable
|
||||||
{
|
{
|
||||||
if (db.Chunks.TryGetValue(name, out var rawChunk))
|
if (!db.TryGetChunk<T>(name, out var chunk))
|
||||||
{
|
{
|
||||||
var chunk = (T)rawChunk;
|
throw new ArgumentException($"No chunk with name ({name}) found", nameof(name));
|
||||||
if (gam != null && gam.Chunks.TryGetValue(name, out var rawGamChunk))
|
|
||||||
{
|
|
||||||
var gamChunk = (T)rawGamChunk;
|
|
||||||
gamChunk.Merge(chunk);
|
|
||||||
return gamChunk;
|
|
||||||
}
|
|
||||||
return chunk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentException($"No chunk with name ({name}) found", nameof(name));
|
if (gam != null && gam.TryGetChunk<T>(name, out var gamChunk))
|
||||||
|
{
|
||||||
|
gamChunk.Merge(chunk);
|
||||||
|
return gamChunk;
|
||||||
|
}
|
||||||
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add parentages
|
// Add parentages
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace KeepersCompound.LGS.Database;
|
namespace KeepersCompound.LGS.Database;
|
||||||
|
|
||||||
public struct Version
|
public struct Version
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace KeepersCompound.LGS;
|
namespace KeepersCompound.LGS;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using KeepersCompound.LGS;
|
using KeepersCompound.LGS;
|
||||||
using KeepersCompound.LGS.Database;
|
using KeepersCompound.LGS.Database;
|
||||||
|
@ -24,6 +23,7 @@ class Program
|
||||||
public float spotlightOuterAngle;
|
public float spotlightOuterAngle;
|
||||||
|
|
||||||
public bool anim;
|
public bool anim;
|
||||||
|
public int animObjId;
|
||||||
public int animLightTableIndex;
|
public int animLightTableIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,12 +57,9 @@ class Program
|
||||||
var mis = Timing.TimeStage("Parse DB", () => new DbFile(misPath));
|
var mis = Timing.TimeStage("Parse DB", () => new DbFile(misPath));
|
||||||
var hierarchy = Timing.TimeStage("Build Hierarchy", () => BuildHierarchy(misPath, mis));
|
var hierarchy = Timing.TimeStage("Build Hierarchy", () => BuildHierarchy(misPath, mis));
|
||||||
|
|
||||||
var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign));
|
|
||||||
|
|
||||||
// Build embree mesh
|
// Build embree mesh
|
||||||
if (!mis.Chunks.TryGetValue("WREXT", out var wrRaw))
|
if (!mis.TryGetChunk<WorldRep>("WREXT", out var worldRep))
|
||||||
return;
|
return;
|
||||||
var worldRep = (WorldRep)wrRaw;
|
|
||||||
var scene = Timing.TimeStage("Build Scene", () =>
|
var scene = Timing.TimeStage("Build Scene", () =>
|
||||||
{
|
{
|
||||||
var rt = new Raytracer();
|
var rt = new Raytracer();
|
||||||
|
@ -72,10 +69,12 @@ class Program
|
||||||
});
|
});
|
||||||
|
|
||||||
// For each lightmap pixel cast against all the brush and object lights
|
// For each lightmap pixel cast against all the brush and object lights
|
||||||
if (!mis.Chunks.TryGetValue("RENDPARAMS", out var rendParamsRaw))
|
if (!mis.TryGetChunk<RendParams>("RENDPARAMS", out var rendParams))
|
||||||
return;
|
return;
|
||||||
var ambient = ((RendParams)rendParamsRaw).ambientLight * 255;
|
var ambient = rendParams.ambientLight * 255;
|
||||||
|
var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign));
|
||||||
Timing.TimeStage("Light", () => CastSceneParallel(scene, worldRep, [.. lights], ambient));
|
Timing.TimeStage("Light", () => CastSceneParallel(scene, worldRep, [.. lights], ambient));
|
||||||
|
Timing.TimeStage("Update Anim Mapping", () => SetAnimLightCellMaps(mis, worldRep, lights));
|
||||||
|
|
||||||
var dir = Path.GetDirectoryName(misPath);
|
var dir = Path.GetDirectoryName(misPath);
|
||||||
var filename = Path.GetFileNameWithoutExtension(misPath);
|
var filename = Path.GetFileNameWithoutExtension(misPath);
|
||||||
|
@ -109,126 +108,226 @@ class Program
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get list of brush lights, and object lights (ignore anim lights for now)
|
private static void SetAnimLightCellMaps(
|
||||||
private static List<Light> BuildLightList(DbFile mis, ObjectHierarchy hierarchy, ResourcePathManager.CampaignResources campaign)
|
DbFile mis,
|
||||||
|
WorldRep worldRep,
|
||||||
|
List<Light> lights)
|
||||||
|
{
|
||||||
|
// Now that we've set all the per-cell stuff we need to aggregate the cell mappings
|
||||||
|
// We can't do this in parallel which is why it's being done afterwards rather than
|
||||||
|
// as we go
|
||||||
|
var map = new Dictionary<ushort, List<WorldRep.LightTable.AnimCellMap>>();
|
||||||
|
for (var i = 0; i < worldRep.Cells.Length; i++)
|
||||||
|
{
|
||||||
|
var cell = worldRep.Cells[i];
|
||||||
|
for (var j = 0; j < cell.AnimLightCount; j++)
|
||||||
|
{
|
||||||
|
var animLightIdx = cell.AnimLights[j];
|
||||||
|
if (!map.TryGetValue(animLightIdx, out var value))
|
||||||
|
{
|
||||||
|
value = [];
|
||||||
|
map[animLightIdx] = value;
|
||||||
|
}
|
||||||
|
value.Add(new WorldRep.LightTable.AnimCellMap
|
||||||
|
{
|
||||||
|
CellIndex = (ushort)i,
|
||||||
|
LightIndex = (ushort)j,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mis.TryGetChunk<PropertyChunk<PropAnimLight>>("P$AnimLight", out var animLightChunk))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (var (lightIdx, animCellMaps) in map)
|
||||||
|
{
|
||||||
|
// Get the appropriate property!!
|
||||||
|
var light = lights.Find((l) => l.anim && l.animLightTableIndex == lightIdx);
|
||||||
|
foreach (var prop in animLightChunk.properties)
|
||||||
|
{
|
||||||
|
if (prop.objectId == light.animObjId)
|
||||||
|
{
|
||||||
|
prop.LightTableLightIndex = lightIdx;
|
||||||
|
prop.LightTableMapIndex = (ushort)worldRep.LightingTable.AnimMapCount;
|
||||||
|
prop.CellsReached = (ushort)animCellMaps.Count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var animCellMap in animCellMaps)
|
||||||
|
{
|
||||||
|
worldRep.LightingTable.AnimCellMaps.Add(animCellMap);
|
||||||
|
worldRep.LightingTable.AnimMapCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather all the brush, object, and anim ligths. Resets the lighting table
|
||||||
|
// TODO: Handle dynamic lights
|
||||||
|
private static List<Light> BuildLightList(
|
||||||
|
DbFile mis,
|
||||||
|
ObjectHierarchy hierarchy,
|
||||||
|
ResourcePathManager.CampaignResources campaign)
|
||||||
{
|
{
|
||||||
var lights = new List<Light>();
|
var lights = new List<Light>();
|
||||||
|
|
||||||
if (mis.Chunks.TryGetValue("BRLIST", out var brListRaw))
|
// Get the chunks we need
|
||||||
|
if (!mis.TryGetChunk<WorldRep>("WREXT", out var worldRep) ||
|
||||||
|
!mis.TryGetChunk<BrList>("BRLIST", out var brList))
|
||||||
{
|
{
|
||||||
var brList = (BrList)brListRaw;
|
return lights;
|
||||||
foreach (var brush in brList.Brushes)
|
}
|
||||||
|
|
||||||
|
worldRep.LightingTable.Reset();
|
||||||
|
|
||||||
|
foreach (var brush in brList.Brushes)
|
||||||
|
{
|
||||||
|
if (brush.media == BrList.Brush.Media.Light)
|
||||||
{
|
{
|
||||||
if (brush.media == BrList.Brush.Media.Light)
|
// For some reason the light table index on brush lights is 1 indexed
|
||||||
|
brush.brushInfo = (uint)worldRep.LightingTable.LightCount + 1;
|
||||||
|
|
||||||
|
var sz = brush.size;
|
||||||
|
var light = new Light
|
||||||
{
|
{
|
||||||
var sz = brush.size;
|
position = brush.position,
|
||||||
lights.Add(new Light
|
color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
|
||||||
|
radius = float.MaxValue,
|
||||||
|
r2 = float.MaxValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
lights.Add(light);
|
||||||
|
worldRep.LightingTable.AddLight(new WorldRep.LightTable.LightData
|
||||||
|
{
|
||||||
|
Location = light.position,
|
||||||
|
Direction = light.spotlightDir,
|
||||||
|
Color = light.color / 32.0f, // TODO: This is based on light_scale config var
|
||||||
|
InnerAngle = -1.0f,
|
||||||
|
Radius = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (brush.media == BrList.Brush.Media.Object)
|
||||||
|
{
|
||||||
|
// TODO: Handle PropSpotlightAndAmbient
|
||||||
|
var id = (int)brush.brushInfo;
|
||||||
|
var propAnimLight = hierarchy.GetProperty<PropAnimLight>(id, "P$AnimLight", false);
|
||||||
|
var propLight = hierarchy.GetProperty<PropLight>(id, "P$Light", false);
|
||||||
|
var propLightColor = hierarchy.GetProperty<PropLightColor>(id, "P$LightColo");
|
||||||
|
var propSpotlight = hierarchy.GetProperty<PropSpotlight>(id, "P$Spotlight");
|
||||||
|
var propModelname = hierarchy.GetProperty<PropLabel>(id, "P$ModelName");
|
||||||
|
|
||||||
|
propLightColor ??= new PropLightColor { Hue = 0, Saturation = 0 };
|
||||||
|
|
||||||
|
var baseLight = new Light
|
||||||
|
{
|
||||||
|
position = brush.position,
|
||||||
|
spotlightDir = -Vector3.UnitZ,
|
||||||
|
spotlightInnerAngle = -1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (propModelname != null)
|
||||||
|
{
|
||||||
|
var resName = $"{propModelname.value.ToLower()}.bin";
|
||||||
|
var modelPath = campaign.GetResourcePath(ResourceType.Object, resName);
|
||||||
|
if (modelPath != null)
|
||||||
{
|
{
|
||||||
position = brush.position,
|
// TODO: Handle failing to find model more gracefully
|
||||||
color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
|
var model = new ModelFile(modelPath);
|
||||||
radius = float.MaxValue,
|
if (model.TryGetVhot(ModelFile.VhotId.LightPosition, out var vhot))
|
||||||
r2 = float.MaxValue,
|
{
|
||||||
});
|
baseLight.position += vhot.Position;
|
||||||
|
}
|
||||||
|
if (model.TryGetVhot(ModelFile.VhotId.LightDirection, out vhot))
|
||||||
|
{
|
||||||
|
baseLight.spotlightDir = vhot.Position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (brush.media == BrList.Brush.Media.Object)
|
|
||||||
|
if (propSpotlight != null)
|
||||||
{
|
{
|
||||||
// TODO: Handle PropSpotlightAndAmbient
|
var rot = Matrix4x4.Identity;
|
||||||
var id = (int)brush.brushInfo;
|
rot *= Matrix4x4.CreateRotationX(float.DegreesToRadians(brush.angle.X));
|
||||||
var propAnimLight = hierarchy.GetProperty<PropAnimLight>(id, "P$AnimLight", false);
|
rot *= Matrix4x4.CreateRotationY(float.DegreesToRadians(brush.angle.Y));
|
||||||
var propLight = hierarchy.GetProperty<PropLight>(id, "P$Light", false);
|
rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z));
|
||||||
var propLightColor = hierarchy.GetProperty<PropLightColor>(id, "P$LightColo");
|
|
||||||
var propSpotlight = hierarchy.GetProperty<PropSpotlight>(id, "P$Spotlight");
|
|
||||||
var propModelname = hierarchy.GetProperty<PropLabel>(id, "P$ModelName");
|
|
||||||
|
|
||||||
propLightColor ??= new PropLightColor { Hue = 0, Saturation = 0 };
|
baseLight.spotlight = true;
|
||||||
|
baseLight.spotlightDir = Vector3.Transform(baseLight.spotlightDir, rot);
|
||||||
|
baseLight.spotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.InnerAngle));
|
||||||
|
baseLight.spotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.OuterAngle));
|
||||||
|
}
|
||||||
|
|
||||||
var baseLight = new Light
|
if (propLight != null)
|
||||||
|
{
|
||||||
|
var light = new Light
|
||||||
{
|
{
|
||||||
position = brush.position,
|
position = baseLight.position + propLight.Offset,
|
||||||
spotlightDir = -Vector3.UnitZ,
|
color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness),
|
||||||
|
innerRadius = propLight.InnerRadius,
|
||||||
|
radius = propLight.Radius,
|
||||||
|
r2 = propLight.Radius * propLight.Radius,
|
||||||
|
spotlight = baseLight.spotlight,
|
||||||
|
spotlightDir = baseLight.spotlightDir,
|
||||||
|
spotlightInnerAngle = baseLight.spotlightInnerAngle,
|
||||||
|
spotlightOuterAngle = baseLight.spotlightOuterAngle,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (propModelname != null)
|
if (propLight.Radius == 0)
|
||||||
{
|
{
|
||||||
var resName = $"{propModelname.value.ToLower()}.bin";
|
light.radius = float.MaxValue;
|
||||||
var modelPath = campaign.GetResourcePath(ResourceType.Object, resName);
|
light.r2 = float.MaxValue;
|
||||||
if (modelPath != null)
|
|
||||||
{
|
|
||||||
// TODO: Handle failing to find model more gracefully
|
|
||||||
var model = new ModelFile(modelPath);
|
|
||||||
if (model.TryGetVhot(ModelFile.VhotId.LightPosition, out var vhot))
|
|
||||||
{
|
|
||||||
baseLight.position += vhot.Position;
|
|
||||||
}
|
|
||||||
if (model.TryGetVhot(ModelFile.VhotId.LightDirection, out vhot))
|
|
||||||
{
|
|
||||||
baseLight.spotlightDir = vhot.Position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propSpotlight != null)
|
lights.Add(light);
|
||||||
|
worldRep.LightingTable.AddLight(new WorldRep.LightTable.LightData
|
||||||
{
|
{
|
||||||
var rot = Matrix4x4.Identity;
|
Location = light.position,
|
||||||
rot *= Matrix4x4.CreateRotationX(float.DegreesToRadians(brush.angle.X));
|
Direction = light.spotlightDir,
|
||||||
rot *= Matrix4x4.CreateRotationY(float.DegreesToRadians(brush.angle.Y));
|
Color = light.color / 32.0f, // TODO: This is based on light_scale config var
|
||||||
rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z));
|
InnerAngle = light.spotlightInnerAngle,
|
||||||
|
OuterAngle = light.spotlightOuterAngle,
|
||||||
|
Radius = propLight.Radius,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
baseLight.spotlight = true;
|
if (propAnimLight != null)
|
||||||
baseLight.spotlightDir = Vector3.Transform(baseLight.spotlightDir, rot);
|
{
|
||||||
baseLight.spotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.InnerAngle));
|
var lightIndex = worldRep.LightingTable.LightCount;
|
||||||
baseLight.spotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.OuterAngle));
|
propAnimLight.LightTableLightIndex = (ushort)lightIndex;
|
||||||
|
|
||||||
|
var light = new Light
|
||||||
|
{
|
||||||
|
position = baseLight.position + propAnimLight.Offset,
|
||||||
|
color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propAnimLight.MaxBrightness),
|
||||||
|
innerRadius = propAnimLight.InnerRadius,
|
||||||
|
radius = propAnimLight.Radius,
|
||||||
|
r2 = propAnimLight.Radius * propAnimLight.Radius,
|
||||||
|
spotlight = baseLight.spotlight,
|
||||||
|
spotlightDir = baseLight.spotlightDir,
|
||||||
|
spotlightInnerAngle = baseLight.spotlightInnerAngle,
|
||||||
|
spotlightOuterAngle = baseLight.spotlightOuterAngle,
|
||||||
|
anim = true,
|
||||||
|
animObjId = id,
|
||||||
|
animLightTableIndex = propAnimLight.LightTableLightIndex,
|
||||||
|
};
|
||||||
|
if (propAnimLight.Radius == 0)
|
||||||
|
{
|
||||||
|
light.radius = float.MaxValue;
|
||||||
|
light.r2 = float.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propLight != null)
|
lights.Add(light);
|
||||||
|
worldRep.LightingTable.AddLight(new WorldRep.LightTable.LightData
|
||||||
{
|
{
|
||||||
var light = new Light
|
Location = light.position,
|
||||||
{
|
Direction = light.spotlightDir,
|
||||||
position = baseLight.position + propLight.Offset,
|
Color = light.color / 32.0f, // TODO: This is based on light_scale config var
|
||||||
color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness),
|
InnerAngle = light.spotlightInnerAngle,
|
||||||
innerRadius = propLight.InnerRadius,
|
OuterAngle = light.spotlightOuterAngle,
|
||||||
radius = propLight.Radius,
|
Radius = propAnimLight.Radius,
|
||||||
r2 = propLight.Radius * propLight.Radius,
|
});
|
||||||
spotlight = baseLight.spotlight,
|
|
||||||
spotlightDir = baseLight.spotlightDir,
|
|
||||||
spotlightInnerAngle = baseLight.spotlightInnerAngle,
|
|
||||||
spotlightOuterAngle = baseLight.spotlightOuterAngle,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (propLight.Radius == 0)
|
|
||||||
{
|
|
||||||
light.radius = float.MaxValue;
|
|
||||||
light.r2 = float.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lights.Add(light);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propAnimLight != null)
|
|
||||||
{
|
|
||||||
var light = new Light
|
|
||||||
{
|
|
||||||
position = baseLight.position + propAnimLight.Offset,
|
|
||||||
color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propAnimLight.MaxBrightness),
|
|
||||||
innerRadius = propAnimLight.InnerRadius,
|
|
||||||
radius = propAnimLight.Radius,
|
|
||||||
r2 = propAnimLight.Radius * propAnimLight.Radius,
|
|
||||||
spotlight = baseLight.spotlight,
|
|
||||||
spotlightDir = baseLight.spotlightDir,
|
|
||||||
spotlightInnerAngle = baseLight.spotlightInnerAngle,
|
|
||||||
spotlightOuterAngle = baseLight.spotlightOuterAngle,
|
|
||||||
anim = true,
|
|
||||||
animLightTableIndex = propAnimLight.LightTableLightIndex,
|
|
||||||
};
|
|
||||||
if (propAnimLight.Radius == 0)
|
|
||||||
{
|
|
||||||
light.radius = float.MaxValue;
|
|
||||||
light.r2 = float.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lights.Add(light);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,11 +338,11 @@ class Program
|
||||||
private static ObjectHierarchy BuildHierarchy(string misPath, DbFile misFile)
|
private static ObjectHierarchy BuildHierarchy(string misPath, DbFile misFile)
|
||||||
{
|
{
|
||||||
ObjectHierarchy objHierarchy;
|
ObjectHierarchy objHierarchy;
|
||||||
if (misFile.Chunks.TryGetValue("GAM_FILE", out var gamFileChunk))
|
if (misFile.TryGetChunk<GamFile>("GAM_FILE", out var gamFile))
|
||||||
{
|
{
|
||||||
var dir = Path.GetDirectoryName(misPath);
|
var dir = Path.GetDirectoryName(misPath);
|
||||||
var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
|
var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
|
||||||
var name = ((GamFile)gamFileChunk).fileName;
|
var name = gamFile.fileName;
|
||||||
var paths = Directory.GetFiles(dir!, name, options);
|
var paths = Directory.GetFiles(dir!, name, options);
|
||||||
if (paths.Length > 0)
|
if (paths.Length > 0)
|
||||||
{
|
{
|
||||||
|
@ -314,6 +413,10 @@ class Program
|
||||||
|
|
||||||
Parallel.ForEach(wr.Cells, cell =>
|
Parallel.ForEach(wr.Cells, cell =>
|
||||||
{
|
{
|
||||||
|
// Reset cell AnimLight palette
|
||||||
|
cell.AnimLightCount = 0;
|
||||||
|
cell.AnimLights.Clear();
|
||||||
|
|
||||||
var numPolys = cell.PolyCount;
|
var numPolys = cell.PolyCount;
|
||||||
var numRenderPolys = cell.RenderPolyCount;
|
var numRenderPolys = cell.RenderPolyCount;
|
||||||
var numPortalPolys = cell.PortalPolyCount;
|
var numPortalPolys = cell.PortalPolyCount;
|
||||||
|
@ -335,7 +438,8 @@ class Program
|
||||||
var info = cell.LightList[polyIdx];
|
var info = cell.LightList[polyIdx];
|
||||||
var lightmap = cell.Lightmaps[polyIdx];
|
var lightmap = cell.Lightmaps[polyIdx];
|
||||||
|
|
||||||
ResetLightmap(ambientLight, lightmap, hdr);
|
info.AnimLightBitmask = 0;
|
||||||
|
lightmap.Reset(ambientLight, hdr);
|
||||||
|
|
||||||
// Get world position of lightmap (0, 0) (+0.5 so we cast from the center of a pixel)
|
// Get world position of lightmap (0, 0) (+0.5 so we cast from the center of a pixel)
|
||||||
var topLeft = cell.Vertices[cell.Indices[cellIdxOffset]];
|
var topLeft = cell.Vertices[cell.Indices[cellIdxOffset]];
|
||||||
|
@ -365,23 +469,10 @@ class Program
|
||||||
var layer = 0;
|
var layer = 0;
|
||||||
if (light.anim)
|
if (light.anim)
|
||||||
{
|
{
|
||||||
var paletteIdx = -1;
|
// Because we're building the AnimLightBitmask in this loop we
|
||||||
for (var i = 0; i < cell.AnimLightCount; i++)
|
// know there aren't any layers set above us. So the target layer
|
||||||
{
|
// is just the number of set bits + 1.
|
||||||
var id = cell.AnimLights[i];
|
layer = BitOperations.PopCount(info.AnimLightBitmask) + 1;
|
||||||
if (id == light.animLightTableIndex)
|
|
||||||
{
|
|
||||||
paletteIdx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paletteIdx == -1 || (info.AnimLightBitmask & (1 << paletteIdx)) == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mask = info.AnimLightBitmask & ((1 << (paletteIdx + 1)) - 1);
|
|
||||||
layer = BitOperations.PopCount((uint)mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if plane normal is facing towards the light
|
// Check if plane normal is facing towards the light
|
||||||
|
@ -451,6 +542,27 @@ class Program
|
||||||
var hit = hitResult && Math.Abs(hitResult.Distance - direction.Length()) < MathUtils.Epsilon;
|
var hit = hitResult && Math.Abs(hitResult.Distance - direction.Length()) < MathUtils.Epsilon;
|
||||||
if (hit)
|
if (hit)
|
||||||
{
|
{
|
||||||
|
// If we're an anim light there's a lot of stuff we need to update
|
||||||
|
// Firstly we need to add the light to the cells anim light palette
|
||||||
|
// Secondly we need to set the appropriate bit of the lightmap's
|
||||||
|
// bitmask. Finally we need to check if the lightmap needs another layer
|
||||||
|
if (light.anim)
|
||||||
|
{
|
||||||
|
// TODO: Don't recalculate this for every point lol
|
||||||
|
var paletteIdx = cell.AnimLights.IndexOf((ushort)light.animLightTableIndex);
|
||||||
|
if (paletteIdx == -1)
|
||||||
|
{
|
||||||
|
paletteIdx = cell.AnimLightCount;
|
||||||
|
cell.AnimLightCount++;
|
||||||
|
cell.AnimLights.Add((ushort)light.animLightTableIndex);
|
||||||
|
}
|
||||||
|
info.AnimLightBitmask |= 1u << paletteIdx;
|
||||||
|
|
||||||
|
if (layer >= lightmap.Layers)
|
||||||
|
{
|
||||||
|
lightmap.AddLayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
var strength = CalculateLightStrengthAtPoint(light, pos, plane);
|
var strength = CalculateLightStrengthAtPoint(light, pos, plane);
|
||||||
lightmap.AddLight(layer, x, y, light.color, strength, hdr);
|
lightmap.AddLight(layer, x, y, light.color, strength, hdr);
|
||||||
}
|
}
|
||||||
|
@ -510,20 +622,4 @@ class Program
|
||||||
|
|
||||||
return strength;
|
return strength;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ResetLightmap(Vector3 ambientLight, WorldRep.Cell.Lightmap lightmap, bool hdr)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < lightmap.Pixels.Length; i++)
|
|
||||||
{
|
|
||||||
lightmap.Pixels[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var y = 0; y < lightmap.Height; y++)
|
|
||||||
{
|
|
||||||
for (var x = 0; x < lightmap.Width; x++)
|
|
||||||
{
|
|
||||||
lightmap.AddLight(0, x, y, ambientLight, 1.0f, hdr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue