Reset lighting table and lightmaps

This commit is contained in:
Jarrod Doyle 2024-10-05 14:52:09 +01:00
parent ea72c3af4a
commit ab738203d6
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
2 changed files with 183 additions and 116 deletions

View File

@ -274,16 +274,16 @@ public class WorldRep : IChunk
AddLight(layer, x, y, c.Z, c.Y, c.X); 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; 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[0][j] = 0;
{
Pixels[i][j] = 0;
}
} }
for (var y = 0; y < Height; y++) for (var y = 0; y < Height; y++)
@ -568,30 +568,31 @@ public class WorldRep : IChunk
public int LightCount; public int LightCount;
public int DynamicLightCount; public int DynamicLightCount;
public int AnimMapCount; public int AnimMapCount;
public LightData[] Lights; public List<LightData> Lights;
public LightData[] ScratchpadLights; public LightData[] ScratchpadLights;
public AnimCellMap[] AnimCellMaps; public List<AnimCellMap> AnimCellMaps;
// TODO: Support olddark // TODO: Support olddark
public LightTable(BinaryReader reader) public LightTable(BinaryReader reader)
{ {
LightCount = reader.ReadInt32(); LightCount = reader.ReadInt32();
DynamicLightCount = reader.ReadInt32(); DynamicLightCount = reader.ReadInt32();
Lights = new LightData[LightCount + DynamicLightCount]; var totalLightCount = LightCount + DynamicLightCount;
for (var i = 0; i < Lights.Length; i++) Lights = new List<LightData>(totalLightCount);
for (var i = 0; i < totalLightCount; i++)
{ {
Lights[i] = new LightData(reader); Lights.Add(new LightData(reader));
} }
ScratchpadLights = new LightData[32]; ScratchpadLights = new LightData[32];
for (var i = 0; i < ScratchpadLights.Length; i++) for (var i = 0; i < 32; i++)
{ {
ScratchpadLights[i] = new LightData(reader); ScratchpadLights[i] = new LightData(reader);
} }
AnimMapCount = reader.ReadInt32(); AnimMapCount = reader.ReadInt32();
AnimCellMaps = new AnimCellMap[AnimMapCount]; AnimCellMaps = new List<AnimCellMap>(AnimMapCount);
for (var i = 0; i < AnimCellMaps.Length; i++) 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); 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; } public ChunkHeader Header { get; set; }

View File

@ -57,8 +57,6 @@ class Program
var mis = Timing.TimeStage("Parse DB", () => new DbFile(misPath)); var mis = Timing.TimeStage("Parse DB", () => new DbFile(misPath));
var hierarchy = Timing.TimeStage("Build Hierarchy", () => BuildHierarchy(misPath, mis)); var hierarchy = Timing.TimeStage("Build Hierarchy", () => BuildHierarchy(misPath, mis));
var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign));
// Build embree mesh // Build embree mesh
if (!mis.Chunks.TryGetValue("WREXT", out var wrRaw)) if (!mis.Chunks.TryGetValue("WREXT", out var wrRaw))
return; return;
@ -75,6 +73,7 @@ class Program
if (!mis.Chunks.TryGetValue("RENDPARAMS", out var rendParamsRaw)) if (!mis.Chunks.TryGetValue("RENDPARAMS", out var rendParamsRaw))
return; return;
var ambient = ((RendParams)rendParamsRaw).ambientLight * 255; var ambient = ((RendParams)rendParamsRaw).ambientLight * 255;
var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign));
Timing.TimeStage("Light", () => CastSceneParallel(scene, worldRep, [.. lights], ambient)); Timing.TimeStage("Light", () => CastSceneParallel(scene, worldRep, [.. lights], ambient));
var dir = Path.GetDirectoryName(misPath); var dir = Path.GetDirectoryName(misPath);
@ -109,126 +108,167 @@ class Program
}; };
} }
// Get list of brush lights, and object lights (ignore anim lights for now) // Gather all the brush, object, and anim ligths. Resets the lighting table
private static List<Light> BuildLightList(DbFile mis, ObjectHierarchy hierarchy, ResourcePathManager.CampaignResources campaign) // TODO: Handle dynamic lights
private static List<Light> BuildLightList(
DbFile mis,
ObjectHierarchy hierarchy,
ResourcePathManager.CampaignResources campaign)
{ {
var lights = new List<Light>(); var lights = new List<Light>();
if (mis.Chunks.TryGetValue("BRLIST", out var brListRaw)) // Get the chunks we need
if (!mis.TryGetChunk<WorldRep>("WREXT", out var worldRep) ||
!mis.TryGetChunk<BrList>("BRLIST", out var brList))
{ {
var brList = (BrList)brListRaw; return lights;
foreach (var brush in brList.Brushes) }
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; position = brush.position,
lights.Add(new Light 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<PropAnimLight>(id, "P$AnimLight", false);
var propLight = hierarchy.GetProperty<PropLight>(id, "P$Light", false);
var propLightColor = hierarchy.GetProperty<PropLightColor>(id, "P$LightColo");
var propSpotlight = hierarchy.GetProperty<PropSpotlight>(id, "P$Spotlight");
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,
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, // TODO: Handle failing to find model more gracefully
color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)), var model = new ModelFile(modelPath);
radius = float.MaxValue, if (model.TryGetVhot(ModelFile.VhotId.LightPosition, out var vhot))
r2 = float.MaxValue, {
}); 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 rot = Matrix4x4.Identity;
var id = (int)brush.brushInfo; rot *= Matrix4x4.CreateRotationX(float.DegreesToRadians(brush.angle.X));
var propAnimLight = hierarchy.GetProperty<PropAnimLight>(id, "P$AnimLight", false); rot *= Matrix4x4.CreateRotationY(float.DegreesToRadians(brush.angle.Y));
var propLight = hierarchy.GetProperty<PropLight>(id, "P$Light", false); rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z));
var propLightColor = hierarchy.GetProperty<PropLightColor>(id, "P$LightColo");
var propSpotlight = hierarchy.GetProperty<PropSpotlight>(id, "P$Spotlight");
var propModelname = hierarchy.GetProperty<PropLabel>(id, "P$ModelName");
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, position = baseLight.position + propLight.Offset,
spotlightDir = -Vector3.UnitZ, 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"; light.radius = float.MaxValue;
var modelPath = campaign.GetResourcePath(ResourceType.Object, resName); light.r2 = float.MaxValue;
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) lights.Add(light);
worldRep.LightingTable.AddLight(new WorldRep.LightTable.LightData
{ {
var rot = Matrix4x4.Identity; Location = light.position,
rot *= Matrix4x4.CreateRotationX(float.DegreesToRadians(brush.angle.X)); Direction = light.spotlightDir,
rot *= Matrix4x4.CreateRotationY(float.DegreesToRadians(brush.angle.Y)); Color = light.color / 32.0f, // TODO: This is based on light_scale config var
rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z)); InnerAngle = light.spotlightInnerAngle,
OuterAngle = light.spotlightOuterAngle,
Radius = propLight.Radius,
});
}
baseLight.spotlight = true; if (propAnimLight != null)
baseLight.spotlightDir = Vector3.Transform(baseLight.spotlightDir, rot); {
baseLight.spotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.InnerAngle)); var lightIndex = worldRep.LightingTable.LightCount;
baseLight.spotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.OuterAngle)); 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 Location = light.position,
{ Direction = light.spotlightDir,
position = baseLight.position + propLight.Offset, Color = light.color / 32.0f, // TODO: This is based on light_scale config var
color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness), InnerAngle = light.spotlightInnerAngle,
innerRadius = propLight.InnerRadius, OuterAngle = light.spotlightOuterAngle,
radius = propLight.Radius, Radius = propAnimLight.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);
}
} }
} }
} }
@ -314,6 +354,8 @@ class Program
Parallel.ForEach(wr.Cells, cell => Parallel.ForEach(wr.Cells, cell =>
{ {
// TODO: Reset cell anim light count and palette
var numPolys = cell.PolyCount; var numPolys = cell.PolyCount;
var numRenderPolys = cell.RenderPolyCount; var numRenderPolys = cell.RenderPolyCount;
var numPortalPolys = cell.PortalPolyCount; var numPortalPolys = cell.PortalPolyCount;
@ -335,6 +377,7 @@ class Program
var info = cell.LightList[polyIdx]; var info = cell.LightList[polyIdx];
var lightmap = cell.Lightmaps[polyIdx]; var lightmap = cell.Lightmaps[polyIdx];
info.AnimLightBitmask = 0;
lightmap.Reset(ambientLight, hdr); lightmap.Reset(ambientLight, hdr);
// Get world position of lightmap (0, 0) (+0.5 so we cast from the center of a pixel) // Get world position of lightmap (0, 0) (+0.5 so we cast from the center of a pixel)