@@ -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