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