From d7fcf7a2d839e5e78682fd2f7c4ed93cc7fd2849 Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Thu, 26 Sep 2024 20:22:01 +0100 Subject: [PATCH] Add model vhots and model light offset/dir --- KeepersCompound.LGS/ModelFile.cs | 45 ++++++++++++++++++++++++++ KeepersCompound.Lightmapper/Program.cs | 44 +++++++++++++++++++++---- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/KeepersCompound.LGS/ModelFile.cs b/KeepersCompound.LGS/ModelFile.cs index 64bb42a..8373233 100644 --- a/KeepersCompound.LGS/ModelFile.cs +++ b/KeepersCompound.LGS/ModelFile.cs @@ -153,6 +153,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 +184,7 @@ public class ModelFile public Vector3[] Normals { get; } public Polygon[] Polygons { get; } public Material[] Materials { get; } + public VHot[] VHots { get; } public ModelFile(string filename) { @@ -202,5 +227,25 @@ 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); + } + } + + 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; } } \ No newline at end of file diff --git a/KeepersCompound.Lightmapper/Program.cs b/KeepersCompound.Lightmapper/Program.cs index 8831630..37bbb10 100644 --- a/KeepersCompound.Lightmapper/Program.cs +++ b/KeepersCompound.Lightmapper/Program.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Numerics; +using KeepersCompound.LGS; using KeepersCompound.LGS.Database; using KeepersCompound.LGS.Database.Chunks; using TinyEmbree; @@ -27,20 +28,34 @@ class Program { Timing.Reset(); - var misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/JAYRUDE_Tests/lm_test.cow"; + // TODO: Read this from args + var installPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/"; + var campaignName = "JAYRUDE_Tests"; + var missionName = "lm_test.cow"; + + // Setup extract path + var tmpDir = Directory.CreateTempSubdirectory("KCLightmapper"); + Console.WriteLine(tmpDir.FullName); + var resPathManager = new ResourcePathManager(tmpDir.FullName); + resPathManager.Init(installPath); + + var campaign = resPathManager.GetCampaign(campaignName); + var misPath = campaign.GetResourcePath(ResourceType.Mission, missionName); + + // misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/JAYRUDE_Tests/lm_test.cow"; // misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/AtdV/miss20.mis"; // misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/TDP20AC_a_burrick_in_a_room/miss20.mis"; - Timing.TimeStage("Total", () => LightmapMission(misPath)); + Timing.TimeStage("Total", () => LightmapMission(campaign, misPath)); Timing.LogAll(); } - private static void LightmapMission(string misPath) + private static void LightmapMission(ResourcePathManager.CampaignResources campaign, string misPath) { 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)); + var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign)); // Build embree mesh if (!mis.Chunks.TryGetValue("WREXT", out var wrRaw)) @@ -93,7 +108,7 @@ class Program } // Get list of brush lights, and object lights (ignore anim lights for now) - private static List BuildLightList(DbFile mis, ObjectHierarchy hierarchy) + private static List BuildLightList(DbFile mis, ObjectHierarchy hierarchy, ResourcePathManager.CampaignResources campaign) { var lights = new List(); @@ -120,6 +135,7 @@ class Program var propLight = hierarchy.GetProperty(id, "P$Light"); var propLightColor = hierarchy.GetProperty(id, "P$LightColo"); var propSpotlight = hierarchy.GetProperty(id, "P$Spotlight"); + var propModelname = hierarchy.GetProperty(id, "P$ModelName"); if (propLight != null) { @@ -134,8 +150,24 @@ class Program innerRadius = propLight.InnerRadius, radius = propLight.Radius, r2 = propLight.Radius * propLight.Radius, + spotlightDir = -Vector3.UnitZ, }; + if (propModelname != null) + { + var resName = $"{propModelname.value.ToLower()}.bin"; + var modelPath = campaign.GetResourcePath(ResourceType.Object, resName); + var model = new ModelFile(modelPath); + if (model.TryGetVhot(ModelFile.VhotId.LightPosition, out var vhot)) + { + light.position += vhot.Position; + } + if (model.TryGetVhot(ModelFile.VhotId.LightDirection, out vhot)) + { + light.spotlightDir = vhot.Position; + } + } + if (propSpotlight != null) { // TODO: Some objects seem to have spotlight direction embedded in the model file @@ -145,7 +177,7 @@ class Program rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z)); light.spotlight = true; - light.spotlightDir = Vector3.Transform(-Vector3.UnitZ, rot); + light.spotlightDir = Vector3.Transform(light.spotlightDir, rot); light.spotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.InnerAngle)); light.spotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.OuterAngle)); }