Compare commits
	
		
			6 Commits
		
	
	
		
			440d0b91b4
			...
			7dc1912390
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						7dc1912390 | |
| 
							
							
								
								 | 
						a35bdb8ce3 | |
| 
							
							
								
								 | 
						d83e020de0 | |
| 
							
							
								
								 | 
						59582bdfb8 | |
| 
							
							
								
								 | 
						beae881f37 | |
| 
							
							
								
								 | 
						3ee172f16c | 
| 
						 | 
				
			
			@ -2,7 +2,6 @@ using System;
 | 
			
		|||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace KeepersCompound.LGS.Database.Chunks;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +45,15 @@ public class BrList : IChunk
 | 
			
		|||
                x = reader.ReadUInt16();
 | 
			
		||||
                y = reader.ReadUInt16();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Write(BinaryWriter writer)
 | 
			
		||||
            {
 | 
			
		||||
                writer.Write(id);
 | 
			
		||||
                writer.Write(rot);
 | 
			
		||||
                writer.Write(scale);
 | 
			
		||||
                writer.Write(x);
 | 
			
		||||
                writer.Write(y);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        public short id;
 | 
			
		||||
| 
						 | 
				
			
			@ -104,6 +112,34 @@ public class BrList : IChunk
 | 
			
		|||
                txs = Array.Empty<TexInfo>();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(BinaryWriter writer)
 | 
			
		||||
        {
 | 
			
		||||
            writer.Write(id);
 | 
			
		||||
            writer.Write(time);
 | 
			
		||||
            writer.Write(brushInfo);
 | 
			
		||||
            writer.Write(textureId);
 | 
			
		||||
            writer.Write((byte)media);
 | 
			
		||||
            writer.Write(flags);
 | 
			
		||||
            writer.WriteVec3(position);
 | 
			
		||||
            writer.WriteVec3(size);
 | 
			
		||||
            writer.WriteRotation(angle);
 | 
			
		||||
            writer.Write(currentFaceIndex);
 | 
			
		||||
            writer.Write(gridLineSpacing);
 | 
			
		||||
            writer.WriteVec3(gridPhaseShift);
 | 
			
		||||
            writer.WriteRotation(gridOrientation);
 | 
			
		||||
            writer.Write(gridEnabled);
 | 
			
		||||
            writer.Write(numFaces);
 | 
			
		||||
            writer.Write(edgeSelected);
 | 
			
		||||
            writer.Write(pointSelected);
 | 
			
		||||
            writer.Write(useFlag);
 | 
			
		||||
            writer.Write(groupId);
 | 
			
		||||
            writer.Write(new byte[4]);
 | 
			
		||||
            foreach (var info in txs)
 | 
			
		||||
            {
 | 
			
		||||
                info.Write(writer);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ChunkHeader Header { get; set; }
 | 
			
		||||
| 
						 | 
				
			
			@ -120,6 +156,9 @@ public class BrList : IChunk
 | 
			
		|||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        foreach (var brush in Brushes)
 | 
			
		||||
        {
 | 
			
		||||
            brush.Write(writer);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,4 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace KeepersCompound.LGS.Database.Chunks;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +14,6 @@ public class GamFile : IChunk
 | 
			
		|||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        writer.WriteNullString(fileName, 256);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +31,11 @@ public record LinkId
 | 
			
		|||
    {
 | 
			
		||||
        return _data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Write(_data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class LinkChunk : IChunk, IMergable
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +54,14 @@ public class LinkChunk : IChunk, IMergable
 | 
			
		|||
            destination = reader.ReadInt32();
 | 
			
		||||
            relation = reader.ReadUInt16();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(BinaryWriter writer)
 | 
			
		||||
        {
 | 
			
		||||
            linkId.Write(writer);
 | 
			
		||||
            writer.Write(source);
 | 
			
		||||
            writer.Write(destination);
 | 
			
		||||
            writer.Write(relation);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ChunkHeader Header { get; set; }
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +78,10 @@ public class LinkChunk : IChunk, IMergable
 | 
			
		|||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        foreach (var link in links)
 | 
			
		||||
        {
 | 
			
		||||
            link.Write(writer);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Merge(IMergable other)
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +103,12 @@ public class LinkDataMetaProp : IChunk, IMergable
 | 
			
		|||
            linkId = new LinkId(reader.ReadUInt32());
 | 
			
		||||
            priority = reader.ReadInt32();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(BinaryWriter writer)
 | 
			
		||||
        {
 | 
			
		||||
            linkId.Write(writer);
 | 
			
		||||
            writer.Write(priority);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ChunkHeader Header { get; set; }
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +127,11 @@ public class LinkDataMetaProp : IChunk, IMergable
 | 
			
		|||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        writer.Write(DataSize);
 | 
			
		||||
        foreach (var data in linkData)
 | 
			
		||||
        {
 | 
			
		||||
            data.Write(writer);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Merge(IMergable other)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,75 @@
 | 
			
		|||
using System.IO;
 | 
			
		||||
 | 
			
		||||
namespace KeepersCompound.LGS.Database.Chunks;
 | 
			
		||||
 | 
			
		||||
public enum SoftnessMode
 | 
			
		||||
{
 | 
			
		||||
    Standard,
 | 
			
		||||
    HighFourPoint,
 | 
			
		||||
    HighFivePoint,
 | 
			
		||||
    HighNinePoint,
 | 
			
		||||
    MediumFourPoint,
 | 
			
		||||
    MediumFivePoint,
 | 
			
		||||
    MediumNinePoint,
 | 
			
		||||
    LowFourPoint,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class LmParams : IChunk
 | 
			
		||||
{
 | 
			
		||||
    public enum LightingMode
 | 
			
		||||
    {
 | 
			
		||||
        Quick,
 | 
			
		||||
        Raycast,
 | 
			
		||||
        Objcast,
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public enum DepthMode
 | 
			
		||||
    {
 | 
			
		||||
        Lm16,
 | 
			
		||||
        Lm32,
 | 
			
		||||
        Lm32x,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ChunkHeader Header { get; set; }
 | 
			
		||||
    public float Attenuation { get; set; }
 | 
			
		||||
    public float Saturation { get; set; }
 | 
			
		||||
    public LightingMode ShadowType { get; set; }
 | 
			
		||||
    public SoftnessMode ShadowSoftness { get; set; }
 | 
			
		||||
    public float CenterWeight { get; set; }
 | 
			
		||||
    public DepthMode ShadowDepth { get; set; }
 | 
			
		||||
    public bool LightmappedWater { get; set; }
 | 
			
		||||
    public int LightmapScale { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    private int _dataSize;
 | 
			
		||||
    private uint _unknown;
 | 
			
		||||
    
 | 
			
		||||
    public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry)
 | 
			
		||||
    {
 | 
			
		||||
        _dataSize = reader.ReadInt32();
 | 
			
		||||
        Attenuation = reader.ReadSingle();
 | 
			
		||||
        Saturation = reader.ReadSingle();
 | 
			
		||||
        ShadowType = (LightingMode)reader.ReadUInt32();
 | 
			
		||||
        ShadowSoftness = (SoftnessMode)reader.ReadUInt32();
 | 
			
		||||
        CenterWeight = reader.ReadSingle();
 | 
			
		||||
        ShadowDepth = (DepthMode)reader.ReadUInt32();
 | 
			
		||||
        LightmappedWater = reader.ReadBoolean();
 | 
			
		||||
        reader.ReadBytes(3);
 | 
			
		||||
        LightmapScale = reader.ReadInt32();
 | 
			
		||||
        _unknown = reader.ReadUInt32();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Write(_dataSize);
 | 
			
		||||
        writer.Write(Attenuation);
 | 
			
		||||
        writer.Write(Saturation);
 | 
			
		||||
        writer.Write((uint)ShadowType);
 | 
			
		||||
        writer.Write((uint)ShadowSoftness);
 | 
			
		||||
        writer.Write(CenterWeight);
 | 
			
		||||
        writer.Write((uint)ShadowDepth);
 | 
			
		||||
        writer.Write(LightmappedWater);
 | 
			
		||||
        writer.Write(new byte[3]);
 | 
			
		||||
        writer.Write(LightmapScale);
 | 
			
		||||
        writer.Write(_unknown);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +14,12 @@ public class Property
 | 
			
		|||
        objectId = reader.ReadInt32();
 | 
			
		||||
        length = (int)reader.ReadUInt32();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public virtual void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Write(objectId);
 | 
			
		||||
        writer.Write((uint)length);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropertyChunk<T> : IChunk, IMergable where T : Property, new()
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +40,10 @@ public class PropertyChunk<T> : IChunk, IMergable where T : Property, new()
 | 
			
		|||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        foreach (var prop in properties)
 | 
			
		||||
        {
 | 
			
		||||
            prop.Write(writer);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Merge(IMergable other)
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +61,12 @@ public class PropGeneric : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        data = reader.ReadBytes(length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropBool : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +78,12 @@ public class PropBool : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        value = reader.ReadInt32() != 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(value ? 1 : 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropInt : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +95,12 @@ public class PropInt : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        value = reader.ReadInt32();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropLabel : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +112,12 @@ public class PropLabel : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        value = reader.ReadNullString(length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.WriteNullString(value, length);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropString : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +131,13 @@ public class PropString : Property
 | 
			
		|||
        stringLength = reader.ReadInt32();
 | 
			
		||||
        value = reader.ReadNullString(stringLength);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(stringLength);
 | 
			
		||||
        writer.WriteNullString(value, stringLength);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropFloat : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +149,12 @@ public class PropFloat : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        value = reader.ReadSingle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropVector : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -120,6 +166,12 @@ public class PropVector : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        value = reader.ReadVec3();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.WriteVec3(value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropRenderType : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +192,12 @@ public class PropRenderType : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        mode = (Mode)reader.ReadUInt32();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write((uint)mode);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropSlayResult : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -156,6 +214,12 @@ public class PropSlayResult : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        effect = (Effect)reader.ReadUInt32();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write((uint)effect);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropInventoryType : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +236,12 @@ public class PropInventoryType : Property
 | 
			
		|||
        base.Read(reader);
 | 
			
		||||
        type = (Slot)reader.ReadUInt32();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write((uint)type);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropCollisionType : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -194,6 +264,19 @@ public class PropCollisionType : Property
 | 
			
		|||
        NoResult = (flags & (0x1 << 4)) != 0;
 | 
			
		||||
        FullCollisionSound = (flags & (0x1 << 5)) != 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        var flags = 0u;
 | 
			
		||||
        if (Bounce) flags += 1;
 | 
			
		||||
        if (DestroyOnImpact) flags += 2;
 | 
			
		||||
        if (SlayOnImpact) flags += 4;
 | 
			
		||||
        if (NoCollisionSound) flags += 8;
 | 
			
		||||
        if (NoResult) flags += 16;
 | 
			
		||||
        if (FullCollisionSound) flags += 32;
 | 
			
		||||
        writer.Write(flags);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropPosition : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -208,6 +291,14 @@ public class PropPosition : Property
 | 
			
		|||
        reader.ReadBytes(4); // Runtime Cell/Hint in editor
 | 
			
		||||
        Rotation = reader.ReadRotation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.WriteVec3(Location);
 | 
			
		||||
        writer.Write(new byte[4]);
 | 
			
		||||
        writer.WriteRotation(Rotation);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropLight : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -228,6 +319,123 @@ public class PropLight : Property
 | 
			
		|||
        reader.ReadBytes(3);
 | 
			
		||||
        InnerRadius = reader.ReadSingle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(Brightness);
 | 
			
		||||
        writer.WriteVec3(Offset);
 | 
			
		||||
        writer.Write(Radius);
 | 
			
		||||
        writer.Write(QuadLit);
 | 
			
		||||
        writer.Write(new byte[3]);
 | 
			
		||||
        writer.Write(InnerRadius);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			@ -241,6 +449,13 @@ public class PropLightColor : Property
 | 
			
		|||
        Hue = reader.ReadSingle();
 | 
			
		||||
        Saturation = reader.ReadSingle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(Hue);
 | 
			
		||||
        writer.Write(Saturation);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropSpotlight : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -255,6 +470,14 @@ public class PropSpotlight : Property
 | 
			
		|||
        OuterAngle = reader.ReadSingle();
 | 
			
		||||
        reader.ReadBytes(4); // Z is unused
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(InnerAngle);
 | 
			
		||||
        writer.Write(OuterAngle);
 | 
			
		||||
        writer.Write(new byte[4]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PropSpotlightAndAmbient : Property
 | 
			
		||||
| 
						 | 
				
			
			@ -270,21 +493,12 @@ public class PropSpotlightAndAmbient : Property
 | 
			
		|||
        OuterAngle = reader.ReadSingle();
 | 
			
		||||
        SpotBrightness = reader.ReadSingle();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: Work out what this property actually does
 | 
			
		||||
public class PropLightBasedAlpha : Property
 | 
			
		||||
{
 | 
			
		||||
    public bool Enabled;
 | 
			
		||||
    public Vector2 AlphaRange;
 | 
			
		||||
    public Vector2 LightRange;
 | 
			
		||||
 | 
			
		||||
    public override void Read(BinaryReader reader)
 | 
			
		||||
    public override void Write(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        base.Read(reader);
 | 
			
		||||
        Enabled = reader.ReadBoolean();
 | 
			
		||||
        reader.ReadBytes(3);
 | 
			
		||||
        AlphaRange = reader.ReadVec2();
 | 
			
		||||
        LightRange = new Vector2(reader.ReadInt32(), reader.ReadInt32());
 | 
			
		||||
        base.Write(writer);
 | 
			
		||||
        writer.Write(InnerAngle);
 | 
			
		||||
        writer.Write(OuterAngle);
 | 
			
		||||
        writer.Write(SpotBrightness);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
using System.IO;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
 | 
			
		||||
namespace KeepersCompound.LGS.Database.Chunks;
 | 
			
		||||
 | 
			
		||||
public class RendParams : IChunk
 | 
			
		||||
{
 | 
			
		||||
    public enum SunlightMode
 | 
			
		||||
    {
 | 
			
		||||
        SingleUnshadowed,
 | 
			
		||||
        QuadObjcastShadows,
 | 
			
		||||
        QuadUnshadowed,
 | 
			
		||||
        SingleObjcastShadows,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ChunkHeader Header { get; set; }
 | 
			
		||||
 | 
			
		||||
    public string palette;
 | 
			
		||||
    public Vector3 ambientLight;
 | 
			
		||||
    public int useSunlight;
 | 
			
		||||
    public SunlightMode sunlightMode;
 | 
			
		||||
    public Vector3 sunlightDirection;
 | 
			
		||||
    public float sunlightHue;
 | 
			
		||||
    public float sunlightSaturation;
 | 
			
		||||
    public float sunlightBrightness;
 | 
			
		||||
    public float viewDistance;
 | 
			
		||||
    public Vector3[] ambientLightZones;
 | 
			
		||||
    public float globalAiVisBias;
 | 
			
		||||
    public float[] ambientZoneAiVisBiases;
 | 
			
		||||
 | 
			
		||||
    public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry)
 | 
			
		||||
    {
 | 
			
		||||
        palette = reader.ReadNullString(16);
 | 
			
		||||
        ambientLight = reader.ReadVec3();
 | 
			
		||||
        useSunlight = reader.ReadInt32();
 | 
			
		||||
        sunlightMode = (SunlightMode)reader.ReadUInt32();
 | 
			
		||||
        sunlightDirection = reader.ReadVec3();
 | 
			
		||||
        sunlightHue = reader.ReadSingle();
 | 
			
		||||
        sunlightSaturation = reader.ReadSingle();
 | 
			
		||||
        sunlightBrightness = reader.ReadSingle();
 | 
			
		||||
        reader.ReadBytes(24);
 | 
			
		||||
        viewDistance = reader.ReadSingle();
 | 
			
		||||
        reader.ReadBytes(12);
 | 
			
		||||
        ambientLightZones = new Vector3[8];
 | 
			
		||||
        for (var i = 0; i < ambientLightZones.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            ambientLightZones[i] = reader.ReadVec3();
 | 
			
		||||
        }
 | 
			
		||||
        globalAiVisBias = reader.ReadSingle();
 | 
			
		||||
        ambientZoneAiVisBiases = new float[8];
 | 
			
		||||
        for (var i = 0; i < ambientZoneAiVisBiases.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            ambientZoneAiVisBiases[i] = reader.ReadSingle();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        writer.WriteNullString(palette, 16);
 | 
			
		||||
        writer.WriteVec3(ambientLight);
 | 
			
		||||
        writer.Write(useSunlight);
 | 
			
		||||
        writer.Write((uint)sunlightMode);
 | 
			
		||||
        writer.WriteVec3(sunlightDirection);
 | 
			
		||||
        writer.Write(sunlightHue);
 | 
			
		||||
        writer.Write(sunlightSaturation);
 | 
			
		||||
        writer.Write(sunlightBrightness);
 | 
			
		||||
        writer.Write(new byte[24]);
 | 
			
		||||
        writer.Write(viewDistance);
 | 
			
		||||
        writer.Write(new byte[12]);
 | 
			
		||||
        foreach (var lightZone in ambientLightZones)
 | 
			
		||||
        {
 | 
			
		||||
            writer.WriteVec3(lightZone);
 | 
			
		||||
        }
 | 
			
		||||
        writer.Write(globalAiVisBias);
 | 
			
		||||
        foreach (var visBias in ambientZoneAiVisBiases)
 | 
			
		||||
        {
 | 
			
		||||
            writer.Write(visBias);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,4 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace KeepersCompound.LGS.Database.Chunks;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +14,12 @@ public class TxList : IChunk
 | 
			
		|||
            Tokens = reader.ReadBytes(4);
 | 
			
		||||
            Name = reader.ReadNullString(16);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public readonly void Write(BinaryWriter writer)
 | 
			
		||||
        {
 | 
			
		||||
            writer.Write(Tokens);
 | 
			
		||||
            writer.WriteNullString(Name, 16);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +31,6 @@ public class TxList : IChunk
 | 
			
		|||
    public string[] Tokens { get; set; }
 | 
			
		||||
    public Item[] Items { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry)
 | 
			
		||||
    {
 | 
			
		||||
        BlockSize = reader.ReadInt32();
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +50,16 @@ public class TxList : IChunk
 | 
			
		|||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        writer.Write(BlockSize);
 | 
			
		||||
        writer.Write(ItemCount);
 | 
			
		||||
        writer.Write(TokenCount);
 | 
			
		||||
        foreach (var token in Tokens)
 | 
			
		||||
        {
 | 
			
		||||
            writer.WriteNullString(token, 16);
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in Items)
 | 
			
		||||
        {
 | 
			
		||||
            item.Write(writer);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,9 +40,20 @@ public class WorldRep : IChunk
 | 
			
		|||
                _ => 1.0f,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public readonly void Write(BinaryWriter writer)
 | 
			
		||||
        {
 | 
			
		||||
            writer.Write(Size);
 | 
			
		||||
            writer.Write(Version);
 | 
			
		||||
            writer.Write(Flags);
 | 
			
		||||
            writer.Write(LightmapFormat);
 | 
			
		||||
            writer.Write(LightmapScale);
 | 
			
		||||
            writer.Write(DataSize);
 | 
			
		||||
            writer.Write(CellCount);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public struct Cell
 | 
			
		||||
    public class Cell
 | 
			
		||||
    {
 | 
			
		||||
        public struct Poly
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +74,17 @@ public class WorldRep : IChunk
 | 
			
		|||
                MotionIndex = reader.ReadByte();
 | 
			
		||||
                reader.ReadByte();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public readonly void Write(BinaryWriter writer)
 | 
			
		||||
            {
 | 
			
		||||
                writer.Write(Flags);
 | 
			
		||||
                writer.Write(VertexCount);
 | 
			
		||||
                writer.Write(PlaneId);
 | 
			
		||||
                writer.Write(ClutId);
 | 
			
		||||
                writer.Write(Destination);
 | 
			
		||||
                writer.Write(MotionIndex);
 | 
			
		||||
                writer.Write((byte)0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public struct RenderPoly
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +105,21 @@ public class WorldRep : IChunk
 | 
			
		|||
                TextureMagnitude = reader.ReadSingle();
 | 
			
		||||
                Center = reader.ReadVec3();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public readonly void Write(BinaryWriter writer)
 | 
			
		||||
            {
 | 
			
		||||
                writer.WriteVec3(TextureVectors.Item1);
 | 
			
		||||
                writer.WriteVec3(TextureVectors.Item2);
 | 
			
		||||
                writer.Write(TextureBases.Item1);
 | 
			
		||||
                writer.Write(TextureBases.Item2);
 | 
			
		||||
                writer.Write(TextureId);
 | 
			
		||||
                writer.Write(CachedSurface);
 | 
			
		||||
                writer.Write(TextureMagnitude);
 | 
			
		||||
                writer.WriteVec3(Center);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public struct LightmapInfo
 | 
			
		||||
        public class LightmapInfo
 | 
			
		||||
        {
 | 
			
		||||
            public (short, short) Bases { get; set; }
 | 
			
		||||
            public short PaddedWidth { get; set; }
 | 
			
		||||
| 
						 | 
				
			
			@ -104,11 +139,24 @@ public class WorldRep : IChunk
 | 
			
		|||
                DynamicLightPtr = reader.ReadUInt32();
 | 
			
		||||
                AnimLightBitmask = reader.ReadUInt32();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Write(BinaryWriter writer)
 | 
			
		||||
            {
 | 
			
		||||
                writer.Write(Bases.Item1);
 | 
			
		||||
                writer.Write(Bases.Item2);
 | 
			
		||||
                writer.Write(PaddedWidth);
 | 
			
		||||
                writer.Write(Height);
 | 
			
		||||
                writer.Write(Width);
 | 
			
		||||
                writer.Write(DataPtr);
 | 
			
		||||
                writer.Write(DynamicLightPtr);
 | 
			
		||||
                writer.Write(AnimLightBitmask);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public struct Lightmap
 | 
			
		||||
        public class Lightmap
 | 
			
		||||
        {
 | 
			
		||||
            public byte[] Pixels { get; set; }
 | 
			
		||||
            private readonly bool[] _litLayers;
 | 
			
		||||
            public List<byte[]> Pixels { get; set; }
 | 
			
		||||
 | 
			
		||||
            public int Layers;
 | 
			
		||||
            public int Width;
 | 
			
		||||
| 
						 | 
				
			
			@ -117,44 +165,52 @@ public class WorldRep : IChunk
 | 
			
		|||
 | 
			
		||||
            public Lightmap(BinaryReader reader, byte width, byte height, uint bitmask, int bytesPerPixel)
 | 
			
		||||
            {
 | 
			
		||||
                var count = 1 + BitOperations.PopCount(bitmask);
 | 
			
		||||
                var length = bytesPerPixel * width * height * count;
 | 
			
		||||
                Pixels = reader.ReadBytes(length);
 | 
			
		||||
                Layers = count;
 | 
			
		||||
                var layers = 1 + BitOperations.PopCount(bitmask);
 | 
			
		||||
                var length = bytesPerPixel * width * height;
 | 
			
		||||
                _litLayers = new bool[33];
 | 
			
		||||
                Pixels = new List<byte[]>();
 | 
			
		||||
                for (var i = 0; i < layers; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    Pixels.Add(reader.ReadBytes(length));
 | 
			
		||||
                    _litLayers[i] = true;
 | 
			
		||||
                }
 | 
			
		||||
                Layers = layers;
 | 
			
		||||
                Width = width;
 | 
			
		||||
                Height = height;
 | 
			
		||||
                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)
 | 
			
		||||
                {
 | 
			
		||||
                    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)
 | 
			
		||||
                {
 | 
			
		||||
                    case 1:
 | 
			
		||||
                        var raw1 = Pixels[idx];
 | 
			
		||||
                        var raw1 = pLayer[idx];
 | 
			
		||||
                        return new Vector4(raw1, raw1, raw1, 255) / 255.0f;
 | 
			
		||||
                    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;
 | 
			
		||||
                    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:
 | 
			
		||||
                        return Vector4.Zero;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public readonly byte[] AsBytesRgba(int layer)
 | 
			
		||||
            public byte[] AsBytesRgba(int layer)
 | 
			
		||||
            {
 | 
			
		||||
                ArgumentOutOfRangeException.ThrowIfLessThan(layer, 0, 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 bytes = new byte[length];
 | 
			
		||||
                for (var i = 0; i < length; i += 4, pIdx += Bpp)
 | 
			
		||||
| 
						 | 
				
			
			@ -162,30 +218,99 @@ public class WorldRep : IChunk
 | 
			
		|||
                    switch (Bpp)
 | 
			
		||||
                    {
 | 
			
		||||
                        case 1:
 | 
			
		||||
                            var raw1 = Pixels[pIdx];
 | 
			
		||||
                            var raw1 = pLayer[pIdx];
 | 
			
		||||
                            bytes[i] = raw1;
 | 
			
		||||
                            bytes[i + 1] = raw1;
 | 
			
		||||
                            bytes[i + 2] = raw1;
 | 
			
		||||
                            bytes[i + 3] = 255;
 | 
			
		||||
                            break;
 | 
			
		||||
                        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 + 1] = (byte)(255 * ((raw2 >> 5) & 31) / 31.0f);
 | 
			
		||||
                            bytes[i + 2] = (byte)(255 * ((raw2 >> 10) & 31) / 31.0f);
 | 
			
		||||
                            bytes[i + 3] = 255;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case 4:
 | 
			
		||||
                            bytes[i] = Pixels[pIdx + 2];
 | 
			
		||||
                            bytes[i + 1] = Pixels[pIdx + 1];
 | 
			
		||||
                            bytes[i + 2] = Pixels[pIdx];
 | 
			
		||||
                            bytes[i + 3] = Pixels[pIdx + 3];
 | 
			
		||||
                            bytes[i] = pLayer[pIdx + 2];
 | 
			
		||||
                            bytes[i + 1] = pLayer[pIdx + 1];
 | 
			
		||||
                            bytes[i + 2] = pLayer[pIdx];
 | 
			
		||||
                            bytes[i + 3] = pLayer[pIdx + 3];
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return bytes;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // TODO: This ONLY works for rgba (bpp = 4)!!!
 | 
			
		||||
            public void AddLight(int layer, int x, int y, float r, float g, float b)
 | 
			
		||||
            {
 | 
			
		||||
                var idx = (x + y * Width) * Bpp;
 | 
			
		||||
                var pLayer = Pixels[layer];
 | 
			
		||||
                pLayer[idx] = (byte)Math.Clamp(pLayer[idx] + r, 0, 255);
 | 
			
		||||
                pLayer[idx + 1] = (byte)Math.Clamp(pLayer[idx + 1] + g, 0, 255);
 | 
			
		||||
                pLayer[idx + 2] = (byte)Math.Clamp(pLayer[idx + 2] + b, 0, 255);
 | 
			
		||||
                pLayer[idx + 3] = 255;
 | 
			
		||||
                
 | 
			
		||||
                _litLayers[layer] = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void AddLight(int layer, int x, int y, Vector3 color, float strength, bool hdr)
 | 
			
		||||
            {
 | 
			
		||||
                if (hdr)
 | 
			
		||||
                {
 | 
			
		||||
                    strength /= 2.0f;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // We need to make sure we don't go over (255, 255, 255).
 | 
			
		||||
                // If we just do Max(color, (255, 255, 255)) then we change
 | 
			
		||||
                // the hue/saturation of coloured lights. Got to make sure we
 | 
			
		||||
                // maintain the colour ratios.
 | 
			
		||||
                var c = color * strength;
 | 
			
		||||
                var ratio = Math.Max(Math.Max(Math.Max(0.0f, c.X), c.Y), c.Z) / 255.0f;
 | 
			
		||||
                if (ratio > 1.0f)
 | 
			
		||||
                {
 | 
			
		||||
                    c /= ratio;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                AddLight(layer, x, y, c.Z, c.Y, c.X);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Reset(Vector3 ambientLight, bool hdr)
 | 
			
		||||
            {
 | 
			
		||||
                Layers = 33;
 | 
			
		||||
                Pixels.Clear();
 | 
			
		||||
                var bytesPerLayer = Width * Height * Bpp;
 | 
			
		||||
                for (var i = 0; i < Layers; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    Pixels.Add(new byte[bytesPerLayer]);
 | 
			
		||||
                    for (var j = 0; j < bytesPerLayer; j++)
 | 
			
		||||
                    {
 | 
			
		||||
                        Pixels[i][j] = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                    _litLayers[i] = false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for (var y = 0; y < Height; y++)
 | 
			
		||||
                {
 | 
			
		||||
                    for (var x = 0; x < Width; x++)
 | 
			
		||||
                    {
 | 
			
		||||
                        AddLight(0, x, y, ambientLight, 1.0f, hdr);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Write(BinaryWriter writer)
 | 
			
		||||
            {
 | 
			
		||||
                for (var i = 0; i < Layers; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_litLayers[i])
 | 
			
		||||
                    {
 | 
			
		||||
                        writer.Write(Pixels[i]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte VertexCount { get; set; }
 | 
			
		||||
| 
						 | 
				
			
			@ -207,11 +332,11 @@ public class WorldRep : IChunk
 | 
			
		|||
        public uint IndexCount { get; set; }
 | 
			
		||||
        public byte[] Indices { get; set; }
 | 
			
		||||
        public Plane[] Planes { get; set; }
 | 
			
		||||
        public ushort[] AnimLights { get; set; }
 | 
			
		||||
        public List<ushort> AnimLights { get; set; }
 | 
			
		||||
        public LightmapInfo[] LightList { get; set; }
 | 
			
		||||
        public Lightmap[] Lightmaps { get; set; }
 | 
			
		||||
        public int LightIndexCount { get; set; }
 | 
			
		||||
        public ushort[] LightIndices { get; set; }
 | 
			
		||||
        public List<ushort> LightIndices { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Cell(BinaryReader reader, int bpp)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -254,10 +379,10 @@ public class WorldRep : IChunk
 | 
			
		|||
            {
 | 
			
		||||
                Planes[i] = new Plane(reader.ReadVec3(), reader.ReadSingle());
 | 
			
		||||
            }
 | 
			
		||||
            AnimLights = new ushort[AnimLightCount];
 | 
			
		||||
            AnimLights = new List<ushort>(AnimLightCount);
 | 
			
		||||
            for (var i = 0; i < AnimLightCount; i++)
 | 
			
		||||
            {
 | 
			
		||||
                AnimLights[i] = reader.ReadUInt16();
 | 
			
		||||
                AnimLights.Add(reader.ReadUInt16());
 | 
			
		||||
            }
 | 
			
		||||
            LightList = new LightmapInfo[RenderPolyCount];
 | 
			
		||||
            for (var i = 0; i < RenderPolyCount; i++)
 | 
			
		||||
| 
						 | 
				
			
			@ -271,17 +396,269 @@ public class WorldRep : IChunk
 | 
			
		|||
                Lightmaps[i] = new Lightmap(reader, info.Width, info.Height, info.AnimLightBitmask, bpp);
 | 
			
		||||
            }
 | 
			
		||||
            LightIndexCount = reader.ReadInt32();
 | 
			
		||||
            LightIndices = new ushort[LightIndexCount];
 | 
			
		||||
            LightIndices = new List<ushort>(LightIndexCount);
 | 
			
		||||
            for (var i = 0; i < LightIndexCount; i++)
 | 
			
		||||
            {
 | 
			
		||||
                LightIndices[i] = reader.ReadUInt16();
 | 
			
		||||
                LightIndices.Add(reader.ReadUInt16());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(BinaryWriter writer)
 | 
			
		||||
        {
 | 
			
		||||
            writer.Write(VertexCount);
 | 
			
		||||
            writer.Write(PolyCount);
 | 
			
		||||
            writer.Write(RenderPolyCount);
 | 
			
		||||
            writer.Write(PortalPolyCount);
 | 
			
		||||
            writer.Write(PlaneCount);
 | 
			
		||||
            writer.Write(Medium);
 | 
			
		||||
            writer.Write(Flags);
 | 
			
		||||
            writer.Write(PortalVertices);
 | 
			
		||||
            writer.Write(NumVList);
 | 
			
		||||
            writer.Write(AnimLightCount);
 | 
			
		||||
            writer.Write(MotionIndex);
 | 
			
		||||
            writer.WriteVec3(SphereCenter);
 | 
			
		||||
            writer.Write(SphereRadius);
 | 
			
		||||
            foreach (var vertex in Vertices)
 | 
			
		||||
            {
 | 
			
		||||
                writer.WriteVec3(vertex);
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var poly in Polys)
 | 
			
		||||
            {
 | 
			
		||||
                poly.Write(writer);
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var renderPoly in RenderPolys)
 | 
			
		||||
            {
 | 
			
		||||
                renderPoly.Write(writer);
 | 
			
		||||
            }
 | 
			
		||||
            writer.Write(IndexCount);
 | 
			
		||||
            writer.Write(Indices);
 | 
			
		||||
            foreach (var plane in Planes)
 | 
			
		||||
            {
 | 
			
		||||
                writer.WriteVec3(plane.Normal);
 | 
			
		||||
                writer.Write(plane.D);
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var animLight in AnimLights)
 | 
			
		||||
            {
 | 
			
		||||
                writer.Write(animLight);
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var lightmapInfo in LightList)
 | 
			
		||||
            {
 | 
			
		||||
                lightmapInfo.Write(writer);
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var lightmap in Lightmaps)
 | 
			
		||||
            {
 | 
			
		||||
                lightmap.Write(writer);
 | 
			
		||||
            }
 | 
			
		||||
            writer.Write(LightIndexCount);
 | 
			
		||||
            foreach (var lightIndex in LightIndices)
 | 
			
		||||
            {
 | 
			
		||||
                writer.Write(lightIndex);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 class LightTable
 | 
			
		||||
    {
 | 
			
		||||
        public struct LightData
 | 
			
		||||
        {
 | 
			
		||||
            public Vector3 Location;
 | 
			
		||||
            public Vector3 Direction;
 | 
			
		||||
            public Vector3 Color;
 | 
			
		||||
            public float InnerAngle; // I'm pretty sure these are the spotlight angles
 | 
			
		||||
            public float OuterAngle;
 | 
			
		||||
            public 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 List<LightData> Lights;
 | 
			
		||||
        public LightData[] ScratchpadLights;
 | 
			
		||||
        public List<AnimCellMap> AnimCellMaps;
 | 
			
		||||
 | 
			
		||||
        // TODO: Support olddark
 | 
			
		||||
        public LightTable(BinaryReader reader)
 | 
			
		||||
        {
 | 
			
		||||
            LightCount = reader.ReadInt32();
 | 
			
		||||
            DynamicLightCount = reader.ReadInt32();
 | 
			
		||||
            var totalLightCount = LightCount + DynamicLightCount;
 | 
			
		||||
            Lights = new List<LightData>(totalLightCount);
 | 
			
		||||
            for (var i = 0; i < totalLightCount; i++)
 | 
			
		||||
            {
 | 
			
		||||
                Lights.Add(new LightData(reader));
 | 
			
		||||
            }
 | 
			
		||||
            ScratchpadLights = new LightData[32];
 | 
			
		||||
            for (var i = 0; i < 32; i++)
 | 
			
		||||
            {
 | 
			
		||||
                ScratchpadLights[i] = new LightData(reader);
 | 
			
		||||
            }
 | 
			
		||||
            AnimMapCount = reader.ReadInt32();
 | 
			
		||||
            AnimCellMaps = new List<AnimCellMap>(AnimMapCount);
 | 
			
		||||
            for (var i = 0; i < AnimMapCount; i++)
 | 
			
		||||
            {
 | 
			
		||||
                AnimCellMaps.Add(new AnimCellMap(reader));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public 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 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 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)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -294,11 +671,27 @@ 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void WriteData(BinaryWriter writer)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        DataHeader.Write(writer);
 | 
			
		||||
        foreach (var cell in Cells)
 | 
			
		||||
        {
 | 
			
		||||
            cell.Write(writer);
 | 
			
		||||
        }
 | 
			
		||||
        Bsp.Write(writer);
 | 
			
		||||
        writer.Write(_unknown);
 | 
			
		||||
        LightingTable.Write(writer);
 | 
			
		||||
        writer.Write(_unreadData);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
| 
						 | 
				
			
			@ -32,13 +31,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 +46,16 @@ 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)
 | 
			
		||||
            {
 | 
			
		||||
                writer.WriteNullString(Name, 12);
 | 
			
		||||
                writer.Write(Offset);
 | 
			
		||||
                writer.Write(Size);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +70,15 @@ public class DbFile
 | 
			
		|||
                Items.Add(new Entry(reader));
 | 
			
		||||
            Items.Sort((a, b) => a.Offset.CompareTo(b.Offset));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public readonly void Write(BinaryWriter writer)
 | 
			
		||||
        {
 | 
			
		||||
            writer.Write(ItemCount);
 | 
			
		||||
            foreach (var entry in Items)
 | 
			
		||||
            {
 | 
			
		||||
                entry.Write(writer);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public FHeader Header { get; private set; }
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +87,7 @@ public class DbFile
 | 
			
		|||
 | 
			
		||||
    public DbFile(string filename)
 | 
			
		||||
    {
 | 
			
		||||
        // TODO: Throw rather than return
 | 
			
		||||
        if (!File.Exists(filename)) return;
 | 
			
		||||
 | 
			
		||||
        using MemoryStream stream = new(File.ReadAllBytes(filename));
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +106,43 @@ public class DbFile
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Save(string filename)
 | 
			
		||||
    {
 | 
			
		||||
        using var stream = File.Open(filename, FileMode.Create);
 | 
			
		||||
        using var writer = new BinaryWriter(stream, Encoding.UTF8, false);
 | 
			
		||||
 | 
			
		||||
        Header.Write(writer);
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
        return entryName switch
 | 
			
		||||
| 
						 | 
				
			
			@ -101,6 +153,8 @@ public class DbFile
 | 
			
		|||
            "TXLIST" => new TxList(),
 | 
			
		||||
            "WREXT" => new WorldRep(),
 | 
			
		||||
            "BRLIST" => new BrList(),
 | 
			
		||||
            "LM_PARAM" => new LmParams(),
 | 
			
		||||
            "RENDPARAMS" => new RendParams(),
 | 
			
		||||
            "P$ModelName" => new PropertyChunk<PropLabel>(),
 | 
			
		||||
            "P$Scale" => new PropertyChunk<PropVector>(),
 | 
			
		||||
            "P$RenderTyp" => new PropertyChunk<PropRenderType>(),
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +162,10 @@ public class DbFile
 | 
			
		|||
            "P$OTxtRepr1" => new PropertyChunk<PropString>(),
 | 
			
		||||
            "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>(),
 | 
			
		||||
            "LD$MetaProp" => new LinkDataMetaProp(),
 | 
			
		||||
            _ when entryName.StartsWith("L$") => new LinkChunk(),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using KeepersCompound.LGS.Database.Chunks;
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +6,7 @@ namespace KeepersCompound.LGS.Database;
 | 
			
		|||
 | 
			
		||||
public class ObjectHierarchy
 | 
			
		||||
{
 | 
			
		||||
    public class DarkObject
 | 
			
		||||
    private class DarkObject
 | 
			
		||||
    {
 | 
			
		||||
        public int objectId;
 | 
			
		||||
        public int parentId;
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +19,7 @@ public class ObjectHierarchy
 | 
			
		|||
            properties = new Dictionary<string, Property>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public T GetProperty<T>(string propName) where T : Property
 | 
			
		||||
        public T? GetProperty<T>(string propName) where T : Property
 | 
			
		||||
        {
 | 
			
		||||
            if (properties.TryGetValue(propName, out var prop))
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -38,18 +37,17 @@ public class ObjectHierarchy
 | 
			
		|||
 | 
			
		||||
        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;
 | 
			
		||||
                if (gam != null && gam.Chunks.TryGetValue(name, out var rawGamChunk))
 | 
			
		||||
                {
 | 
			
		||||
                    var gamChunk = (T)rawGamChunk;
 | 
			
		||||
                    chunk.Merge(gamChunk);
 | 
			
		||||
                }
 | 
			
		||||
                return chunk;
 | 
			
		||||
                throw new ArgumentException($"No chunk with name ({name}) found", nameof(name));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw new ArgumentException($"No chunk with name ({name}) found", nameof(name));
 | 
			
		||||
            if (gam != null && gam.TryGetChunk<T>(name, out var gamChunk))
 | 
			
		||||
            {
 | 
			
		||||
                gamChunk.Merge(chunk);
 | 
			
		||||
                return gamChunk;
 | 
			
		||||
            }
 | 
			
		||||
            return chunk;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add parentages
 | 
			
		||||
| 
						 | 
				
			
			@ -83,11 +81,12 @@ public class ObjectHierarchy
 | 
			
		|||
            foreach (var prop in chunk.properties)
 | 
			
		||||
            {
 | 
			
		||||
                var id = prop.objectId;
 | 
			
		||||
                if (!_objects.ContainsKey(id))
 | 
			
		||||
                if (!_objects.TryGetValue(id, out var value))
 | 
			
		||||
                {
 | 
			
		||||
                    _objects.Add(id, new DarkObject(id));
 | 
			
		||||
                    value = new DarkObject(id);
 | 
			
		||||
                    _objects.Add(id, value);
 | 
			
		||||
                }
 | 
			
		||||
                _objects[id].properties.TryAdd(name, prop);
 | 
			
		||||
                value.properties.TryAdd(name, prop);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -99,9 +98,14 @@ public class ObjectHierarchy
 | 
			
		|||
        AddProp<PropString>("P$OTxtRepr2");
 | 
			
		||||
        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))
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +121,7 @@ public class ObjectHierarchy
 | 
			
		|||
            }
 | 
			
		||||
 | 
			
		||||
            var prop = obj.GetProperty<T>(propName);
 | 
			
		||||
            if (prop != null)
 | 
			
		||||
            if (prop != null || !inherit)
 | 
			
		||||
            {
 | 
			
		||||
                return prop;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
using System.Text;
 | 
			
		||||
| 
						 | 
				
			
			@ -12,16 +13,37 @@ public static class Extensions
 | 
			
		|||
        return raw * 360 / (ushort.MaxValue + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void WriteRotation(this BinaryWriter writer, Vector3 rotation)
 | 
			
		||||
    {
 | 
			
		||||
        var raw = rotation * (ushort.MaxValue + 1) / 360;
 | 
			
		||||
        writer.Write((ushort)raw.X);
 | 
			
		||||
        writer.Write((ushort)raw.Y);
 | 
			
		||||
        writer.Write((ushort)raw.Z);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Vector3 ReadVec3(this BinaryReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void WriteVec3(this BinaryWriter writer, Vector3 vec)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Write(vec.X);
 | 
			
		||||
        writer.Write(vec.Y);
 | 
			
		||||
        writer.Write(vec.Z);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Vector2 ReadVec2(this BinaryReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        return new Vector2(reader.ReadSingle(), reader.ReadSingle());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void WriteVec2(this BinaryWriter writer, Vector2 vec)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Write(vec.X);
 | 
			
		||||
        writer.Write(vec.Y);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string ReadNullString(this BinaryReader reader, int length)
 | 
			
		||||
    {
 | 
			
		||||
        var tmpName = Encoding.UTF8.GetString(reader.ReadBytes(length));
 | 
			
		||||
| 
						 | 
				
			
			@ -29,4 +51,12 @@ public static class Extensions
 | 
			
		|||
        if (idx >= 0) tmpName = tmpName[..idx];
 | 
			
		||||
        return tmpName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void WriteNullString(this BinaryWriter writer, string nullString, int length)
 | 
			
		||||
    {
 | 
			
		||||
        var writeBytes = new byte[length];
 | 
			
		||||
        var stringBytes = Encoding.UTF8.GetBytes(nullString);
 | 
			
		||||
        stringBytes[..Math.Min(length, stringBytes.Length)].CopyTo(writeBytes, 0);
 | 
			
		||||
        writer.Write(writeBytes);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
using System.Text;
 | 
			
		||||
| 
						 | 
				
			
			@ -94,6 +93,54 @@ public class ModelFile
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public struct SubObject
 | 
			
		||||
    {
 | 
			
		||||
        public string Name;
 | 
			
		||||
        public byte Type;
 | 
			
		||||
        public int Joint;
 | 
			
		||||
        public float MinJointValue;
 | 
			
		||||
        public float MaxJointValue;
 | 
			
		||||
        public Matrix4x4 Transform;
 | 
			
		||||
        public short Child;
 | 
			
		||||
        public short Next;
 | 
			
		||||
        public ushort VhotIdx;
 | 
			
		||||
        public ushort VhotCount;
 | 
			
		||||
        public ushort PointIdx;
 | 
			
		||||
        public ushort PointCount;
 | 
			
		||||
        public ushort LightIdx;
 | 
			
		||||
        public ushort LightCount;
 | 
			
		||||
        public ushort NormalIdx;
 | 
			
		||||
        public ushort NormalCount;
 | 
			
		||||
        public ushort NodeIdx;
 | 
			
		||||
        public ushort NodeCount;
 | 
			
		||||
 | 
			
		||||
        public SubObject(BinaryReader reader)
 | 
			
		||||
        {
 | 
			
		||||
            Name = reader.ReadNullString(8);
 | 
			
		||||
            Type = reader.ReadByte();
 | 
			
		||||
            Joint = reader.ReadInt32();
 | 
			
		||||
            MinJointValue = reader.ReadSingle();
 | 
			
		||||
            MaxJointValue = reader.ReadSingle();
 | 
			
		||||
            var v1 = reader.ReadVec3();
 | 
			
		||||
            var v2 = reader.ReadVec3();
 | 
			
		||||
            var v3 = reader.ReadVec3();
 | 
			
		||||
            var v4 = reader.ReadVec3();
 | 
			
		||||
            Transform = new Matrix4x4(v1.X, v1.Y, v1.Z, 0, v2.X, v2.Y, v2.Z, 0, v3.X, v3.Y, v3.Z, 0, v4.X, v4.Y, v4.Z, 1);
 | 
			
		||||
            Child = reader.ReadInt16();
 | 
			
		||||
            Next = reader.ReadInt16();
 | 
			
		||||
            VhotIdx = reader.ReadUInt16();
 | 
			
		||||
            VhotCount = reader.ReadUInt16();
 | 
			
		||||
            PointIdx = reader.ReadUInt16();
 | 
			
		||||
            PointCount = reader.ReadUInt16();
 | 
			
		||||
            LightIdx = reader.ReadUInt16();
 | 
			
		||||
            LightCount = reader.ReadUInt16();
 | 
			
		||||
            NormalIdx = reader.ReadUInt16();
 | 
			
		||||
            NormalCount = reader.ReadUInt16();
 | 
			
		||||
            NodeIdx = reader.ReadUInt16();
 | 
			
		||||
            NodeCount = reader.ReadUInt16();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public struct Polygon
 | 
			
		||||
    {
 | 
			
		||||
        public ushort Index;
 | 
			
		||||
| 
						 | 
				
			
			@ -153,6 +200,30 @@ public class ModelFile
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum VhotId
 | 
			
		||||
    {
 | 
			
		||||
        LightPosition = 1,
 | 
			
		||||
        LightDirection = 8,
 | 
			
		||||
        Anchor = 2,
 | 
			
		||||
        Particle1 = 3,
 | 
			
		||||
        Particle2 = 4,
 | 
			
		||||
        Particle3 = 5,
 | 
			
		||||
        Particle4 = 6,
 | 
			
		||||
        Particle5 = 7,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public struct VHot
 | 
			
		||||
    {
 | 
			
		||||
        public int Id;
 | 
			
		||||
        public Vector3 Position;
 | 
			
		||||
 | 
			
		||||
        public VHot(BinaryReader reader)
 | 
			
		||||
        {
 | 
			
		||||
            Id = reader.ReadInt32();
 | 
			
		||||
            Position = reader.ReadVec3();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BHeader BinHeader { get; set; }
 | 
			
		||||
    public MHeader Header { get; set; }
 | 
			
		||||
    public Vector3[] Vertices { get; }
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +231,8 @@ public class ModelFile
 | 
			
		|||
    public Vector3[] Normals { get; }
 | 
			
		||||
    public Polygon[] Polygons { get; }
 | 
			
		||||
    public Material[] Materials { get; }
 | 
			
		||||
    public VHot[] VHots { get; }
 | 
			
		||||
    public SubObject[] Objects { get; }
 | 
			
		||||
 | 
			
		||||
    public ModelFile(string filename)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -202,5 +275,31 @@ public class ModelFile
 | 
			
		|||
        {
 | 
			
		||||
            Materials[i] = new Material(reader);
 | 
			
		||||
        }
 | 
			
		||||
        stream.Seek(Header.VHotOffset, SeekOrigin.Begin);
 | 
			
		||||
        VHots = new VHot[Header.VHotCount];
 | 
			
		||||
        for (var i = 0; i < VHots.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            VHots[i] = new VHot(reader);
 | 
			
		||||
        }
 | 
			
		||||
        stream.Seek(Header.ObjectOffset, SeekOrigin.Begin);
 | 
			
		||||
        Objects = new SubObject[Header.ObjectCount];
 | 
			
		||||
        for (var i = 0; i < Objects.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            Objects[i] = new SubObject(reader);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool TryGetVhot(VhotId id, out VHot vhot)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var v in VHots)
 | 
			
		||||
        {
 | 
			
		||||
            if (v.Id == (int)id)
 | 
			
		||||
            {
 | 
			
		||||
                vhot = v;
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        vhot = new VHot();
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -261,16 +261,17 @@ public partial class Mission : Node3D
 | 
			
		|||
			var pos = brush.position.ToGodotVec3();
 | 
			
		||||
			var rot = brush.angle.ToGodotVec3(false);
 | 
			
		||||
			var scale = scaleProp == null ? Vector3.One : scaleProp.value.ToGodotVec3(false);
 | 
			
		||||
			var model = Timing.TimeStage("Get Models", () =>
 | 
			
		||||
			{
 | 
			
		||||
				return Context.Instance.ModelLoader.Load(modelName);
 | 
			
		||||
			});
 | 
			
		||||
			if (model != null)
 | 
			
		||||
			var meshDetails = Timing.TimeStage("Get Models", () => Context.Instance.ModelLoader.Load(modelName));
 | 
			
		||||
			if (meshDetails.Length != 0)
 | 
			
		||||
			{
 | 
			
		||||
				var model = new Node3D();
 | 
			
		||||
				model.Position = pos;
 | 
			
		||||
				model.RotationDegrees = rot;
 | 
			
		||||
				model.Scale = scale;
 | 
			
		||||
				
 | 
			
		||||
				// TODO: Apply real joints
 | 
			
		||||
				var meshes = ModelLoader.TransformMeshes([45, 180, 0, 0, 0, 0], meshDetails);
 | 
			
		||||
				
 | 
			
		||||
				bool GetTextReplPath(PropString prop, out string path)
 | 
			
		||||
				{
 | 
			
		||||
					path = "";
 | 
			
		||||
| 
						 | 
				
			
			@ -299,6 +300,8 @@ public partial class Mission : Node3D
 | 
			
		|||
				}
 | 
			
		||||
 | 
			
		||||
				var repls = new PropString[] { txtRepl0, txtRepl1, txtRepl2, txtRepl3 };
 | 
			
		||||
				foreach (var meshInstance in meshes)
 | 
			
		||||
				{
 | 
			
		||||
					for (var i = 0; i < 4; i++)
 | 
			
		||||
					{
 | 
			
		||||
						if (GetTextReplPath(repls[i], out var path))
 | 
			
		||||
| 
						 | 
				
			
			@ -309,13 +312,13 @@ public partial class Mission : Node3D
 | 
			
		|||
								Transparency = BaseMaterial3D.TransparencyEnum.AlphaDepthPrePass,
 | 
			
		||||
							};
 | 
			
		||||
 | 
			
		||||
						var surfaceCount = model.Mesh.GetSurfaceCount();
 | 
			
		||||
							var surfaceCount = meshInstance.Mesh.GetSurfaceCount();
 | 
			
		||||
							for (var idx = 0; idx < surfaceCount; idx++)
 | 
			
		||||
							{
 | 
			
		||||
							var surfaceMat = model.Mesh.SurfaceGetMaterial(idx);
 | 
			
		||||
								var surfaceMat = meshInstance.Mesh.SurfaceGetMaterial(idx);
 | 
			
		||||
								if (surfaceMat.HasMeta($"TxtRepl{i}"))
 | 
			
		||||
								{
 | 
			
		||||
								model.SetSurfaceOverrideMaterial(idx, overrideMat);
 | 
			
		||||
									meshInstance.SetSurfaceOverrideMaterial(idx, overrideMat);
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
| 
						 | 
				
			
			@ -323,7 +326,10 @@ public partial class Mission : Node3D
 | 
			
		|||
 | 
			
		||||
					if (renderAlpha != null)
 | 
			
		||||
					{
 | 
			
		||||
					model.Transparency = 1.0f - renderAlpha.value;
 | 
			
		||||
						meshInstance.Transparency = 1.0f - renderAlpha.value;
 | 
			
		||||
					}
 | 
			
		||||
					
 | 
			
		||||
					model.AddChild(meshInstance);
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				model.AddToGroup(OBJECT_MODELS_GROUP);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,13 @@ public partial class Model : Node3D
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        Context.Instance.SetCampaign(campaignName);
 | 
			
		||||
        var model = Context.Instance.ModelLoader.Load(modelPath);
 | 
			
		||||
        var model = new Node3D();
 | 
			
		||||
        var meshDetails = Context.Instance.ModelLoader.Load(modelPath);
 | 
			
		||||
        var meshes = ModelLoader.TransformMeshes([0, 0, 0, 0, 0, 0], meshDetails);
 | 
			
		||||
        foreach (var meshInstance in meshes)
 | 
			
		||||
        {
 | 
			
		||||
            model.AddChild(meshInstance);
 | 
			
		||||
        }
 | 
			
		||||
        AddChild(model);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,9 +8,16 @@ namespace KeepersCompound.TMV;
 | 
			
		|||
// TODO: Work out a way to share base game models again in the cache
 | 
			
		||||
public class ModelLoader
 | 
			
		||||
{
 | 
			
		||||
    private readonly Dictionary<(string, string), MeshInstance3D> _cache = new();
 | 
			
		||||
    public struct MeshDetails(int jointIdx, Transform3D transform, MeshInstance3D mesh)
 | 
			
		||||
    {
 | 
			
		||||
        public readonly int JointIdx = jointIdx;
 | 
			
		||||
        public readonly Transform3D Transform = transform;
 | 
			
		||||
        public readonly MeshInstance3D Mesh = mesh;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public MeshInstance3D Load(string modelName, bool forceLoad = false)
 | 
			
		||||
    private readonly Dictionary<(string, string), MeshDetails[]> _cache = new();
 | 
			
		||||
 | 
			
		||||
    public MeshDetails[] Load(string modelName, bool forceLoad = false)
 | 
			
		||||
    {
 | 
			
		||||
        var campaignResources = Context.Instance.CampaignResources;
 | 
			
		||||
        var campaignName = campaignResources.name;
 | 
			
		||||
| 
						 | 
				
			
			@ -18,36 +25,60 @@ public class ModelLoader
 | 
			
		|||
        
 | 
			
		||||
        if (!forceLoad)
 | 
			
		||||
        {
 | 
			
		||||
            if (_cache.TryGetValue((campaignName, modelName), out var fmModel))
 | 
			
		||||
            if (_cache.TryGetValue((campaignName, modelName), out var fmDetails))
 | 
			
		||||
            {
 | 
			
		||||
                return fmModel?.Duplicate() as MeshInstance3D;
 | 
			
		||||
                return fmDetails;
 | 
			
		||||
            }
 | 
			
		||||
            else if (_cache.TryGetValue(("", modelName), out var omModel))
 | 
			
		||||
            if (_cache.TryGetValue(("", modelName), out var omDetails))
 | 
			
		||||
            {
 | 
			
		||||
                return omModel?.Duplicate() as MeshInstance3D;
 | 
			
		||||
                return omDetails;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // We don't care if this is null actually, we'll still cache that it's null lol
 | 
			
		||||
        var model = Timing.TimeStage("Load Models", () => { return LoadModel(modelName); });
 | 
			
		||||
        _cache[(campaignName, modelName)] = model;
 | 
			
		||||
        return model?.Duplicate() as MeshInstance3D;
 | 
			
		||||
        var details = Timing.TimeStage("Load Models", () => LoadModel(modelName));
 | 
			
		||||
        _cache[(campaignName, modelName)] = details;
 | 
			
		||||
        return details;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static MeshInstance3D LoadModel(string modelName)
 | 
			
		||||
    public static MeshInstance3D[] TransformMeshes(float[] joints, MeshDetails[] meshDetails)
 | 
			
		||||
    {
 | 
			
		||||
        var meshes = new List<MeshInstance3D>();
 | 
			
		||||
        foreach (var details in meshDetails)
 | 
			
		||||
        {
 | 
			
		||||
            var mesh = details.Mesh.Duplicate() as MeshInstance3D;
 | 
			
		||||
            if (details.JointIdx != -1)
 | 
			
		||||
            {
 | 
			
		||||
                var ang = float.DegreesToRadians(joints[details.JointIdx]);
 | 
			
		||||
                var r1 = new Quaternion(new Vector3(0, 0, 1), ang);
 | 
			
		||||
                var r2 = details.Transform.Basis.GetRotationQuaternion();
 | 
			
		||||
                var basis = new Basis(r2 * r1);
 | 
			
		||||
                mesh.SetTransform(new Transform3D(basis, details.Transform.Origin));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                mesh.SetTransform(details.Transform);
 | 
			
		||||
            }
 | 
			
		||||
        
 | 
			
		||||
            meshes.Add(mesh);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [..meshes];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static MeshDetails[] LoadModel(string modelName)
 | 
			
		||||
    {
 | 
			
		||||
        var campaignResources = Context.Instance.CampaignResources;
 | 
			
		||||
        var modelPath = campaignResources.GetResourcePath(ResourceType.Object, modelName);
 | 
			
		||||
        if (modelPath == null)
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var modelFile = new ModelFile(modelPath);
 | 
			
		||||
        if (modelFile == null)
 | 
			
		||||
        {
 | 
			
		||||
            GD.Print($"Failed to load model file: {modelPath}");
 | 
			
		||||
            return null;
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var materials = new List<StandardMaterial3D>();
 | 
			
		||||
| 
						 | 
				
			
			@ -94,19 +125,32 @@ public class ModelLoader
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var objCount = modelFile.Objects.Length;
 | 
			
		||||
        var meshDetails = new MeshDetails[objCount];
 | 
			
		||||
        for (var i = 0; i < objCount; i++)
 | 
			
		||||
        {
 | 
			
		||||
            var subObj = modelFile.Objects[i];
 | 
			
		||||
            var jointIdx = subObj.Joint;
 | 
			
		||||
            var transform = subObj.Type == 0 ? Transform3D.Identity : subObj.Transform.ToGodotTransform3D();
 | 
			
		||||
            var surfaceDataMap = new Dictionary<int, MeshSurfaceData>();
 | 
			
		||||
            foreach (var poly in modelFile.Polygons)
 | 
			
		||||
            {
 | 
			
		||||
                var v0 = poly.VertexIndices[0];
 | 
			
		||||
                if (v0 < subObj.PointIdx || v0 >= subObj.PointIdx + subObj.PointCount)
 | 
			
		||||
                {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                var vertices = new List<Vector3>();
 | 
			
		||||
                var normal = modelFile.Normals[poly.Normal].ToGodotVec3();
 | 
			
		||||
                var uvs = new List<Vector2>();
 | 
			
		||||
            for (var i = 0; i < poly.VertexCount; i++)
 | 
			
		||||
                for (var j = 0; j < poly.VertexCount; j++)
 | 
			
		||||
                {
 | 
			
		||||
                var vertex = modelFile.Vertices[poly.VertexIndices[i]];
 | 
			
		||||
                    var vertex = modelFile.Vertices[poly.VertexIndices[j]];
 | 
			
		||||
                    vertices.Add(vertex.ToGodotVec3());
 | 
			
		||||
                if (i < poly.UvIndices.Length)
 | 
			
		||||
                    if (j < poly.UvIndices.Length)
 | 
			
		||||
                    {
 | 
			
		||||
                    var uv = modelFile.Uvs[poly.UvIndices[i]];
 | 
			
		||||
                        var uv = modelFile.Uvs[poly.UvIndices[j]];
 | 
			
		||||
                        uvs.Add(new Vector2(uv.X, uv.Y));
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
| 
						 | 
				
			
			@ -129,12 +173,12 @@ public class ModelLoader
 | 
			
		|||
                var array = surfaceData.BuildSurfaceArray();
 | 
			
		||||
                var surfaceIdx = mesh.GetSurfaceCount();
 | 
			
		||||
                mesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, array);
 | 
			
		||||
            for (var i = 0; i < materials.Count; i++)
 | 
			
		||||
                for (var j = 0; j < materials.Count; j++)
 | 
			
		||||
                {
 | 
			
		||||
                var m = modelFile.Materials[i];
 | 
			
		||||
                    var m = modelFile.Materials[j];
 | 
			
		||||
                    if (m.Slot == materialId)
 | 
			
		||||
                    {
 | 
			
		||||
                    mesh.SurfaceSetMaterial(surfaceIdx, materials[i]);
 | 
			
		||||
                        mesh.SurfaceSetMaterial(surfaceIdx, materials[j]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +186,9 @@ public class ModelLoader
 | 
			
		|||
 | 
			
		||||
            var pos = -modelFile.Header.Center.ToGodotVec3();
 | 
			
		||||
            var meshInstance = new MeshInstance3D { Mesh = mesh, Position = pos };
 | 
			
		||||
        return meshInstance;
 | 
			
		||||
            meshDetails[i] = new MeshDetails(jointIdx, transform, meshInstance);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return meshDetails;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -9,4 +9,10 @@ public static class Utils
 | 
			
		|||
    {
 | 
			
		||||
        return new Godot.Vector3(vec.Y, vec.Z, vec.X) / (scale ? InverseScale : 1.0f);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public static Godot.Transform3D ToGodotTransform3D(this Matrix4x4 mat, bool scale = true)
 | 
			
		||||
    {
 | 
			
		||||
        var t = mat.Translation / (scale ? InverseScale : 1.0f);
 | 
			
		||||
        return new Godot.Transform3D(mat.M22, mat.M21, mat.M23, mat.M31, mat.M33, mat.M32, mat.M12, mat.M13, mat.M11, t.Y, t.Z, t.X);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +10,6 @@
 | 
			
		|||
[ext_resource type="Texture2D" uid="uid://bfswg75r148mr" path="res://project/assets/icons/ActionCopy.svg" id="7_2pq2g"]
 | 
			
		||||
 | 
			
		||||
[node name="AssetBrowser" type="Control"]
 | 
			
		||||
visible = false
 | 
			
		||||
layout_mode = 3
 | 
			
		||||
anchors_preset = 15
 | 
			
		||||
anchor_right = 1.0
 | 
			
		||||
| 
						 | 
				
			
			@ -22,9 +21,10 @@ size_flags_vertical = 3
 | 
			
		|||
script = ExtResource("1_5rr8c")
 | 
			
		||||
 | 
			
		||||
[node name="TabContainer" type="TabContainer" parent="."]
 | 
			
		||||
layout_mode = 2
 | 
			
		||||
offset_right = 1152.0
 | 
			
		||||
offset_bottom = 648.0
 | 
			
		||||
layout_mode = 1
 | 
			
		||||
anchors_preset = 15
 | 
			
		||||
anchor_right = 1.0
 | 
			
		||||
anchor_bottom = 1.0
 | 
			
		||||
grow_horizontal = 2
 | 
			
		||||
grow_vertical = 2
 | 
			
		||||
current_tab = 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,8 @@
 | 
			
		|||
[gd_scene load_steps=7 format=3 uid="uid://boxi211q3kx6c"]
 | 
			
		||||
[gd_scene load_steps=6 format=3 uid="uid://boxi211q3kx6c"]
 | 
			
		||||
 | 
			
		||||
[ext_resource type="Script" path="res://project/code/TMV/Mission.cs" id="1_3gnqe"]
 | 
			
		||||
[ext_resource type="Script" path="res://project/code/camera.gd" id="2_w5otl"]
 | 
			
		||||
[ext_resource type="PackedScene" uid="uid://bfxdpxkcgwlkx" path="res://project/scenes/ui/resource_selector.tscn" id="3_kdn7u"]
 | 
			
		||||
[ext_resource type="PackedScene" uid="uid://byknmqac1a5vn" path="res://project/scenes/asset_browser/asset_browser.tscn" id="3_noiti"]
 | 
			
		||||
[ext_resource type="PackedScene" uid="uid://0h2w7w84vbea" path="res://project/scenes/ui/lightmap_layer_toggler.tscn" id="4_naip8"]
 | 
			
		||||
 | 
			
		||||
[sub_resource type="Environment" id="Environment_cckyk"]
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +28,5 @@ unique_name_in_owner = true
 | 
			
		|||
unique_name_in_owner = true
 | 
			
		||||
visible = false
 | 
			
		||||
 | 
			
		||||
[node name="AssetBrowser" parent="UI" instance=ExtResource("3_noiti")]
 | 
			
		||||
 | 
			
		||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
 | 
			
		||||
environment = SubResource("Environment_cckyk")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue