Skip to content

Commit a42c834

Browse files
committed
Light handling changes
1 parent 3d4f187 commit a42c834

File tree

5 files changed

+129
-70
lines changed

5 files changed

+129
-70
lines changed

Source/ORTS.Common/ConsistGenerator.cs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,42 @@ public class ConsistGenerator
3939
/// </summary>
4040
public static bool GltfVisualTestRun;
4141

42-
const string ConsistTemplateStart = @"Train (
43-
TrainCfg ( ""trainname""
44-
Name ( ""trainname"" )
45-
MaxVelocity ( 50 1 )
42+
const string ConsistTemplateStart =
43+
@"Train (
44+
TrainCfg ( ""trainname""
45+
Name ( ""trainname"" )
46+
MaxVelocity ( 50 1 )
4647
";
4748
const string ConsistTemplateRecord = @"Engine ( UID ( xx ) EngineData ( ""engfile"" ""trainsetdir"" ) )" + "\r\n";
4849
const string ConsistTemplateEnd = ")\r\n)";
49-
const string EngineTemplate = @"Wagon ( ""enginname""
50+
const string EngineTemplate =
51+
@"Wagon ( ""enginname""
5052
Type ( Engine )
5153
WagonShape ( ""shapefilename"" )
5254
Size (100m 7m 100m )
55+
Lights ( 1
56+
Light (
57+
Type ( 1 )
58+
ShapeHierarchy ( Headlight )
59+
Conditions (
60+
Headlight ( 3 )
61+
Unit ( 2 )
62+
)
63+
Cycle ( 0 )
64+
FadeIn ( 1 )
65+
FadeOut ( 1 )
66+
States ( 1
67+
State (
68+
Duration ( 0.0 )
69+
LightColour ( ffffffff )
70+
Position ( 0 0 0 )
71+
Transition ( 0 )
72+
Radius ( 400 )
73+
Angle ( 15 )
74+
)
75+
)
76+
)
77+
)
5378
)
5479
Engine ( ""enginname""
5580
Wagon ( ""enginname"" )

Source/RunActivity/Viewer3D/GltfShape.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,7 @@ public class GltfSubObject : SubObject
10711071

10721072
public GltfSubObject(KHR_lights_punctual light, int hierarchyIndex, int[] hierarchy, Gltf gltfFile, GltfShape shape, GltfDistanceLevel distanceLevel)
10731073
{
1074+
HierarchyIndex = hierarchyIndex;
10741075
ShapePrimitives = new[] { new GltfPrimitive(light, gltfFile, distanceLevel, hierarchyIndex, hierarchy) };
10751076
}
10761077

@@ -1434,21 +1435,22 @@ public class GltfPrimitive : ShapePrimitive
14341435

14351436

14361437
public GltfPrimitive(KHR_lights_punctual light, Gltf gltfFile, GltfDistanceLevel distanceLevel, int hierarchyIndex, int[] hierarchy)
1437-
: this(new EmptyMaterial(distanceLevel.Viewer), Enumerable.Empty<VertexBufferBinding>().ToList(), gltfFile, distanceLevel, new GltfIndexBufferSet(), null, hierarchyIndex, hierarchy, Vector4.Zero, Vector4.Zero, 0, new int[0])
1438+
: this(new EmptyMaterial(distanceLevel.Viewer), Enumerable.Empty<VertexBufferBinding>().ToList(), gltfFile, distanceLevel, new GltfIndexBufferSet(), null, hierarchyIndex, hierarchy, Vector4.Zero, Vector4.Zero, 0, Array.Empty<int>())
14381439
{
1439-
Light = new ShapeLight
1440+
object extension = null;
1441+
Light = new StaticLight
14401442
{
14411443
Name = light.name,
14421444
Type = light.type,
14431445
Color = light.color != null && light.color.Length > 2 ? new Vector3(light.color[0], light.color[1], light.color[2]) : Vector3.Zero,
14441446
Intensity = light.intensity,
1445-
Range = light.range,
1447+
Range = light.range == 0 ? 2000 : light.range,
1448+
InnerConeCos = (float)Math.Cos(light.spot?.innerConeAngle ?? 0),
1449+
OuterConeCos = (float)Math.Cos(light.spot?.outerConeAngle ?? MathHelper.Pi),
1450+
ManagedName = (light.Extras?.TryGetValue("OPENRAILS_light_name", out extension) ?? false) && extension is string a ? a : null,
14461451
};
1447-
if (Light.Type == LightMode.Spot && light.spot != null)
1448-
{
1449-
Light.InnerConeCos = (float)Math.Cos(light.spot.innerConeAngle);
1450-
Light.OuterConeCos = (float)Math.Cos(light.spot.outerConeAngle);
1451-
}
1452+
if (Light.ManagedName != null)
1453+
distanceLevel.MatrixNames.Add(Light.ManagedName);
14521454
}
14531455

14541456
public GltfPrimitive(Material material, List<VertexBufferBinding> vertexAttributes, Gltf gltfFile, GltfDistanceLevel distanceLevel, GltfIndexBufferSet indexBufferSet, Skin skin, int hierarchyIndex, int[] hierarchy, Vector4 texCoords1, Vector4 texCoords2, int texturePacking, int[] morphConfig)
@@ -1636,6 +1638,8 @@ public class KHR_lights_punctual
16361638
public float range { get; set; }
16371639

16381640
public KHR_lights_punctual_spot spot { get; set; }
1641+
1642+
public Dictionary<string, object> Extras { get; set; }
16391643
}
16401644

16411645
public class KHR_lights_punctual_spot

Source/RunActivity/Viewer3D/Lights.cs

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,6 @@ public class LightViewer
8585
public float LightConeMinDotProduct;
8686
public Vector4 LightConeColor;
8787

88-
bool PreviousIsLightConeActive; // previous IsLightConeActive
89-
ElapsedTime FadeTimer = ElapsedTime.Zero;
90-
9188
public LightViewer(Viewer viewer, TrainCar car, TrainCarViewer carViewer)
9289
{
9390
Viewer = viewer;
@@ -130,16 +127,19 @@ public LightViewer(Viewer viewer, TrainCar car, TrainCarViewer carViewer)
130127
{
131128
if (light.ShapeHierarchy != null)
132129
{
133-
if ((CarViewer as MSTSWagonViewer).TrainCarShape.SharedShape.MatrixNames.Contains(light.ShapeHierarchy))
134-
{
135-
light.ShapeIndex = (CarViewer as MSTSWagonViewer).TrainCarShape.SharedShape.MatrixNames.IndexOf(light.ShapeHierarchy);
136-
}
137-
else
130+
if (TrySetLightManaged((CarViewer as MSTSWagonViewer).TrainCarShape.SharedShape, light.ShapeHierarchy) is var index && index < 0)
131+
index = (CarViewer as MSTSWagonViewer).TrainCarShape.SharedShape.MatrixNames.IndexOf(light.ShapeHierarchy);
132+
133+
if (index < 0)
138134
{
139135
Trace.TraceWarning("Light in car {0} has invalid shape index defined, shape name {1} does not exist",
140136
(Car as MSTSWagon).WagFilePath, light.ShapeHierarchy);
141137
light.ShapeIndex = 0;
142138
}
139+
else
140+
{
141+
light.ShapeIndex = index;
142+
}
143143
}
144144
else
145145
light.ShapeIndex = 0;
@@ -251,9 +251,6 @@ public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
251251
frame.AddPrimitive(LightConeMaterial, lightPrimitive, RenderPrimitiveGroup.Lights, ref xnaDTileTranslation);
252252
#endif
253253

254-
FadeTimer += elapsedTime;
255-
256-
// TODO: Theoretically the headlight handling could be moved to the TrainCar level. Now we can have multiple of these.
257254
if (HasLightCone && ActiveLightCone != null)
258255
{
259256
int coneIndex = ActiveLightCone.Light.ShapeIndex;
@@ -266,23 +263,18 @@ public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
266263
LightConeMinDotProduct = (float)Math.Cos(MathHelper.Lerp(ActiveLightCone.Angle1, ActiveLightCone.Angle2, ActiveLightCone.Fade.Y));
267264
LightConeColor = Vector4.Lerp(ActiveLightCone.Color1, ActiveLightCone.Color2, ActiveLightCone.Fade.Y);
268265

269-
if (PreviousIsLightConeActive != IsLightConeActive)
270-
{
271-
PreviousIsLightConeActive = IsLightConeActive;
272-
FadeTimer = ElapsedTime.Zero;
273-
}
274-
275-
// The original shaders followed the phylisophy of wanting 50% brightness at half the range. (LightConeDistance is only the half)
276-
// For the new calculation method the full range is needed.
277-
var range = LightConeDistance * 2;
278-
var color = new Vector3(LightConeColor.X, LightConeColor.Y, LightConeColor.Z);
279-
280-
var fadingIntensity = FadeTimer.ClockSeconds / (Math.Max(LightConeFadeIn, LightConeFadeOut) + float.Epsilon);
281-
if (!IsLightConeActive)
282-
fadingIntensity = 1 - fadingIntensity;
283-
284-
if (fadingIntensity > 0)
285-
frame.AddLight(LightMode.Spot, LightConePosition, LightConeDirection, color, RenderFrame.HeadLightIntensity, range, 1, LightConeMinDotProduct, fadingIntensity, false);
266+
if (ActiveLightCone.Fade.X > 0)
267+
frame.AddLight(LightMode.Spot,
268+
LightConePosition,
269+
LightConeDirection,
270+
new Vector3(LightConeColor.X, LightConeColor.Y, LightConeColor.Z),
271+
RenderFrame.HeadLightIntensity,
272+
// The original shaders followed the phylisophy of wanting 50% brightness at half the range. (LightConeDistance is only the half)
273+
LightConeDistance * 2, // For the new calculation method the full range is needed.
274+
1,
275+
LightConeMinDotProduct,
276+
ActiveLightCone.Fade.X,
277+
false);
286278
}
287279
}
288280

@@ -446,6 +438,20 @@ bool UpdateState()
446438
}
447439
return false;
448440
}
441+
442+
static int TrySetLightManaged(SharedShape sharedShape, string lightName)
443+
{
444+
if (sharedShape.LodControls
445+
.SelectMany(l => l.DistanceLevels)
446+
.SelectMany(d => d.SubObjects)
447+
.SelectMany(s => s.ShapePrimitives)
448+
.FirstOrDefault(p => lightName.Equals(p.Light?.ManagedName, StringComparison.OrdinalIgnoreCase)) is var primitive && primitive != null)
449+
{
450+
primitive.Light.IsManaged = true;
451+
return primitive.HierarchyIndex;
452+
}
453+
return -1;
454+
}
449455
}
450456

451457
public abstract class LightPrimitive : RenderPrimitive
@@ -705,7 +711,7 @@ public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
705711
else if (FadeOut)
706712
{
707713
FadeTime += elapsedTime.ClockSeconds;
708-
Fade.X = 1 - FadeTime / Light.FadeIn;
714+
Fade.X = 1 - FadeTime / Light.FadeOut;
709715
if (Fade.X < 0)
710716
{
711717
FadeOut = false;
@@ -785,6 +791,20 @@ public override void Draw(GraphicsDevice graphicsDevice)
785791
}
786792
}
787793

794+
public class StaticLight
795+
{
796+
public string Name;
797+
public LightMode Type;
798+
public Vector3 Color;
799+
public float Intensity;
800+
public float Range;
801+
public float InnerConeCos;
802+
public float OuterConeCos;
803+
804+
public string ManagedName;
805+
public bool IsManaged;
806+
}
807+
788808
struct LightGlowVertex
789809
{
790810
public Vector3 PositionO;

Source/RunActivity/Viewer3D/RenderFrame.cs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ static internal VertexBuffer GetDummyVertexBuffer(GraphicsDevice graphicsDevice)
144144
}
145145
return DummyVertexBuffer;
146146
}
147+
148+
public StaticLight Light { get; protected set; }
147149
}
148150

149151
[DebuggerDisplay("{Material} {RenderPrimitive} {Flags}")]
@@ -391,7 +393,7 @@ public class RenderFrame
391393
const float SunIntensity = 1;
392394
const float MoonIntensity = SunIntensity / 380000;
393395
public const float HeadLightIntensity = 25000; // See some sample values: https://docs.unity3d.com/Packages/com.unity.cloud.gltfast@5.2/manual/LightUnits.html
394-
float LightClampTo = 1;
396+
float LightDayNightClampTo = 1;
395397
float LightDayNightMultiplier = 1;
396398

397399
// Local shadow map data.
@@ -566,17 +568,17 @@ public void PrepareFrame(Viewer viewer)
566568

567569
if (SolarDirection.Y <= -0.05)
568570
{
569-
LightClampTo = 1; // at nighttime max light
571+
LightDayNightClampTo = 1; // at nighttime max light
570572
LightDayNightMultiplier = 1;
571573
}
572574
else if (SolarDirection.Y >= 0.15)
573575
{
574-
LightClampTo = 0.5f; // at daytime min light
576+
LightDayNightClampTo = 0.5f; // at daytime min light
575577
LightDayNightMultiplier = 0.1f;
576578
}
577579
else
578580
{
579-
LightClampTo = 1 - 2.5f * (SolarDirection.Y + 0.05f); // in the meantime interpolate
581+
LightDayNightClampTo = 1 - 2.5f * (SolarDirection.Y + 0.05f); // in the meantime interpolate
580582
LightDayNightMultiplier = 1 - 4.5f * (SolarDirection.Y + 0.05f);
581583
}
582584
}
@@ -663,7 +665,12 @@ public void AddAutoPrimitive(Vector3 mstsLocation, float objectRadius, float obj
663665
if (float.IsPositiveInfinity(objectViewingDistance) || (Camera != null && Camera.InRange(mstsLocation, objectRadius, objectViewingDistance)))
664666
{
665667
if (Camera != null && Camera.InFov(mstsLocation, objectRadius))
668+
{
666669
AddPrimitive(material, primitive, group, ref xnaMatrix, flags, itemData);
670+
671+
if (primitive?.Light != null && !primitive.Light.IsManaged && primitive.Light.Range > 0 && Camera.InRange(mstsLocation, objectRadius, primitive.Light.Range))
672+
AddLight(primitive.Light, ref xnaMatrix);
673+
}
667674
}
668675

669676
if (Game.Settings.DynamicShadows && (RenderProcess.ShadowMapCount > 0) && ((flags & ShapeFlags.ShadowCaster) != 0))
@@ -1049,17 +1056,35 @@ void DrawSequencesDistantMountains(GraphicsDevice graphicsDevice, bool logging)
10491056
}
10501057
}
10511058

1052-
public void AddLight(ShapeLight light, ref Matrix worldMatrix, float lodBias, float fadingIntensity)
1059+
/// <summary>
1060+
/// Add unmanaged static lights
1061+
/// </summary>
1062+
public void AddLight(StaticLight light, ref Matrix worldMatrix)
10531063
{
10541064
// Do not allow directional light injection. That is reserved to the sun and the moon.
1055-
if (light != null && light.Type != LightMode.Directional)
1056-
AddLight(light.Type, worldMatrix.Translation, Vector3.TransformNormal(-Vector3.UnitZ, worldMatrix), light.Color, light.Intensity, light.Range, light.InnerConeCos, light.OuterConeCos, fadingIntensity, false);
1065+
if (light != null && !light.IsManaged && light.Type != LightMode.Directional)
1066+
AddLight(light.Type, worldMatrix.Translation, Vector3.TransformNormal(-Vector3.UnitZ, worldMatrix), light.Color, light.Intensity, light.Range, light.InnerConeCos, light.OuterConeCos, 1, false);
10571067
}
1068+
1069+
/// <summary>
1070+
/// Used for the Sun / Moon only, intensity is in lm/m2
1071+
/// </summary>
1072+
void AddLight(Vector3 direction, Vector3 color, float intensity, float fade) => AddLight(LightMode.Directional, Vector3.Zero, direction, color, intensity, -1, 0, 0, fade, true);
1073+
10581074
/// <summary>
1059-
/// Used for Sun / Moon only
1075+
/// Add an active light to the actually compiled frame.
10601076
/// </summary>
1061-
public void AddLight(Vector3 direction, Vector3 color, float intensity, float fadingIntensity) => AddLight(LightMode.Directional, Vector3.Zero, direction, color, intensity, -1, 0, 0, fadingIntensity, true);
1062-
public void AddLight(LightMode type, Vector3 position, Vector3 direction, Vector3 color, float intensity, float range, float innerConeCos, float outerConeCos, float fadingIntensity, bool ignoreDayNight)
1077+
/// <param name="type">Can be Point or Spot only. Use the Directional type only for the Sun and the Moon.</param>
1078+
/// <param name="position">The worldMatrix.Translation</param>
1079+
/// <param name="direction">The light direction</param>
1080+
/// <param name="color">The light color</param>
1081+
/// <param name="intensity">Luminous intensity in candela (lm/sr)</param>
1082+
/// <param name="range">Cutoff distance</param>
1083+
/// <param name="innerConeCos"></param>
1084+
/// <param name="outerConeCos"></param>
1085+
/// <param name="fade">Fading, 0 no light, 1 full light</param>
1086+
/// <param name="ignoreDayNight">At daytime the intensity is automatically reduced to match the sunlight. Disable this by this parameter.</param>
1087+
public void AddLight(LightMode type, Vector3 position, Vector3 direction, Vector3 color, float intensity, float range, float innerConeCos, float outerConeCos, float fade, bool ignoreDayNight)
10631088
{
10641089
if (NumLights >= RenderProcess.MAX_LIGHTS)
10651090
return;
@@ -1068,8 +1093,9 @@ public void AddLight(LightMode type, Vector3 position, Vector3 direction, Vector
10681093
LightPositions[NumLights] = position;
10691094
LightDirections[NumLights] = direction;
10701095
LightColors[NumLights] = color;
1071-
LightIntensities[NumLights] = intensity
1072-
* (ignoreDayNight ? MathHelper.Clamp(fadingIntensity, 0, 1) : MathHelper.Clamp(fadingIntensity * LightDayNightMultiplier * LightClampTo, 0, LightClampTo));
1096+
LightIntensities[NumLights] = intensity * (ignoreDayNight
1097+
? MathHelper.Clamp(fade, 0, 1)
1098+
: MathHelper.Clamp(fade * LightDayNightMultiplier * LightDayNightClampTo, 0, LightDayNightClampTo));
10731099
LightRanges[NumLights] = range * (ignoreDayNight ? 1 : LightDayNightMultiplier);
10741100
LightInnerConeCos[NumLights] = innerConeCos;
10751101
LightOuterConeCos[NumLights] = outerConeCos;

Source/RunActivity/Viewer3D/Shapes.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ public SharedShape Get(string path)
7979
}
8080
catch (Exception error)
8181
{
82-
if (Debugger.IsAttached) Debugger.Break();
8382
Trace.WriteLine(new FileLoadException(path, error));
8483
Shapes.Add(path, EmptyShape);
8584
}
@@ -1783,7 +1782,6 @@ public class ShapePrimitive : RenderPrimitive, IDisposable
17831782
public Material Material { get; protected set; }
17841783
public int[] Hierarchy { get; protected set; } // the hierarchy from the sub_object
17851784
public int HierarchyIndex { get; protected set; } // index into the hiearchy array which provides pose for this primitive
1786-
public ShapeLight Light { get; protected set; }
17871785

17881786
protected internal VertexBuffer VertexBuffer;
17891787
protected internal IndexBuffer IndexBuffer;
@@ -2660,7 +2658,6 @@ public void PrepareFrame(RenderFrame frame, WorldPosition location, Matrix[] ani
26602658

26612659
var interior = (flags & ShapeFlags.Interior) != 0;
26622660
frame.AddAutoPrimitive(mstsLocation, distanceDetail.ViewSphereRadius, distanceDetail.ViewingDistance * lodBias, shapePrimitive.Material, shapePrimitive, interior ? RenderPrimitiveGroup.Interior : RenderPrimitiveGroup.World, ref xnaMatrix, flags, bones);
2663-
frame.AddLight(shapePrimitive.Light, ref xnaMatrix, lodBias, 1);
26642661
}
26652662
}
26662663
}
@@ -2744,19 +2741,6 @@ public void Dispose()
27442741
}
27452742
}
27462743

2747-
public class ShapeLight
2748-
{
2749-
public string Name;
2750-
public LightMode Type;
2751-
public Vector3 Position;
2752-
public Vector3 Direction;
2753-
public Vector3 Color;
2754-
public float Intensity;
2755-
public float Range;
2756-
public float InnerConeCos;
2757-
public float OuterConeCos;
2758-
}
2759-
27602744
public class TrItemLabel
27612745
{
27622746
public readonly WorldPosition Location;

0 commit comments

Comments
 (0)