Compare commits

..

No commits in common. "b263bdd77e088220c0e2a8ca2fb08195e8595a9e" and "c162a5028d3ef0eb2a8cb27106d63032333473f5" have entirely different histories.

1 changed files with 170 additions and 188 deletions

View File

@ -25,19 +25,6 @@ class Program
public int objId; public int objId;
public int lightTableIndex; public int lightTableIndex;
public bool anim; public bool anim;
public WorldRep.LightTable.LightData ToLightData(float lightScale)
{
return new WorldRep.LightTable.LightData
{
Location = position,
Direction = spotlightDir,
Color = color / lightScale,
InnerAngle = spotlightInnerAngle,
OuterAngle = spotlightOuterAngle,
Radius = radius == float.MaxValue ? 0 : radius,
};
}
} }
static void Main(string[] args) static void Main(string[] args)
@ -108,10 +95,10 @@ class Program
// We can't do this in parallel which is why it's being done afterwards rather than // We can't do this in parallel which is why it's being done afterwards rather than
// as we go // as we go
var map = new Dictionary<ushort, List<WorldRep.LightTable.AnimCellMap>>(); var map = new Dictionary<ushort, List<WorldRep.LightTable.AnimCellMap>>();
for (ushort i = 0; i < worldRep.Cells.Length; i++) for (var i = 0; i < worldRep.Cells.Length; i++)
{ {
var cell = worldRep.Cells[i]; var cell = worldRep.Cells[i];
for (ushort j = 0; j < cell.AnimLightCount; j++) for (var j = 0; j < cell.AnimLightCount; j++)
{ {
var animLightIdx = cell.AnimLights[j]; var animLightIdx = cell.AnimLights[j];
if (!map.TryGetValue(animLightIdx, out var value)) if (!map.TryGetValue(animLightIdx, out var value))
@ -121,8 +108,8 @@ class Program
} }
value.Add(new WorldRep.LightTable.AnimCellMap value.Add(new WorldRep.LightTable.AnimCellMap
{ {
CellIndex = i, CellIndex = (ushort)i,
LightIndex = j, LightIndex = (ushort)j,
}); });
} }
} }
@ -133,16 +120,24 @@ class Program
} }
foreach (var (lightIdx, animCellMaps) in map) foreach (var (lightIdx, animCellMaps) in map)
{ {
// We need to update the object property so it knows its mapping range // Get the appropriate property!!
// TODO: Handle nulls var light = lights.Find((l) => l.anim && l.lightTableIndex == lightIdx);
var light = lights.Find(l => l.anim && l.lightTableIndex == lightIdx); foreach (var prop in animLightChunk.properties)
var prop = animLightChunk.properties.Find(p => p.objectId == light.objId); {
prop.LightTableLightIndex = lightIdx; if (prop.objectId == light.objId)
prop.LightTableMapIndex = (ushort)worldRep.LightingTable.AnimMapCount; {
prop.CellsReached = (ushort)animCellMaps.Count; prop.LightTableLightIndex = lightIdx;
prop.LightTableMapIndex = (ushort)worldRep.LightingTable.AnimMapCount;
prop.CellsReached = (ushort)animCellMaps.Count;
break;
}
}
worldRep.LightingTable.AnimCellMaps.AddRange(animCellMaps); foreach (var animCellMap in animCellMaps)
worldRep.LightingTable.AnimMapCount += animCellMaps.Count; {
worldRep.LightingTable.AnimCellMaps.Add(animCellMap);
worldRep.LightingTable.AnimMapCount++;
}
} }
} }
@ -163,168 +158,163 @@ class Program
} }
worldRep.LightingTable.Reset(); worldRep.LightingTable.Reset();
foreach (var brush in brList.Brushes) foreach (var brush in brList.Brushes)
{ {
switch (brush.media) if (brush.media == BrList.Brush.Media.Light)
{ {
case BrList.Brush.Media.Light: // For some reason the light table index on brush lights is 1 indexed
ProcessBrushLight(lights, worldRep.LightingTable, brush); brush.brushInfo = (uint)worldRep.LightingTable.LightCount + 1;
break;
case BrList.Brush.Media.Object: var sz = brush.size;
ProcessObjectLight( var light = new Light
lights, {
hierarchy, position = brush.position,
campaign, color = Utils.HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
worldRep.LightingTable, radius = float.MaxValue,
brush); r2 = float.MaxValue,
break; lightTableIndex = worldRep.LightingTable.LightCount,
};
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)
{
// 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)
{
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));
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 (propLight != null)
{
var light = new Light
{
position = baseLight.position + propLight.Offset,
color = Utils.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,
lightTableIndex = worldRep.LightingTable.LightCount,
};
if (propLight.Radius == 0)
{
light.radius = float.MaxValue;
light.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 = light.spotlightInnerAngle,
OuterAngle = light.spotlightOuterAngle,
Radius = propLight.Radius,
});
}
if (propAnimLight != null)
{
var lightIndex = worldRep.LightingTable.LightCount;
propAnimLight.LightTableLightIndex = (ushort)lightIndex;
var light = new Light
{
position = baseLight.position + propAnimLight.Offset,
color = Utils.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,
objId = id,
lightTableIndex = propAnimLight.LightTableLightIndex,
};
if (propAnimLight.Radius == 0)
{
light.radius = float.MaxValue;
light.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 = light.spotlightInnerAngle,
OuterAngle = light.spotlightOuterAngle,
Radius = propAnimLight.Radius,
});
}
} }
} }
return lights; return lights;
} }
// TODO: Check if this works (brush is a record type)
private static void ProcessBrushLight(List<Light> lights, WorldRep.LightTable lightTable, BrList.Brush brush)
{
// For some reason the light table index on brush lights is 1 indexed
brush.brushInfo = (uint)lightTable.LightCount + 1;
var sz = brush.size;
var light = new Light
{
position = brush.position,
color = Utils.HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
radius = float.MaxValue,
r2 = float.MaxValue,
lightTableIndex = lightTable.LightCount,
};
lights.Add(light);
lightTable.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,
});
}
private static void ProcessObjectLight(
List<Light> lights,
ObjectHierarchy hierarchy,
ResourcePathManager.CampaignResources campaign,
WorldRep.LightTable lightTable,
BrList.Brush brush)
{
// 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)
{
// 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)
{
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));
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 (propLight != null)
{
var light = new Light
{
position = baseLight.position + propLight.Offset,
color = Utils.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,
lightTableIndex = lightTable.LightCount,
};
if (propLight.Radius == 0)
{
light.radius = float.MaxValue;
light.r2 = float.MaxValue;
}
lights.Add(light);
lightTable.AddLight(light.ToLightData(32.0f));
}
if (propAnimLight != null)
{
var lightIndex = lightTable.LightCount;
propAnimLight.LightTableLightIndex = (ushort)lightIndex;
var light = new Light
{
position = baseLight.position + propAnimLight.Offset,
color = Utils.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,
objId = id,
lightTableIndex = propAnimLight.LightTableLightIndex,
};
if (propAnimLight.Radius == 0)
{
light.radius = float.MaxValue;
light.r2 = float.MaxValue;
}
lights.Add(light);
lightTable.AddLight(light.ToLightData(32.0f));
}
}
private static ObjectHierarchy BuildHierarchy(string misPath, DbFile misFile) private static ObjectHierarchy BuildHierarchy(string misPath, DbFile misFile)
{ {
ObjectHierarchy objHierarchy; ObjectHierarchy objHierarchy;
@ -554,19 +544,12 @@ class Program
private static void SetCellLightIndices(WorldRep wr, Light[] lights) private static void SetCellLightIndices(WorldRep wr, Light[] lights)
{ {
// 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
// lighting phase takes a lot of shortcuts that we don't want // lighting phase takes a lot of shortcuts that we don't want
Parallel.ForEach(wr.Cells, cell => Parallel.ForEach(wr.Cells, cell =>
{ {
cell.LightIndexCount = 0; cell.LightIndexCount = 0;
cell.LightIndices.Clear(); cell.LightIndices.Clear();
// The first element of the light indices array is used to store how many
// actual lights are in the list. Which is just LightIndexCount - 1...
// Odd choice I know
cell.LightIndexCount++;
cell.LightIndices.Add(0);
// The OG lightmapper uses the cell traversal to work out all the cells that // The OG lightmapper uses the cell traversal to work out all the cells that
// are actually visited. We're a lot more coarse and just say if a cell is // are actually visited. We're a lot more coarse and just say if a cell is
@ -580,7 +563,6 @@ class Program
{ {
cell.LightIndexCount++; cell.LightIndexCount++;
cell.LightIndices.Add((ushort)light.lightTableIndex); cell.LightIndices.Add((ushort)light.lightTableIndex);
cell.LightIndices[0]++;
} }
} }
}); });