Compare commits

...

3 Commits

Author SHA1 Message Date
Jarrod Doyle 2420bbef4f
Handle spotlight property 2024-09-26 17:16:11 +01:00
Jarrod Doyle 51e3a3a9e7
Add prop spotlight parsing 2024-09-26 16:45:48 +01:00
Jarrod Doyle 22600a27e5
Add inner radius support 2024-09-26 16:03:50 +01:00
3 changed files with 61 additions and 2 deletions

View File

@ -144,6 +144,7 @@ public class DbFile
"P$OTxtRepr3" => new PropertyChunk<PropString>(), "P$OTxtRepr3" => new PropertyChunk<PropString>(),
"P$Light" => new PropertyChunk<PropLight>(), "P$Light" => new PropertyChunk<PropLight>(),
"P$LightColo" => new PropertyChunk<PropLightColor>(), "P$LightColo" => new PropertyChunk<PropLightColor>(),
"P$Spotlight" => new PropertyChunk<PropSpotlight>(),
"P$RenderAlp" => new PropertyChunk<PropFloat>(), "P$RenderAlp" => new PropertyChunk<PropFloat>(),
"LD$MetaProp" => new LinkDataMetaProp(), "LD$MetaProp" => new LinkDataMetaProp(),
_ when entryName.StartsWith("L$") => new LinkChunk(), _ when entryName.StartsWith("L$") => new LinkChunk(),

View File

@ -102,6 +102,7 @@ public class ObjectHierarchy
AddProp<PropFloat>("P$RenderAlp"); AddProp<PropFloat>("P$RenderAlp");
AddProp<PropLight>("P$Light"); AddProp<PropLight>("P$Light");
AddProp<PropLightColor>("P$LightColo"); AddProp<PropLightColor>("P$LightColo");
AddProp<PropSpotlight>("P$Spotlight");
} }
public T GetProperty<T>(int objectId, string propName) where T : Property public T GetProperty<T>(int objectId, string propName) where T : Property

View File

@ -13,8 +13,14 @@ class Program
{ {
public Vector3 position; public Vector3 position;
public Vector3 color; public Vector3 color;
public float innerRadius;
public float radius; public float radius;
public float r2; public float r2;
public bool spotlight;
public Vector3 spotlightDir;
public float spotlightInnerAngle;
public float spotlightOuterAngle;
} }
static void Main(string[] args) static void Main(string[] args)
@ -23,7 +29,7 @@ class Program
var misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/JAYRUDE_Tests/lm_test.cow"; var misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/JAYRUDE_Tests/lm_test.cow";
// misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/AtdV/miss20.mis"; // misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/AtdV/miss20.mis";
misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/TDP20AC_a_burrick_in_a_room/miss20.mis"; // misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/TDP20AC_a_burrick_in_a_room/miss20.mis";
Timing.TimeStage("Total", () => LightmapMission(misPath)); Timing.TimeStage("Total", () => LightmapMission(misPath));
Timing.LogAll(); Timing.LogAll();
@ -104,14 +110,16 @@ class Program
position = brush.position, position = brush.position,
color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)), color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
radius = float.MaxValue, radius = float.MaxValue,
r2 = float.MaxValue r2 = float.MaxValue,
}); });
} }
else if (brush.media == BrList.Brush.Media.Object) else if (brush.media == BrList.Brush.Media.Object)
{ {
// TODO: Handle PropSpotlightAndAmbient
var id = (int)brush.brushInfo; var id = (int)brush.brushInfo;
var propLight = hierarchy.GetProperty<PropLight>(id, "P$Light"); var propLight = hierarchy.GetProperty<PropLight>(id, "P$Light");
var propLightColor = hierarchy.GetProperty<PropLightColor>(id, "P$LightColo"); var propLightColor = hierarchy.GetProperty<PropLightColor>(id, "P$LightColo");
var propSpotlight = hierarchy.GetProperty<PropSpotlight>(id, "P$Spotlight");
if (propLight != null) if (propLight != null)
{ {
@ -123,10 +131,25 @@ class Program
{ {
position = brush.position + propLight.Offset, position = brush.position + propLight.Offset,
color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness), color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness),
innerRadius = propLight.InnerRadius,
radius = propLight.Radius, radius = propLight.Radius,
r2 = propLight.Radius * propLight.Radius, r2 = propLight.Radius * propLight.Radius,
}; };
if (propSpotlight != null)
{
// TODO: Some objects seem to have spotlight direction embedded in the model file
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));
light.spotlight = true;
light.spotlightDir = Vector3.Transform(-Vector3.UnitZ, rot);
light.spotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.InnerAngle));
light.spotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.OuterAngle));
}
if (propLight.Radius == 0) if (propLight.Radius == 0)
{ {
light.radius = float.MaxValue; light.radius = float.MaxValue;
@ -358,6 +381,40 @@ class Program
var slen = len / 4.0f; var slen = len / 4.0f;
var strength = (angle + 1.0f) / slen; var strength = (angle + 1.0f) / slen;
// Inner radius starts a linear falloff to 0 at the radius
if (light.innerRadius != 0 && len > light.innerRadius)
{
strength *= (light.radius - len) / (light.radius - light.innerRadius);
}
// This is basically the same as how inner radius works. It just applies
// a linear falloff to 0 between the inner angle and outer angle.
if (light.spotlight)
{
var spotAngle = Vector3.Dot(-Vector3.Normalize(dir), light.spotlightDir);
var inner = light.spotlightInnerAngle;
var outer = light.spotlightOuterAngle;
// In an improperly configured spotlight inner and outer angles might be the
// same. So to avoid division by zero (and some clamping) we explicitly handle
// some cases
float spotlightMultiplier;
if (spotAngle >= inner)
{
spotlightMultiplier = 1.0f;
}
else if (spotAngle <= outer)
{
spotlightMultiplier = 0.0f;
}
else
{
spotlightMultiplier = (spotAngle - outer) / (inner - outer);
}
strength *= spotlightMultiplier;
}
return strength; return strength;
} }