Compare commits
	
		
			16 Commits
		
	
	
		
			bc01e91dbc
			...
			1d32b3ef7b
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 1d32b3ef7b | |
|  | 5d72ba546e | |
|  | 55c9c2cd31 | |
|  | 7c733fc9fd | |
|  | 6d665302d2 | |
|  | 1f05663460 | |
|  | dff2a9da2c | |
|  | 0a4dcc6de0 | |
|  | 0bcf24f640 | |
|  | 3fa86a233f | |
|  | ab738203d6 | |
|  | ea72c3af4a | |
|  | 013a3b845a | |
|  | 25e8e50f5e | |
|  | b9eae0e437 | |
|  | 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,21 +35,19 @@ 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)) |             } | ||||||
|  | 
 | ||||||
|  |             if (gam != null && gam.TryGetChunk<T>(name, out var gamChunk)) | ||||||
|             { |             { | ||||||
|                     var gamChunk = (T)rawGamChunk; |  | ||||||
|                 gamChunk.Merge(chunk); |                 gamChunk.Merge(chunk); | ||||||
|                 return gamChunk; |                 return gamChunk; | ||||||
|             } |             } | ||||||
|             return chunk; |             return chunk; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|             throw new ArgumentException($"No chunk with name ({name}) found", nameof(name)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Add parentages |         // Add parentages | ||||||
|         var metaPropLinks = GetMergedChunk<LinkChunk>("L$MetaProp"); |         var metaPropLinks = GetMergedChunk<LinkChunk>("L$MetaProp"); | ||||||
|         var metaPropLinkData = GetMergedChunk<LinkDataMetaProp>("LD$MetaProp"); |         var metaPropLinkData = GetMergedChunk<LinkDataMetaProp>("LD$MetaProp"); | ||||||
|  |  | ||||||
|  | @ -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,25 +108,103 @@ 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; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         worldRep.LightingTable.Reset(); | ||||||
|  | 
 | ||||||
|         foreach (var brush in brList.Brushes) |         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 sz = brush.size; | ||||||
|                     lights.Add(new Light |                 var light = new Light | ||||||
|                 { |                 { | ||||||
|                     position = brush.position, |                     position = brush.position, | ||||||
|                     color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)), |                     color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)), | ||||||
|                     radius = float.MaxValue, |                     radius = float.MaxValue, | ||||||
|                     r2 = 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) |             else if (brush.media == BrList.Brush.Media.Object) | ||||||
|  | @ -146,6 +223,7 @@ class Program | ||||||
|                 { |                 { | ||||||
|                     position = brush.position, |                     position = brush.position, | ||||||
|                     spotlightDir = -Vector3.UnitZ, |                     spotlightDir = -Vector3.UnitZ, | ||||||
|  |                     spotlightInnerAngle = -1.0f, | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 if (propModelname != null) |                 if (propModelname != null) | ||||||
|  | @ -203,10 +281,22 @@ class Program | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     lights.Add(light); |                     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 = light.spotlightInnerAngle, | ||||||
|  |                         OuterAngle = light.spotlightOuterAngle, | ||||||
|  |                         Radius = propLight.Radius, | ||||||
|  |                     }); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (propAnimLight != null) |                 if (propAnimLight != null) | ||||||
|                 { |                 { | ||||||
|  |                     var lightIndex = worldRep.LightingTable.LightCount; | ||||||
|  |                     propAnimLight.LightTableLightIndex = (ushort)lightIndex; | ||||||
|  | 
 | ||||||
|                     var light = new Light |                     var light = new Light | ||||||
|                     { |                     { | ||||||
|                         position = baseLight.position + propAnimLight.Offset, |                         position = baseLight.position + propAnimLight.Offset, | ||||||
|  | @ -219,6 +309,7 @@ class Program | ||||||
|                         spotlightInnerAngle = baseLight.spotlightInnerAngle, |                         spotlightInnerAngle = baseLight.spotlightInnerAngle, | ||||||
|                         spotlightOuterAngle = baseLight.spotlightOuterAngle, |                         spotlightOuterAngle = baseLight.spotlightOuterAngle, | ||||||
|                         anim = true, |                         anim = true, | ||||||
|  |                         animObjId = id, | ||||||
|                         animLightTableIndex = propAnimLight.LightTableLightIndex, |                         animLightTableIndex = propAnimLight.LightTableLightIndex, | ||||||
|                     }; |                     }; | ||||||
|                     if (propAnimLight.Radius == 0) |                     if (propAnimLight.Radius == 0) | ||||||
|  | @ -228,7 +319,15 @@ class Program | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     lights.Add(light); |                     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 = light.spotlightInnerAngle, | ||||||
|  |                         OuterAngle = light.spotlightOuterAngle, | ||||||
|  |                         Radius = propAnimLight.Radius, | ||||||
|  |                     }); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -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