Compare commits
No commits in common. "ccd1c6ef82ca13046972080a1d49618c3a5056f7" and "1c2210dbabfde3bd14fde556ec8140cfe6a7806b" have entirely different histories.
ccd1c6ef82
...
1c2210dbab
|
@ -59,22 +59,22 @@ public class LightMapper
|
||||||
|
|
||||||
_campaign = pathManager.GetCampaign(campaignName);
|
_campaign = pathManager.GetCampaign(campaignName);
|
||||||
_misPath = _campaign.GetResourcePath(ResourceType.Mission, missionName);
|
_misPath = _campaign.GetResourcePath(ResourceType.Mission, missionName);
|
||||||
_mission = Timing.TimeStage("Load Mission File", () => new DbFile(_misPath));
|
_mission = Timing.TimeStage("Parse DB", () => new DbFile(_misPath));
|
||||||
_hierarchy = Timing.TimeStage("Build Object Hierarchy", BuildHierarchy);
|
_hierarchy = Timing.TimeStage("Build Hierarchy", BuildHierarchy);
|
||||||
_lights = [];
|
_lights = [];
|
||||||
|
|
||||||
VerifyRequiredChunksExist();
|
VerifyRequiredChunksExist();
|
||||||
|
|
||||||
var (noObjMesh, fullMesh) = Timing.TimeStage("Build Raytracing Meshes", BuildMeshes);
|
var (noObjMesh, fullMesh) = Timing.TimeStage("Build Meshes", BuildMeshes);
|
||||||
_triangleTypeMap = fullMesh.TriangleSurfaceMap;
|
_triangleTypeMap = fullMesh.TriangleSurfaceMap;
|
||||||
_sceneNoObj = Timing.TimeStage("Upload Raytracing Scenes", () =>
|
_sceneNoObj = Timing.TimeStage("Build RT NoObj Scene", () =>
|
||||||
{
|
{
|
||||||
var rt = new Raytracer();
|
var rt = new Raytracer();
|
||||||
rt.AddMesh(new TriangleMesh(noObjMesh.Vertices, noObjMesh.Indices));
|
rt.AddMesh(new TriangleMesh(noObjMesh.Vertices, noObjMesh.Indices));
|
||||||
rt.CommitScene();
|
rt.CommitScene();
|
||||||
return rt;
|
return rt;
|
||||||
});
|
});
|
||||||
_scene = Timing.TimeStage("Upload Raytracing Scenes", () =>
|
_scene = Timing.TimeStage("Build RT Scene", () =>
|
||||||
{
|
{
|
||||||
var rt = new Raytracer();
|
var rt = new Raytracer();
|
||||||
rt.AddMesh(new TriangleMesh(fullMesh.Vertices, fullMesh.Indices));
|
rt.AddMesh(new TriangleMesh(fullMesh.Vertices, fullMesh.Indices));
|
||||||
|
@ -122,14 +122,9 @@ public class LightMapper
|
||||||
AnimLightCutoff = lmParams.AnimLightCutoff,
|
AnimLightCutoff = lmParams.AnimLightCutoff,
|
||||||
FastPvs = pvs,
|
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("Gather Lights", () => BuildLightList(settings));
|
||||||
Timing.TimeStage("Set Light Visibility", () => SetCellLightIndices(settings));
|
Timing.TimeStage("Set Light Indices", () => SetCellLightIndices(settings));
|
||||||
Timing.TimeStage("Trace Scene", () => TraceScene(settings));
|
Timing.TimeStage("Trace Scene", () => TraceScene(settings));
|
||||||
Timing.TimeStage("Update AnimLight Cell Mapping", SetAnimLightCellMaps);
|
Timing.TimeStage("Update AnimLight Cell Mapping", SetAnimLightCellMaps);
|
||||||
|
|
||||||
|
@ -149,7 +144,7 @@ public class LightMapper
|
||||||
var ext = Path.GetExtension(_misPath);
|
var ext = Path.GetExtension(_misPath);
|
||||||
var dir = Path.GetDirectoryName(_misPath);
|
var dir = Path.GetDirectoryName(_misPath);
|
||||||
var savePath = Path.Join(dir, missionName + ext);
|
var savePath = Path.Join(dir, missionName + ext);
|
||||||
Timing.TimeStage("Save Mission File", () => _mission.Save(savePath));
|
Timing.TimeStage("Save DB", () => _mission.Save(savePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool VerifyRequiredChunksExist()
|
private bool VerifyRequiredChunksExist()
|
||||||
|
@ -245,53 +240,22 @@ public class LightMapper
|
||||||
switch (brush.media)
|
switch (brush.media)
|
||||||
{
|
{
|
||||||
case BrList.Brush.Media.Light:
|
case BrList.Brush.Media.Light:
|
||||||
ProcessBrushLight(brush, settings);
|
ProcessBrushLight(worldRep.LightingTable, brush, settings);
|
||||||
break;
|
break;
|
||||||
case BrList.Brush.Media.Object:
|
case BrList.Brush.Media.Object:
|
||||||
ProcessObjectLight(brush, settings);
|
ProcessObjectLight(worldRep.LightingTable, brush, settings);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidateLightConfigurations();
|
CheckLightConfigurations();
|
||||||
|
|
||||||
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 CheckLightConfigurations()
|
||||||
private void ValidateLightConfigurations()
|
|
||||||
{
|
{
|
||||||
var infinite = 0;
|
var infinite = 0;
|
||||||
for (var i = _lights.Count - 1; i > 0; i--)
|
foreach (var light in _lights)
|
||||||
{
|
{
|
||||||
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)
|
if (light.Radius == float.MaxValue)
|
||||||
{
|
{
|
||||||
|
@ -326,10 +290,19 @@ public class LightMapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessBrushLight(BrList.Brush brush, Settings settings)
|
// TODO: Check if this works (brush is a record type)
|
||||||
|
private void ProcessBrushLight(WorldRep.LightTable lightTable, 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;
|
var sz = brush.size;
|
||||||
|
|
||||||
|
// Ignore 0 brightness lights
|
||||||
|
if (sz.X == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var brightness = Math.Min(sz.X, 255.0f);
|
var brightness = Math.Min(sz.X, 255.0f);
|
||||||
var saturation = sz.Z * settings.Saturation;
|
var saturation = sz.Z * settings.Saturation;
|
||||||
var light = new Light
|
var light = new Light
|
||||||
|
@ -339,14 +312,16 @@ public class LightMapper
|
||||||
Brightness = brightness,
|
Brightness = brightness,
|
||||||
Radius = float.MaxValue,
|
Radius = float.MaxValue,
|
||||||
R2 = float.MaxValue,
|
R2 = float.MaxValue,
|
||||||
|
LightTableIndex = lightTable.LightCount,
|
||||||
SpotlightInnerAngle = -1f,
|
SpotlightInnerAngle = -1f,
|
||||||
ObjId = -1,
|
ObjId = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
_lights.Add(light);
|
_lights.Add(light);
|
||||||
|
lightTable.AddLight(light.ToLightData(32.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessObjectLight(BrList.Brush brush, Settings settings)
|
private void ProcessObjectLight(WorldRep.LightTable lightTable, BrList.Brush brush, Settings settings)
|
||||||
{
|
{
|
||||||
// TODO: Handle PropSpotlightAndAmbient
|
// TODO: Handle PropSpotlightAndAmbient
|
||||||
var id = (int)brush.brushInfo;
|
var id = (int)brush.brushInfo;
|
||||||
|
@ -396,6 +371,9 @@ public class LightMapper
|
||||||
|
|
||||||
if (propAnimLight != null)
|
if (propAnimLight != null)
|
||||||
{
|
{
|
||||||
|
var lightIndex = lightTable.LightCount;
|
||||||
|
propAnimLight.LightTableLightIndex = (ushort)lightIndex;
|
||||||
|
|
||||||
var light = new Light
|
var light = new Light
|
||||||
{
|
{
|
||||||
Position = propAnimLight.Offset,
|
Position = propAnimLight.Offset,
|
||||||
|
@ -406,6 +384,7 @@ public class LightMapper
|
||||||
R2 = propAnimLight.Radius * propAnimLight.Radius,
|
R2 = propAnimLight.Radius * propAnimLight.Radius,
|
||||||
QuadLit = propAnimLight.QuadLit,
|
QuadLit = propAnimLight.QuadLit,
|
||||||
ObjId = id,
|
ObjId = id,
|
||||||
|
LightTableIndex = propAnimLight.LightTableLightIndex,
|
||||||
Anim = true,
|
Anim = true,
|
||||||
Dynamic = propAnimLight.Dynamic,
|
Dynamic = propAnimLight.Dynamic,
|
||||||
SpotlightInnerAngle = -1f,
|
SpotlightInnerAngle = -1f,
|
||||||
|
@ -422,9 +401,15 @@ public class LightMapper
|
||||||
light.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
|
light.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
|
||||||
|
|
||||||
_lights.Add(light);
|
_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)
|
if (propLight != null && propLight.Brightness != 0)
|
||||||
{
|
{
|
||||||
var light = new Light
|
var light = new Light
|
||||||
{
|
{
|
||||||
|
@ -436,6 +421,7 @@ public class LightMapper
|
||||||
R2 = propLight.Radius * propLight.Radius,
|
R2 = propLight.Radius * propLight.Radius,
|
||||||
QuadLit = propLight.QuadLit,
|
QuadLit = propLight.QuadLit,
|
||||||
ObjId = id,
|
ObjId = id,
|
||||||
|
LightTableIndex = lightTable.LightCount,
|
||||||
SpotlightInnerAngle = -1f,
|
SpotlightInnerAngle = -1f,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -454,12 +440,16 @@ public class LightMapper
|
||||||
SpotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotAmb.InnerAngle)),
|
SpotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotAmb.InnerAngle)),
|
||||||
SpotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotAmb.OuterAngle)),
|
SpotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotAmb.OuterAngle)),
|
||||||
ObjId = light.ObjId,
|
ObjId = light.ObjId,
|
||||||
|
LightTableIndex = light.LightTableIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
light.LightTableIndex++; // Because we're inserting the spotlight part first
|
||||||
|
|
||||||
spot.FixRadius();
|
spot.FixRadius();
|
||||||
spot.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
|
spot.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
|
||||||
|
|
||||||
_lights.Add(spot);
|
_lights.Add(spot);
|
||||||
|
lightTable.AddLight(spot.ToLightData(32.0f));
|
||||||
}
|
}
|
||||||
else if (propSpotlight != null)
|
else if (propSpotlight != null)
|
||||||
{
|
{
|
||||||
|
@ -472,93 +462,102 @@ public class LightMapper
|
||||||
light.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
|
light.ApplyTransforms(vhotLightPos, vhotLightDir, translate, rotate, scale);
|
||||||
|
|
||||||
_lights.Add(light);
|
_lights.Add(light);
|
||||||
|
lightTable.AddLight(light.ToLightData(32.0f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetCellLightIndices(Settings settings)
|
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))
|
if (!_mission.TryGetChunk<WorldRep>("WREXT", out var worldRep))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var cellCount = worldRep.Cells.Length;
|
var lightVisibleCells = Timing.TimeStage("Light PVS", () =>
|
||||||
var aabbs = new MathUtils.Aabb[worldRep.Cells.Length];
|
|
||||||
Parallel.For(0, cellCount, i => aabbs[i] = new MathUtils.Aabb(worldRep.Cells[i].Vertices));
|
|
||||||
|
|
||||||
var lightCellMap = new int[_lights.Count];
|
|
||||||
Parallel.For(0, _lights.Count, i =>
|
|
||||||
{
|
{
|
||||||
lightCellMap[i] = -1;
|
var cellCount = worldRep.Cells.Length;
|
||||||
var light = _lights[i];
|
var aabbs = new MathUtils.Aabb[worldRep.Cells.Length];
|
||||||
for (var j = 0; j < cellCount; j++)
|
Parallel.For(0, cellCount, i => aabbs[i] = new MathUtils.Aabb(worldRep.Cells[i].Vertices));
|
||||||
{
|
|
||||||
if (!MathUtils.Intersects(aabbs[j], light.Position))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Half-space contained
|
var lightCellMap = new int[_lights.Count];
|
||||||
var cell = worldRep.Cells[j];
|
Parallel.For(0, _lights.Count, i =>
|
||||||
var contained = true;
|
{
|
||||||
for (var k = 0; k < cell.PlaneCount; k++)
|
lightCellMap[i] = -1;
|
||||||
|
var light = _lights[i];
|
||||||
|
for (var j = 0; j < cellCount; j++)
|
||||||
{
|
{
|
||||||
var plane = cell.Planes[k];
|
if (!MathUtils.Intersects(aabbs[j], light.Position))
|
||||||
if (MathUtils.DistanceFromPlane(plane, light.Position) < -MathUtils.Epsilon)
|
|
||||||
{
|
{
|
||||||
contained = false;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Half-space contained
|
||||||
|
var cell = worldRep.Cells[j];
|
||||||
|
var contained = true;
|
||||||
|
for (var k = 0; k < cell.PlaneCount; k++)
|
||||||
|
{
|
||||||
|
var plane = cell.Planes[k];
|
||||||
|
if (MathUtils.DistanceFromPlane(plane, light.Position) < -MathUtils.Epsilon)
|
||||||
|
{
|
||||||
|
contained = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contained)
|
||||||
|
{
|
||||||
|
lightCellMap[i] = j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contained)
|
if (lightCellMap[i] == -1)
|
||||||
{
|
{
|
||||||
lightCellMap[i] = j;
|
if (light.ObjId != -1)
|
||||||
break;
|
{
|
||||||
|
Log.Warning("Object {Id}: Light is inside solid terrain.", light.ObjId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Warning("Brush at {Position}: Light is inside solid terrain.", light.Position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (lightCellMap[i] == -1)
|
|
||||||
{
|
|
||||||
if (light.ObjId != -1)
|
|
||||||
{
|
|
||||||
Log.Warning("Object {Id}: Light is inside solid terrain.", light.ObjId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.Warning("Brush at {Position}: Light is inside solid terrain.", light.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Log.Information("Mission has {c} lights", _lights.Count);
|
|
||||||
|
|
||||||
var pvs = new PotentiallyVisibleSet(worldRep.Cells);
|
|
||||||
var visibleCellMap = new HashSet<int>[_lights.Count];
|
|
||||||
|
|
||||||
// Exact visibility doesn't use MightSee (yet?) so we only bother computing it if we're doing fast vis
|
|
||||||
if (settings.FastPvs)
|
|
||||||
{
|
|
||||||
Parallel.ForEach(lightCellMap, i =>
|
|
||||||
{
|
|
||||||
if (i != -1) pvs.ComputeCellMightSee(i);
|
|
||||||
});
|
});
|
||||||
}
|
Log.Information("Mission has {c} lights", _lights.Count);
|
||||||
|
|
||||||
Parallel.For(0, _lights.Count, i =>
|
|
||||||
{
|
|
||||||
var cellIdx = lightCellMap[i];
|
|
||||||
if (cellIdx == -1)
|
|
||||||
{
|
|
||||||
visibleCellMap[i] = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var visibleSet = settings.FastPvs switch {
|
|
||||||
true => pvs.ComputeVisibilityFast(lightCellMap[i]),
|
|
||||||
false => pvs.ComputeVisibilityExact(_lights[i].Position, lightCellMap[i], _lights[i].Radius)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Log.Information("Light {i} sees {c} cells", i, visibleSet.Count);
|
var pvs = new PotentiallyVisibleSet(worldRep.Cells);
|
||||||
visibleCellMap[i] = visibleSet;
|
var visibleCellMap = new HashSet<int>[_lights.Count];
|
||||||
|
|
||||||
|
// Exact visibility doesn't use MightSee (yet?) so we only bother computing it if we're doing fast vis
|
||||||
|
if (settings.FastPvs)
|
||||||
|
{
|
||||||
|
Parallel.ForEach(lightCellMap, i =>
|
||||||
|
{
|
||||||
|
if (i != -1) pvs.ComputeCellMightSee(i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Parallel.For(0, _lights.Count, i =>
|
||||||
|
{
|
||||||
|
var cellIdx = lightCellMap[i];
|
||||||
|
if (cellIdx == -1)
|
||||||
|
{
|
||||||
|
visibleCellMap[i] = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var visibleSet = settings.FastPvs switch {
|
||||||
|
true => pvs.ComputeVisibilityFast(lightCellMap[i]),
|
||||||
|
false => pvs.ComputeVisibilityExact(_lights[i].Position, lightCellMap[i], _lights[i].Radius)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log.Information("Light {i} sees {c} cells", i, visibleSet.Count);
|
||||||
|
visibleCellMap[i] = visibleSet;
|
||||||
|
});
|
||||||
|
|
||||||
|
return visibleCellMap;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// TODO: Move this functionality to the LGS library
|
// TODO: Move this functionality to the LGS library
|
||||||
// We set up light indices in separately from lighting because the actual
|
// We set up light indices in separately from lighting because the actual
|
||||||
|
@ -602,7 +601,7 @@ public class LightMapper
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!visibleCellMap[j].Contains(i))
|
if (!lightVisibleCells[j].Contains(i))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ internal static class Program
|
||||||
|
|
||||||
Log.Logger = config
|
Log.Logger = config
|
||||||
.WriteTo.Console(theme: AnsiConsoleTheme.Sixteen, outputTemplate: outputTemplate)
|
.WriteTo.Console(theme: AnsiConsoleTheme.Sixteen, outputTemplate: outputTemplate)
|
||||||
.WriteTo.File(logPath, outputTemplate: outputTemplate)
|
.WriteTo.File(logPath, outputTemplate: outputTemplate, buffered: true)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,12 +49,11 @@ public class LightCommand
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
Timing.Reset();
|
Timing.Reset();
|
||||||
Timing.TimeStage("Total", () =>
|
|
||||||
{
|
var lightMapper = new LightMapper(InstallPath, CampaignName, MissionName);
|
||||||
var lightMapper = new LightMapper(InstallPath, CampaignName, MissionName);
|
lightMapper.Light(FastPvs);
|
||||||
lightMapper.Light(FastPvs);
|
lightMapper.Save(OutputName);
|
||||||
lightMapper.Save(OutputName);
|
|
||||||
});
|
|
||||||
Timing.LogAll();
|
Timing.LogAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue