Skip to content

Commit 24cbc87

Browse files
committed
Light handling changes
1 parent 8cc4bcd commit 24cbc87

File tree

5 files changed

+117
-97
lines changed

5 files changed

+117
-97
lines changed

Source/ORTS.Common/ConsistGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class ConsistGenerator
6767
State (
6868
Duration ( 0.0 )
6969
LightColour ( ffffffff )
70-
Position ( 0 0 0 )
70+
Position ( 0 3.5 0 )
7171
Transition ( 0 )
7272
Radius ( 400 )
7373
Angle ( 15 )

Source/RunActivity/Content/SceneryShader.fx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ float3 _PSGetLightVector(in int i, in float3 pointAbsolutePosition, inout float3
709709
}
710710
else
711711
{
712-
rangeAttenuation /= pow(pointLightDistance, 2); // The realistic range attenuation is inverse-squared.
712+
rangeAttenuation /= pow(pointLightDistance, 1.5); // The realistic range attenuation is inverse-squared.
713713
if (LightRanges[i] > 0)
714714
rangeAttenuation *= clamp(1 - pow(pointLightDistance / LightRanges[i], 4), 0, 1);
715715
}

Source/RunActivity/Viewer3D/GltfShape.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,19 +1438,19 @@ public GltfPrimitive(KHR_lights_punctual light, Gltf gltfFile, GltfDistanceLevel
14381438
: this(new EmptyMaterial(distanceLevel.Viewer), Enumerable.Empty<VertexBufferBinding>().ToList(), gltfFile, distanceLevel, new GltfIndexBufferSet(), null, hierarchyIndex, hierarchy, Vector4.Zero, Vector4.Zero, 0, Array.Empty<int>())
14391439
{
14401440
object extension = null;
1441-
Light = new StaticLight
1441+
AttachedLight = new StaticLight
14421442
{
14431443
Name = light.name,
14441444
Type = light.type,
14451445
Color = light.color != null && light.color.Length > 2 ? new Vector3(light.color[0], light.color[1], light.color[2]) : Vector3.Zero,
14461446
Intensity = light.intensity,
14471447
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),
1448+
InnerConeAngle = light.spot?.innerConeAngle ?? 0,
1449+
OuterConeAngle = light.spot?.outerConeAngle ?? MathHelper.PiOver4,
14501450
ManagedName = (light.Extras?.TryGetValue("OPENRAILS_light_name", out extension) ?? false) && extension is string a ? a : null,
14511451
};
1452-
if (Light.ManagedName != null)
1453-
distanceLevel.MatrixNames.Add(Light.ManagedName);
1452+
if (AttachedLight.ManagedName != null)
1453+
distanceLevel.MatrixNames.Add(AttachedLight.ManagedName);
14541454
}
14551455

14561456
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)
@@ -1856,6 +1856,7 @@ public void Animate(int animationNumber, float time, Matrix[] animatedMatrices)
18561856
{ "NormalTangentTest".ToLower(), Matrix.CreateScale(2) * Matrix.CreateTranslation(0, 2, 0) },
18571857
{ "OrientationTest".ToLower(), Matrix.CreateScale(0.2f) * Matrix.CreateTranslation(0, 2, 0) },
18581858
{ "PlaysetLightTest".ToLower(), Matrix.CreateScale(30) },
1859+
{ "PointLightIntensityTest".ToLower(), Matrix.CreateTranslation(0, 5, 0) },
18591860
{ "PotOfCoals".ToLower(), Matrix.CreateScale(30) },
18601861
{ "PrimitiveModeNormalsTest".ToLower(), Matrix.CreateScale(0.5f) * Matrix.CreateTranslation(0, 5, 0) },
18611862
{ "ReciprocatingSaw".ToLower(), Matrix.CreateScale(0.01f) * Matrix.CreateTranslation(0, 3, 0) },

Source/RunActivity/Viewer3D/Lights.cs

Lines changed: 73 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ public class LightViewer
8282
public Vector3 LightConePosition;
8383
public Vector3 LightConeDirection;
8484
public float LightConeDistance;
85-
public float LightConeMinDotProduct;
86-
public Vector4 LightConeColor;
85+
public float LightConeOuterAngle;
86+
public Vector3 LightConeColor;
8787

8888
public LightViewer(Viewer viewer, TrainCar car, TrainCarViewer carViewer)
8989
{
@@ -99,19 +99,7 @@ public LightViewer(Viewer viewer, TrainCar car, TrainCarViewer carViewer)
9999
{
100100
foreach (var light in Car.Lights.Lights)
101101
{
102-
switch (light.Type)
103-
{
104-
case LightType.Glow:
105-
LightPrimitives.Add(new LightGlowPrimitive(this, Viewer.RenderProcess, light));
106-
if (light.Graphic != null)
107-
(LightPrimitives.Last() as LightGlowPrimitive).SpecificGlowMaterial = viewer.MaterialManager.Load("LightGlow", DefineFullTexturePath(light.Graphic, true));
108-
else
109-
(LightPrimitives.Last() as LightGlowPrimitive).SpecificGlowMaterial = LightGlowMaterial;
110-
break;
111-
case LightType.Cone:
112-
LightPrimitives.Add(new LightConePrimitive(this, Viewer.RenderProcess, light));
113-
break;
114-
}
102+
StaticLight staticLight = null;
115103

116104
// Initialization step for light shape attachment, can't do this step in LightCollection
117105
if (light.ShapeIndex != -1)
@@ -127,26 +115,48 @@ public LightViewer(Viewer viewer, TrainCar car, TrainCarViewer carViewer)
127115
{
128116
if (light.ShapeHierarchy != null)
129117
{
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)
118+
if ((CarViewer as MSTSWagonViewer).TrainCarShape.SharedShape.LodControls
119+
.SelectMany(l => l.DistanceLevels)
120+
.SelectMany(d => d.SubObjects)
121+
.SelectMany(s => s.ShapePrimitives)
122+
.FirstOrDefault(p => light.ShapeHierarchy.Equals(p.AttachedLight?.ManagedName, StringComparison.OrdinalIgnoreCase)) is var primitive && primitive != null)
134123
{
135-
Trace.TraceWarning("Light in car {0} has invalid shape index defined, shape name {1} does not exist",
136-
(Car as MSTSWagon).WagFilePath, light.ShapeHierarchy);
137-
light.ShapeIndex = 0;
124+
light.ShapeIndex = primitive.HierarchyIndex;
125+
staticLight = primitive.AttachedLight;
126+
staticLight.IntensityX = 0; // Off by default if managed from here
138127
}
139-
else
128+
else if ((CarViewer as MSTSWagonViewer).TrainCarShape.SharedShape.MatrixNames.IndexOf(light.ShapeHierarchy) is var index && index >= 0)
140129
{
141130
light.ShapeIndex = index;
142131
}
132+
else
133+
{
134+
Trace.TraceWarning("Light in car {0} has invalid shape index defined, shape name {1} does not exist",
135+
(Car as MSTSWagon).WagFilePath, light.ShapeHierarchy);
136+
light.ShapeIndex = 0;
137+
}
143138
}
144139
else
145140
light.ShapeIndex = 0;
146141
}
147142

148143
if (!ShapeXNATranslations.ContainsKey(light.ShapeIndex))
149144
ShapeXNATranslations.Add(light.ShapeIndex, Matrix.Identity);
145+
146+
switch (light.Type)
147+
{
148+
case LightType.Glow:
149+
LightPrimitives.Add(new LightGlowPrimitive(this, Viewer.RenderProcess, light, staticLight));
150+
if (light.Graphic != null)
151+
(LightPrimitives.Last() as LightGlowPrimitive).SpecificGlowMaterial = viewer.MaterialManager.Load("LightGlow", DefineFullTexturePath(light.Graphic, true));
152+
else
153+
(LightPrimitives.Last() as LightGlowPrimitive).SpecificGlowMaterial = LightGlowMaterial;
154+
break;
155+
case LightType.Cone:
156+
LightPrimitives.Add(new LightConePrimitive(this, Viewer.RenderProcess, light, staticLight));
157+
break;
158+
}
159+
150160
}
151161
}
152162
HasLightCone = LightPrimitives.Any(lm => lm is LightConePrimitive);
@@ -199,6 +209,7 @@ void UpdateActiveLightCone()
199209
Console.WriteLine();
200210
}
201211
#endif
212+
// Keep the light cone active while fading out
202213
if (newLightCone != null || LightConeFadeOut == 0)
203214
ActiveLightCone = newLightCone;
204215
}
@@ -253,28 +264,27 @@ public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
253264

254265
if (HasLightCone && ActiveLightCone != null)
255266
{
256-
int coneIndex = ActiveLightCone.Light.ShapeIndex;
257-
258-
LightConePosition = Vector3.Transform(Vector3.Lerp(ActiveLightCone.Position1, ActiveLightCone.Position2, ActiveLightCone.Fade.Y), ShapeXNATranslations[coneIndex]);
259-
LightConeDirection = Vector3.Transform(Vector3.Lerp(ActiveLightCone.Direction1, ActiveLightCone.Direction2, ActiveLightCone.Fade.Y), ShapeXNATranslations[coneIndex]);
260-
LightConeDirection -= ShapeXNATranslations[coneIndex].Translation;
261-
LightConeDirection.Normalize();
262-
LightConeDistance = MathHelper.Lerp(ActiveLightCone.Distance1, ActiveLightCone.Distance2, ActiveLightCone.Fade.Y);
263-
LightConeMinDotProduct = (float)Math.Cos(MathHelper.Lerp(ActiveLightCone.Angle1, ActiveLightCone.Angle2, ActiveLightCone.Fade.Y));
264-
LightConeColor = Vector4.Lerp(ActiveLightCone.Color1, ActiveLightCone.Color2, ActiveLightCone.Fade.Y);
265-
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);
267+
if (ActiveLightCone.StaticLight == null)
268+
{
269+
int coneIndex = ActiveLightCone.Light.ShapeIndex;
270+
271+
LightConePosition = Vector3.Transform(Vector3.Lerp(ActiveLightCone.Position1, ActiveLightCone.Position2, ActiveLightCone.Fade.Y), ShapeXNATranslations[coneIndex]);
272+
LightConeDirection = Vector3.Transform(Vector3.Lerp(ActiveLightCone.Direction1, ActiveLightCone.Direction2, ActiveLightCone.Fade.Y), ShapeXNATranslations[coneIndex]);
273+
LightConeDirection -= ShapeXNATranslations[coneIndex].Translation;
274+
LightConeDirection.Normalize();
275+
// The original shaders followed the phylisophy of wanting 50% brightness at half the range. (LightConeDistance is only the half.) Now we need the full range.
276+
LightConeDistance = 2 * MathHelper.Lerp(ActiveLightCone.Distance1, ActiveLightCone.Distance2, ActiveLightCone.Fade.Y);
277+
LightConeOuterAngle = MathHelper.Lerp(ActiveLightCone.Angle1, ActiveLightCone.Angle2, ActiveLightCone.Fade.Y);
278+
var lightConeColor = Vector4.Lerp(ActiveLightCone.Color1, ActiveLightCone.Color2, ActiveLightCone.Fade.Y);
279+
LightConeColor = new Vector3(lightConeColor.X, lightConeColor.Y, lightConeColor.Z);
280+
281+
frame.AddLight(LightMode.Spot, LightConePosition, LightConeDirection, LightConeColor, RenderFrame.HeadLightIntensity, LightConeDistance, 0, LightConeOuterAngle, ActiveLightCone.Fade.X, false);
282+
}
283+
else
284+
{
285+
// Only set the properties, the light is added in frame.AddAutoPrimitive()
286+
ActiveLightCone.StaticLight.IntensityX = ActiveLightCone.Fade.X;
287+
}
278288
}
279289
}
280290

@@ -295,7 +305,7 @@ public static void CalculateLightCone(LightState lightState, out Vector3 positio
295305
position = lightState.Position;
296306
position.Z *= -1;
297307
direction = Vector3.Transform(Vector3.Transform(-Vector3.UnitZ, Matrix.CreateRotationX(MathHelper.ToRadians(-lightState.Elevation.Y))), Matrix.CreateRotationY(MathHelper.ToRadians(-lightState.Azimuth.Y)));
298-
angle = MathHelper.ToRadians(lightState.Angle) / 2;
308+
angle = MathHelper.ToRadians(lightState.Angle);
299309
radius = lightState.Radius / 2;
300310
distance = (float)(radius / Math.Sin(angle));
301311
color = lightState.Color.ToVector4();
@@ -438,25 +448,12 @@ bool UpdateState()
438448
}
439449
return false;
440450
}
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-
}
455451
}
456452

457453
public abstract class LightPrimitive : RenderPrimitive
458454
{
459455
public Light Light;
456+
public StaticLight StaticLight;
460457
public bool Enabled;
461458
public Vector2 Fade;
462459
public bool FadeIn;
@@ -467,9 +464,10 @@ public abstract class LightPrimitive : RenderPrimitive
467464
protected float StateTime;
468465

469466
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
470-
public LightPrimitive(Light light)
467+
public LightPrimitive(Light light, StaticLight staticLight)
471468
{
472469
Light = light;
470+
StaticLight = staticLight;
473471
StateCount = Math.Max(Light.Cycle ? 2 * Light.States.Count - 2 : Light.States.Count, 1);
474472
UpdateStates(State, (State + 1) % StateCount);
475473
}
@@ -732,8 +730,8 @@ public class LightGlowPrimitive : LightPrimitive
732730
static IndexBuffer IndexBuffer;
733731
public Material SpecificGlowMaterial;
734732

735-
public LightGlowPrimitive(LightViewer lightViewer, RenderProcess renderProcess, Light light)
736-
: base(light)
733+
public LightGlowPrimitive(LightViewer lightViewer, RenderProcess renderProcess, Light light, StaticLight staticLight)
734+
: base(light, staticLight)
737735
{
738736
Debug.Assert(light.Type == LightType.Glow, "LightGlowPrimitive is only for LightType.Glow lights.");
739737

@@ -798,11 +796,18 @@ public class StaticLight
798796
public Vector3 Color;
799797
public float Intensity;
800798
public float Range;
801-
public float InnerConeCos;
802-
public float OuterConeCos;
799+
public float InnerConeAngle;
800+
public float OuterConeAngle;
801+
802+
public Vector3 ColorX = Vector3.One;
803+
public float IntensityX = 1;
804+
public float RangeX = 1;
805+
public float InnerConeAngleX = 1;
806+
public float OuterConeAngleX = 1;
803807

804808
public string ManagedName;
805-
public bool IsManaged;
809+
810+
public Matrix WorldMatrix;
806811
}
807812

808813
struct LightGlowVertex
@@ -852,8 +857,8 @@ public class LightConePrimitive : LightPrimitive
852857
static IndexBuffer IndexBuffer;
853858
static BlendState BlendState_SourceZeroDestOne;
854859

855-
public LightConePrimitive(LightViewer lightViewer, RenderProcess renderProcess, Light light)
856-
: base(light)
860+
public LightConePrimitive(LightViewer lightViewer, RenderProcess renderProcess, Light light, StaticLight staticLight)
861+
: base(light, staticLight)
857862
{
858863
Debug.Assert(light.Type == LightType.Cone, "LightConePrimitive is only for LightType.Cone lights.");
859864

0 commit comments

Comments
 (0)