Add RendParams and writers for each chunk
This commit is contained in:
parent
cf7ad880fb
commit
4bc55b9130
|
@ -46,6 +46,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 +113,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 +157,9 @@ public class BrList : IChunk
|
|||
|
||||
public void WriteData(BinaryWriter writer)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
foreach (var brush in Brushes)
|
||||
{
|
||||
brush.Write(writer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,3 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace KeepersCompound.LGS.Database.Chunks;
|
||||
|
||||
public class GamFile : IChunk
|
||||
|
@ -16,6 +12,6 @@ public class GamFile : IChunk
|
|||
|
||||
public void WriteData(BinaryWriter writer)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
writer.WriteNullString(fileName, 256);
|
||||
}
|
||||
}
|
|
@ -32,6 +32,11 @@ public record LinkId
|
|||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(_data);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinkChunk : IChunk, IMergable
|
||||
|
@ -50,6 +55,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 +79,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 +104,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 +128,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)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
|
||||
namespace KeepersCompound.LGS.Database.Chunks;
|
||||
|
@ -14,6 +12,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 +38,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 +59,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 +76,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 +93,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 +110,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 +129,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 +147,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 +164,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 +190,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 +212,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 +234,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 +262,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 +289,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 +317,17 @@ 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 PropLightColor : Property
|
||||
|
@ -241,6 +341,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 +362,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 +385,12 @@ public class PropSpotlightAndAmbient : Property
|
|||
OuterAngle = reader.ReadSingle();
|
||||
SpotBrightness = reader.ReadSingle();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Work out what this property actually does
|
||||
public class PropLightBasedAlpha : Property
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
public bool Enabled;
|
||||
public Vector2 AlphaRange;
|
||||
public Vector2 LightRange;
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
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.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,7 +1,3 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace KeepersCompound.LGS.Database.Chunks;
|
||||
|
||||
public class TxList : IChunk
|
||||
|
@ -16,6 +12,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 +29,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 +48,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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,6 +39,17 @@ 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
|
||||
|
@ -62,6 +73,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,6 +104,18 @@ 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
|
||||
|
@ -104,6 +138,18 @@ public class WorldRep : IChunk
|
|||
DynamicLightPtr = reader.ReadUInt32();
|
||||
AnimLightBitmask = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
public readonly 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
|
||||
|
@ -186,6 +232,21 @@ public class WorldRep : IChunk
|
|||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// TODO: This ONLY works for rgba (bpp = 4)!!!
|
||||
public void AddLight(int layer, int x, int y, byte r, byte g, byte b)
|
||||
{
|
||||
var idx = (x + y * Width + layer * Width * Height) * Bpp;
|
||||
Pixels[idx] = (byte)Math.Min(Pixels[idx] + b, 255);
|
||||
Pixels[idx + 1] = (byte)Math.Min(Pixels[idx + 1] + g, 255);
|
||||
Pixels[idx + 2] = (byte)Math.Min(Pixels[idx + 2] + r, 255);
|
||||
Pixels[idx + 3] = 255;
|
||||
}
|
||||
|
||||
public readonly void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Pixels);
|
||||
}
|
||||
}
|
||||
|
||||
public byte VertexCount { get; set; }
|
||||
|
@ -277,11 +338,65 @@ public class WorldRep : IChunk
|
|||
LightIndices[i] = reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly 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 ChunkHeader Header { get; set; }
|
||||
public WrHeader DataHeader { get; set; }
|
||||
public Cell[] Cells { get; set; }
|
||||
private byte[] _unreadData;
|
||||
|
||||
public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry)
|
||||
{
|
||||
|
@ -295,10 +410,17 @@ public class WorldRep : IChunk
|
|||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
writer.Write(_unreadData);
|
||||
}
|
||||
}
|
|
@ -52,6 +52,13 @@ public class DbFile
|
|||
// return $"Name: {Name}, Offset: {O}"
|
||||
return base.ToString();
|
||||
}
|
||||
|
||||
public readonly void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.WriteNullString(Name, 12);
|
||||
writer.Write(Offset);
|
||||
writer.Write(Size);
|
||||
}
|
||||
}
|
||||
|
||||
public uint ItemCount { get; }
|
||||
|
@ -65,6 +72,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 +89,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 +108,22 @@ public class DbFile
|
|||
}
|
||||
}
|
||||
|
||||
public void Save(string filename)
|
||||
{
|
||||
// !HACK: Right now we don't need to adjust TOC offset or anything because we're only
|
||||
// overwriting data, not writing new lengths of data
|
||||
|
||||
using var stream = File.Open(filename, FileMode.Create);
|
||||
using var writer = new BinaryWriter(stream, Encoding.UTF8, false);
|
||||
|
||||
Header.Write(writer);
|
||||
foreach (var (name, chunk) in Chunks)
|
||||
{
|
||||
chunk.Write(writer);
|
||||
}
|
||||
Toc.Write(writer);
|
||||
}
|
||||
|
||||
private static IChunk NewChunk(string entryName)
|
||||
{
|
||||
return entryName switch
|
||||
|
@ -101,6 +134,7 @@ public class DbFile
|
|||
"TXLIST" => new TxList(),
|
||||
"WREXT" => new WorldRep(),
|
||||
"BRLIST" => new BrList(),
|
||||
"RENDPARAMS" => new RendParams(),
|
||||
"P$ModelName" => new PropertyChunk<PropLabel>(),
|
||||
"P$Scale" => new PropertyChunk<PropVector>(),
|
||||
"P$RenderTyp" => new PropertyChunk<PropRenderType>(),
|
||||
|
|
|
@ -1,2 +1,243 @@
|
|||
// See https://aka.ms/new-console-template for more information
|
||||
Console.WriteLine("Hello, World!");
|
||||
using System.Numerics;
|
||||
using KeepersCompound.LGS.Database;
|
||||
using KeepersCompound.LGS.Database.Chunks;
|
||||
using TinyEmbree;
|
||||
|
||||
namespace KeepersCompound.Lightmapper;
|
||||
|
||||
class Program
|
||||
{
|
||||
// Super simple for now
|
||||
private record Light
|
||||
{
|
||||
public Vector3 position;
|
||||
public Vector3 color;
|
||||
public float radius;
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/JAYRUDE_Tests/lm_test.cow";
|
||||
var mis = new DbFile(misPath);
|
||||
var hierarchy = BuildHierarchy(misPath, mis);
|
||||
|
||||
// Get list of brush lights, and object lights (ignore anim lights for now)
|
||||
var lights = new List<Light>();
|
||||
if (mis.Chunks.TryGetValue("BRLIST", out var brListRaw))
|
||||
{
|
||||
var brList = (BrList)brListRaw;
|
||||
foreach (var brush in brList.Brushes)
|
||||
{
|
||||
if (brush.media == BrList.Brush.Media.Light)
|
||||
{
|
||||
var sz = brush.size;
|
||||
lights.Add(new Light
|
||||
{
|
||||
position = brush.position,
|
||||
color = HsbToRgb(360 * sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
|
||||
radius = float.MaxValue
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: object lights
|
||||
}
|
||||
|
||||
// Build embree mesh
|
||||
if (!mis.Chunks.TryGetValue("WREXT", out var wrRaw))
|
||||
return;
|
||||
var worldRep = (WorldRep)wrRaw;
|
||||
var scene = new Raytracer();
|
||||
scene.AddMesh(BuildWrMesh(worldRep));
|
||||
scene.CommitScene();
|
||||
|
||||
// For each lightmap pixel cast against all the brush and object lights
|
||||
if (!mis.Chunks.TryGetValue("RENDPARAMS", out var rendParamsRaw))
|
||||
return;
|
||||
var ambient = ((RendParams)rendParamsRaw).ambientLight * 255;
|
||||
CastScene(scene, worldRep, [.. lights], ambient);
|
||||
|
||||
var dir = Path.GetDirectoryName(misPath);
|
||||
var filename = Path.GetFileNameWithoutExtension(misPath);
|
||||
mis.Save(Path.Join(dir, $"{filename}-lit.cow"));
|
||||
}
|
||||
|
||||
// Expects Hue to be 0-360, saturation 0-1, brightness 0-255
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
|
||||
private static Vector3 HsbToRgb(float hue, float saturation, float brightness)
|
||||
{
|
||||
var hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
|
||||
var f = hue / 60 - Math.Floor(hue / 60);
|
||||
|
||||
var v = Convert.ToInt32(brightness);
|
||||
var p = Convert.ToInt32(brightness * (1 - saturation));
|
||||
var q = Convert.ToInt32(brightness * (1 - f * saturation));
|
||||
var t = Convert.ToInt32(brightness * (1 - (1 - f) * saturation));
|
||||
|
||||
return hi switch
|
||||
{
|
||||
0 => new Vector3(v, t, p),
|
||||
1 => new Vector3(q, v, p),
|
||||
2 => new Vector3(p, v, t),
|
||||
3 => new Vector3(p, q, v),
|
||||
4 => new Vector3(t, p, v),
|
||||
_ => new Vector3(v, p, q),
|
||||
};
|
||||
}
|
||||
|
||||
private static ObjectHierarchy BuildHierarchy(string misPath, DbFile misFile)
|
||||
{
|
||||
ObjectHierarchy objHierarchy;
|
||||
if (misFile.Chunks.TryGetValue("GAM_FILE", out var gamFileChunk))
|
||||
{
|
||||
var dir = Path.GetDirectoryName(misPath);
|
||||
var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
|
||||
var name = ((GamFile)gamFileChunk).fileName;
|
||||
var paths = Directory.GetFiles(dir!, name, options);
|
||||
if (paths.Length > 0)
|
||||
{
|
||||
objHierarchy = new ObjectHierarchy(misFile, new DbFile(paths[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
objHierarchy = new ObjectHierarchy(misFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
objHierarchy = new ObjectHierarchy(misFile);
|
||||
}
|
||||
return objHierarchy;
|
||||
}
|
||||
|
||||
private static TriangleMesh BuildWrMesh(WorldRep worldRep)
|
||||
{
|
||||
var vertices = new List<Vector3>();
|
||||
var indices = new List<int>();
|
||||
|
||||
var cells = worldRep.Cells;
|
||||
for (var cellIdx = 0; cellIdx < cells.Length; cellIdx++)
|
||||
{
|
||||
var cell = cells[cellIdx];
|
||||
var numPolys = cell.PolyCount;
|
||||
var numRenderPolys = cell.RenderPolyCount;
|
||||
var numPortalPolys = cell.PortalPolyCount;
|
||||
|
||||
// There's nothing to render
|
||||
if (numRenderPolys == 0 || numPortalPolys >= numPolys)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
|
||||
var cellIdxOffset = 0;
|
||||
for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
|
||||
{
|
||||
var poly = cell.Polys[polyIdx];
|
||||
|
||||
var meshIndexOffset = vertices.Count;
|
||||
var numPolyVertices = poly.VertexCount;
|
||||
for (var j = 0; j < numPolyVertices; j++)
|
||||
{
|
||||
var vertex = cell.Vertices[cell.Indices[cellIdxOffset + j]];
|
||||
// Console.WriteLine($"Cell: {cellIdx}, Poly: {polyIdx}, V: {j}, Vert: {vertex}");
|
||||
vertices.Add(vertex);
|
||||
}
|
||||
|
||||
for (int j = 1; j < numPolyVertices - 1; j++)
|
||||
{
|
||||
indices.Add(meshIndexOffset);
|
||||
indices.Add(meshIndexOffset + j);
|
||||
indices.Add(meshIndexOffset + j + 1);
|
||||
}
|
||||
|
||||
cellIdxOffset += cell.Polys[polyIdx].VertexCount;
|
||||
}
|
||||
}
|
||||
|
||||
return new TriangleMesh([.. vertices], [.. indices]);
|
||||
}
|
||||
|
||||
private static void CastScene(Raytracer scene, WorldRep wr, Light[] lights, Vector3 ambientLight)
|
||||
{
|
||||
var cells = wr.Cells;
|
||||
for (var cellIdx = 0; cellIdx < cells.Length; cellIdx++)
|
||||
{
|
||||
var cell = cells[cellIdx];
|
||||
var numPolys = cell.PolyCount;
|
||||
var numRenderPolys = cell.RenderPolyCount;
|
||||
var numPortalPolys = cell.PortalPolyCount;
|
||||
|
||||
// There's nothing to render
|
||||
if (numRenderPolys == 0 || numPortalPolys >= numPolys)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
|
||||
for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
|
||||
{
|
||||
var poly = cell.Polys[polyIdx];
|
||||
var plane = cell.Planes[poly.PlaneId];
|
||||
var renderPoly = cell.RenderPolys[polyIdx];
|
||||
var info = cell.LightList[polyIdx];
|
||||
var lightmap = cell.Lightmaps[polyIdx];
|
||||
|
||||
// Clear existing lightmap data
|
||||
for (var i = 0; i < lightmap.Pixels.Length; i++)
|
||||
{
|
||||
lightmap.Pixels[i] = 0;
|
||||
}
|
||||
|
||||
for (var y = 0; y < lightmap.Height; y++)
|
||||
{
|
||||
for (var x = 0; x < lightmap.Width; x++)
|
||||
{
|
||||
lightmap.AddLight(0, x, y, (byte)ambientLight.X, (byte)ambientLight.Y, (byte)ambientLight.Z);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var light in lights)
|
||||
{
|
||||
Console.WriteLine("Doing a light...");
|
||||
// Check if plane normal is facing towards the light
|
||||
var direction = renderPoly.Center - light.position;
|
||||
Console.WriteLine($"Light Pos: {light.position}, poly center: {renderPoly.Center}");
|
||||
Console.WriteLine($"Dir: {direction}");
|
||||
// if (Vector3.Dot(plane.Normal, direction) < 0)
|
||||
{
|
||||
// Cast from the light to the center (later each pixel)
|
||||
var hit = scene.Trace(new Ray
|
||||
{
|
||||
Origin = light.position,
|
||||
Direction = Vector3.Normalize(direction)
|
||||
});
|
||||
|
||||
// cheeky epsilon
|
||||
var goodHit = hit && Math.Abs(hit.Distance - direction.Length()) < 0.001;
|
||||
Console.WriteLine($"Did we hit? {goodHit}");
|
||||
Console.WriteLine($"Distance: {hit.Distance}, target dist: {direction.Length()}");
|
||||
Console.WriteLine($"Pos: {hit.Position}, Target Pos: {renderPoly.Center}");
|
||||
|
||||
// Iterate all pixels
|
||||
var color = goodHit ? light.color : ambientLight;
|
||||
for (var y = 0; y < lightmap.Height; y++)
|
||||
{
|
||||
for (var x = 0; x < lightmap.Width; x++)
|
||||
{
|
||||
lightmap.AddLight(0, x, y, (byte)color.X, (byte)color.Y, (byte)color.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// // TODO: Get world position of each pixel?
|
||||
|
||||
// var poly = cell.Polys[polyIdx];
|
||||
|
||||
// poly.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue