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