Compare commits
	
		
			11 Commits
		
	
	
		
			d7fcf7a2d8
			...
			7ec7f71239
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 7ec7f71239 | |
|  | 47d95307b5 | |
|  | 69b29c15f6 | |
|  | 93410a46d5 | |
|  | 6efceff852 | |
|  | 067cd2cc00 | |
|  | b57d15f6f0 | |
|  | bd72554b9a | |
|  | 46fe99bbfc | |
|  | 8b7ffcd7a3 | |
|  | bcc60eff52 | 
|  | @ -330,6 +330,112 @@ public class PropLight : Property | |||
|     } | ||||
| } | ||||
| 
 | ||||
| public class PropAnimLight : Property | ||||
| { | ||||
|     public enum AnimMode | ||||
|     { | ||||
|         FlipMinMax, | ||||
|         SlideSmoothly, | ||||
|         Random, | ||||
|         MinBrightness, | ||||
|         MaxBrightness, | ||||
|         ZeroBrightness, | ||||
|         SmoothlyBrighten, | ||||
|         SmoothlyDim, | ||||
|         RandomButCoherent, | ||||
|         FlickerMinMax, | ||||
|     } | ||||
| 
 | ||||
|     // Standard light props | ||||
|     public float Brightness; | ||||
|     public Vector3 Offset; | ||||
|     public float Radius; | ||||
|     public float InnerRadius; | ||||
|     public bool QuadLit; | ||||
|     public bool Dynamic; | ||||
| 
 | ||||
|     // Animation | ||||
|     public AnimMode Mode; | ||||
|     public int MsToBrighten; | ||||
|     public int MsToDim; | ||||
|     public float MinBrightness; | ||||
|     public float MaxBrightness; | ||||
| 
 | ||||
|     // Start state | ||||
|     public float CurrentBrightness; | ||||
|     public bool Rising; | ||||
|     public int Timer; | ||||
|     public bool Inactive; | ||||
| 
 | ||||
|     // World rep info | ||||
|     public bool Refresh; // Not relevant to us. It's used to tell dromed it needs to relight | ||||
|     public ushort LightTableMapIndex; | ||||
|     public ushort LightTableLightIndex; | ||||
|     public ushort CellsReached; | ||||
| 
 | ||||
|     private int _unknown; | ||||
| 
 | ||||
|     public override void Read(BinaryReader reader) | ||||
|     { | ||||
|         base.Read(reader); | ||||
|         Brightness = reader.ReadSingle(); | ||||
|         Offset = reader.ReadVec3(); | ||||
|         Refresh = reader.ReadBoolean(); | ||||
|         reader.ReadBytes(3); | ||||
|         LightTableMapIndex = reader.ReadUInt16(); | ||||
|         CellsReached = reader.ReadUInt16(); | ||||
|         LightTableLightIndex = reader.ReadUInt16(); | ||||
|         Mode = (AnimMode)reader.ReadUInt16(); | ||||
|         MsToBrighten = reader.ReadInt32(); | ||||
|         MsToDim = reader.ReadInt32(); | ||||
|         MinBrightness = reader.ReadSingle(); | ||||
|         MaxBrightness = reader.ReadSingle(); | ||||
|         CurrentBrightness = reader.ReadSingle(); | ||||
|         Rising = reader.ReadBoolean(); | ||||
|         reader.ReadBytes(3); | ||||
|         Timer = reader.ReadInt32(); | ||||
|         Inactive = reader.ReadBoolean(); | ||||
|         reader.ReadBytes(3); | ||||
|         Radius = reader.ReadSingle(); | ||||
|         _unknown = reader.ReadInt32(); | ||||
|         QuadLit = reader.ReadBoolean(); | ||||
|         reader.ReadBytes(3); | ||||
|         InnerRadius = reader.ReadSingle(); | ||||
|         Dynamic = reader.ReadBoolean(); | ||||
|         reader.ReadBytes(3); | ||||
|     } | ||||
| 
 | ||||
|     public override void Write(BinaryWriter writer) | ||||
|     { | ||||
|         base.Write(writer); | ||||
|         writer.Write(Brightness); | ||||
|         writer.WriteVec3(Offset); | ||||
|         writer.Write(Refresh); | ||||
|         writer.Write(new byte[3]); | ||||
|         writer.Write(LightTableMapIndex); | ||||
|         writer.Write(CellsReached); | ||||
|         writer.Write(LightTableLightIndex); | ||||
|         writer.Write((ushort)Mode); | ||||
|         writer.Write(MsToBrighten); | ||||
|         writer.Write(MsToDim); | ||||
|         writer.Write(MinBrightness); | ||||
|         writer.Write(MaxBrightness); | ||||
|         writer.Write(CurrentBrightness); | ||||
|         writer.Write(Rising); | ||||
|         writer.Write(new byte[3]); | ||||
|         writer.Write(Timer); | ||||
|         writer.Write(Inactive); | ||||
|         writer.Write(new byte[3]); | ||||
|         writer.Write(Radius); | ||||
|         writer.Write(_unknown); | ||||
|         writer.Write(QuadLit); | ||||
|         writer.Write(new byte[3]); | ||||
|         writer.Write(InnerRadius); | ||||
|         writer.Write(Dynamic); | ||||
|         writer.Write(new byte[3]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| public class PropLightColor : Property | ||||
| { | ||||
|     public float Hue; | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| using System; | ||||
| using System.IO; | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace KeepersCompound.LGS.Database.Chunks; | ||||
|  | @ -419,9 +417,179 @@ public class WorldRep : IChunk | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public struct LightTable | ||||
|     { | ||||
|         public struct LightData | ||||
|         { | ||||
|             public Vector3 Location; | ||||
|             public Vector3 Direction; | ||||
|             public Vector3 Color; | ||||
|             float InnerAngle; // I'm pretty sure these are the spotlight angles | ||||
|             float OuterAngle; | ||||
|             float Radius; | ||||
| 
 | ||||
|             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; | ||||
|         public LightData[] Lights; | ||||
|         public LightData[] ScratchpadLights; | ||||
|         public AnimCellMap[] AnimCellMaps; | ||||
| 
 | ||||
|         // TODO: Support olddark | ||||
|         public LightTable(BinaryReader reader) | ||||
|         { | ||||
|             LightCount = reader.ReadInt32(); | ||||
|             DynamicLightCount = reader.ReadInt32(); | ||||
|             Lights = new LightData[LightCount + DynamicLightCount]; | ||||
|             for (var i = 0; i < Lights.Length; i++) | ||||
|             { | ||||
|                 Lights[i] = new LightData(reader); | ||||
|             } | ||||
|             ScratchpadLights = new LightData[32]; | ||||
|             for (var i = 0; i < ScratchpadLights.Length; i++) | ||||
|             { | ||||
|                 ScratchpadLights[i] = new LightData(reader); | ||||
|             } | ||||
|             AnimMapCount = reader.ReadInt32(); | ||||
|             AnimCellMaps = new AnimCellMap[AnimMapCount]; | ||||
|             for (var i = 0; i < AnimCellMaps.Length; i++) | ||||
|             { | ||||
|                 AnimCellMaps[i] = new AnimCellMap(reader); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public readonly void Write(BinaryWriter writer) | ||||
|         { | ||||
|             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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public ChunkHeader Header { get; set; } | ||||
|     public WrHeader DataHeader { get; set; } | ||||
|     public Cell[] Cells { get; set; } | ||||
|     public BspTree Bsp { get; set; } | ||||
|     public LightTable LightingTable { get; set; } | ||||
|     private byte[] _unknown; | ||||
|     private byte[] _unreadData; | ||||
| 
 | ||||
|     public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry) | ||||
|  | @ -435,6 +603,12 @@ public class WorldRep : IChunk | |||
|             Cells[i] = new Cell(reader, bpp); | ||||
|         } | ||||
| 
 | ||||
|         Bsp = new BspTree(reader); | ||||
| 
 | ||||
|         // TODO: Work out what this is | ||||
|         _unknown = reader.ReadBytes(Cells.Length); | ||||
|         LightingTable = new LightTable(reader); | ||||
| 
 | ||||
|         // TODO: All the other info lol | ||||
|         var length = entry.Offset + entry.Size + 24 - reader.BaseStream.Position; | ||||
|         _unreadData = reader.ReadBytes((int)length); | ||||
|  | @ -447,6 +621,9 @@ public class WorldRep : IChunk | |||
|         { | ||||
|             cell.Write(writer); | ||||
|         } | ||||
|         Bsp.Write(writer); | ||||
|         writer.Write(_unknown); | ||||
|         LightingTable.Write(writer); | ||||
|         writer.Write(_unreadData); | ||||
|     } | ||||
| } | ||||
|  | @ -32,13 +32,13 @@ public class DbFile | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public readonly struct TableOfContents | ||||
|     public struct TableOfContents | ||||
|     { | ||||
|         public readonly struct Entry | ||||
|         public struct Entry | ||||
|         { | ||||
|             public string Name { get; } | ||||
|             public uint Offset { get; } | ||||
|             public uint Size { get; } | ||||
|             public string Name; | ||||
|             public uint Offset; | ||||
|             public uint Size; | ||||
| 
 | ||||
|             public Entry(BinaryReader reader) | ||||
|             { | ||||
|  | @ -47,10 +47,9 @@ public class DbFile | |||
|                 Size = reader.ReadUInt32(); | ||||
|             } | ||||
| 
 | ||||
|             public override string ToString() | ||||
|             public override readonly string ToString() | ||||
|             { | ||||
|                 // return $"Name: {Name}, Offset: {O}" | ||||
|                 return base.ToString(); | ||||
|                 return $"Name: {Name}, Offset: {Offset}, Size: {Size}"; | ||||
|             } | ||||
| 
 | ||||
|             public readonly void Write(BinaryWriter writer) | ||||
|  | @ -110,18 +109,28 @@ public class DbFile | |||
| 
 | ||||
|     public void Save(string filename) | ||||
|     { | ||||
|         // !HACK: Right now we don't need to adjust TOC offset or anything because we're only | ||||
|         // overwriting data, not writing new lengths of data | ||||
| 
 | ||||
|         using var stream = File.Open(filename, FileMode.Create); | ||||
|         using var writer = new BinaryWriter(stream, Encoding.UTF8, false); | ||||
| 
 | ||||
|         Header.Write(writer); | ||||
|         foreach (var (name, chunk) in Chunks) | ||||
|         for (var i = 0; i < Toc.ItemCount; i++) | ||||
|         { | ||||
|             var item = Toc.Items[i]; | ||||
|             var pos = stream.Position; | ||||
| 
 | ||||
|             var chunk = Chunks[item.Name]; | ||||
|             chunk.Write(writer); | ||||
|             var size = stream.Position - pos - 24; | ||||
| 
 | ||||
|             item.Offset = (uint)pos; | ||||
|             item.Size = (uint)size; | ||||
|             Toc.Items[i] = item; | ||||
|         } | ||||
|         var tocOffset = (uint)stream.Position; | ||||
|         Toc.Write(writer); | ||||
| 
 | ||||
|         stream.Seek(0, SeekOrigin.Begin); | ||||
|         writer.Write(tocOffset); | ||||
|     } | ||||
| 
 | ||||
|     private static IChunk NewChunk(string entryName) | ||||
|  | @ -143,6 +152,7 @@ public class DbFile | |||
|             "P$OTxtRepr2" => new PropertyChunk<PropString>(), | ||||
|             "P$OTxtRepr3" => new PropertyChunk<PropString>(), | ||||
|             "P$Light" => new PropertyChunk<PropLight>(), | ||||
|             "P$AnimLight" => new PropertyChunk<PropAnimLight>(), | ||||
|             "P$LightColo" => new PropertyChunk<PropLightColor>(), | ||||
|             "P$Spotlight" => new PropertyChunk<PropSpotlight>(), | ||||
|             "P$RenderAlp" => new PropertyChunk<PropFloat>(), | ||||
|  |  | |||
|  | @ -101,11 +101,13 @@ public class ObjectHierarchy | |||
|         AddProp<PropString>("P$OTxtRepr3"); | ||||
|         AddProp<PropFloat>("P$RenderAlp"); | ||||
|         AddProp<PropLight>("P$Light"); | ||||
|         AddProp<PropAnimLight>("P$AnimLight"); | ||||
|         AddProp<PropLightColor>("P$LightColo"); | ||||
|         AddProp<PropSpotlight>("P$Spotlight"); | ||||
|     } | ||||
| 
 | ||||
|     public T GetProperty<T>(int objectId, string propName) where T : Property | ||||
|     // TODO: Work out if there's some nice way to automatically decide if we inherit | ||||
|     public T GetProperty<T>(int objectId, string propName, bool inherit = true) where T : Property | ||||
|     { | ||||
|         if (!_objects.ContainsKey(objectId)) | ||||
|         { | ||||
|  | @ -121,7 +123,7 @@ public class ObjectHierarchy | |||
|             } | ||||
| 
 | ||||
|             var prop = obj.GetProperty<T>(propName); | ||||
|             if (prop != null) | ||||
|             if (prop != null || !inherit) | ||||
|             { | ||||
|                 return prop; | ||||
|             } | ||||
|  |  | |||
|  | @ -22,6 +22,9 @@ class Program | |||
|         public Vector3 spotlightDir; | ||||
|         public float spotlightInnerAngle; | ||||
|         public float spotlightOuterAngle; | ||||
| 
 | ||||
|         public bool anim; | ||||
|         public int animLightTableIndex; | ||||
|     } | ||||
| 
 | ||||
|     static void Main(string[] args) | ||||
|  | @ -33,18 +36,16 @@ class Program | |||
|         var campaignName = "JAYRUDE_Tests"; | ||||
|         var missionName = "lm_test.cow"; | ||||
| 
 | ||||
|         // campaignName = "TDP20AC_a_burrick_in_a_room"; | ||||
|         // missionName = "miss20.mis"; | ||||
| 
 | ||||
|         // Setup extract path | ||||
|         var tmpDir = Directory.CreateTempSubdirectory("KCLightmapper"); | ||||
|         Console.WriteLine(tmpDir.FullName); | ||||
|         var resPathManager = new ResourcePathManager(tmpDir.FullName); | ||||
|         resPathManager.Init(installPath); | ||||
| 
 | ||||
|         var campaign = resPathManager.GetCampaign(campaignName); | ||||
|         var misPath = campaign.GetResourcePath(ResourceType.Mission, missionName); | ||||
| 
 | ||||
|         // misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/JAYRUDE_Tests/lm_test.cow"; | ||||
|         // misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/AtdV/miss20.mis"; | ||||
|         // misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/TDP20AC_a_burrick_in_a_room/miss20.mis"; | ||||
|         Timing.TimeStage("Total", () => LightmapMission(campaign, misPath)); | ||||
| 
 | ||||
|         Timing.LogAll(); | ||||
|  | @ -132,56 +133,64 @@ class Program | |||
|                 { | ||||
|                     // TODO: Handle PropSpotlightAndAmbient | ||||
|                     var id = (int)brush.brushInfo; | ||||
|                     var propLight = hierarchy.GetProperty<PropLight>(id, "P$Light"); | ||||
|                     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, | ||||
|                     }; | ||||
| 
 | ||||
|                     if (propModelname != null) | ||||
|                     { | ||||
|                         var resName = $"{propModelname.value.ToLower()}.bin"; | ||||
|                         var modelPath = campaign.GetResourcePath(ResourceType.Object, resName); | ||||
|                         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) | ||||
|                     { | ||||
|                         var rot = Matrix4x4.Identity; | ||||
|                         rot *= Matrix4x4.CreateRotationX(float.DegreesToRadians(brush.angle.X)); | ||||
|                         rot *= Matrix4x4.CreateRotationY(float.DegreesToRadians(brush.angle.Y)); | ||||
|                         rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z)); | ||||
| 
 | ||||
|                         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)); | ||||
|                     } | ||||
| 
 | ||||
|                     if (propLight != null) | ||||
|                     { | ||||
|                         propLightColor ??= new PropLightColor { Hue = 0, Saturation = 0 }; | ||||
| 
 | ||||
|                         // TODO: There's still some lights that aren't positioned right such as Streetlamp. | ||||
|                         //       Perhaps there's a light point specified in model files? | ||||
|                         var light = new Light | ||||
|                         { | ||||
|                             position = brush.position + propLight.Offset, | ||||
|                             position = baseLight.position + propLight.Offset, | ||||
|                             color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness), | ||||
|                             innerRadius = propLight.InnerRadius, | ||||
|                             radius = propLight.Radius, | ||||
|                             r2 = propLight.Radius * propLight.Radius, | ||||
|                             spotlightDir = -Vector3.UnitZ, | ||||
|                         }; | ||||
| 
 | ||||
|                         if (propModelname != null) | ||||
|                         { | ||||
|                             var resName = $"{propModelname.value.ToLower()}.bin"; | ||||
|                             var modelPath = campaign.GetResourcePath(ResourceType.Object, resName); | ||||
|                             var model = new ModelFile(modelPath); | ||||
|                             if (model.TryGetVhot(ModelFile.VhotId.LightPosition, out var vhot)) | ||||
|                             { | ||||
|                                 light.position += vhot.Position; | ||||
|                             } | ||||
|                             if (model.TryGetVhot(ModelFile.VhotId.LightDirection, out vhot)) | ||||
|                             { | ||||
|                                 light.spotlightDir = vhot.Position; | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         if (propSpotlight != null) | ||||
|                         { | ||||
|                             // TODO: Some objects seem to have spotlight direction embedded in the model file | ||||
|                             var rot = Matrix4x4.Identity; | ||||
|                             rot *= Matrix4x4.CreateRotationX(float.DegreesToRadians(brush.angle.X)); | ||||
|                             rot *= Matrix4x4.CreateRotationY(float.DegreesToRadians(brush.angle.Y)); | ||||
|                             rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z)); | ||||
| 
 | ||||
|                             light.spotlight = true; | ||||
|                             light.spotlightDir = Vector3.Transform(light.spotlightDir, rot); | ||||
|                             light.spotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.InnerAngle)); | ||||
|                             light.spotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.OuterAngle)); | ||||
|                         } | ||||
| 
 | ||||
|                         if (propLight.Radius == 0) | ||||
|                         { | ||||
|                             light.radius = float.MaxValue; | ||||
|  | @ -190,6 +199,27 @@ class Program | |||
| 
 | ||||
|                         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, | ||||
|                             anim = true, | ||||
|                             animLightTableIndex = propAnimLight.LightTableLightIndex, | ||||
|                         }; | ||||
|                         if (propAnimLight.Radius == 0) | ||||
|                         { | ||||
|                             light.radius = float.MaxValue; | ||||
|                             light.r2 = float.MaxValue; | ||||
|                         } | ||||
| 
 | ||||
|                         lights.Add(light); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -323,6 +353,28 @@ class Program | |||
| 
 | ||||
|                 foreach (var light in lights) | ||||
|                 { | ||||
|                     var layer = 0; | ||||
|                     if (light.anim) | ||||
|                     { | ||||
|                         var paletteIdx = -1; | ||||
|                         for (var i = 0; i < cell.AnimLightCount; i++) | ||||
|                         { | ||||
|                             var id = cell.AnimLights[i]; | ||||
|                             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 | ||||
|                     // If it's not then we're never going to be (directly) lit by this | ||||
|                     // light. | ||||
|  | @ -391,7 +443,7 @@ class Program | |||
|                             if (hit) | ||||
|                             { | ||||
|                                 var strength = CalculateLightStrengthAtPoint(light, pos, plane); | ||||
|                                 lightmap.AddLight(0, x, y, light.color, strength, hdr); | ||||
|                                 lightmap.AddLight(layer, x, y, light.color, strength, hdr); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue