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 SignalShapeHead(Viewer viewer, SignalShape signalShape, int index, SignalHead signalHead, SignalItem mstsSignalItem, Formats.Msts.SignalShape.SignalSubObj mstsSignalSubObj) { Viewer = viewer; SignalShape = signalShape; #if DEBUG_SIGNAL_SHAPES Index = index; #endif SignalHead = signalHead; for (int mindex = 0; mindex <= signalShape.SharedShape.MatrixNames.Count - 1; mindex++) { string MatrixName = signalShape.SharedShape.MatrixNames[mindex]; if (String.Equals(MatrixName, mstsSignalSubObj.MatrixName)) { MatrixIndices.Add(mindex); } } if (!viewer.SIGCFG.SignalTypes.ContainsKey(mstsSignalSubObj.SignalSubSignalType)) { return; } var mstsSignalType = viewer.SIGCFG.SignalTypes[mstsSignalSubObj.SignalSubSignalType]; SignalTypeData = viewer.SignalTypeDataManager.Get(mstsSignalType); if (SignalTypeData.Semaphore) { // Check whether we have to correct the Semaphore position indexes following the strange rule of MSTS // Such strange rule is that, if there are only two animation steps in the related .s file, MSTS behaves as follows: // a SemaphorePos (2) in sigcfg.dat is executed as SemaphorePos (1) // a SemaphorePos (1) in sigcfg.dat is executed as SemaphorePos (0) // a SemaphorePos (0) in sigcfg.dat is executed as SemaphorePos (0) // First we check if there are only two animation steps if (signalShape.SharedShape.Animations != null && signalShape.SharedShape.Animations.Count != 0 && MatrixIndices.Count > 0 && signalShape.SharedShape.Animations[0].anim_nodes[MatrixIndices[0]].controllers.Count != 0 && signalShape.SharedShape.Animations[0].anim_nodes[MatrixIndices[0]].controllers[0].Count == 2) { // OK, now we check if maximum SemaphorePos is 2 (we won't correct if there are only SemaphorePos 1 and 0, // because they would both be executed as SemaphorePos (0) accordingly to above law, therefore leading to a static semaphore) float maxIndex = float.MinValue; foreach (SignalAspectData drAsp in SignalTypeData.DrawAspects.Values) { if (drAsp.SemaphorePos > maxIndex) { maxIndex = drAsp.SemaphorePos; } } if (maxIndex == 2) { // in this case we modify the SemaphorePositions for compatibility with MSTS. foreach (SignalAspectData drAsp in SignalTypeData.DrawAspects.Values) { switch ((int)drAsp.SemaphorePos) { case 2: drAsp.SemaphorePos = 1; break; case 1: drAsp.SemaphorePos = 0; break; default: break; } } if (!SignalTypeData.AreSemaphoresReindexed) { Trace.TraceInformation("Reindexing semaphore entries of signal type {0} for compatibility with MSTS", mstsSignalType.Name); SignalTypeData.AreSemaphoresReindexed = true; } } } foreach (int mindex in MatrixIndices) { if (mindex == 0 && (signalShape.SharedShape.Animations == null || signalShape.SharedShape.Animations.Count == 0 || signalShape.SharedShape.Animations[0].anim_nodes[mindex].controllers.Count == 0)) { continue; } AnimatedPart SemaphorePart = new AnimatedPart(signalShape); SemaphorePart.AddMatrix(mindex); SemaphoreParts.Add(SemaphorePart); } if (Viewer.Simulator.TRK.Tr_RouteFile.DefaultSignalSMS != null) { var soundPath = Viewer.Simulator.RoutePath + @"\\sound\\" + Viewer.Simulator.TRK.Tr_RouteFile.DefaultSignalSMS; try { Sound = new SoundSource(Viewer, SignalShape.Location.WorldLocation, Events.Source.MSTSSignal, soundPath); Viewer.SoundProcess.AddSoundSources(this, new List <SoundSourceBase>() { Sound }); } catch (Exception error) { Trace.WriteLine(new FileLoadException(soundPath, error)); } } } lightStates = new SignalLightState[SignalTypeData.Lights.Count]; for (var i = 0; i < SignalTypeData.Lights.Count; i++) { lightStates[i] = new SignalLightState(SignalTypeData.OnOffTimeS); } #if DEBUG_SIGNAL_SHAPES Console.Write(" HEAD type={0,-8} lights={1,-2} sem={2}", SignalTypeData.Type, SignalTypeData.Lights.Count, SignalTypeData.Semaphore); #endif }