public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime) { // Adjust dome position so the bottom edge is not visible Vector3 ViewerXNAPosition = new Vector3(Viewer.Camera.Location.X, Viewer.Camera.Location.Y - 100, -Viewer.Camera.Location.Z); Matrix XNASkyWorldLocation = Matrix.CreateTranslation(ViewerXNAPosition); if (worldLoc == null) { // First time around, initialize the following items: worldLoc = new WorldLatLon(); skySteps.OldClockTime = Viewer.Simulator.ClockTime % 86400; while (skySteps.OldClockTime < 0) { skySteps.OldClockTime += 86400; } skySteps.Step1 = skySteps.Step2 = (int)(skySteps.OldClockTime / 1200); skySteps.Step2 = skySteps.Step2 < skySteps.MaxSteps - 1 ? skySteps.Step2 + 1 : 0; // limit to max. steps in case activity starts near midnight // Get the current latitude and longitude coordinates worldLoc.ConvertWTC(Viewer.Camera.TileX, Viewer.Camera.TileZ, Viewer.Camera.Location, ref latitude, ref longitude); if (seasonType != (int)Viewer.Simulator.Season) { seasonType = (int)Viewer.Simulator.Season; date.ordinalDate = latitude >= 0 ? 82 + seasonType * 91 : (82 + (seasonType + 2) * 91) % 365; // TODO: Set the following three externally from ORTS route files (future) date.month = 1 + date.ordinalDate / 30; date.day = 21; date.year = 2017; } // Fill in the sun- and moon-position lookup tables for (int i = 0; i < skySteps.MaxSteps; i++) { solarPosArray[i] = SunMoonPos.SolarAngle(latitude, longitude, ((float)i / skySteps.MaxSteps), date); lunarPosArray[i] = SunMoonPos.LunarAngle(latitude, longitude, ((float)i / skySteps.MaxSteps), date); } // Phase of the moon is generated at random moonPhase = Viewer.Random.Next(8); if (moonPhase == 6 && date.ordinalDate > 45 && date.ordinalDate < 330) { moonPhase = 3; // Moon dog only occurs in winter } } skySteps.SetSunAndMoonDirection(ref solarDirection, ref lunarDirection, ref solarPosArray, ref lunarPosArray, Viewer.Simulator.ClockTime); frame.AddPrimitive(Material, Primitive, RenderPrimitiveGroup.Sky, ref XNASkyWorldLocation); }
public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime) { var gameTime = (float)Viewer.Simulator.GameTime; Pricipitation.DynamicUpdate(WeatherControl, Weather, Viewer, ref Wind); Pricipitation.Update(gameTime, elapsedTime, Weather.PricipitationIntensityPPSPM2, Viewer); // Note: This is quite a hack. We ideally should be able to pass this through RenderItem somehow. var XNAWorldLocation = Matrix.Identity; XNAWorldLocation.M11 = gameTime; XNAWorldLocation.M21 = Viewer.Camera.TileX; XNAWorldLocation.M22 = Viewer.Camera.TileZ; frame.AddPrimitive(Material, Pricipitation, RenderPrimitiveGroup.Precipitation, ref XNAWorldLocation); }
public void PrepareFrame(RenderFrame frame) { var dTileX = TileX - Viewer.Camera.TileX; var dTileZ = TileZ - Viewer.Camera.TileZ; var mstsLocation = new Vector3(dTileX * 2048 - 1024 + 1024 * Size, 0, dTileZ * 2048 - 1024 + 1024 * Size); if (Viewer.Camera.InFov(mstsLocation, Size * 1448f) && WaterLayers != null) { xnaMatrix.M41 = mstsLocation.X; xnaMatrix.M43 = -mstsLocation.Z; foreach (var waterLayer in WaterLayers) { xnaMatrix.M42 = mstsLocation.Y + waterLayer.Key; frame.AddPrimitive(waterLayer.Value, this, RenderPrimitiveGroup.World, ref xnaMatrix); } } }
public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime) { var gameTime = (float)Viewer.Simulator.GameTime; Emitter.Update(gameTime, elapsedTime); // Note: This is quite a hack. We ideally should be able to pass this through RenderItem somehow. var XNAWorldLocation = Matrix.Identity; XNAWorldLocation.M11 = gameTime; XNAWorldLocation.M21 = Viewer.Camera.TileX; XNAWorldLocation.M22 = Viewer.Camera.TileZ; if (Emitter.HasParticlesToRender()) { frame.AddPrimitive(Material, Emitter, RenderPrimitiveGroup.Particles, ref XNAWorldLocation); } #if DEBUG_EMITTER_INPUT InputCycle++; InputCycle %= InputCycleLimit; #endif }
/// <summary> /// Used to update information affecting the SkyMesh /// </summary> public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime) { // Adjust dome position so the bottom edge is not visible Vector3 ViewerXNAPosition = new Vector3(MSTSSkyViewer.Camera.Location.X, MSTSSkyViewer.Camera.Location.Y - 100, -MSTSSkyViewer.Camera.Location.Z); Matrix XNASkyWorldLocation = Matrix.CreateTranslation(ViewerXNAPosition); if (mstsskyworldLoc == null) { // First time around, initialize the following items: mstsskyworldLoc = new WorldLatLon(); mstsskyoldClockTime = MSTSSkyViewer.Simulator.ClockTime % 86400; while (mstsskyoldClockTime < 0) { mstsskyoldClockTime += 86400; } step1 = step2 = (int)(mstsskyoldClockTime / 1200); step2 = step2 < maxSteps - 1 ? step2 + 1 : 0; // limit to max. steps in case activity starts near midnight // Get the current latitude and longitude coordinates mstsskyworldLoc.ConvertWTC(MSTSSkyViewer.Camera.TileX, MSTSSkyViewer.Camera.TileZ, MSTSSkyViewer.Camera.Location, ref mstsskylatitude, ref mstsskylongitude); if (mstsskyseasonType != (int)MSTSSkyViewer.Simulator.Season) { mstsskyseasonType = (int)MSTSSkyViewer.Simulator.Season; date.ordinalDate = mstsskylatitude >= 0 ? 82 + mstsskyseasonType * 91 : (82 + (mstsskyseasonType + 2) * 91) % 365; // TODO: Set the following three externally from ORTS route files (future) date.month = 1 + date.ordinalDate / 30; date.day = 21; date.year = 2017; } // Fill in the sun- and moon-position lookup tables for (int i = 0; i < maxSteps; i++) { mstsskysolarPosArray[i] = SunMoonPos.SolarAngle(mstsskylatitude, mstsskylongitude, ((float)i / maxSteps), date); mstsskylunarPosArray[i] = SunMoonPos.LunarAngle(mstsskylatitude, mstsskylongitude, ((float)i / maxSteps), date); } // Phase of the moon is generated at random mstsskymoonPhase = Viewer.Random.Next(8); if (mstsskymoonPhase == 6 && date.ordinalDate > 45 && date.ordinalDate < 330) { mstsskymoonPhase = 3; // Moon dog only occurs in winter } // Overcast factor: 0.0=almost no clouds; 0.1=wispy clouds; 1.0=total overcast //mstsskyovercastFactor = MSTSSkyViewer.World.WeatherControl.overcastFactor; mstsskyfogDistance = MSTSSkyViewer.Simulator.Weather.FogDistance; } MPManager manager = MPManager.Instance(); if (MPManager.IsClient() && manager.weatherChanged) { //received message about weather change if (manager.overcastFactor >= 0) { mstsskyovercastFactor = manager.overcastFactor; } //received message about weather change if (manager.fogDistance > 0) { mstsskyfogDistance = manager.fogDistance; } if (manager.overcastFactor >= 0 || manager.fogDistance > 0) { manager.weatherChanged = false; manager.overcastFactor = -1; manager.fogDistance = -1; } } ////////////////////// T E M P O R A R Y /////////////////////////// // The following keyboard commands are used for viewing sky and weather effects in "demo" mode. // Control- and Control+ for overcast, Shift- and Shift+ for fog and - and + for time. // Don't let multiplayer clients adjust the weather. if (!MPManager.IsClient()) { // Overcast ranges from 0 (completely clear) to 1 (completely overcast). if (UserInput.IsDown(UserCommand.DebugOvercastIncrease)) { mstsskyovercastFactor = MathHelper.Clamp(mstsskyovercastFactor + elapsedTime.RealSeconds / 10, 0, 1); } if (UserInput.IsDown(UserCommand.DebugOvercastDecrease)) { mstsskyovercastFactor = MathHelper.Clamp(mstsskyovercastFactor - elapsedTime.RealSeconds / 10, 0, 1); } // Fog ranges from 10m (can't see anything) to 100km (clear arctic conditions). if (UserInput.IsDown(UserCommand.DebugFogIncrease)) { mstsskyfogDistance = MathHelper.Clamp(mstsskyfogDistance - elapsedTime.RealSeconds * mstsskyfogDistance, 10, 100000); } if (UserInput.IsDown(UserCommand.DebugFogDecrease)) { mstsskyfogDistance = MathHelper.Clamp(mstsskyfogDistance + elapsedTime.RealSeconds * mstsskyfogDistance, 10, 100000); } } // Don't let clock shift if multiplayer. if (!MPManager.IsMultiPlayer()) { // Shift the clock forwards or backwards at 1h-per-second. if (UserInput.IsDown(UserCommand.DebugClockForwards)) { MSTSSkyViewer.Simulator.ClockTime += elapsedTime.RealSeconds * 3600; } if (UserInput.IsDown(UserCommand.DebugClockBackwards)) { MSTSSkyViewer.Simulator.ClockTime -= elapsedTime.RealSeconds * 3600; } } // Server needs to notify clients of weather changes. if (MPManager.IsServer()) { if (UserInput.IsReleased(UserCommand.DebugOvercastIncrease) || UserInput.IsReleased(UserCommand.DebugOvercastDecrease) || UserInput.IsReleased(UserCommand.DebugFogIncrease) || UserInput.IsReleased(UserCommand.DebugFogDecrease)) { manager.SetEnvInfo(mstsskyovercastFactor, mstsskyfogDistance); MPManager.Notify(new MSGWeather(-1, mstsskyovercastFactor, -1, mstsskyfogDistance).ToString()); } } //////////////////////////////////////////////////////////////////// // Current solar and lunar position are calculated by interpolation in the lookup arrays. // Using the Lerp() function, so need to calculate the in-between differential float diff = (float)(MSTSSkyViewer.Simulator.ClockTime - mstsskyoldClockTime) / 1200; // The rest of this increments/decrements the array indices and checks for overshoot/undershoot. if (MSTSSkyViewer.Simulator.ClockTime >= (mstsskyoldClockTime + 1200)) // Plus key, or normal forward in time { step1++; step2++; mstsskyoldClockTime = MSTSSkyViewer.Simulator.ClockTime; diff = 0; if (step2 >= maxSteps) // Midnight. { step2 = 0; } if (step1 >= maxSteps) // Midnight. { step1 = 0; } } if (MSTSSkyViewer.Simulator.ClockTime <= (mstsskyoldClockTime - 1200)) // Minus key { step1--; step2--; mstsskyoldClockTime = MSTSSkyViewer.Simulator.ClockTime; diff = 0; if (step1 < 0) // Midnight. { step1 = maxSteps - 1; } if (step2 < 0) // Midnight. { step2 = maxSteps - 1; } } mstsskysolarDirection.X = MathHelper.Lerp(mstsskysolarPosArray[step1].X, mstsskysolarPosArray[step2].X, diff); mstsskysolarDirection.Y = MathHelper.Lerp(mstsskysolarPosArray[step1].Y, mstsskysolarPosArray[step2].Y, diff); mstsskysolarDirection.Z = MathHelper.Lerp(mstsskysolarPosArray[step1].Z, mstsskysolarPosArray[step2].Z, diff); mstsskylunarDirection.X = MathHelper.Lerp(mstsskylunarPosArray[step1].X, mstsskylunarPosArray[step2].X, diff); mstsskylunarDirection.Y = MathHelper.Lerp(mstsskylunarPosArray[step1].Y, mstsskylunarPosArray[step2].Y, diff); mstsskylunarDirection.Z = MathHelper.Lerp(mstsskylunarPosArray[step1].Z, mstsskylunarPosArray[step2].Z, diff); frame.AddPrimitive(MSTSSkyMaterial, MSTSSkyMesh, RenderPrimitiveGroup.Sky, ref XNASkyWorldLocation); }
public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime, Matrix xnaTileTranslation) { var initialise = DisplayState == -1; if (DisplayState != SignalHead.draw_state) { #if DEBUG_SIGNAL_SHAPES Console.WriteLine("{5} {0} signal {1} unit {2} state: {3} --> {4}", SignalShape.Location, SignalShape.UID, Index, DisplayState, SignalHead.draw_state, InfoDisplay.FormattedTime(Viewer.Simulator.ClockTime)); #endif DisplayState = SignalHead.draw_state; if (SignalTypeData.DrawAspects.ContainsKey(DisplayState)) { SemaphoreTarget = SignalTypeData.DrawAspects[DisplayState].SemaphorePos; SemaphoreSpeed = SignalTypeData.SemaphoreAnimationTime <= 0 ? 0 : (SemaphoreTarget > SemaphorePos ? +1 : -1) / SignalTypeData.SemaphoreAnimationTime; if (Sound != null) { Sound.HandleEvent(Event.SemaphoreArm); } } } CumulativeTime += elapsedTime.ClockSeconds; while (CumulativeTime > SignalTypeData.FlashTimeTotal) { CumulativeTime -= SignalTypeData.FlashTimeTotal; } if (DisplayState < 0 || !SignalTypeData.DrawAspects.ContainsKey(DisplayState)) { return; } if (SignalTypeData.Semaphore) { // We reset the animation matrix before preparing the lights, because they need to be positioned // based on the original matrix only. foreach (AnimatedPart SemaphorePart in SemaphoreParts) { SemaphorePart.SetFrameWrap(0); } } for (var i = 0; i < SignalTypeData.Lights.Count; i++) { SignalLightState state = lightStates[i]; bool semaphoreDark = SemaphorePos != SemaphoreTarget && SignalTypeData.LightsSemaphoreChange[i]; bool constantDark = !SignalTypeData.DrawAspects[DisplayState].DrawLights[i]; bool flashingDark = SignalTypeData.DrawAspects[DisplayState].FlashLights[i] && (CumulativeTime > SignalTypeData.FlashTimeOn); state.UpdateIntensity(semaphoreDark || constantDark || flashingDark ? 0 : 1, elapsedTime); if (!state.IsIlluminated()) { continue; } bool isDay; if (Viewer.Settings.UseMSTSEnv == false) { isDay = Viewer.World.Sky.solarDirection.Y > 0; } else { isDay = Viewer.World.MSTSSky.mstsskysolarDirection.Y > 0; } bool isPoorVisibility = Viewer.Simulator.Weather.FogDistance < 200; if (!SignalTypeData.DayLight && isDay && !isPoorVisibility) { continue; } var slp = SignalTypeData.Lights[i]; var xnaMatrix = Matrix.CreateTranslation(slp.Position); foreach (int MatrixIndex in MatrixIndices) { Matrix.Multiply(ref xnaMatrix, ref SignalShape.XNAMatrices[MatrixIndex], out xnaMatrix); } Matrix.Multiply(ref xnaMatrix, ref xnaTileTranslation, out xnaMatrix); void renderEffect(Material material) { frame.AddPrimitive(material, slp, RenderPrimitiveGroup.Lights, ref xnaMatrix, ShapeFlags.None, state); } renderEffect(SignalTypeData.Material); if (Viewer.Settings.SignalLightGlow) { renderEffect(SignalTypeData.GlowMaterial); } } if (SignalTypeData.Semaphore) { // Now we update and re-animate the semaphore arm. if (SignalTypeData.SemaphoreAnimationTime <= 0 || initialise) { // No timing (so instant switch) or we're initialising. SemaphorePos = SemaphoreTarget; SemaphoreSpeed = 0; } else { // Animate slowly to target position. SemaphorePos += SemaphoreSpeed * elapsedTime.ClockSeconds; if (SemaphorePos * Math.Sign(SemaphoreSpeed) > SemaphoreTarget * Math.Sign(SemaphoreSpeed)) { SemaphorePos = SemaphoreTarget; SemaphoreSpeed = 0; } } foreach (AnimatedPart SemaphorePart in SemaphoreParts) { SemaphorePart.SetFrameCycle(SemaphorePos); } } }
public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime) { if (UpdateState()) { foreach (var lightPrimitive in LightPrimitives) { lightPrimitive.UpdateState(this); } #if DEBUG_LIGHT_STATES Console.WriteLine(); #endif UpdateActiveLightCone(); } foreach (var lightPrimitive in LightPrimitives) { lightPrimitive.PrepareFrame(frame, elapsedTime); } int dTileX = Car.WorldPosition.TileX - Viewer.Camera.TileX; int dTileZ = Car.WorldPosition.TileZ - Viewer.Camera.TileZ; Matrix xnaDTileTranslation = Matrix.CreateTranslation(dTileX * 2048, 0, -dTileZ * 2048); // object is offset from camera this many tiles xnaDTileTranslation = Car.WorldPosition.XNAMatrix * xnaDTileTranslation; Vector3 mstsLocation = new Vector3(xnaDTileTranslation.Translation.X, xnaDTileTranslation.Translation.Y, -xnaDTileTranslation.Translation.Z); float objectRadius = 20; // Even more arbitrary. float objectViewingDistance = Viewer.Settings.ViewingDistance; // Arbitrary. if (Viewer.Camera.CanSee(mstsLocation, objectRadius, objectViewingDistance)) { foreach (var lightPrimitive in LightPrimitives) { if (lightPrimitive.Enabled || lightPrimitive.FadeOut) { if (lightPrimitive is LightGlowPrimitive) { frame.AddPrimitive(LightGlowMaterial, lightPrimitive, RenderPrimitiveGroup.Lights, ref xnaDTileTranslation); } } } } #if DEBUG_LIGHT_CONE foreach (var lightPrimitive in LightPrimitives) { if (lightPrimitive.Enabled || lightPrimitive.FadeOut) { if (lightPrimitive is LightConePrimitive) { frame.AddPrimitive(LightConeMaterial, lightPrimitive, RenderPrimitiveGroup.Lights, ref xnaDTileTranslation); } } } #endif // Set the active light cone info for the material code. if (HasLightCone && ActiveLightCone != null) { LightConePosition = Vector3.Transform(Vector3.Lerp(ActiveLightCone.Position1, ActiveLightCone.Position2, ActiveLightCone.Fade.Y), xnaDTileTranslation); LightConeDirection = Vector3.Transform(Vector3.Lerp(ActiveLightCone.Direction1, ActiveLightCone.Direction2, ActiveLightCone.Fade.Y), Car.WorldPosition.XNAMatrix); LightConeDirection -= Car.WorldPosition.XNAMatrix.Translation; LightConeDirection.Normalize(); LightConeDistance = MathHelper.Lerp(ActiveLightCone.Distance1, ActiveLightCone.Distance2, ActiveLightCone.Fade.Y); LightConeMinDotProduct = (float)Math.Cos(MathHelper.Lerp(ActiveLightCone.Angle1, ActiveLightCone.Angle2, ActiveLightCone.Fade.Y)); LightConeColor = Vector4.Lerp(ActiveLightCone.Color1, ActiveLightCone.Color2, ActiveLightCone.Fade.Y); } }
public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime) { // Adjust dome position so the bottom edge is not visible Vector3 ViewerXNAPosition = new Vector3(Viewer.Camera.Location.X, Viewer.Camera.Location.Y - 100, -Viewer.Camera.Location.Z); Matrix XNASkyWorldLocation = Matrix.CreateTranslation(ViewerXNAPosition); if (worldLoc == null) { // First time around, initialize the following items: worldLoc = new WorldLatLon(); oldClockTime = Viewer.Simulator.ClockTime % 86400; while (oldClockTime < 0) { oldClockTime += 86400; } step1 = step2 = (int)(oldClockTime / 1200); step2 = step2 < maxSteps - 1 ? step2 + 1 : 0; // limit to max. steps in case activity starts near midnight // Get the current latitude and longitude coordinates worldLoc.ConvertWTC(Viewer.Camera.TileX, Viewer.Camera.TileZ, Viewer.Camera.Location, ref latitude, ref longitude); if (seasonType != (int)Viewer.Simulator.Season) { seasonType = (int)Viewer.Simulator.Season; date.ordinalDate = latitude >= 0 ? 82 + seasonType * 91 : (82 + (seasonType + 2) * 91) % 365; // TODO: Set the following three externally from ORTS route files (future) date.month = 1 + date.ordinalDate / 30; date.day = 21; date.year = 2017; } // Fill in the sun- and moon-position lookup tables for (int i = 0; i < maxSteps; i++) { solarPosArray[i] = SunMoonPos.SolarAngle(latitude, longitude, ((float)i / maxSteps), date); lunarPosArray[i] = SunMoonPos.LunarAngle(latitude, longitude, ((float)i / maxSteps), date); } // Phase of the moon is generated at random moonPhase = Viewer.Random.Next(8); if (moonPhase == 6 && date.ordinalDate > 45 && date.ordinalDate < 330) { moonPhase = 3; // Moon dog only occurs in winter } } // Current solar and lunar position are calculated by interpolation in the lookup arrays. // The arrays have intervals of 1200 secs or 20 mins. // Using the Lerp() function, so need to calculate the in-between differential float diff = GetCelestialDiff(); // The rest of this increments/decrements the array indices and checks for overshoot/undershoot. while (Viewer.Simulator.ClockTime >= (oldClockTime + 1200)) // Plus key, or normal forward in time; <CSComment> better so in case of fast forward { oldClockTime = oldClockTime + 1200; diff = GetCelestialDiff(); step1++; step2++; if (step2 >= maxSteps) // Midnight. { step2 = 0; } if (step1 >= maxSteps) // Midnight. { step1 = 0; } } if (Viewer.Simulator.ClockTime <= (oldClockTime - 1200)) // Minus key { oldClockTime = Viewer.Simulator.ClockTime; diff = 0; step1--; step2--; if (step1 < 0) // Midnight. { step1 = maxSteps - 1; } if (step2 < 0) // Midnight. { step2 = maxSteps - 1; } } solarDirection.X = MathHelper.Lerp(solarPosArray[step1].X, solarPosArray[step2].X, diff); solarDirection.Y = MathHelper.Lerp(solarPosArray[step1].Y, solarPosArray[step2].Y, diff); solarDirection.Z = MathHelper.Lerp(solarPosArray[step1].Z, solarPosArray[step2].Z, diff); lunarDirection.X = MathHelper.Lerp(lunarPosArray[step1].X, lunarPosArray[step2].X, diff); lunarDirection.Y = MathHelper.Lerp(lunarPosArray[step1].Y, lunarPosArray[step2].Y, diff); lunarDirection.Z = MathHelper.Lerp(lunarPosArray[step1].Z, lunarPosArray[step2].Z, diff); frame.AddPrimitive(Material, Primitive, RenderPrimitiveGroup.Sky, ref XNASkyWorldLocation); }
public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime, Matrix xnaTileTranslation) { var initialise = DisplayState == -1; if (DisplayState != SignalHead.draw_state) { #if DEBUG_SIGNAL_SHAPES Console.WriteLine("{5} {0} signal {1} unit {2} state: {3} --> {4}", SignalShape.Location, SignalShape.UID, Index, DisplayState, SignalHead.draw_state, InfoDisplay.FormattedTime(Viewer.Simulator.ClockTime)); #endif DisplayState = SignalHead.draw_state; if (SignalTypeData.DrawAspects.ContainsKey(DisplayState)) { SemaphoreTarget = SignalTypeData.DrawAspects[DisplayState].SemaphorePos; SemaphoreSpeed = SignalTypeData.SemaphoreAnimationTime <= 0 ? 0 : (SemaphoreTarget > SemaphorePos ? +1 : -1) / SignalTypeData.SemaphoreAnimationTime; if (Sound != null) Sound.HandleEvent(Event.SemaphoreArm); } } CumulativeTime += elapsedTime.ClockSeconds; while (CumulativeTime > SignalTypeData.FlashTimeTotal) CumulativeTime -= SignalTypeData.FlashTimeTotal; if (DisplayState < 0 || !SignalTypeData.DrawAspects.ContainsKey(DisplayState)) return; if (SignalTypeData.Semaphore) { // We reset the animation matrix before preparing the lights, because they need to be positioned // based on the original matrix only. foreach (AnimatedPart SemaphorePart in SemaphoreParts) { SemaphorePart.SetFrameWrap(0); } } for (var i = 0; i < SignalTypeData.Lights.Count; i++) { if (SemaphorePos != SemaphoreTarget && SignalTypeData.LightsSemaphoreChange[i]) continue; if (!SignalTypeData.DrawAspects[DisplayState].DrawLights[i]) continue; if (SignalTypeData.DrawAspects[DisplayState].FlashLights[i] && (CumulativeTime > SignalTypeData.FlashTimeOn)) continue; var xnaMatrix = Matrix.CreateTranslation(SignalTypeData.Lights[i].Position); foreach (int MatrixIndex in MatrixIndices) { Matrix.Multiply(ref xnaMatrix, ref SignalShape.XNAMatrices[MatrixIndex], out xnaMatrix); } Matrix.Multiply(ref xnaMatrix, ref xnaTileTranslation, out xnaMatrix); frame.AddPrimitive(SignalTypeData.Material, SignalTypeData.Lights[i], RenderPrimitiveGroup.Lights, ref xnaMatrix); if (Viewer.Settings.SignalLightGlow) frame.AddPrimitive(SignalTypeData.GlowMaterial, SignalTypeData.Lights[i], RenderPrimitiveGroup.Lights, ref xnaMatrix); } if (SignalTypeData.Semaphore) { // Now we update and re-animate the semaphore arm. if (SignalTypeData.SemaphoreAnimationTime <= 0 || initialise) { // No timing (so instant switch) or we're initialising. SemaphorePos = SemaphoreTarget; SemaphoreSpeed = 0; } else { // Animate slowly to target position. SemaphorePos += SemaphoreSpeed * elapsedTime.ClockSeconds; if (SemaphorePos * Math.Sign(SemaphoreSpeed) > SemaphoreTarget * Math.Sign(SemaphoreSpeed)) { SemaphorePos = SemaphoreTarget; SemaphoreSpeed = 0; } } foreach (AnimatedPart SemaphorePart in SemaphoreParts) { SemaphorePart.SetFrameCycle(SemaphorePos); } } }