Compare commits
3 Commits
c162a5028d
...
b263bdd77e
Author | SHA1 | Date |
---|---|---|
Jarrod Doyle | b263bdd77e | |
Jarrod Doyle | c8e13a7f7b | |
Jarrod Doyle | a9d2d2e193 |
|
@ -25,6 +25,19 @@ class Program
|
|||
public int objId;
|
||||
public int lightTableIndex;
|
||||
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)
|
||||
|
@ -95,10 +108,10 @@ class Program
|
|||
// We can't do this in parallel which is why it's being done afterwards rather than
|
||||
// as we go
|
||||
var map = new Dictionary<ushort, List<WorldRep.LightTable.AnimCellMap>>();
|
||||
for (var i = 0; i < worldRep.Cells.Length; i++)
|
||||
for (ushort i = 0; i < worldRep.Cells.Length; i++)
|
||||
{
|
||||
var cell = worldRep.Cells[i];
|
||||
for (var j = 0; j < cell.AnimLightCount; j++)
|
||||
for (ushort j = 0; j < cell.AnimLightCount; j++)
|
||||
{
|
||||
var animLightIdx = cell.AnimLights[j];
|
||||
if (!map.TryGetValue(animLightIdx, out var value))
|
||||
|
@ -108,8 +121,8 @@ class Program
|
|||
}
|
||||
value.Add(new WorldRep.LightTable.AnimCellMap
|
||||
{
|
||||
CellIndex = (ushort)i,
|
||||
LightIndex = (ushort)j,
|
||||
CellIndex = i,
|
||||
LightIndex = j,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -120,24 +133,16 @@ class Program
|
|||
}
|
||||
foreach (var (lightIdx, animCellMaps) in map)
|
||||
{
|
||||
// Get the appropriate property!!
|
||||
var light = lights.Find((l) => l.anim && l.lightTableIndex == lightIdx);
|
||||
foreach (var prop in animLightChunk.properties)
|
||||
{
|
||||
if (prop.objectId == light.objId)
|
||||
{
|
||||
prop.LightTableLightIndex = lightIdx;
|
||||
prop.LightTableMapIndex = (ushort)worldRep.LightingTable.AnimMapCount;
|
||||
prop.CellsReached = (ushort)animCellMaps.Count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We need to update the object property so it knows its mapping range
|
||||
// TODO: Handle nulls
|
||||
var light = lights.Find(l => l.anim && l.lightTableIndex == lightIdx);
|
||||
var prop = animLightChunk.properties.Find(p => p.objectId == light.objId);
|
||||
prop.LightTableLightIndex = lightIdx;
|
||||
prop.LightTableMapIndex = (ushort)worldRep.LightingTable.AnimMapCount;
|
||||
prop.CellsReached = (ushort)animCellMaps.Count;
|
||||
|
||||
foreach (var animCellMap in animCellMaps)
|
||||
{
|
||||
worldRep.LightingTable.AnimCellMaps.Add(animCellMap);
|
||||
worldRep.LightingTable.AnimMapCount++;
|
||||
}
|
||||
worldRep.LightingTable.AnimCellMaps.AddRange(animCellMaps);
|
||||
worldRep.LightingTable.AnimMapCount += animCellMaps.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,163 +163,168 @@ class Program
|
|||
}
|
||||
|
||||
worldRep.LightingTable.Reset();
|
||||
|
||||
|
||||
foreach (var brush in brList.Brushes)
|
||||
{
|
||||
if (brush.media == BrList.Brush.Media.Light)
|
||||
switch (brush.media)
|
||||
{
|
||||
// For some reason the light table index on brush lights is 1 indexed
|
||||
brush.brushInfo = (uint)worldRep.LightingTable.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 = 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,
|
||||
});
|
||||
}
|
||||
case BrList.Brush.Media.Light:
|
||||
ProcessBrushLight(lights, worldRep.LightingTable, brush);
|
||||
break;
|
||||
case BrList.Brush.Media.Object:
|
||||
ProcessObjectLight(
|
||||
lights,
|
||||
hierarchy,
|
||||
campaign,
|
||||
worldRep.LightingTable,
|
||||
brush);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
ObjectHierarchy objHierarchy;
|
||||
|
@ -544,12 +554,19 @@ class Program
|
|||
|
||||
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
|
||||
// lighting phase takes a lot of shortcuts that we don't want
|
||||
Parallel.ForEach(wr.Cells, cell =>
|
||||
{
|
||||
cell.LightIndexCount = 0;
|
||||
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
|
||||
// are actually visited. We're a lot more coarse and just say if a cell is
|
||||
|
@ -563,6 +580,7 @@ class Program
|
|||
{
|
||||
cell.LightIndexCount++;
|
||||
cell.LightIndices.Add((ushort)light.lightTableIndex);
|
||||
cell.LightIndices[0]++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue