From ab738203d67bc80dc1fa82f04b08f95f9d8ae64b Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Sat, 5 Oct 2024 14:52:09 +0100 Subject: [PATCH] Reset lighting table and lightmaps --- .../Database/Chunks/WorldRep.cs | 56 ++-- KeepersCompound.Lightmapper/Program.cs | 243 +++++++++++------- 2 files changed, 183 insertions(+), 116 deletions(-) diff --git a/KeepersCompound.LGS/Database/Chunks/WorldRep.cs b/KeepersCompound.LGS/Database/Chunks/WorldRep.cs index 08eb6cf..a5a69fd 100644 --- a/KeepersCompound.LGS/Database/Chunks/WorldRep.cs +++ b/KeepersCompound.LGS/Database/Chunks/WorldRep.cs @@ -274,16 +274,16 @@ public class WorldRep : IChunk AddLight(layer, x, y, c.Z, c.Y, c.X); } - public readonly void Reset(Vector3 ambientLight, bool hdr) + public void Reset(Vector3 ambientLight, bool hdr) { - // TODO: This should set to one layer when we write our own lighttable etc var bytesPerLayer = Width * Height * Bpp; - for (var i = 0; i < Layers; i++) + Pixels.Clear(); + Pixels.Add(new byte[bytesPerLayer]); + Layers = 1; + + for (var j = 0; j < bytesPerLayer; j++) { - for (var j = 0; j < bytesPerLayer; j++) - { - Pixels[i][j] = 0; - } + Pixels[0][j] = 0; } for (var y = 0; y < Height; y++) @@ -568,30 +568,31 @@ public class WorldRep : IChunk public int LightCount; public int DynamicLightCount; public int AnimMapCount; - public LightData[] Lights; + public List Lights; public LightData[] ScratchpadLights; - public AnimCellMap[] AnimCellMaps; + public List 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++) + var totalLightCount = LightCount + DynamicLightCount; + Lights = new List(totalLightCount); + for (var i = 0; i < totalLightCount; i++) { - Lights[i] = new LightData(reader); + Lights.Add(new LightData(reader)); } ScratchpadLights = new LightData[32]; - for (var i = 0; i < ScratchpadLights.Length; i++) + for (var i = 0; i < 32; i++) { ScratchpadLights[i] = new LightData(reader); } AnimMapCount = reader.ReadInt32(); - AnimCellMaps = new AnimCellMap[AnimMapCount]; - for (var i = 0; i < AnimCellMaps.Length; i++) + AnimCellMaps = new List(AnimMapCount); + for (var i = 0; i < AnimMapCount; i++) { - AnimCellMaps[i] = new AnimCellMap(reader); + AnimCellMaps.Add(new AnimCellMap(reader)); } } @@ -613,6 +614,29 @@ public class WorldRep : IChunk map.Write(writer); } } + + public void Reset() + { + LightCount = 0; + DynamicLightCount = 0; + AnimMapCount = 0; + Lights.Clear(); + AnimCellMaps.Clear(); + } + + public void AddLight(LightData data, bool dynamicLight = false) + { + if (dynamicLight) + { + DynamicLightCount++; + } + else + { + LightCount++; + } + + Lights.Add(data); + } } public ChunkHeader Header { get; set; } diff --git a/KeepersCompound.Lightmapper/Program.cs b/KeepersCompound.Lightmapper/Program.cs index 641cf4a..4c31aac 100644 --- a/KeepersCompound.Lightmapper/Program.cs +++ b/KeepersCompound.Lightmapper/Program.cs @@ -57,8 +57,6 @@ class Program var mis = Timing.TimeStage("Parse DB", () => new DbFile(misPath)); var hierarchy = Timing.TimeStage("Build Hierarchy", () => BuildHierarchy(misPath, mis)); - var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign)); - // Build embree mesh if (!mis.Chunks.TryGetValue("WREXT", out var wrRaw)) return; @@ -75,6 +73,7 @@ class Program if (!mis.Chunks.TryGetValue("RENDPARAMS", out var rendParamsRaw)) return; var ambient = ((RendParams)rendParamsRaw).ambientLight * 255; + var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign)); Timing.TimeStage("Light", () => CastSceneParallel(scene, worldRep, [.. lights], ambient)); var dir = Path.GetDirectoryName(misPath); @@ -109,126 +108,167 @@ class Program }; } - // Get list of brush lights, and object lights (ignore anim lights for now) - private static List BuildLightList(DbFile mis, ObjectHierarchy hierarchy, ResourcePathManager.CampaignResources campaign) + // Gather all the brush, object, and anim ligths. Resets the lighting table + // TODO: Handle dynamic lights + private static List BuildLightList( + DbFile mis, + ObjectHierarchy hierarchy, + ResourcePathManager.CampaignResources campaign) { var lights = new List(); - if (mis.Chunks.TryGetValue("BRLIST", out var brListRaw)) + // Get the chunks we need + if (!mis.TryGetChunk("WREXT", out var worldRep) || + !mis.TryGetChunk("BRLIST", out var brList)) { - var brList = (BrList)brListRaw; - foreach (var brush in brList.Brushes) + return lights; + } + + worldRep.LightingTable.Reset(); + + foreach (var brush in brList.Brushes) + { + if (brush.media == BrList.Brush.Media.Light) { - if (brush.media == BrList.Brush.Media.Light) + var sz = brush.size; + var light = new Light { - var sz = brush.size; - lights.Add(new Light + position = brush.position, + color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)), + radius = float.MaxValue, + r2 = float.MaxValue, + }; + + lights.Add(light); + worldRep.LightingTable.AddLight(new WorldRep.LightTable.LightData + { + Location = light.position, + Direction = light.spotlightDir, + Color = light.color / 32.0f, // TODO: This is based on light_scale config var + InnerAngle = -1.0f, + Radius = 0, + }); + } + else if (brush.media == BrList.Brush.Media.Object) + { + // TODO: Handle PropSpotlightAndAmbient + var id = (int)brush.brushInfo; + var propAnimLight = hierarchy.GetProperty(id, "P$AnimLight", false); + var propLight = hierarchy.GetProperty(id, "P$Light", false); + var propLightColor = hierarchy.GetProperty(id, "P$LightColo"); + var propSpotlight = hierarchy.GetProperty(id, "P$Spotlight"); + var propModelname = hierarchy.GetProperty(id, "P$ModelName"); + + propLightColor ??= new PropLightColor { Hue = 0, Saturation = 0 }; + + var baseLight = new Light + { + position = brush.position, + spotlightDir = -Vector3.UnitZ, + spotlightInnerAngle = -1.0f, + }; + + if (propModelname != null) + { + var resName = $"{propModelname.value.ToLower()}.bin"; + var modelPath = campaign.GetResourcePath(ResourceType.Object, resName); + if (modelPath != null) { - position = brush.position, - color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)), - radius = float.MaxValue, - r2 = float.MaxValue, - }); + // 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; + } + } + } - else if (brush.media == BrList.Brush.Media.Object) + + if (propSpotlight != null) { - // TODO: Handle PropSpotlightAndAmbient - var id = (int)brush.brushInfo; - var propAnimLight = hierarchy.GetProperty(id, "P$AnimLight", false); - var propLight = hierarchy.GetProperty(id, "P$Light", false); - var propLightColor = hierarchy.GetProperty(id, "P$LightColo"); - var propSpotlight = hierarchy.GetProperty(id, "P$Spotlight"); - var propModelname = hierarchy.GetProperty(id, "P$ModelName"); + 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)); - propLightColor ??= new PropLightColor { Hue = 0, Saturation = 0 }; + 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)); + } - var baseLight = new Light + if (propLight != null) + { + var light = new Light { - position = brush.position, - spotlightDir = -Vector3.UnitZ, + position = baseLight.position + propLight.Offset, + color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness), + innerRadius = propLight.InnerRadius, + radius = propLight.Radius, + r2 = propLight.Radius * propLight.Radius, + spotlight = baseLight.spotlight, + spotlightDir = baseLight.spotlightDir, + spotlightInnerAngle = baseLight.spotlightInnerAngle, + spotlightOuterAngle = baseLight.spotlightOuterAngle, }; - if (propModelname != null) + if (propLight.Radius == 0) { - 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; - } - } - + light.radius = float.MaxValue; + light.r2 = float.MaxValue; } - if (propSpotlight != null) + lights.Add(light); + worldRep.LightingTable.AddLight(new WorldRep.LightTable.LightData { - 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)); + Location = light.position, + Direction = light.spotlightDir, + Color = light.color / 32.0f, // TODO: This is based on light_scale config var + InnerAngle = light.spotlightInnerAngle, + OuterAngle = light.spotlightOuterAngle, + Radius = propLight.Radius, + }); + } - 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 (propAnimLight != null) + { + var lightIndex = worldRep.LightingTable.LightCount; + propAnimLight.LightTableLightIndex = (ushort)lightIndex; + + var light = new Light + { + position = baseLight.position + propAnimLight.Offset, + color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propAnimLight.MaxBrightness), + innerRadius = propAnimLight.InnerRadius, + radius = propAnimLight.Radius, + r2 = propAnimLight.Radius * propAnimLight.Radius, + spotlight = baseLight.spotlight, + spotlightDir = baseLight.spotlightDir, + spotlightInnerAngle = baseLight.spotlightInnerAngle, + spotlightOuterAngle = baseLight.spotlightOuterAngle, + anim = true, + animLightTableIndex = propAnimLight.LightTableLightIndex, + }; + if (propAnimLight.Radius == 0) + { + light.radius = float.MaxValue; + light.r2 = float.MaxValue; } - if (propLight != null) + lights.Add(light); + worldRep.LightingTable.AddLight(new WorldRep.LightTable.LightData { - var light = new Light - { - position = baseLight.position + propLight.Offset, - color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness), - innerRadius = propLight.InnerRadius, - radius = propLight.Radius, - r2 = propLight.Radius * propLight.Radius, - spotlight = baseLight.spotlight, - spotlightDir = baseLight.spotlightDir, - spotlightInnerAngle = baseLight.spotlightInnerAngle, - spotlightOuterAngle = baseLight.spotlightOuterAngle, - }; - - if (propLight.Radius == 0) - { - light.radius = float.MaxValue; - light.r2 = float.MaxValue; - } - - lights.Add(light); - } - - if (propAnimLight != null) - { - var light = new Light - { - position = baseLight.position + propAnimLight.Offset, - color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propAnimLight.MaxBrightness), - innerRadius = propAnimLight.InnerRadius, - radius = propAnimLight.Radius, - r2 = propAnimLight.Radius * propAnimLight.Radius, - spotlight = baseLight.spotlight, - spotlightDir = baseLight.spotlightDir, - spotlightInnerAngle = baseLight.spotlightInnerAngle, - spotlightOuterAngle = baseLight.spotlightOuterAngle, - anim = true, - animLightTableIndex = propAnimLight.LightTableLightIndex, - }; - if (propAnimLight.Radius == 0) - { - light.radius = float.MaxValue; - light.r2 = float.MaxValue; - } - - lights.Add(light); - } + Location = light.position, + Direction = light.spotlightDir, + Color = light.color / 32.0f, // TODO: This is based on light_scale config var + InnerAngle = light.spotlightInnerAngle, + OuterAngle = light.spotlightOuterAngle, + Radius = propAnimLight.Radius, + }); } } } @@ -314,6 +354,8 @@ class Program Parallel.ForEach(wr.Cells, cell => { + // TODO: Reset cell anim light count and palette + var numPolys = cell.PolyCount; var numRenderPolys = cell.RenderPolyCount; var numPortalPolys = cell.PortalPolyCount; @@ -335,6 +377,7 @@ class Program var info = cell.LightList[polyIdx]; var lightmap = cell.Lightmaps[polyIdx]; + info.AnimLightBitmask = 0; lightmap.Reset(ambientLight, hdr); // Get world position of lightmap (0, 0) (+0.5 so we cast from the center of a pixel)