Compare commits

...

8 Commits

Author SHA1 Message Date
Jarrod Doyle ccd1c6ef82
Remove PVS lighting stage 2025-02-23 10:21:06 +00:00
Jarrod Doyle db0a70607d
Reword timing stages 2025-02-23 10:18:04 +00:00
Jarrod Doyle dacc9ea5e1
Add total timing 2025-02-23 10:13:12 +00:00
Jarrod Doyle 00537fa3ca
Add AnimLightCutoff warning 2025-02-23 10:12:27 +00:00
Jarrod Doyle bb50b2aba4
Handle all light validation warnings in one place 2025-02-23 10:05:21 +00:00
Jarrod Doyle cfd1d7577c
Add zero brightness brush light warning 2025-02-23 09:41:07 +00:00
Jarrod Doyle 796b570347
Update 0 brightness light warning 2025-02-23 09:29:40 +00:00
Jarrod Doyle 21425d8cd3
Disable buffered log writes
Logs were getting cut off due to the buffering. Could likely be solved using SerilogSinksAsync, but this works for now.
2025-02-23 09:26:16 +00:00
2 changed files with 124 additions and 122 deletions

View File

@ -59,22 +59,22 @@ public class LightMapper
_campaign = pathManager.GetCampaign(campaignName);
_misPath = _campaign.GetResourcePath(ResourceType.Mission, missionName);
_mission = Timing.TimeStage("Parse DB", () => new DbFile(_misPath));
_hierarchy = Timing.TimeStage("Build Hierarchy", BuildHierarchy);
_mission = Timing.TimeStage("Load Mission File", () => new DbFile(_misPath));
_hierarchy = Timing.TimeStage("Build Object Hierarchy", BuildHierarchy);
_lights = [];
VerifyRequiredChunksExist();
var (noObjMesh, fullMesh) = Timing.TimeStage("Build Meshes", BuildMeshes);
var (noObjMesh, fullMesh) = Timing.TimeStage("Build Raytracing Meshes", BuildMeshes);
_triangleTypeMap = fullMesh.TriangleSurfaceMap;
_sceneNoObj = Timing.TimeStage("Build RT NoObj Scene", () =>
_sceneNoObj = Timing.TimeStage("Upload Raytracing Scenes", () =>
{
var rt = new Raytracer();
rt.AddMesh(new TriangleMesh(noObjMesh.Vertices, noObjMesh.Indices));
rt.CommitScene();
return rt;
});
_scene = Timing.TimeStage("Build RT Scene", () =>
_scene = Timing.TimeStage("Upload Raytracing Scenes", () =>
{
var rt = new Raytracer();
rt.AddMesh(new TriangleMesh(fullMesh.Vertices, fullMesh.Indices));
@ -123,8 +123,13 @@ public class LightMapper
FastPvs = pvs,
};
if (settings.AnimLightCutoff > 0)
{
Log.Warning("Non-zero anim_light_cutoff ({Cutoff}). AnimLight lightmap shadow radius may not match lightgem shadow radius.", settings.AnimLightCutoff);
}
Timing.TimeStage("Gather Lights", () => BuildLightList(settings));
Timing.TimeStage("Set Light Indices", () => SetCellLightIndices(settings));
Timing.TimeStage("Set Light Visibility", () => SetCellLightIndices(settings));
Timing.TimeStage("Trace Scene", () => TraceScene(settings));
Timing.TimeStage("Update AnimLight Cell Mapping", SetAnimLightCellMaps);
@ -144,7 +149,7 @@ public class LightMapper
var ext = Path.GetExtension(_misPath);
var dir = Path.GetDirectoryName(_misPath);
var savePath = Path.Join(dir, missionName + ext);
Timing.TimeStage("Save DB", () => _mission.Save(savePath));
Timing.TimeStage("Save Mission File", () => _mission.Save(savePath));
}
private bool VerifyRequiredChunksExist()
@ -240,22 +245,53 @@ public class LightMapper
switch (brush.media)
{
case BrList.Brush.Media.Light:
ProcessBrushLight(worldRep.LightingTable, brush, settings);
ProcessBrushLight(brush, settings);
break;
case BrList.Brush.Media.Object:
ProcessObjectLight(worldRep.LightingTable, brush, settings);
ProcessObjectLight(brush, settings);
break;
}
}
CheckLightConfigurations();
}
ValidateLightConfigurations();
private void CheckLightConfigurations()
{
var infinite = 0;
worldRep.LightingTable.Reset();
foreach (var light in _lights)
{
// TODO: Set brush light index
light.LightTableIndex = worldRep.LightingTable.LightCount;
if (light.Anim)
{
var propAnimLight = _hierarchy.GetProperty<PropAnimLight>(light.ObjId, "P$AnimLight", false);
propAnimLight!.LightTableLightIndex = (ushort)light.LightTableIndex;
}
worldRep.LightingTable.AddLight(light.ToLightData(32.0f));
}
}
// TODO: Validate in-world here? Set cell idx on lights maybe?
private void ValidateLightConfigurations()
{
var infinite = 0;
for (var i = _lights.Count - 1; i > 0; i--)
{
var light = _lights[i];
if (light.Brightness == 0)
{
if (light.ObjId != -1)
{
Log.Warning("Object {Id}: Zero brightness static light. Adjust brightness or remove un-used Light property.", light.ObjId);
}
else
{
Log.Warning("Brush at {Id}: Zero brightness static light. Adjust brightness or remove light.", light.Position);
}
_lights.RemoveAt(i);
}
if (light.Radius == float.MaxValue)
{
@ -290,19 +326,10 @@ public class LightMapper
}
}
// TODO: Check if this works (brush is a record type)
private void ProcessBrushLight(WorldRep.LightTable lightTable, BrList.Brush brush, Settings settings)
private void ProcessBrushLight(BrList.Brush brush, Settings settings)
{
// For some reason the light table index on brush lights is 1 indexed
brush.brushInfo = (uint)lightTable.LightCount + 1;
var sz = brush.size;
// Ignore 0 brightness lights
if (sz.X == 0)
{
return;
}
var brightness = Math.Min(sz.X, 255.0f);
var saturation = sz.Z * settings.Saturation;
var light = new Light
@ -312,16 +339,14 @@ public class LightMapper
Brightness = brightness,
Radius = float.MaxValue,
R2 = float.MaxValue,
LightTableIndex = lightTable.LightCount,
SpotlightInnerAngle = -1f,
ObjId = -1,
};
_lights.Add(light);
lightTable.AddLight(light.ToLightData(32.0f));
}
private void ProcessObjectLight(WorldRep.LightTable lightTable, BrList.Brush brush, Settings settings)
private void ProcessObjectLight(BrList.Brush brush, Settings settings)
{
// TODO: Handle PropSpotlightAndAmbient
var id = (int)brush.brushInfo;
@ -371,9 +396,6 @@ public class LightMapper
if (propAnimLight != null)
{
var lightIndex = lightTable.LightCount;
propAnimLight.LightTableLightIndex = (ushort)lightIndex;
var light = new Light
{
Position = propAnimLight.Offset,
@ -384,7 +406,6 @@ public class LightMapper
R2 = propAnimLight.Radius * propAnimLight.Radius,
QuadLit = propAnimLight.QuadLit,
ObjId = id,
LightTableIndex = propAnimLight.LightTableLightIndex,
Anim = true,
Dynamic = propAnimLight.Dynamic,
SpotlightInnerAngle = -1f,
@ -401,15 +422,9 @@ public class LightMapper
light.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
_lights.Add(light);
lightTable.AddLight(light.ToLightData(32.0f), propAnimLight.Dynamic);
}
if (propLight != null && propLight.Brightness == 0)
{
Log.Warning("Concrete object {Id} has Light property with 0 brightness. Adjust brightness or remove property.", id);
}
if (propLight != null && propLight.Brightness != 0)
if (propLight != null)
{
var light = new Light
{
@ -421,7 +436,6 @@ public class LightMapper
R2 = propLight.Radius * propLight.Radius,
QuadLit = propLight.QuadLit,
ObjId = id,
LightTableIndex = lightTable.LightCount,
SpotlightInnerAngle = -1f,
};
@ -440,16 +454,12 @@ public class LightMapper
SpotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotAmb.InnerAngle)),
SpotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotAmb.OuterAngle)),
ObjId = light.ObjId,
LightTableIndex = light.LightTableIndex,
};
light.LightTableIndex++; // Because we're inserting the spotlight part first
spot.FixRadius();
spot.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
_lights.Add(spot);
lightTable.AddLight(spot.ToLightData(32.0f));
}
else if (propSpotlight != null)
{
@ -462,19 +472,14 @@ public class LightMapper
light.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
_lights.Add(light);
lightTable.AddLight(light.ToLightData(32.0f));
}
}
private void SetCellLightIndices(Settings settings)
{
// TODO: Doors aren't blocking lights. Need to do some cell traversal to remove light indices :(
if (!_mission.TryGetChunk<WorldRep>("WREXT", out var worldRep))
return;
var lightVisibleCells = Timing.TimeStage("Light PVS", () =>
{
var cellCount = worldRep.Cells.Length;
var aabbs = new MathUtils.Aabb[worldRep.Cells.Length];
Parallel.For(0, cellCount, i => aabbs[i] = new MathUtils.Aabb(worldRep.Cells[i].Vertices));
@ -555,10 +560,6 @@ public class LightMapper
visibleCellMap[i] = visibleSet;
});
return visibleCellMap;
});
// TODO: Move this functionality to the LGS library
// We set up light indices in separately from lighting because the actual
// lighting phase takes a lot of shortcuts that we don't want
@ -601,7 +602,7 @@ public class LightMapper
continue;
}
if (!lightVisibleCells[j].Contains(i))
if (!visibleCellMap[j].Contains(i))
{
continue;
}

View File

@ -17,7 +17,7 @@ internal static class Program
Log.Logger = config
.WriteTo.Console(theme: AnsiConsoleTheme.Sixteen, outputTemplate: outputTemplate)
.WriteTo.File(logPath, outputTemplate: outputTemplate, buffered: true)
.WriteTo.File(logPath, outputTemplate: outputTemplate)
.CreateLogger();
}
@ -49,11 +49,12 @@ public class LightCommand
public void Run()
{
Timing.Reset();
Timing.TimeStage("Total", () =>
{
var lightMapper = new LightMapper(InstallPath, CampaignName, MissionName);
lightMapper.Light(FastPvs);
lightMapper.Save(OutputName);
});
Timing.LogAll();
}
}