internal Block(double location, Block previous) { this.Position = previous.Position; this.Orientation = previous.Orientation; this.Location = location; this.TurnRatio = 0.0; this.CurveRadius = previous.CurveRadius; this.CurveCant = previous.CurveCant; this.Pitch = previous.Pitch; this.Height = 0.0; this.HeightDefined = false; this.Rails = new Rail[previous.Rails.Length]; for (int i = 0; i < previous.Rails.Length; i++) { if (previous.Rails[i] != null) { this.Rails[i] = new Rail(previous.Rails[i]); } else { this.Rails[i] = null; } } this.RailCount = previous.RailCount; this.GroundCycle = previous.GroundCycle; this.FreeObjs = null; this.FreeObjCount = 0; }
// constructors internal Block(double location) { this.Position = OpenBveApi.Math.Vector3.Null; this.Orientation = OpenBveApi.Math.Orientation3.Default; this.Location = location; this.TurnRatio = 0.0; this.CurveRadius = 0.0; this.CurveCant = 0.0; this.Pitch = 0.0; this.Height = 0.0; this.HeightDefined = false; this.Rails = new Rail[1]; this.RailCount = 0; this.GroundCycle = 0; this.FreeObjs = null; this.FreeObjCount = 0; }
// constructors /// <summary>Creates a new instance of this class.</summary> /// <param name="libraryIndex">A reference to an object in the object library.</param> /// <param name="faceIndex">The face within the object.</param> /// <param name="position">The absolute world position.</param> /// <param name="orientation">The absolute world orientation.</param> /// <param name="index">An index by which to identify this face. The value is specific to the function that uses this data structure.</param> internal PositionedFace(int libraryIndex, int faceIndex, OpenBveApi.Math.Vector3 position, OpenBveApi.Math.Orientation3 orientation, int index) { this.LibraryIndex = libraryIndex; this.FaceIndex = faceIndex; this.Position = position; this.Orientation = orientation; OpenBveApi.Geometry.FaceVertexMesh mesh = ObjectLibrary.Library.Objects[libraryIndex] as OpenBveApi.Geometry.FaceVertexMesh; if (mesh != null && mesh.Faces[faceIndex].Vertices.Length >= 3) { OpenBveApi.Math.Vector3 vectorA = mesh.Vertices[mesh.Faces[faceIndex].Vertices[0]].SpatialCoordinates; OpenBveApi.Math.Vector3 vectorB = mesh.Vertices[mesh.Faces[faceIndex].Vertices[1]].SpatialCoordinates; OpenBveApi.Math.Vector3 vectorC = mesh.Vertices[mesh.Faces[faceIndex].Vertices[2]].SpatialCoordinates; if (OpenBveApi.Math.Vector3.CreateNormal(vectorA, vectorB, vectorC, out this.Normal)) { this.Normal.Rotate(orientation); } else { this.Normal = OpenBveApi.Math.Vector3.Up; } } else { this.Normal = OpenBveApi.Math.Vector3.Up; } this.Index = index; this.Tag = 0.0; }
// constructors /// <summary>Creates a new instance of this class.</summary> internal Options() { this.UnitsOfLength = new double[] { 1.0 }; this.UnitOfSpeed = 0.277777777777777778; this.BlockLength = 25.0; this.Light = new OpenBveApi.Route.DirectionalLight( new OpenBveApi.Color.ColorRGB(0.625f, 0.625f, 0.625f), new OpenBveApi.Color.ColorRGB(0.625f, 0.625f, 0.625f), new OpenBveApi.Color.ColorRGB(0.0f, 0.0f, 0.0f), new OpenBveApi.Math.Vector3(-0.219185573394538, -0.86602540378444, 0.449397023149582) ); this.InitialPosition = OpenBveApi.Math.Vector3.Null; this.InitialOrientation = OpenBveApi.Math.Orientation3.Default; }
internal void Update(double tb, double tf) { if (baseTrain.CurrentSpeed > -0.277777777777778 & baseTrain.CurrentSpeed < 0.277777777777778) { // correct stop position if (!Lit & (baseTrain.StationDistanceToStopPoint > tb | baseTrain.StationDistanceToStopPoint < -tf)) { SoundBuffer buffer = AdjustAlarm.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = AdjustAlarm.Position; Program.Sounds.PlaySound(buffer, 1.0, 1.0, pos, baseTrain.Cars[baseTrain.DriverCar], false); } if (baseTrain.IsPlayerTrain) { MessageManager.AddMessage(Translations.GetInterfaceString("message_station_correct"), MessageDependency.None, GameMode.Normal, MessageColor.Orange, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } Lit = true; } } else { Lit = false; } }
// --- constructors --- /// <summary>Creates a new sound source.</summary> /// <param name="buffer">The sound buffer.</param> /// <param name="radius">The effective sound radius.</param> /// <param name="pitch">The pitch change factor.</param> /// <param name="volume">The volume change factor.</param> /// <param name="position">The position. If a train and car are specified, the position is relative to the car, otherwise absolute.</param> /// <param name="parent">The parent object this sound source is attached to, or a null reference.</param> /// <param name="car">The car this sound source is attached to, or a null reference.</param> /// <param name="looped">Whether this sound source plays in a loop.</param> internal SoundSource(SoundBuffer buffer, double radius, double pitch, double volume, OpenBveApi.Math.Vector3 position, object parent, int car, bool looped) { this.Buffer = buffer; this.Radius = radius; this.Pitch = pitch; this.Volume = volume; this.Position = position; this.Parent = parent; this.Car = car; this.Looped = looped; this.State = SoundSourceState.PlayPending; this.OpenAlSourceName = 0; //Set the sound type to undefined to use Michelle's original processing if (parent is TrainManager.Train) { this.Type = SoundType.TrainCar; } else if (parent is ObjectManager.WorldSound) { this.Type = SoundType.AnimatedObject; } else { this.Type = SoundType.Undefined; } }
// --- constructors --- /// <summary>Creates a new camera. A call to SetViewingAngle must be made to set the perspective in OpenGL.</summary> /// <param name="viewingDistance">The viewing distance in meters.</param> internal Camera(double viewingDistance) { const double degrees = 0.0174532925199433; this.Position = OpenBveApi.Math.Vector3.Null; this.Orientation = OpenBveApi.Math.Orientation3.Default; this.HorizontalViewingAngle = 45.0 * degrees; this.VerticalViewingAngle = 45.0 * degrees; this.ViewingDistance = viewingDistance; }
/// <summary>Opens the left-hand or right-hand doors for the specified train</summary> /// <param name="Train">The train</param> /// <param name="Left">Whether to open the left-hand doors</param> /// <param name="Right">Whether to open the right-hand doors</param> internal static void OpenTrainDoors(Train Train, bool Left, bool Right) { bool sl = false, sr = false; for (int i = 0; i < Train.Cars.Length; i++) { if (Left & !Train.Cars[i].Doors[0].AnticipatedOpen) { Train.Cars[i].Doors[0].AnticipatedOpen = true; sl = true; } if (Right & !Train.Cars[i].Doors[1].AnticipatedOpen) { Train.Cars[i].Doors[1].AnticipatedOpen = true; sr = true; } } if (sl) { for (int i = 0; i < Train.Cars.Length; i++) { Sounds.SoundBuffer buffer = Train.Cars[i].Doors[0].OpenSound.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Doors[0].OpenSound.Position; Sounds.PlaySound(buffer, Train.Cars[i].Specs.DoorOpenPitch, 1.0, pos, Train, i, false); } for (int j = 0; j < Train.Cars[i].Doors.Length; j++) { if (Train.Cars[i].Doors[j].Direction == -1) { Train.Cars[i].Doors[j].DoorLockDuration = 0.0; } } } } if (sr) { for (int i = 0; i < Train.Cars.Length; i++) { Sounds.SoundBuffer buffer = Train.Cars[i].Doors[1].OpenSound.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Doors[1].OpenSound.Position; Sounds.PlaySound(buffer, Train.Cars[i].Specs.DoorOpenPitch, 1.0, pos, Train, i, false); } for (int j = 0; j < Train.Cars[i].Doors.Length; j++) { if (Train.Cars[i].Doors[j].Direction == 1) { Train.Cars[i].Doors[j].DoorLockDuration = 0.0; } } } } }
internal void LeaveCheck(Vector2 Point) { if (!Loading.SimulationSetup) { return; } if (renderer.Camera.CurrentMode != CameraViewMode.Interior && renderer.Camera.CurrentMode != CameraViewMode.InteriorLookAhead) { return; } TrainManager.Car Car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; int add = Car.CarSections[0].CurrentAdditionalGroup + 1; if (add >= Car.CarSections[0].Groups.Length) { return; } TrainManager.TouchElement[] TouchElements = Car.CarSections[0].Groups[add].TouchElements; if (TouchElements == null) { return; } ObjectState pickedObject = ParseFBO(Point, 5, 5); foreach (TrainManager.TouchElement TouchElement in TouchElements) { if (TouchElement.Element.internalObject == pickedObject) { Car.CarSections[0].CurrentAdditionalGroup = TouchElement.JumpScreenIndex; Car.ChangeCarSection(TrainManager.CarSectionType.Interior); foreach (var index in TouchElement.SoundIndices.Where(x => x >= 0 && x < Car.Sounds.Touch.Length)) { SoundBuffer Buffer = Car.Sounds.Touch[index].Buffer; OpenBveApi.Math.Vector3 Position = Car.Sounds.Touch[index].Position; Program.Sounds.PlaySound(Buffer, 1.0, 1.0, Position, TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar], false); } } // HACK: Normally terminate the command issued once. if (TouchElement.Element.internalObject == pickedObject || (pickedObject != prePickedObject && TouchElement.Element.internalObject == prePickedObject)) { foreach (int index in TouchElement.ControlIndices) { Interface.CurrentControls[index].AnalogState = 0.0; Interface.CurrentControls[index].DigitalState = Interface.DigitalControlState.Released; MainLoop.RemoveControlRepeat(index); } } } }
/// <summary>Register the position to play microphone input.</summary> /// <param name="position">The position.</param> /// <param name="backwardTolerance">allowed tolerance in the backward direction</param> /// <param name="forwardTolerance">allowed tolerance in the forward direction</param> public void PlayMicSound(OpenBveApi.Math.Vector3 position, double backwardTolerance, double forwardTolerance) { if (OpenAlMic == null) { // This hardware has no AudioCapture device. return; } MicSources.Add(new MicSource(OpenAlMic, MicStore, position, backwardTolerance, forwardTolerance)); }
/// <summary>Opens the left-hand or right-hand doors within a specific car in a specified train</summary> /// <param name="Train">The train</param> /// <param name="CarIndex">Car Index number - 1</param> /// <param name="Left">Whether to open the left-hand doors</param> /// <param name="Right">Whether to open the right-hand doors</param> internal static void OpenTrainDoors(Train Train, int CarIndex, bool Left, bool Right) { bool sl = false, sr = false; if (Left & !Train.Cars[CarIndex].Doors[0].AnticipatedOpen & (Train.SafetySystems.DoorInterlockState == DoorInterlockStates.Left | Train.SafetySystems.DoorInterlockState == DoorInterlockStates.Unlocked)) { Train.Cars[CarIndex].Doors[0].AnticipatedOpen = true; sl = true; } if (Right & !Train.Cars[CarIndex].Doors[1].AnticipatedOpen & (Train.SafetySystems.DoorInterlockState == DoorInterlockStates.Right | Train.SafetySystems.DoorInterlockState == DoorInterlockStates.Unlocked)) { Train.Cars[CarIndex].Doors[1].AnticipatedOpen = true; sr = true; } if (sl) { SoundBuffer buffer = Train.Cars[CarIndex].Doors[0].OpenSound.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Doors[0].OpenSound.Position; Program.Sounds.PlaySound(buffer, Train.Cars[CarIndex].Specs.DoorOpenPitch, 1.0, pos, Train.Cars[CarIndex], false); } for (int i = 0; i < Train.Cars[CarIndex].Doors.Length; i++) { if (Train.Cars[CarIndex].Doors[i].Direction == -1) { Train.Cars[CarIndex].Doors[i].DoorLockDuration = 0.0; } } } if (sr) { SoundBuffer buffer = Train.Cars[CarIndex].Doors[1].OpenSound.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Doors[1].OpenSound.Position; Program.Sounds.PlaySound(buffer, Train.Cars[CarIndex].Specs.DoorOpenPitch, 1.0, pos, Train.Cars[CarIndex], false); } for (int i = 0; i < Train.Cars[CarIndex].Doors.Length; i++) { if (Train.Cars[CarIndex].Doors[i].Direction == 1) { Train.Cars[CarIndex].Doors[i].DoorLockDuration = 0.0; } } } for (int i = 0; i < Train.Cars[CarIndex].Doors.Length; i++) { if (Train.Cars[CarIndex].Doors[i].AnticipatedOpen) { Train.Cars[CarIndex].Doors[i].NextReopenTime = 0.0; Train.Cars[CarIndex].Doors[i].ReopenCounter++; } } }
// reset camera internal static void ResetCamera() { World.AbsoluteCameraPosition = new Vector3(-5.0, 2.5, -25.0); World.AbsoluteCameraDirection = new Vector3(-World.AbsoluteCameraPosition.X, -World.AbsoluteCameraPosition.Y, -World.AbsoluteCameraPosition.Z); World.AbsoluteCameraSide = new Vector3(-World.AbsoluteCameraPosition.Z, 0.0, World.AbsoluteCameraPosition.X); World.AbsoluteCameraDirection.Normalize(); World.AbsoluteCameraSide.Normalize(); World.AbsoluteCameraUp = Vector3.Cross(World.AbsoluteCameraDirection, World.AbsoluteCameraSide); World.VerticalViewingAngle = 45.0 * 0.0174532925199433; World.HorizontalViewingAngle = 2.0 * Math.Atan(Math.Tan(0.5 * World.VerticalViewingAngle) * World.AspectRatio); World.OriginalVerticalViewingAngle = World.VerticalViewingAngle; }
// --- constructors --- /// <summary>Creates a new sound source.</summary> /// <param name="buffer">The sound buffer.</param> /// <param name="radius">The effective sound radius.</param> /// <param name="pitch">The pitch change factor.</param> /// <param name="volume">The volume change factor.</param> /// <param name="position">The position. If a train and car are specified, the position is relative to the car, otherwise absolute.</param> /// <param name="train">The train this sound source is attached to, or a null reference.</param> /// <param name="car">The car this sound source is attached to, or a null reference.</param> /// <param name="looped">Whether this sound source plays in a loop.</param> internal SoundSource(SoundBuffer buffer, double radius, double pitch, double volume, OpenBveApi.Math.Vector3 position, TrainManager.Train train, int car, bool looped) { this.Buffer = buffer; this.Radius = radius; this.Pitch = pitch; this.Volume = volume; this.Position = position; this.Train = train; this.Car = car; this.Looped = looped; this.State = SoundSourceState.PlayPending; this.OpenAlSourceName = 0; }
// --- constructors --- /// <summary>Creates a new microphone source.</summary> /// <param name="position">The position.</param> /// <param name="backwardTolerance">allowed tolerance in the backward direction</param> /// <param name="forwardTolerance">allowed tolerance in the forward direction</param> internal MicSource(OpenBveApi.Math.Vector3 position, double backwardTolerance, double forwardTolerance) { this.Position = position; this.BackwardTolerance = backwardTolerance; this.ForwardTolerance = forwardTolerance; AL.GenSources(1, out OpenAlSourceName); // Prepare for monitoring the playback state. int dummyBuffer = AL.GenBuffer(); AL.BufferData(dummyBuffer, OpenAlMic.SampleFormat, MicStore, 0, OpenAlMic.SampleFrequency); AL.SourceQueueBuffer(OpenAlSourceName, dummyBuffer); AL.SourcePlay(OpenAlSourceName); AL.Source(OpenAlSourceName, ALSourceb.SourceRelative, true); }
/// <summary>Resets the renderer to the default state</summary> public static void Reset() { Objects = new RendererObject[256]; ObjectCount = 0; StaticOpaque = new ObjectGroup[] { }; DynamicOpaque = new ObjectList(); DynamicAlpha = new ObjectList(); OverlayOpaque = new ObjectList(); OverlayAlpha = new ObjectList(); OptionLighting = true; OptionAmbientColor = new Color24(160, 160, 160); OptionDiffuseColor = new Color24(160, 160, 160); OptionLightPosition = new Vector3(0.215920077052065f, 0.875724044222352f, -0.431840154104129f); OptionLightingResultingAmount = 1.0f; GL.Disable(EnableCap.Fog); FogEnabled = false; }
/// <inheritdoc/> public override void CloseDoors(bool Left, bool Right) { bool sl = false, sr = false; for (int i = 0; i < Cars.Length; i++) { if (Left & Cars[i].Doors[0].AnticipatedOpen & (SafetySystems.DoorInterlockState == DoorInterlockStates.Left | SafetySystems.DoorInterlockState == DoorInterlockStates.Unlocked)) { Cars[i].Doors[0].AnticipatedOpen = false; sl = true; } if (Right & Cars[i].Doors[1].AnticipatedOpen & (SafetySystems.DoorInterlockState == DoorInterlockStates.Right | SafetySystems.DoorInterlockState == DoorInterlockStates.Unlocked)) { Cars[i].Doors[1].AnticipatedOpen = false; sr = true; } } if (sl) { for (int i = 0; i < Cars.Length; i++) { SoundBuffer buffer = Cars[i].Doors[0].CloseSound.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Cars[i].Doors[0].CloseSound.Position; Program.Sounds.PlaySound(buffer, Cars[i].Specs.DoorClosePitch, 1.0, pos, Cars[i], false); } } } if (sr) { for (int i = 0; i < Cars.Length; i++) { SoundBuffer buffer = Cars[i].Doors[1].CloseSound.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Cars[i].Doors[1].CloseSound.Position; Program.Sounds.PlaySound(buffer, Cars[i].Specs.DoorClosePitch, 1.0, pos, Cars[i], false); } } } }
/// <summary>Applies a reverser notch</summary> /// <param name="Train">The train</param> /// <param name="Value">The notch to apply</param> /// <param name="Relative">Whether this is an absolute value or relative to the previous</param> internal static void ApplyReverser(Train Train, int Value, bool Relative) { int a = (int)Train.Handles.Reverser.Driver; int r = Relative ? a + Value : Value; if (r < -1) { r = -1; } if (r > 1) { r = 1; } if (a != r) { Train.Handles.Reverser.Driver = (ReverserPosition)r; if (Train.Plugin != null) { Train.Plugin.UpdateReverser(); } Game.AddBlackBoxEntry(Game.BlackBoxEventToken.None); // sound if (a == 0 & r != 0) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.ReverserOn.Buffer; if (buffer == null) { return; } OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.ReverserOn.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } else if (a != 0 & r == 0) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.ReverserOff.Buffer; if (buffer == null) { return; } OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.ReverserOff.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } }
/// <summary> /// Resets the state of the renderer /// </summary> internal static void Reset() { LoadTexturesImmediately = LoadTextureImmediatelyMode.NotYet; Objects = new Object[256]; ObjectCount = 0; StaticOpaque = new ObjectGroup[] { }; StaticOpaqueForceUpdate = true; DynamicOpaque = new ObjectList(); DynamicAlpha = new ObjectList(); OverlayOpaque = new ObjectList(); OverlayAlpha = new ObjectList(); OptionLighting = true; OptionAmbientColor = new Color24(160, 160, 160); OptionDiffuseColor = new Color24(160, 160, 160); OptionLightPosition = new Vector3(0.223606797749979f, 0.86602540378444f, -0.447213595499958f); OptionLightingResultingAmount = 1.0f; OptionClock = false; OptionBrakeSystems = false; }
/// <summary>Closes the left-hand or right-hand doors for the specified train</summary> /// <param name="Train">The train</param> /// <param name="Left">Whether to close the left-hand doors</param> /// <param name="Right">Whether to close the right-hand doors</param> internal static void CloseTrainDoors(Train Train, bool Left, bool Right) { bool sl = false, sr = false; for (int i = 0; i < Train.Cars.Length; i++) { if (Left & Train.Cars[i].Doors[0].AnticipatedOpen) { Train.Cars[i].Doors[0].AnticipatedOpen = false; sl = true; } if (Right & Train.Cars[i].Doors[1].AnticipatedOpen) { Train.Cars[i].Doors[1].AnticipatedOpen = false; sr = true; } } if (sl) { for (int i = 0; i < Train.Cars.Length; i++) { Sounds.SoundBuffer buffer = Train.Cars[i].Doors[0].CloseSound.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Doors[0].CloseSound.Position; Sounds.PlaySound(buffer, Train.Cars[i].Specs.DoorClosePitch, 1.0, pos, Train, i, false); } } } if (sr) { for (int i = 0; i < Train.Cars.Length; i++) { Sounds.SoundBuffer buffer = Train.Cars[i].Doors[1].CloseSound.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Doors[1].CloseSound.Position; Sounds.PlaySound(buffer, Train.Cars[i].Specs.DoorClosePitch, 1.0, pos, Train, i, false); } } } }
/// <summary>May be called from a .Net plugin, in order to play a sound from a specific car of a train</summary> /// <param name="index">The plugin-based of the sound to play</param> /// <param name="volume">The volume of the sound- A volume of 1.0 represents nominal volume</param> /// <param name="pitch">The pitch of the sound- A pitch of 1.0 represents nominal pitch</param> /// <param name="looped">Whether the sound is looped</param> /// <param name="CarIndex">The index of the car which is to emit the sound</param> /// <returns>The sound handle, or null if not successful</returns> internal SoundHandleEx PlaySound(int index, double volume, double pitch, bool looped, int CarIndex) { if (index >= 0 && index < this.Train.Cars[this.Train.DriverCar].Sounds.Plugin.Length && this.Train.Cars[this.Train.DriverCar].Sounds.Plugin[index].Buffer != null && CarIndex < this.Train.Cars.Length && CarIndex >= 0) { Sounds.SoundBuffer buffer = this.Train.Cars[CarIndex].Sounds.Plugin[index].Buffer; OpenBveApi.Math.Vector3 position = this.Train.Cars[CarIndex].Sounds.Plugin[index].Position; Sounds.SoundSource source = Sounds.PlaySound(buffer, pitch, volume, position, this.Train, CarIndex, looped); if (this.SoundHandlesCount == this.SoundHandles.Length) { Array.Resize <SoundHandleEx>(ref this.SoundHandles, this.SoundHandles.Length << 1); } this.SoundHandles[this.SoundHandlesCount] = new SoundHandleEx(volume, pitch, source); this.SoundHandlesCount++; return(this.SoundHandles[this.SoundHandlesCount - 1]); } else { return(null); } }
internal static void MouseEvent(object sender, MouseButtonEventArgs e) { MouseCameraPosition = World.AbsoluteCameraPosition; MouseCameraDirection = World.AbsoluteCameraDirection; MouseCameraUp = World.AbsoluteCameraUp; MouseCameraSide = World.AbsoluteCameraSide; if (e.Button == OpenTK.Input.MouseButton.Left) { MouseButton = e.Mouse.LeftButton == ButtonState.Pressed ? 1 : 0; } if (e.Button == OpenTK.Input.MouseButton.Right) { MouseButton = e.Mouse.RightButton == ButtonState.Pressed ? 2 : 0; } if (e.Button == OpenTK.Input.MouseButton.Middle) { MouseButton = e.Mouse.RightButton == ButtonState.Pressed ? 3 : 0; } previousMouseState = Mouse.GetState(); }
private static void RenderFace(ref ObjectFace Face, Vector3 Camera, bool IsDebugTouchMode = false) { if (CullEnabled) { if (!OptionBackfaceCulling || (ObjectManager.Objects[Face.ObjectIndex].Mesh.Faces[Face.FaceIndex].Flags & MeshFace.Face2Mask) != 0) { GL.Disable(EnableCap.CullFace); CullEnabled = false; } } else if (OptionBackfaceCulling) { if ((ObjectManager.Objects[Face.ObjectIndex].Mesh.Faces[Face.FaceIndex].Flags & MeshFace.Face2Mask) == 0) { GL.Enable(EnableCap.CullFace); CullEnabled = true; } } int r = (int)ObjectManager.Objects[Face.ObjectIndex].Mesh.Faces[Face.FaceIndex].Material; RenderFace(ref ObjectManager.Objects[Face.ObjectIndex].Mesh.Materials[r], ObjectManager.Objects[Face.ObjectIndex].Mesh.Vertices, Face.Wrap, ref ObjectManager.Objects[Face.ObjectIndex].Mesh.Faces[Face.FaceIndex], Camera, IsDebugTouchMode); }
// --- constructors --- /// <summary>Creates a new sound source.</summary> /// <param name="buffer">The sound buffer.</param> /// <param name="radius">The effective sound radius.</param> /// <param name="pitch">The pitch change factor.</param> /// <param name="volume">The volume change factor.</param> /// <param name="position">The position. If a train and car are specified, the position is relative to the car, otherwise absolute.</param> /// <param name="train">The train this sound source is attached to, or a null reference.</param> /// <param name="car">The car this sound source is attached to, or a null reference.</param> /// <param name="looped">Whether this sound source plays in a loop.</param> internal SoundSource(SoundBuffer buffer, double radius, double pitch, double volume, OpenBveApi.Math.Vector3 position, TrainManager.Train train, int car, bool looped) { this.Buffer = buffer; this.Radius = radius; this.Pitch = pitch; this.Volume = volume; this.Position = position; this.Train = train; this.Car = car; this.Looped = looped; this.State = SoundSourceState.PlayPending; this.OpenAlSourceName = 0; //Set the sound type to undefined to use Michelle's original processing if (train != null) { this.Type = SoundType.TrainCar; } else { this.Type = SoundType.Undefined; } }
private static void UpdateLighting(double secondsSinceMidnight) { if (RoundTheClockLighting) { double hour = (secondsSinceMidnight % 86400.0) / 3600.0; LightAmbientColor = OpenBveApi.Color.ColorRGB.Black; LightDiffuseColor = OpenBveApi.Color.ColorRGB.Black; double sunIntensity; double moonIntensity; /* The sun */ { double sunAngle = secondsSinceMidnight * 2.0 * Math.PI / 86400.0; SunPosition = new OpenBveApi.Math.Vector3(Math.Sin(sunAngle), -Math.Cos(sunAngle), 0.0); double r = 0.6 * Math.Max(0.0, Math.Min(1.0, SunPosition.Y + 0.18)); double g = 0.6 * Math.Max(0.0, Math.Min(1.0, SunPosition.Y + 0.09)); double b = 0.6 * Math.Max(0.0, Math.Min(1.0, SunPosition.Y + 0.03)); sunIntensity = (r + g + b) / 3.0; LightAmbientColor += new OpenBveApi.Color.ColorRGB((float)r, (float)g, (float)b); LightDiffuseColor += new OpenBveApi.Color.ColorRGB((float)r, (float)g, (float)b); } /* The moon */ { double moonAngle = secondsSinceMidnight * 2.0 * Math.PI / 86400.0 + 0.9 * Math.PI; MoonPosition = new OpenBveApi.Math.Vector3(0.0, -Math.Cos(moonAngle), Math.Sin(moonAngle)); double r = Math.Max(0.0, Math.Min(1.0, 0.05 * MoonPosition.Y + 0.12)); double g = Math.Max(0.0, Math.Min(1.0, 0.07 * MoonPosition.Y + 0.12)); double b = Math.Max(0.0, Math.Min(1.0, 0.13 * MoonPosition.Y + 0.12)); moonIntensity = (r + g + b) / 3.0; LightAmbientColor += new OpenBveApi.Color.ColorRGB((float)r, (float)g, (float)b); LightDiffuseColor += new OpenBveApi.Color.ColorRGB((float)r, (float)g, (float)b); } double sunFactor = sunIntensity / (sunIntensity + moonIntensity); double moonFactor = moonIntensity / (sunIntensity + moonIntensity); LightPosition = SunPosition * sunFactor + MoonPosition * moonFactor; LightPosition.Normalize(); } }
internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.FrontCarFrontAxle) { if (Direction > 0) { int d = Train.DriverCar; Sounds.SoundBuffer buffer = Train.Cars[d].Sounds.Halt.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[d].Sounds.Halt.Position; if (Train.Specs.PassAlarm == TrainManager.PassAlarmType.Single) { Train.Cars[d].Sounds.Halt.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, d, false); } else if (Train.Specs.PassAlarm == TrainManager.PassAlarmType.Loop) { Train.Cars[d].Sounds.Halt.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, d, true); } } this.DontTriggerAnymore = true; } } }
/// <summary>Is called once a frame, to update the door states of the specified train</summary> /// <param name="Train">The train</param> /// <param name="TimeElapsed">The frame time elapsed</param> private static void UpdateTrainDoors(Train Train, double TimeElapsed) { OpenBveApi.Runtime.DoorStates oldState = OpenBveApi.Runtime.DoorStates.None; OpenBveApi.Runtime.DoorStates newState = OpenBveApi.Runtime.DoorStates.None; for (int i = 0; i < Train.Cars.Length; i++) { bool ld = Train.Cars[i].Doors[0].AnticipatedOpen; bool rd = Train.Cars[i].Doors[1].AnticipatedOpen; double os = Train.Cars[i].Specs.DoorOpenFrequency; double cs = Train.Cars[i].Specs.DoorCloseFrequency; for (int j = 0; j < Train.Cars[i].Doors.Length; j++) { if (Train.Cars[i].Doors[j].Direction == -1 | Train.Cars[i].Doors[j].Direction == 1) { bool shouldBeOpen = Train.Cars[i].Doors[j].Direction == -1 ? ld : rd; if (Train.Cars[i].Doors[j].State > 0.0) { if (Train.Cars[i].Doors[j].Direction == -1) { oldState |= OpenBveApi.Runtime.DoorStates.Left; } else { oldState |= OpenBveApi.Runtime.DoorStates.Right; } } if (shouldBeOpen) { // open Train.Cars[i].Doors[j].State += os * TimeElapsed; if (Train.Cars[i].Doors[j].State > 1.0) { Train.Cars[i].Doors[j].State = 1.0; } } else { // close if (Train.Cars[i].Doors[j].DoorLockDuration > 0.0) { if (Train.Cars[i].Doors[j].State > Train.Cars[i].Doors[j].DoorLockState) { Train.Cars[i].Doors[j].State -= cs * TimeElapsed; } if (Train.Cars[i].Doors[j].State < Train.Cars[i].Doors[j].DoorLockState) { Train.Cars[i].Doors[j].State = Train.Cars[i].Doors[j].DoorLockState; } Train.Cars[i].Doors[j].DoorLockDuration -= TimeElapsed; if (Train.Cars[i].Doors[j].DoorLockDuration < 0.0) { Train.Cars[i].Doors[j].DoorLockDuration = 0.0; } } else { Train.Cars[i].Doors[j].State -= cs * TimeElapsed; } if (Train.Cars[i].Doors[j].AnticipatedReopen && Train.Cars[i].Doors[j].State < Train.Cars[i].Doors[j].InterferingObjectRate) { Train.Cars[i].Doors[j].State = Train.Cars[i].Doors[j].InterferingObjectRate; } if (Train.Cars[i].Doors[j].State < 0.0) { Train.Cars[i].Doors[j].State = 0.0; } } if (Train.Cars[i].Doors[j].State > 0.0) { if (Train.Cars[i].Doors[j].Direction == -1) { newState |= OpenBveApi.Runtime.DoorStates.Left; } else { newState |= OpenBveApi.Runtime.DoorStates.Right; } } } } } if (oldState != OpenBveApi.Runtime.DoorStates.None & newState == OpenBveApi.Runtime.DoorStates.None) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.PilotLampOn.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.PilotLampOn.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } else if (oldState == OpenBveApi.Runtime.DoorStates.None & newState != OpenBveApi.Runtime.DoorStates.None) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.PilotLampOff.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.PilotLampOff.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } if (oldState != newState) { if (Train.Plugin != null) { Train.Plugin.DoorChange(oldState, newState); } } }
protected override void OnRenderFrame(FrameEventArgs e) { Program.MouseMovement(); double timeElapsed = CPreciseTimer.GetElapsedTime(); DateTime time = DateTime.Now; Game.SecondsSinceMidnight = (double)(3600 * time.Hour + 60 * time.Minute + time.Second) + 0.001 * (double)time.Millisecond; lock (Program.LockObj) { ObjectManager.UpdateAnimatedWorldObjects(timeElapsed, false); } if (Program.ReducedMode) { System.Threading.Thread.Sleep(125); } else { System.Threading.Thread.Sleep(1); } bool updatelight = false; bool keep = false; // rotate x if (Program.RotateX == 0) { double d = (1.0 + Math.Abs(RotateXSpeed)) * timeElapsed; if (RotateXSpeed >= -d & RotateXSpeed <= d) { RotateXSpeed = 0.0; } else { RotateXSpeed -= (double)Math.Sign(RotateXSpeed) * d; } } else { double d = (1.0 + 1.0 - 1.0 / (1.0 + RotateXSpeed * RotateXSpeed)) * timeElapsed; double m = 1.0; RotateXSpeed += (double)Program.RotateX * d; if (RotateXSpeed < -m) { RotateXSpeed = -m; } else if (RotateXSpeed > m) { RotateXSpeed = m; } } if (RotateXSpeed != 0.0) { double cosa = Math.Cos(RotateXSpeed * timeElapsed); double sina = Math.Sin(RotateXSpeed * timeElapsed); Program.Renderer.Camera.AbsoluteDirection.Rotate(Vector3.Down, cosa, sina); Program.Renderer.Camera.AbsoluteUp.Rotate(Vector3.Down, cosa, sina); Program.Renderer.Camera.AbsoluteSide.Rotate(Vector3.Down, cosa, sina); keep = true; } // rotate y if (Program.RotateY == 0) { double d = (1.0 + Math.Abs(RotateYSpeed)) * timeElapsed; if (RotateYSpeed >= -d & RotateYSpeed <= d) { RotateYSpeed = 0.0; } else { RotateYSpeed -= (double)Math.Sign(RotateYSpeed) * d; } } else { double d = (1.0 + 1.0 - 1.0 / (1.0 + RotateYSpeed * RotateYSpeed)) * timeElapsed; double m = 1.0; RotateYSpeed += (double)Program.RotateY * d; if (RotateYSpeed < -m) { RotateYSpeed = -m; } else if (RotateYSpeed > m) { RotateYSpeed = m; } } if (RotateYSpeed != 0.0) { double cosa = Math.Cos(RotateYSpeed * timeElapsed); double sina = Math.Sin(RotateYSpeed * timeElapsed); Program.Renderer.Camera.AbsoluteDirection.Rotate(Program.Renderer.Camera.AbsoluteSide, cosa, sina); Program.Renderer.Camera.AbsoluteUp.Rotate(Program.Renderer.Camera.AbsoluteSide, cosa, sina); keep = true; } // move x if (Program.MoveX == 0) { double d = (2.5 + Math.Abs(MoveXSpeed)) * timeElapsed; if (MoveXSpeed >= -d & MoveXSpeed <= d) { MoveXSpeed = 0.0; } else { MoveXSpeed -= (double)Math.Sign(MoveXSpeed) * d; } } else { double d = (5.0 + 10.0 - 10.0 / (1.0 + MoveXSpeed * MoveXSpeed)) * timeElapsed; double m = 25.0; MoveXSpeed += (double)Program.MoveX * d; if (MoveXSpeed < -m) { MoveXSpeed = -m; } else if (MoveXSpeed > m) { MoveXSpeed = m; } } if (MoveXSpeed != 0.0) { Program.Renderer.Camera.AbsolutePosition += MoveXSpeed * timeElapsed * Program.Renderer.Camera.AbsoluteSide; keep = true; } // move y if (Program.MoveY == 0) { double d = (2.5 + Math.Abs(MoveYSpeed)) * timeElapsed; if (MoveYSpeed >= -d & MoveYSpeed <= d) { MoveYSpeed = 0.0; } else { MoveYSpeed -= (double)Math.Sign(MoveYSpeed) * d; } } else { double d = (5.0 + 10.0 - 10.0 / (1.0 + MoveYSpeed * MoveYSpeed)) * timeElapsed; double m = 25.0; MoveYSpeed += (double)Program.MoveY * d; if (MoveYSpeed < -m) { MoveYSpeed = -m; } else if (MoveYSpeed > m) { MoveYSpeed = m; } } if (MoveYSpeed != 0.0) { Program.Renderer.Camera.AbsolutePosition += MoveYSpeed * timeElapsed * Program.Renderer.Camera.AbsoluteUp; keep = true; } // move z if (Program.MoveZ == 0) { double d = (2.5 + Math.Abs(MoveZSpeed)) * timeElapsed; if (MoveZSpeed >= -d & MoveZSpeed <= d) { MoveZSpeed = 0.0; } else { MoveZSpeed -= (double)Math.Sign(MoveZSpeed) * d; } } else { double d = (5.0 + 10.0 - 10.0 / (1.0 + MoveZSpeed * MoveZSpeed)) * timeElapsed; double m = 25.0; MoveZSpeed += (double)Program.MoveZ * d; if (MoveZSpeed < -m) { MoveZSpeed = -m; } else if (MoveZSpeed > m) { MoveZSpeed = m; } } if (MoveZSpeed != 0.0) { Program.Renderer.Camera.AbsolutePosition += MoveZSpeed * timeElapsed * Program.Renderer.Camera.AbsoluteDirection; keep = true; } // lighting if (Program.LightingRelative == -1) { Program.LightingRelative = (double)Program.LightingTarget; updatelight = true; } if (Program.LightingTarget == 0) { if (Program.LightingRelative != 0.0) { Program.LightingRelative -= 0.5 * timeElapsed; if (Program.LightingRelative < 0.0) { Program.LightingRelative = 0.0; } updatelight = true; keep = true; } } else { if (Program.LightingRelative != 1.0) { Program.LightingRelative += 0.5 * timeElapsed; if (Program.LightingRelative > 1.0) { Program.LightingRelative = 1.0; } updatelight = true; keep = true; } } // continue if (Program.ReducedMode) { ReducedModeEnteringTime = 3.0; } else { if (keep) { ReducedModeEnteringTime = 3.0; } else if (ReducedModeEnteringTime <= 0) { Program.ReducedMode = true; Program.Renderer.Camera.AbsoluteSide.Y = 0.0; Program.Renderer.Camera.AbsoluteSide.Normalize(); Program.Renderer.Camera.AbsoluteDirection.Normalize(); Program.Renderer.Camera.AbsoluteUp = Vector3.Cross(Program.Renderer.Camera.AbsoluteDirection, Program.Renderer.Camera.AbsoluteSide); } else { ReducedModeEnteringTime -= timeElapsed; } } if (updatelight) { Program.Renderer.Lighting.OptionAmbientColor.R = (byte)Math.Round(32.0 + 128.0 * Program.LightingRelative * (2.0 - Program.LightingRelative)); Program.Renderer.Lighting.OptionAmbientColor.G = (byte)Math.Round(32.0 + 128.0 * 0.5 * (Program.LightingRelative + Program.LightingRelative * (2.0 - Program.LightingRelative))); Program.Renderer.Lighting.OptionAmbientColor.B = (byte)Math.Round(32.0 + 128.0 * Program.LightingRelative); Program.Renderer.Lighting.OptionDiffuseColor.R = (byte)Math.Round(32.0 + 128.0 * Program.LightingRelative); Program.Renderer.Lighting.OptionDiffuseColor.G = (byte)Math.Round(32.0 + 128.0 * Program.LightingRelative); Program.Renderer.Lighting.OptionDiffuseColor.B = (byte)Math.Round(32.0 + 128.0 * Math.Sqrt(Program.LightingRelative)); } Program.Renderer.Lighting.Initialize(); Program.Renderer.RenderScene(); SwapBuffers(); }
/// <summary>Creates or updates the display list for the static objects. If the display list already exists, it is recreated.</summary> internal void CreateOrUpdateDisplayList() { /* * Load all textures used by the objects in this leaf node. * */ this.LoadTextures(); /* * Begin rendering to the display list. * */ Renderer.OpenGlState state; this.DisplayList.Begin(out state); /* * Render all attached static objects. * */ Renderer.RenderStaticOpaqueObjects(this.StaticOpaqueObjects, this.StaticOpaqueObjectCount, ref state); if (Program.CurrentOptions.ShowGrid) { /* * Render the rectangle and bounding rectangle for debugging purposes. * */ OpenBveApi.Color.ColorRGB brightColor = new OpenBveApi.Color.ColorRGB( (float)Program.RandomNumberGenerator.NextDouble(), (float)Program.RandomNumberGenerator.NextDouble(), (float)Program.RandomNumberGenerator.NextDouble() ); OpenBveApi.Color.ColorRGB darkColor = new OpenBveApi.Color.ColorRGB( 0.5f * brightColor.R, 0.5f * brightColor.G, 0.5f * brightColor.B ); { /* Render the rectangle. */ double x = 0.5 * (this.Rectangle.Right - this.Rectangle.Left); double z = 0.5 * (this.Rectangle.Far - this.Rectangle.Near); OpenBveApi.Math.Vector3[] vertices = new OpenBveApi.Math.Vector3[] { new OpenBveApi.Math.Vector3(-x, -1.1, -z), new OpenBveApi.Math.Vector3(-x, -1.1, z), new OpenBveApi.Math.Vector3(x, -1.1, z), new OpenBveApi.Math.Vector3(x, -1.1, -z) }; Renderer.RenderPolygonFromVertices(vertices, darkColor, OpenBveApi.Color.ColorRGB.Black, ref state); } { /* Render the bounding rectangle. */ OpenBveApi.Math.Vector2 center = new OpenBveApi.Math.Vector2( 0.5 * (this.Rectangle.Left + this.Rectangle.Right), 0.5 * (this.Rectangle.Near + this.Rectangle.Far) ); OpenBveApi.Math.Vector2 nearLeft = new OpenBveApi.Math.Vector2( this.BoundingRectangle.Left - center.X, this.BoundingRectangle.Near - center.Y ); OpenBveApi.Math.Vector2 nearRight = new OpenBveApi.Math.Vector2( this.BoundingRectangle.Right - center.X, this.BoundingRectangle.Near - center.Y ); OpenBveApi.Math.Vector2 farLeft = new OpenBveApi.Math.Vector2( this.BoundingRectangle.Left - center.X, this.BoundingRectangle.Far - center.Y ); OpenBveApi.Math.Vector2 farRight = new OpenBveApi.Math.Vector2( this.BoundingRectangle.Right - center.X, this.BoundingRectangle.Far - center.Y ); OpenBveApi.Math.Vector3[] vertices = new OpenBveApi.Math.Vector3[] { new OpenBveApi.Math.Vector3(nearLeft.X, -1.0, nearLeft.Y), new OpenBveApi.Math.Vector3(farLeft.X, -1.0, farLeft.Y), new OpenBveApi.Math.Vector3(farRight.X, -1.0, farRight.Y), new OpenBveApi.Math.Vector3(nearRight.X, -1.0, nearRight.Y) }; Renderer.RenderPolygonFromVertices(vertices, brightColor, OpenBveApi.Color.ColorRGB.Black, ref state); } } /* * End rendering to the display list. * */ this.DisplayList.End(ref state); }
// process structure /// <summary>Loads binary .X object data and returns a compatible mesh.</summary> /// <param name="fileName">The platform-specific absolute file name of the object.</param> /// <param name="structure">The object structure to process.</param> /// <param name="obj">Receives the compatible mesh.</param> /// <param name="encoding">The fallback encoding.</param> private static bool ProcessStructure(string fileName, Structure structure, out OpenBveApi.Geometry.GenericObject obj, System.Text.Encoding encoding) { System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.InvariantCulture; OpenBveApi.Geometry.FaceVertexMesh compatibleMesh = new OpenBveApi.Geometry.FaceVertexMesh(); obj = null; /* * file */ for (int i = 0; i < structure.Data.Length; i++) { Structure f = structure.Data[i] as Structure; if (f == null) { IO.ReportError(fileName, "Top-level inlined arguments are invalid"); return false; } switch (f.Name) { case "Mesh": { /* * mesh */ if (f.Data.Length < 4) { IO.ReportError(fileName, "Mesh is expected to have at least 4 arguments"); return false; } else if (!(f.Data[0] is int)) { IO.ReportError(fileName, "nVertices is expected to be a DWORD in Mesh"); return false; } else if (!(f.Data[1] is Structure[])) { IO.ReportError(fileName, "vertices[nVertices] is expected to be a Vector array in Mesh"); return false; } else if (!(f.Data[2] is int)) { IO.ReportError(fileName, "nFaces is expected to be a DWORD in Mesh"); return false; } else if (!(f.Data[3] is Structure[])) { IO.ReportError(fileName, "faces[nFaces] is expected to be a MeshFace array in Mesh"); return false; } int nVertices = (int)f.Data[0]; if (nVertices < 0) { IO.ReportError(fileName, "nVertices is expected to be non-negative in Mesh"); return false; } Structure[] sVertices = (Structure[])f.Data[1]; if (nVertices != sVertices.Length) { IO.ReportError(fileName, "nVertices does not match with the length of array vertices in Mesh"); return false; } int nFaces = (int)f.Data[2]; if (nFaces < 0) { IO.ReportError(fileName, "nFaces is expected to be non-negative in Mesh"); return false; } Structure[] sFaces = (Structure[])f.Data[3]; if (nFaces != sFaces.Length) { IO.ReportError(fileName, "nFaces does not match with the length of array faces in Mesh"); return false; } /* * collect vertices */ OpenBveApi.Geometry.Vertex[] vertices = new OpenBveApi.Geometry.Vertex[nVertices]; for (int j = 0; j < nVertices; j++) { if (sVertices[j].Name != "Vector") { IO.ReportError(fileName, "vertices[" + j.ToString(culture) + "] is expected to be of template Vertex in Mesh"); return false; } else if (sVertices[j].Data.Length != 3) { IO.ReportError(fileName, "vertices[" + j.ToString(culture) + "] is expected to have 3 arguments in Mesh"); return false; } else if (!(sVertices[j].Data[0] is double)) { IO.ReportError(fileName, "x is expected to be a float in vertices[" + j.ToString(culture) + "] in Mesh"); return false; } else if (!(sVertices[j].Data[1] is double)) { IO.ReportError(fileName, "y is expected to be a float in vertices[" + j.ToString(culture) + "] in Mesh"); return false; } else if (!(sVertices[j].Data[2] is double)) { IO.ReportError(fileName, "z is expected to be a float in vertices[" + j.ToString(culture) + "] in Mesh"); return false; } double x = (double)sVertices[j].Data[0]; double y = (double)sVertices[j].Data[1]; double z = (double)sVertices[j].Data[2]; vertices[j].SpatialCoordinates = new OpenBveApi.Math.Vector3(x, y, z); } /* * collect faces */ int[][] faces = new int[nFaces][]; OpenBveApi.Math.Vector3[][] faceNormals = new OpenBveApi.Math.Vector3[nFaces][]; int[] faceMaterials = new int[nFaces]; for (int j = 0; j < nFaces; j++) { faceMaterials[j] = -1; } for (int j = 0; j < nFaces; j++) { if (sFaces[j].Name != "MeshFace") { IO.ReportError(fileName, "faces[" + j.ToString(culture) + "] is expected to be of template MeshFace in Mesh"); return false; } else if (sFaces[j].Data.Length != 2) { IO.ReportError(fileName, "face[" + j.ToString(culture) + "] is expected to have 2 arguments in Mesh"); return false; } else if (!(sFaces[j].Data[0] is int)) { IO.ReportError(fileName, "nFaceVertexIndices is expected to be a DWORD in face[" + j.ToString(culture) + "] in Mesh"); return false; } else if (!(sFaces[j].Data[1] is int[])) { IO.ReportError(fileName, "faceVertexIndices[nFaceVertexIndices] is expected to be a DWORD array in face[" + j.ToString(culture) + "] in Mesh"); return false; } int nFaceVertexIndices = (int)sFaces[j].Data[0]; if (nFaceVertexIndices < 0) { IO.ReportError(fileName, "nFaceVertexIndices is expected to be non-negative in MeshFace in Mesh"); return false; } int[] faceVertexIndices = (int[])sFaces[j].Data[1]; if (nFaceVertexIndices != faceVertexIndices.Length) { IO.ReportError(fileName, "nFaceVertexIndices does not match with the length of array faceVertexIndices in face[" + j.ToString(culture) + "] in Mesh"); return false; } faces[j] = new int[nFaceVertexIndices]; faceNormals[j] = new OpenBveApi.Math.Vector3[nFaceVertexIndices]; for (int k = 0; k < nFaceVertexIndices; k++) { if (faceVertexIndices[k] < 0 | faceVertexIndices[k] >= nVertices) { IO.ReportError(fileName, "faceVertexIndices[" + k.ToString(culture) + "] does not reference a valid vertex in face[" + j.ToString(culture) + "] in Mesh"); return false; } faces[j][k] = faceVertexIndices[k]; faceNormals[j][k] = new OpenBveApi.Math.Vector3(0.0f, 0.0f, 0.0f); } } /* * collect additional templates */ Material[] materials = new Material[] { }; for (int j = 4; j < f.Data.Length; j++) { Structure g = f.Data[j] as Structure; if (g == null) { IO.ReportError(fileName, "Unexpected inlined argument encountered in Mesh"); return false; } switch (g.Name) { case "MeshMaterialList": { /* * meshmateriallist */ if (g.Data.Length < 3) { IO.ReportError(fileName, "MeshMaterialList is expected to have at least 3 arguments in Mesh"); return false; } else if (!(g.Data[0] is int)) { IO.ReportError(fileName, "nMaterials is expected to be a DWORD in MeshMaterialList in Mesh"); return false; } else if (!(g.Data[1] is int)) { IO.ReportError(fileName, "nFaceIndexes is expected to be a DWORD in MeshMaterialList in Mesh"); return false; } else if (!(g.Data[2] is int[])) { IO.ReportError(fileName, "faceIndexes[nFaceIndexes] is expected to be a DWORD array in MeshMaterialList in Mesh"); return false; } int nMaterials = (int)g.Data[0]; if (nMaterials < 0) { IO.ReportError(fileName, "nMaterials is expected to be non-negative in MeshMaterialList in Mesh"); return false; } int nFaceIndexes = (int)g.Data[1]; if (nFaceIndexes < 0) { IO.ReportError(fileName, "nFaceIndexes is expected to be non-negative in MeshMaterialList in Mesh"); return false; } else if (nFaceIndexes > nFaces) { IO.ReportError(fileName, "nFaceIndexes does not reference valid faces in MeshMaterialList in Mesh"); return false; } int[] faceIndexes = (int[])g.Data[2]; if (nFaceIndexes != faceIndexes.Length) { IO.ReportError(fileName, "nFaceIndexes does not match with the length of array faceIndexes in face[" + j.ToString(culture) + "] in Mesh"); return false; } for (int k = 0; k < nFaceIndexes; k++) { if (faceIndexes[k] < 0 | faceIndexes[k] >= nMaterials) { IO.ReportError(fileName, "faceIndexes[" + k.ToString(culture) + "] does not reference a valid Material template in MeshMaterialList in Mesh"); return false; } } /* * collect material templates */ int mn = materials.Length; Array.Resize<Material>(ref materials, mn + nMaterials); for (int k = 0; k < nMaterials; k++) { materials[mn + k].FaceColor = new OpenBveApi.Color.ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); materials[mn + k].SpecularColor = new OpenBveApi.Color.ColorRGB(0.0f, 0.0f, 0.0f); materials[mn + k].EmissiveColor = new OpenBveApi.Color.ColorRGB(0.0f, 0.0f, 0.0f); materials[mn + k].TextureFilename = null; } int materialIndex = mn; for (int k = 3; k < g.Data.Length; k++) { Structure h = g.Data[k] as Structure; if (h == null) { IO.ReportError(fileName, "Unexpected inlined argument encountered in MeshMaterialList in Mesh"); return false; } else if (h.Name != "Material") { IO.ReportError(fileName, "Material template expected in MeshMaterialList in Mesh"); return false; } else { /* * material */ if (h.Data.Length < 4) { IO.ReportError(fileName, "Material is expected to have at least 4 arguments in Material in MeshMaterialList in Mesh"); return false; } else if (!(h.Data[0] is Structure)) { IO.ReportError(fileName, "faceColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh"); return false; } else if (!(h.Data[1] is double)) { IO.ReportError(fileName, "power is expected to be a float in Material in MeshMaterialList in Mesh"); return false; } else if (!(h.Data[2] is Structure)) { IO.ReportError(fileName, "specularColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh"); return false; } else if (!(h.Data[3] is Structure)) { IO.ReportError(fileName, "emissiveColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh"); return false; } Structure faceColor = (Structure)h.Data[0]; Structure specularColor = (Structure)h.Data[2]; Structure emissiveColor = (Structure)h.Data[3]; double red, green, blue, alpha; /* * collect face color */ if (faceColor.Name != "ColorRGBA") { IO.ReportError(fileName, "faceColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh"); return false; } else if (faceColor.Data.Length != 4) { IO.ReportError(fileName, "faceColor is expected to have 4 arguments in Material in MeshMaterialList in Mesh"); return false; } else if (!(faceColor.Data[0] is double)) { IO.ReportError(fileName, "red is expected to be a float in faceColor in Material in MeshMaterialList in Mesh"); return false; } else if (!(faceColor.Data[1] is double)) { IO.ReportError(fileName, "green is expected to be a float in faceColor in Material in MeshMaterialList in Mesh"); return false; } else if (!(faceColor.Data[2] is double)) { IO.ReportError(fileName, "blue is expected to be a float in faceColor in Material in MeshMaterialList in Mesh"); return false; } else if (!(faceColor.Data[3] is double)) { IO.ReportError(fileName, "alpha is expected to be a float in faceColor in Material in MeshMaterialList in Mesh"); return false; } red = (double)faceColor.Data[0]; green = (double)faceColor.Data[1]; blue = (double)faceColor.Data[2]; alpha = (double)faceColor.Data[3]; if (red < 0.0 | red > 1.0) { IO.ReportError(fileName, "red is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh"); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { IO.ReportError(fileName, "green is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh"); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { IO.ReportError(fileName, "blue is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh"); blue = blue < 0.5 ? 0.0 : 1.0; } if (alpha < 0.0 | alpha > 1.0) { IO.ReportError(fileName, "alpha is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh"); alpha = alpha < 0.5 ? 0.0 : 1.0; } materials[materialIndex].FaceColor = new OpenBveApi.Color.ColorRGBA((float)red, (float)green, (float)blue, (float)alpha); /* * collect specular color */ if (specularColor.Name != "ColorRGB") { IO.ReportError(fileName, "specularColor is expected to be a ColorRGB in Material in MeshMaterialList in Mesh"); return false; } else if (specularColor.Data.Length != 3) { IO.ReportError(fileName, "specularColor is expected to have 3 arguments in Material in MeshMaterialList in Mesh"); return false; } else if (!(specularColor.Data[0] is double)) { IO.ReportError(fileName, "red is expected to be a float in specularColor in Material in MeshMaterialList in Mesh"); return false; } else if (!(specularColor.Data[1] is double)) { IO.ReportError(fileName, "green is expected to be a float in specularColor in Material in MeshMaterialList in Mesh"); return false; } else if (!(specularColor.Data[2] is double)) { IO.ReportError(fileName, "blue is expected to be a float in specularColor in Material in MeshMaterialList in Mesh"); return false; } red = (double)specularColor.Data[0]; green = (double)specularColor.Data[1]; blue = (double)specularColor.Data[2]; if (red < 0.0 | red > 1.0) { IO.ReportError(fileName, "red is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh"); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { IO.ReportError(fileName, "green is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh"); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { IO.ReportError(fileName, "blue is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh"); blue = blue < 0.5 ? 0.0 : 1.0; } materials[materialIndex].SpecularColor = new OpenBveApi.Color.ColorRGB((float)red, (float)green, (float)blue); /* * collect emissive color */ if (emissiveColor.Name != "ColorRGB") { IO.ReportError(fileName, "emissiveColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh"); return false; } else if (emissiveColor.Data.Length != 3) { IO.ReportError(fileName, "emissiveColor is expected to have 3 arguments in Material in MeshMaterialList in Mesh"); return false; } else if (!(emissiveColor.Data[0] is double)) { IO.ReportError(fileName, "red is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh"); return false; } else if (!(emissiveColor.Data[1] is double)) { IO.ReportError(fileName, "green is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh"); return false; } else if (!(emissiveColor.Data[2] is double)) { IO.ReportError(fileName, "blue is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh"); return false; } red = (double)emissiveColor.Data[0]; green = (double)emissiveColor.Data[1]; blue = (double)emissiveColor.Data[2]; if (red < 0.0 | red > 1.0) { IO.ReportError(fileName, "red is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh"); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { IO.ReportError(fileName, "green is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh"); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { IO.ReportError(fileName, "blue is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh"); blue = blue < 0.5 ? 0.0 : 1.0; } materials[materialIndex].EmissiveColor = new OpenBveApi.Color.ColorRGB((float)red, (float)green, (float)blue); /* * collect additional templates */ for (int l = 4; l < h.Data.Length; l++) { Structure e = h.Data[l] as Structure; if (e == null) { IO.ReportError(fileName, "Unexpected inlined argument encountered in Material in MeshMaterialList in Mesh"); return false; } switch (e.Name) { case "TextureFilename": { /* * texturefilename */ if (e.Data.Length != 1) { IO.ReportError(fileName, "filename is expected to have 1 argument in TextureFilename in Material in MeshMaterialList in Mesh"); return false; } else if (!(e.Data[0] is string)) { IO.ReportError(fileName, "filename is expected to be a string in TextureFilename in Material in MeshMaterialList in Mesh"); return false; } string filename = (string)e.Data[0]; materials[materialIndex].TextureFilename = filename; } break; default: /* * unknown */ IO.ReportError(fileName, "Unsupported template " + e.Name + " encountered in MeshMaterialList in Mesh"); break; } } /* * finish */ materialIndex++; } } if (materialIndex != mn + nMaterials) { IO.ReportError(fileName, "nMaterials does not match the number of Material templates encountered in Material in MeshMaterialList in Mesh"); return false; } /* * assign materials */ for (int k = 0; k < nFaceIndexes; k++) { faceMaterials[k] = faceIndexes[k]; } if (nMaterials != 0) { for (int k = 0; k < nFaces; k++) { if (faceMaterials[k] == -1) { faceMaterials[k] = 0; } } } /* * assign reflective colors to the vertices within each face which uses each material */ for (int currentMaterial = 0; currentMaterial < materials.Length; currentMaterial++) { for (int faceIndex = 0; faceIndex < faces.Length; faceIndex++) { if (faceMaterials[faceIndex] == currentMaterial) { foreach (int vertex in faces[faceIndex]) { vertices[vertex].ReflectiveColor = materials[currentMaterial].FaceColor; } } } } } break; case "MeshTextureCoords": { /* * meshtexturecoords */ if (g.Data.Length != 2) { IO.ReportError(fileName, "MeshTextureCoords is expected to have 2 arguments in Mesh"); return false; } else if (!(g.Data[0] is int)) { IO.ReportError(fileName, "nTextureCoords is expected to be a DWORD in MeshTextureCoords in Mesh"); return false; } else if (!(g.Data[1] is Structure[])) { IO.ReportError(fileName, "textureCoords[nTextureCoords] is expected to be a Coords2d array in MeshTextureCoords in Mesh"); return false; } int nTextureCoords = (int)g.Data[0]; Structure[] textureCoords = (Structure[])g.Data[1]; if (nTextureCoords < 0 | nTextureCoords > nVertices) { IO.ReportError(fileName, "nTextureCoords does not reference valid vertices in MeshTextureCoords in Mesh"); return false; } for (int k = 0; k < nTextureCoords; k++) { if (textureCoords[k].Name != "Coords2d") { IO.ReportError(fileName, "textureCoords[" + k.ToString(culture) + "] is expected to be a Coords2d in MeshTextureCoords in Mesh"); return false; } else if (textureCoords[k].Data.Length != 2) { IO.ReportError(fileName, "textureCoords[" + k.ToString(culture) + "] is expected to have 2 arguments in MeshTextureCoords in Mesh"); return false; } else if (!(textureCoords[k].Data[0] is double)) { IO.ReportError(fileName, "u is expected to be a float in textureCoords[" + k.ToString(culture) + "] in MeshTextureCoords in Mesh"); return false; } else if (!(textureCoords[k].Data[1] is double)) { IO.ReportError(fileName, "v is expected to be a float in textureCoords[" + k.ToString(culture) + "] in MeshTextureCoords in Mesh"); return false; } double u = (double)textureCoords[k].Data[0]; double v = (double)textureCoords[k].Data[1]; vertices[k].TextureCoordinates = new OpenBveApi.Math.Vector2(u, v); } } break; case "MeshNormals": { /* * meshnormals */ if (g.Data.Length != 4) { IO.ReportError(fileName, "MeshNormals is expected to have 4 arguments in Mesh"); return false; } else if (!(g.Data[0] is int)) { IO.ReportError(fileName, "nNormals is expected to be a DWORD in MeshNormals in Mesh"); return false; } else if (!(g.Data[1] is Structure[])) { IO.ReportError(fileName, "normals is expected to be a Vector array in MeshNormals in Mesh"); return false; } else if (!(g.Data[2] is int)) { IO.ReportError(fileName, "nFaceNormals is expected to be a DWORD in MeshNormals in Mesh"); return false; } else if (!(g.Data[3] is Structure[])) { IO.ReportError(fileName, "faceNormals is expected to be a MeshFace array in MeshNormals in Mesh"); return false; } int nNormals = (int)g.Data[0]; if (nNormals < 0) { IO.ReportError(fileName, "nNormals is expected to be non-negative in MeshNormals in Mesh"); return false; } Structure[] sNormals = (Structure[])g.Data[1]; if (nNormals != sNormals.Length) { IO.ReportError(fileName, "nNormals does not match with the length of array normals in MeshNormals in Mesh"); return false; } int nFaceNormals = (int)g.Data[2]; if (nFaceNormals < 0 | nFaceNormals > nFaces) { IO.ReportError(fileName, "nNormals does not reference valid vertices in MeshNormals in Mesh"); return false; } Structure[] sFaceNormals = (Structure[])g.Data[3]; if (nFaceNormals != sFaceNormals.Length) { IO.ReportError(fileName, "nFaceNormals does not match with the length of array faceNormals in MeshNormals in Mesh"); return false; } /* * collect normals */ OpenBveApi.Math.Vector3[] normals = new OpenBveApi.Math.Vector3[nNormals]; for (int k = 0; k < nNormals; k++) { if (sNormals[k].Name != "Vector") { IO.ReportError(fileName, "normals[" + k.ToString(culture) + "] is expected to be of template Vertex in MeshNormals in Mesh"); return false; } else if (sNormals[k].Data.Length != 3) { IO.ReportError(fileName, "normals[" + k.ToString(culture) + "] is expected to have 3 arguments in MeshNormals in Mesh"); return false; } else if (!(sNormals[k].Data[0] is double)) { IO.ReportError(fileName, "x is expected to be a float in normals[" + k.ToString(culture) + "] in MeshNormals in Mesh"); return false; } else if (!(sNormals[k].Data[1] is double)) { IO.ReportError(fileName, "y is expected to be a float in normals[" + k.ToString(culture) + " ]in MeshNormals in Mesh"); return false; } else if (!(sNormals[k].Data[2] is double)) { IO.ReportError(fileName, "z is expected to be a float in normals[" + k.ToString(culture) + "] in MeshNormals in Mesh"); return false; } double x = (double)sNormals[k].Data[0]; double y = (double)sNormals[k].Data[1]; double z = (double)sNormals[k].Data[2]; OpenBveApi.Math.Vector3 normal = new OpenBveApi.Math.Vector3(x, y, z); if (!normal.IsNullVector()) { normal.Normalize(); } normals[k] = normal; } /* * collect faces */ for (int k = 0; k < nFaceNormals; k++) { if (sFaceNormals[k].Name != "MeshFace") { IO.ReportError(fileName, "faceNormals[" + k.ToString(culture) + "] is expected to be of template MeshFace in MeshNormals in Mesh"); return false; } else if (sFaceNormals[k].Data.Length != 2) { IO.ReportError(fileName, "faceNormals[" + k.ToString(culture) + "] is expected to have 2 arguments in MeshNormals in Mesh"); return false; } else if (!(sFaceNormals[k].Data[0] is int)) { IO.ReportError(fileName, "nFaceVertexIndices is expected to be a DWORD in faceNormals[" + k.ToString(culture) + "] in MeshNormals in Mesh"); return false; } else if (!(sFaceNormals[k].Data[1] is int[])) { IO.ReportError(fileName, "faceVertexIndices[nFaceVertexIndices] is expected to be a DWORD array in faceNormals[" + k.ToString(culture) + "] in MeshNormals in Mesh"); return false; } int nFaceVertexIndices = (int)sFaceNormals[k].Data[0]; if (nFaceVertexIndices < 0 | nFaceVertexIndices > faces[k].Length) { IO.ReportError(fileName, "nFaceVertexIndices does not reference a valid vertex in MeshFace in MeshNormals in Mesh"); return false; } int[] faceVertexIndices = (int[])sFaceNormals[k].Data[1]; if (nFaceVertexIndices != faceVertexIndices.Length) { IO.ReportError(fileName, "nFaceVertexIndices does not match with the length of array faceVertexIndices in faceNormals[" + k.ToString(culture) + "] in MeshFace in MeshNormals in Mesh"); return false; } for (int l = 0; l < nFaceVertexIndices; l++) { if (faceVertexIndices[l] < 0 | faceVertexIndices[l] >= nNormals) { IO.ReportError(fileName, "faceVertexIndices[" + l.ToString(culture) + "] does not reference a valid normal in faceNormals[" + k.ToString(culture) + "] in MeshFace in MeshNormals in Mesh"); return false; } faceNormals[k][l] = normals[faceVertexIndices[l]]; } } } break; default: /* * unknown */ IO.ReportError(fileName, "Unsupported template " + g.Name + " encountered in Mesh"); break; } } /* * default material */ if (materials.Length == 0) { materials = new Material[1]; materials[0].FaceColor = new OpenBveApi.Color.ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); materials[0].EmissiveColor = new OpenBveApi.Color.ColorRGB(0.0f, 0.0f, 0.0f); materials[0].SpecularColor = new OpenBveApi.Color.ColorRGB(0.0f, 0.0f, 0.0f); materials[0].TextureFilename = null; for (int j = 0; j < nFaces; j++) { faceMaterials[j] = 0; } } /* * eliminate non-visible faces and associated properties */ int[][] visibleFaces = new int[nFaces][]; int newFaceCount = 0; for (int currentFace = 0; currentFace < faces.Length; currentFace++) { if (faces[currentFace].Length >= 3) { visibleFaces[newFaceCount] = faces[currentFace]; faceNormals[newFaceCount] = faceNormals[currentFace]; faceMaterials[newFaceCount] = faceMaterials[currentFace]; newFaceCount++; } } Array.Resize<int[]>(ref visibleFaces, newFaceCount); Array.Resize<OpenBveApi.Math.Vector3[]>(ref faceNormals, newFaceCount); Array.Resize<int>(ref faceMaterials, newFaceCount); /* * prepare compatible mesh for data */ int vertexCount = 0; for (int j = 0; j < visibleFaces.Length; j++) { foreach (int face in visibleFaces[j]) { vertexCount++; } } Array.Resize<OpenBveApi.Geometry.Vertex>(ref compatibleMesh.Vertices, vertexCount); Array.Resize<OpenBveApi.Geometry.Face>(ref compatibleMesh.Faces, visibleFaces.Length); Array.Resize<OpenBveApi.Geometry.Material>(ref compatibleMesh.Materials, materials.Length); /* * apply vertex data */ vertexCount = 0; for (int faceIndex = 0; faceIndex < visibleFaces.Length; faceIndex++) { compatibleMesh.Faces[faceIndex].Vertices = new int[visibleFaces[faceIndex].Length]; /* * go through each list of vertices in face and get vertex data */ for (int vertexIndex = 0; vertexIndex < visibleFaces[faceIndex].Length; vertexIndex++) { OpenBveApi.Geometry.Vertex newVertex = new OpenBveApi.Geometry.Vertex(); newVertex.SpatialCoordinates = vertices[visibleFaces[faceIndex][vertexIndex]].SpatialCoordinates; newVertex.ReflectiveColor = vertices[visibleFaces[faceIndex][vertexIndex]].ReflectiveColor; newVertex.TextureCoordinates = vertices[visibleFaces[faceIndex][vertexIndex]].TextureCoordinates; newVertex.Tag = visibleFaces[faceIndex][vertexIndex]; /* * check for null vector normal and create face derived normal if necessary */ if (!faceNormals[faceIndex][vertexIndex].IsNullVector()) { newVertex.Normal = faceNormals[faceIndex][vertexIndex]; } else if (visibleFaces[faceIndex].Length >= 3) { OpenBveApi.Math.Vector3 vertexA = vertices[visibleFaces[faceIndex][0]].SpatialCoordinates; OpenBveApi.Math.Vector3 vertexB = vertices[visibleFaces[faceIndex][1]].SpatialCoordinates; OpenBveApi.Math.Vector3 vertexC = vertices[visibleFaces[faceIndex][2]].SpatialCoordinates; if (!OpenBveApi.Math.Vector3.CreateNormal(vertexA, vertexB, vertexC, out newVertex.Normal)) { newVertex.Normal = OpenBveApi.Math.Vector3.Up; } } /* * assign vertex to mesh and finalise faces */ compatibleMesh.Vertices[vertexCount] = newVertex; compatibleMesh.Faces[faceIndex].Vertices[vertexIndex] = vertexCount; compatibleMesh.Faces[faceIndex].Type = OpenBveApi.Geometry.FaceType.Polygon; vertexCount++; } } /* * assign texture wrap mode, texture handles, X format transparent color, and emissive colors */ for (int materialIndex = 0; materialIndex < materials.Length; materialIndex++) { bool emissive = materials[materialIndex].EmissiveColor.R != 0 | materials[materialIndex].EmissiveColor.G != 0 | materials[materialIndex].EmissiveColor.B != 0; if (materials[materialIndex].TextureFilename != null) { /* * default to clamp to edge and override if necessary */ OpenBveApi.Texture.TextureWrapMode horizontalWrapMode, verticalWrapMode; horizontalWrapMode = OpenBveApi.Texture.TextureWrapMode.ClampToEdge; verticalWrapMode = OpenBveApi.Texture.TextureWrapMode.ClampToEdge; for (int j = 0; j < compatibleMesh.Faces.Length; j++) { for (int k = 0; k < visibleFaces[j].Length; k++) { if (vertices[visibleFaces[j][k]].TextureCoordinates.X < 0.0 | vertices[visibleFaces[j][k]].TextureCoordinates.X > 1.0) { horizontalWrapMode = OpenBveApi.Texture.TextureWrapMode.Repeat; } if (vertices[visibleFaces[j][k]].TextureCoordinates.Y < 0.0 | vertices[visibleFaces[j][k]].TextureCoordinates.Y > 1.0) { verticalWrapMode = OpenBveApi.Texture.TextureWrapMode.Repeat; } } } /* * register texture and get handle */ string folder = System.IO.Path.GetDirectoryName(fileName); string textureFile = OpenBveApi.Path.CombineFile(folder, materials[materialIndex].TextureFilename); OpenBveApi.Path.PathReference path = new OpenBveApi.Path.FileReference(textureFile); OpenBveApi.General.Origin origin = new OpenBveApi.General.Origin(path, null, encoding); OpenBveApi.Color.TransparentColor transparentColor = new OpenBveApi.Color.TransparentColor(0, 0, 0, true); OpenBveApi.Texture.TextureParameters parameters = new OpenBveApi.Texture.TextureParameters(transparentColor, horizontalWrapMode, verticalWrapMode, null); OpenBveApi.Texture.TextureHandle handle; if (Interfaces.Host.RegisterTexture(origin, parameters, out handle) == OpenBveApi.General.Result.Successful) { compatibleMesh.Materials[materialIndex].DaytimeTexture = handle; } else { compatibleMesh.Materials[materialIndex].DaytimeTexture = null; } if (!System.IO.File.Exists(textureFile)) { IO.ReportError(fileName, materialIndex.ToString(), textureFile.ToString()); } } compatibleMesh.Materials[materialIndex].EmissiveColor = materials[materialIndex].EmissiveColor; compatibleMesh.Materials[materialIndex].NighttimeTexture = null; } /* * assign material reference to face */ for (int j = 0; j < faceMaterials.Length; j++) { compatibleMesh.Faces[j].Material = faceMaterials[j]; } break; } case "Header": break; default: /* * unknown */ IO.ReportError(fileName, "Unsupported template " + f.Name + " encountered"); break; } } /* * return */ obj = compatibleMesh; return true; }
internal static void PlaySound(ref int SoundSourceIndex, int SoundBufferIndex, TrainManager.Train Train, int CarIndex, Vector3 Position, Importance Important, bool Looped, double Pitch, double Gain) { PlaySound(ref SoundSourceIndex, true, SoundBufferIndex, Train, CarIndex, Position, Important, Looped, Pitch, Gain); }
/// <summary>Updates the sound component. Should be called every frame.</summary> /// <param name="timeElapsed">The time in seconds that elapsed since the last call to this function.</param> private static void UpdateLinearModel(double timeElapsed) { /* * Set up the listener * */ OpenBveApi.Math.Vector3 listenerPosition = World.AbsoluteCameraPosition; OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); OpenBveApi.Math.Vector3 listenerVelocity = World.CameraAlignmentSpeed.Position; AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); var Orientation = new[] { (float)listenerOrientation.Z.X, (float)listenerOrientation.Z.Y, (float)listenerOrientation.Z.Z, -(float)listenerOrientation.Y.X, -(float)listenerOrientation.Y.Y, -(float)listenerOrientation.Y.Z }; AL.Listener(ALListenerfv.Orientation, ref Orientation); /* * Set up the atmospheric attributes * */ double elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double airTemperature = Game.GetAirTemperature(elevation); double airPressure = Game.GetAirPressure(elevation, airTemperature); double speedOfSound = Game.GetSpeedOfSound(airPressure, airTemperature); try { AL.SpeedOfSound((float)speedOfSound); } catch { } /* * Update the sound sources * */ int actuallyPlaying = 0; for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.StopPending) { /* * The sound is still playing but is to be stopped. * Stop the sound, then remove it from the list of * sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (Sources[i].State == SoundSourceState.Stopped) { /* * The sound was already stopped. Remove it from * the list of sound sources. * */ Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (GlobalMute) { /* * The sound is playing or about to be played, but * the global mute option is enabled. Stop the sound * sound if necessary, then remove it from the list * of sound sources if the sound is not looping. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * The sound is to be played or is already playing. * Calculate the sound gain. * */ OpenBveApi.Math.Vector3 position; OpenBveApi.Math.Vector3 velocity; switch (Sources[i].Type) { case SoundType.TrainCar: OpenBveApi.Math.Vector3 direction; Sources[i].Train.Cars[Sources[i].Car].CreateWorldCoordinates(Sources[i].Position, out position, out direction); velocity = Sources[i].Train.Cars[Sources[i].Car].Specs.CurrentSpeed * direction; break; default: position = Sources[i].Position; velocity = OpenBveApi.Math.Vector3.Zero; break; } OpenBveApi.Math.Vector3 positionDifference = position - listenerPosition; double gain; if (GlobalMute) { gain = 0.0; } else { double distance = positionDifference.Norm(); double innerRadius = Sources[i].Radius; if (World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { innerRadius *= 0.5; } } double outerRadius = OuterRadiusFactor * innerRadius; if (distance < outerRadius) { if (distance <= innerRadius) { gain = Sources[i].Volume; } else { gain = (distance - outerRadius) / (innerRadius - outerRadius); gain *= Sources[i].Volume; } gain = 3.0 * gain * gain - 2.0 * gain * gain * gain; } else { gain = 0.0; } } if (gain <= GainThreshold) { /* * If the gain is too low to be audible, stop the sound. * If the sound is not looping, stop it if necessary, * then remove it from the list of sound sources. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * Play the sound and update position, velocity, pitch and gain. * For non-looping sounds, check if the sound is still playing. * */ gain = (gain - GainThreshold) / (1.0 - GainThreshold); if (Sources[i].State != SoundSourceState.Playing) { LoadBuffer(Sources[i].Buffer); if (Sources[i].Buffer.Loaded) { AL.GenSources(1, out Sources[i].OpenAlSourceName); AL.Source(Sources[i].OpenAlSourceName, ALSourcei.Buffer, Sources[i].Buffer.OpenAlBufferName); } else { /* * We cannot play the sound because * the buffer could not be loaded. * */ Sources[i].State = SoundSourceState.Stopped; continue; } } AL.Source(Sources[i].OpenAlSourceName, ALSource3f.Position, (float)positionDifference.X, (float)positionDifference.Y, (float)positionDifference.Z); AL.Source(Sources[i].OpenAlSourceName, ALSource3f.Velocity, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); AL.Source(Sources[i].OpenAlSourceName, ALSourcef.Pitch, (float)Sources[i].Pitch); AL.Source(Sources[i].OpenAlSourceName, ALSourcef.Gain, (float)gain); if (Sources[i].State != SoundSourceState.Playing) { AL.Source(Sources[i].OpenAlSourceName, ALSourceb.Looping, Sources[i].Looped); AL.SourcePlay(Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Playing; } if (!Sources[i].Looped) { int state; AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out state); if (state != (int)ALSourceState.Initial & state != (int)ALSourceState.Playing) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else { actuallyPlaying++; } } else { actuallyPlaying++; } } } } /* * Adjust the outer radius factor / the clamp factor. * */ if (actuallyPlaying >= Interface.CurrentOptions.SoundNumber - 2) { /* * Too many sounds are playing. * Reduce the outer radius factor. * */ OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < -OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = -OuterRadiusFactorMaximumSpeed; } } else if (actuallyPlaying <= Interface.CurrentOptions.SoundNumber - 6) { /* * Only few sounds are playing. * Increase the outer radius factor. * */ OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = OuterRadiusFactorMaximumSpeed; } } else { /* * Neither too many nor too few sounds are playing. * Stabilize the outer radius factor. * */ if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > 0.0) { OuterRadiusFactorSpeed = 0.0; } } else { OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed = 0.0; } } } OuterRadiusFactor += OuterRadiusFactorSpeed * timeElapsed; if (OuterRadiusFactor < OuterRadiusFactorMinimum) { OuterRadiusFactor = OuterRadiusFactorMinimum; OuterRadiusFactorSpeed = 0.0; } else if (OuterRadiusFactor > OuterRadiusFactorMaximum) { OuterRadiusFactor = OuterRadiusFactorMaximum; OuterRadiusFactorSpeed = 0.0; } }
// instance functions /// <summary>Takes an object, its position and orientation on the grid node, and then updates the bounding rectangle accordingly.</summary> /// <param name="libraryIndex">A reference to an object stored in the object library.</param> /// <param name="gridPosition">The position of the object relative to the center of the contained grid node.</param> /// <param name="gridOrientation">The orientation of the object.</param> internal void UpdateBoundingRectangle(int libraryIndex, OpenBveApi.Math.Vector3 gridPosition, OpenBveApi.Math.Orientation3 gridOrientation) { OpenBveApi.Geometry.FaceVertexMesh mesh = ObjectLibrary.Library.Objects[libraryIndex] as OpenBveApi.Geometry.FaceVertexMesh; if (mesh != null) { OpenBveApi.Math.Vector3 gridCenter = new OpenBveApi.Math.Vector3( 0.5 * (this.Rectangle.Left + this.Rectangle.Right), 0.0, 0.5 * (this.Rectangle.Near + this.Rectangle.Far) ); OpenBveApi.Math.Vector3 absolutePosition = gridCenter + gridPosition; for (int i = 0; i < mesh.Vertices.Length; i++) { OpenBveApi.Math.Vector3 vector = absolutePosition + OpenBveApi.Math.Vector3.Rotate(mesh.Vertices[i].SpatialCoordinates, gridOrientation); if (vector.X < this.BoundingRectangle.Left) { this.BoundingRectangle.Left = vector.X; } if (vector.X > this.BoundingRectangle.Right) { this.BoundingRectangle.Right = vector.X; } if (vector.Z < this.BoundingRectangle.Near) { this.BoundingRectangle.Near = vector.Z; } if (vector.Z > this.BoundingRectangle.Far) { this.BoundingRectangle.Far = vector.Z; } } } if (this.Parent is GridInternalNode) { GridInternalNode intern = (GridInternalNode)this.Parent; intern.UpdateBoundingRectangle(); } }
// --- render the whole scene --- /// <summary>Renders the whole screen, including the scene and the interface components.</summary> internal static void Render(double elapsedTime) { /* * Initialize rendering this frame. * */ Gl.glClearColor(LightAmbientColor.R, LightAmbientColor.G, LightAmbientColor.B, 1.0f); //Gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glLoadIdentity(); if (false) { const double radius = 30.0; double angle = 0.001 * (double)System.Environment.TickCount; Camera.Position = new OpenBveApi.Math.Vector3(Math.Sin(angle) * radius, 10.0, -Math.Cos(angle) * radius); OpenBveApi.Math.Vector3 center = new OpenBveApi.Math.Vector3(0.0, 0.0, 0.0); OpenBveApi.Math.Vector3 direction = OpenBveApi.Math.Vector3.Normalize(center - Camera.Position); OpenBveApi.Math.Vector3 side = new OpenBveApi.Math.Vector3(direction.Z, 0.0, -direction.X); OpenBveApi.Math.Vector3 up = OpenBveApi.Math.Vector3.Cross(direction, side); Camera.Orientation = new OpenBveApi.Math.Orientation3(side, up, direction); } /* * Ensure that the leaf node the camera is currently in * has been updated to reflect the latest camera position. * */ Camera.UpdateGridLeafNode(); /* * Apply the camera's position and orientation to OpenGL. * */ { OpenBveApi.Math.Vector3 position = Camera.Position - Camera.GridLeafNodeCenter; OpenBveApi.Math.Vector3 direction = Camera.Orientation.Z; OpenBveApi.Math.Vector3 up = Camera.Orientation.Y; OpenBveApi.Math.Vector3 center = position + direction; Glu.gluLookAt(position.X, position.Y, position.Z, center.X, center.Y, center.Z, up.X, up.Y, up.Z); } /* * Set lighting conditions * */ Gl.glEnable(Gl.GL_LIGHTING); UpdateLighting(Timing.SecondsSinceMidnight); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, new float[] { (float)LightPosition.X, (float)LightPosition.Y, (float)LightPosition.Z, 0.0f }); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, new float[] { LightAmbientColor.R, LightAmbientColor.G, LightAmbientColor.B, 1.0f }); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, new float[] { LightDiffuseColor.R, LightDiffuseColor.G, LightDiffuseColor.B, 1.0f }); /* * Render all leaf nodes visible from the leaf node the camera is currently in. * */ Triangle[] triangles = null; if (Program.CurrentOptions.BlockClipping) { InitializeBlockClipping(out triangles); } int blockCount = 0; int blocksClipped = 0; int objectCount = 0; if (Camera.GridLeafNode != null) { int length = Camera.GridLeafNode.VisibleLeafNodes.Length; for (int i = 0; i < Camera.GridLeafNode.VisibleLeafNodes.Length; i++) { bool visible; if (Program.CurrentOptions.BlockClipping) { visible = BlockIntersectsTriangles(Camera.GridLeafNode.VisibleLeafNodes[i].BoundingRectangle, triangles); } else { visible = true; } if (visible) { RenderGridLeafNode(Camera.GridLeafNode.VisibleLeafNodes[i], false); objectCount += Camera.GridLeafNode.VisibleLeafNodes[i].StaticOpaqueObjectCount; } else { blocksClipped++; } blockCount++; } } //Windows.UpdateDebugText("blockCount=" + blockCount.ToString() + ", blocksClipped=" + blocksClipped.ToString()); // if (Camera.GridLeafNode == null) { // Windows.UpdateDebugText("NULL"); // } else if (Camera.GridLeafNode is ObjectGrid.GridPopulatedLeafNode) { // ObjectGrid.GridLeafNode leaf = (ObjectGrid.GridLeafNode)Camera.GridLeafNode; // Windows.UpdateDebugText("GridPopulatedLeafNode, rect(left=" + leaf.Rectangle.Left.ToString("0") + ", right=" + leaf.Rectangle.Right.ToString("0") + ", near=" + leaf.Rectangle.Near.ToString("0") + ", far=" + leaf.Rectangle.Far.ToString("0") + "), bounding(left=" + leaf.BoundingRectangle.Left.ToString("0") + ", right=" + leaf.BoundingRectangle.Right.ToString("0") + ", near=" + leaf.BoundingRectangle.Near.ToString("0") + ", far=" + leaf.BoundingRectangle.Far.ToString("0") + "), count=" + leaf.VisibleLeafNodes.Length.ToString()); // } else if (Camera.GridLeafNode is ObjectGrid.GridUnpopulatedLeafNode) { // ObjectGrid.GridLeafNode leaf = (ObjectGrid.GridLeafNode)Camera.GridLeafNode; // Windows.UpdateDebugText("GridUnpopulatedLeafNode, rect(left=" + leaf.Rectangle.Left.ToString("0") + ", right=" + leaf.Rectangle.Right.ToString("0") + ", near=" + leaf.Rectangle.Near.ToString("0") + ", far=" + leaf.Rectangle.Far.ToString("0") + "), bounding(left=" + leaf.BoundingRectangle.Left.ToString("0") + ", right=" + leaf.BoundingRectangle.Right.ToString("0") + ", near=" + leaf.BoundingRectangle.Near.ToString("0") + ", far=" + leaf.BoundingRectangle.Far.ToString("0") + "), count=" + leaf.VisibleLeafNodes.Length.ToString()); // } else { // Windows.UpdateDebugText("invalid"); // } if (Camera.GridLeafNode == null) { Windows.UpdateDebugText("You have left the bounds of the grid root node."); } /* * Render transparent world faces. * */ TransparentWorldFaces.SortAllFaces(elapsedTime, false); TransparentWorldFaces.RenderAllLists(); /* * Render overlays in screen coordinates. * */ { /* * Prepare the projection matrix to work * temporarily in screen coordinates. * */ Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPushMatrix(); Gl.glLoadIdentity(); Gl.glOrtho(0.0, (double)Screen.Properties.Width, (double)Screen.Properties.Height, 0.0, -1.0, 1.0); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); Gl.glLoadIdentity(); Gl.glDisable(Gl.GL_LIGHTING); /* * Render GUI. * */ Windows.Render(); /* * Undo the matrix transformation. * */ Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_MODELVIEW); } /* * Perform frame-finalizing tasks. * */ Sdl.SDL_GL_SwapBuffers(); #if DEBUG CheckForOpenGlError("Renderer.Render"); #endif }
// instance functions /// <summary>Adds a new instance of a static object to the grid.</summary> /// <param name="libraryIndex">The index to a library object.</param> /// <param name="position">The absolute world position of the object.</param> /// <param name="orientation">The absolute world orientation of the object.</param> internal void Add(int libraryIndex, OpenBveApi.Math.Vector3 position, OpenBveApi.Math.Orientation3 orientation) { if (this.Root == null) { // the root node does not exist yet OpenBveApi.Math.Vector3 gridPosition = new OpenBveApi.Math.Vector3(0.0, position.Y, 0.0); GridPopulatedLeafNode leaf = new GridPopulatedLeafNode( null, new GridBounds( position.X - 0.5 * this.SideLength, position.X + 0.5 * this.SideLength, position.Z - 0.5 * this.SideLength, position.Z + 0.5 * this.SideLength ), new StaticOpaqueObject(libraryIndex, gridPosition, orientation) ); leaf.UpdateBoundingRectangle(libraryIndex, gridPosition, orientation); this.Root = leaf; } else { // the root node exists while (true) { if (position.X >= this.Root.Rectangle.Left & position.X <= this.Root.Rectangle.Right & position.Z >= this.Root.Rectangle.Near & position.Z <= this.Root.Rectangle.Far) { // the position is within the bounds of the root node GridNode node = this.Root; double left = this.Root.Rectangle.Left; double right = this.Root.Rectangle.Right; double near = this.Root.Rectangle.Near; double far = this.Root.Rectangle.Far; while (true) { if (node is GridPopulatedLeafNode) { // populated leaf node OpenBveApi.Math.Vector3 gridPosition = new OpenBveApi.Math.Vector3( position.X - 0.5 * (left + right), position.Y, position.Z - 0.5 * (near + far) ); GridPopulatedLeafNode leaf = (GridPopulatedLeafNode)node; if (leaf.StaticOpaqueObjectCount == leaf.StaticOpaqueObjects.Length) { Array.Resize<StaticOpaqueObject>(ref leaf.StaticOpaqueObjects, leaf.StaticOpaqueObjects.Length << 1); } leaf.VisibleLeafNodes = null; leaf.StaticOpaqueObjects[leaf.StaticOpaqueObjectCount] = new StaticOpaqueObject(libraryIndex, gridPosition, orientation); leaf.StaticOpaqueObjectCount++; leaf.UpdateBoundingRectangle(libraryIndex, gridPosition, orientation); break; } else if (node is GridInternalNode) { // internal node GridInternalNode intern = (GridInternalNode)node; int index; double centerX = 0.5 * (left + right); double centerZ = 0.5 * (near + far); if (position.Z <= centerZ) { if (position.X <= centerX) { index = 0; right = centerX; far = centerZ; } else { index = 1; left = centerX; far = centerZ; } } else { if (position.X <= centerX) { index = 2; right = centerX; near = centerZ; } else { index = 3; left = centerX; near = centerZ; } } if (intern.Children[index] is GridUnpopulatedLeafNode) { double sideLength = 0.5 * (right - left + far - near); const double toleranceFactor = 1.01; if (sideLength < toleranceFactor * this.SideLength) { // create populated leaf child GridPopulatedLeafNode child = new GridPopulatedLeafNode( intern, new GridBounds(left, right, near, far), null ); child.BoundingRectangle = GridBounds.Uninitialized; intern.Children[index] = child; node = child; } else { // create internal child GridInternalNode child = new GridInternalNode( intern, new GridBounds(left, right, near, far), new GridNode[] { null, null, null, null } ); child.Children[0] = new GridUnpopulatedLeafNode(child, new GridBounds(left, 0.5 * (left + right), near, 0.5 * (near + far))); child.Children[1] = new GridUnpopulatedLeafNode(child, new GridBounds(0.5 * (left + right), right, near, 0.5 * (near + far))); child.Children[2] = new GridUnpopulatedLeafNode(child, new GridBounds(left, 0.5 * (left + right), 0.5 * (near + far), far)); child.Children[3] = new GridUnpopulatedLeafNode(child, new GridBounds(0.5 * (left + right), right, 0.5 * (near + far), far)); intern.Children[index] = child; node = child; } } else { // go to child node = intern.Children[index]; } } else { throw new InvalidOperationException(); } } break; } else { // the position is outside the bounds of the root node if (position.Z <= 0.5 * (this.Root.Rectangle.Near + this.Root.Rectangle.Far)) { if (position.X <= 0.5 * (this.Root.Rectangle.Left + this.Root.Rectangle.Right)) { // expand toward near-left GridInternalNode intern = new GridInternalNode( null, new GridBounds( 2.0 * this.Root.Rectangle.Left - this.Root.Rectangle.Right, this.Root.Rectangle.Right, 2.0 * this.Root.Rectangle.Near - this.Root.Rectangle.Far, this.Root.Rectangle.Far ), new GridNode[] { null, null, null, this.Root } ); this.Root.Parent = intern; intern.Children[0] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[1] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[2] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.UpdateBoundingRectangle(); this.Root = intern; } else { // expand toward near-right GridInternalNode intern = new GridInternalNode( null, new GridBounds( this.Root.Rectangle.Left, 2.0 * this.Root.Rectangle.Right - this.Root.Rectangle.Left, 2.0 * this.Root.Rectangle.Near - this.Root.Rectangle.Far, this.Root.Rectangle.Far ), new GridNode[] { null, null, this.Root, null } ); this.Root.Parent = intern; intern.Children[0] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[1] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[3] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.UpdateBoundingRectangle(); this.Root = intern; } } else { if (position.X <= 0.5 * (this.Root.Rectangle.Left + this.Root.Rectangle.Right)) { // expand toward far-left GridInternalNode intern = new GridInternalNode( null, new GridBounds( 2.0 * this.Root.Rectangle.Left - this.Root.Rectangle.Right, this.Root.Rectangle.Right, this.Root.Rectangle.Near, 2.0 * this.Root.Rectangle.Far - this.Root.Rectangle.Near ), new GridNode[] { null, this.Root, null, null } ); this.Root.Parent = intern; intern.Children[0] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[2] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.Children[3] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.UpdateBoundingRectangle(); this.Root = intern; } else { // expand toward far-right GridInternalNode intern = new GridInternalNode( null, new GridBounds( this.Root.Rectangle.Left, 2.0 * this.Root.Rectangle.Right - this.Root.Rectangle.Left, this.Root.Rectangle.Near, 2.0 * this.Root.Rectangle.Far - this.Root.Rectangle.Near ), new GridNode[] { this.Root, null, null, null } ); this.Root.Parent = intern; intern.Children[1] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[2] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.Children[3] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.UpdateBoundingRectangle(); this.Root = intern; } } } } } }
// constructors /// <summary>Creates a new instance of this class.</summary> /// <param name="libraryIndex">A reference to an object stored in the object library.</param> /// <param name="gridPosition">The position of the object relative to the center of the grid node.</param> /// <param name="gridOrientation">The orientation of the object.</param> internal StaticOpaqueObject(int libraryIndex, OpenBveApi.Math.Vector3 gridPosition, OpenBveApi.Math.Orientation3 gridOrientation) { this.LibraryIndex = libraryIndex; this.GridPosition = gridPosition; this.GridOrientation = gridOrientation; }
/// <summary>Is called once a frame to update the station state for the given train</summary> /// <param name="Train">The train</param> /// <param name="TimeElapsed">The frame time elapsed</param> private static void UpdateTrainStation(Train Train, double TimeElapsed) { if (Train.Station >= 0) { int i = Train.Station; int n = Program.CurrentRoute.Stations[Train.Station].GetStopIndex(Train.NumberOfCars); double tf, tb; if (n >= 0) { double p0 = Train.Cars[0].FrontAxle.Follower.TrackPosition - Train.Cars[0].FrontAxle.Position + 0.5 * Train.Cars[0].Length; double p1 = Program.CurrentRoute.Stations[i].Stops[n].TrackPosition; tf = Program.CurrentRoute.Stations[i].Stops[n].ForwardTolerance; tb = Program.CurrentRoute.Stations[i].Stops[n].BackwardTolerance; Train.StationDistanceToStopPoint = p1 - p0; } else { Train.StationDistanceToStopPoint = 0.0; tf = 5.0; tb = 5.0; } if (Train.StationState == TrainStopState.Pending) { Train.StationDepartureSoundPlayed = false; if (Program.CurrentRoute.Stations[i].StopsHere(Train)) { Train.StationDepartureSoundPlayed = false; //Check whether all doors are controlled by the driver if (Train.Specs.DoorOpenMode != DoorMode.Manual) { //Check that we are not moving if (Math.Abs(Train.CurrentSpeed) < 0.1 / 3.6 & Math.Abs(Train.Specs.CurrentAverageAcceleration) < 0.1 / 3.6) { AttemptToOpenDoors(Train, i, tb, tf); } } // detect arrival if (Train.CurrentSpeed > -0.277777777777778 & Train.CurrentSpeed < 0.277777777777778) { bool left, right; if (Program.CurrentRoute.Stations[i].OpenLeftDoors) { left = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[0].AnticipatedOpen) { left = true; break; } } } else { left = true; } if (Program.CurrentRoute.Stations[i].OpenRightDoors) { right = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[1].AnticipatedOpen) { right = true; break; } } } else { right = true; } if (left & right) { // arrival Train.StationState = TrainStopState.Boarding; Train.SafetySystems.StationAdjust.Lit = false; Train.Specs.DoorClosureAttempted = false; Train.SafetySystems.PassAlarm.Halt(); SoundBuffer buffer = (SoundBuffer)Program.CurrentRoute.Stations[i].ArrivalSoundBuffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Program.CurrentRoute.Stations[i].SoundOrigin; Program.Sounds.PlaySound(buffer, 1.0, 1.0, pos, false); } Train.StationArrivalTime = Program.CurrentRoute.SecondsSinceMidnight; Train.StationDepartureTime = Program.CurrentRoute.Stations[i].DepartureTime - Train.TimetableDelta; if (Train.StationDepartureTime - Program.CurrentRoute.SecondsSinceMidnight < Program.CurrentRoute.Stations[i].StopTime) { Train.StationDepartureTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].StopTime; } Train.Passengers.PassengerRatio = Program.CurrentRoute.Stations[i].PassengerRatio; UpdateTrainMassFromPassengerRatio(Train); if (Train.IsPlayerTrain) { double early = 0.0; if (Program.CurrentRoute.Stations[i].ArrivalTime >= 0.0) { early = (Program.CurrentRoute.Stations[i].ArrivalTime - Train.TimetableDelta) - Train.StationArrivalTime; } string s; if (early < -1.0) { s = Translations.GetInterfaceString("message_station_arrival_late"); } else if (early > 1.0) { s = Translations.GetInterfaceString("message_station_arrival_early"); } else { s = Translations.GetInterfaceString("message_station_arrival"); } System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; TimeSpan a = TimeSpan.FromSeconds(Math.Abs(early)); string b = a.Hours.ToString("00", Culture) + ":" + a.Minutes.ToString("00", Culture) + ":" + a.Seconds.ToString("00", Culture); if (Train.StationDistanceToStopPoint < -0.1) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_overrun"); } else if (Train.StationDistanceToStopPoint > 0.1) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_underrun"); } double d = Math.Abs(Train.StationDistanceToStopPoint); string c = d.ToString("0.0", Culture); if (Program.CurrentRoute.Stations[i].Type == StationType.Terminal) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_terminal"); } s = s.Replace("[name]", Program.CurrentRoute.Stations[i].Name); s = s.Replace("[time]", b); s = s.Replace("[difference]", c); MessageManager.AddMessage(s, MessageDependency.StationArrival, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); if (Program.CurrentRoute.Stations[i].Type == StationType.Normal) { s = Translations.GetInterfaceString("message_station_deadline"); MessageManager.AddMessage(s, MessageDependency.StationDeparture, GameMode.Normal, MessageColor.White, double.PositiveInfinity, null); } Timetable.UpdateCustomTimetable(Program.CurrentRoute.Stations[i].TimetableDaytimeTexture, Program.CurrentRoute.Stations[i].TimetableNighttimeTexture); } // schedule door locks (passengers stuck between the doors) for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { Train.Cars[j].Doors[k].DoorLockDuration = 0.0; if (Program.CurrentRoute.Stations[i].OpenLeftDoors & Train.Cars[j].Doors[k].Direction == -1 | Program.CurrentRoute.Stations[i].OpenRightDoors & Train.Cars[j].Doors[k].Direction == 1) { double p = 0.005 * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio; if (Program.RandomNumberGenerator.NextDouble() < p) { /* * -- door lock at state -- * minimum: 0.2 (nearly closed) * maximum: 0.8 (nearly opened) * */ Train.Cars[j].Doors[k].DoorLockState = 0.2 + 0.6 * Program.RandomNumberGenerator.NextDouble(); /* -- waiting time -- * minimum: 2.9 s * maximum: 40.0 s * average: 7.6 s * */ p = Program.RandomNumberGenerator.NextDouble(); Train.Cars[j].Doors[k].DoorLockDuration = (50.0 - 10.0 * p) / (17.0 - 16.0 * p); } } } } } else { if (Train.SafetySystems.StationAdjust != null) { Train.SafetySystems.StationAdjust.Update(tb, tf); } } } } } else if (Train.StationState == TrainStopState.Boarding) { for (int j = 0; j < Train.Cars.Length; j++) { if (GetDoorsState(Train, j, Program.CurrentRoute.Stations[i].OpenLeftDoors, Program.CurrentRoute.Stations[i].OpenRightDoors) == (TrainDoorState.Opened | TrainDoorState.AllOpened)) { //Check whether all doors are controlled by the driver, and whether this is a non-standard station type //e.g. Change ends if (Train.Specs.DoorCloseMode != DoorMode.Manual & Program.CurrentRoute.Stations[i].Type == StationType.Normal) { AttemptToCloseDoors(Train); if (Train.Specs.DoorClosureAttempted) { if (Program.CurrentRoute.Stations[i].OpenLeftDoors && !Train.Cars[j].Doors[0].AnticipatedReopen && Program.RandomNumberGenerator.NextDouble() < Program.CurrentRoute.Stations[i].ReopenDoor) { Train.Cars[j].Doors[0].ReopenLimit = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].ReopenStationLimit); Train.Cars[j].Doors[0].ReopenCounter = 0; Train.Cars[j].Doors[0].InterferingObjectRate = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].MaxInterferingObjectRate) * 0.01; if (Train.Cars[j].Doors[0].InterferingObjectRate * Train.Cars[j].Doors[0].Width >= Train.Cars[j].Doors[0].MaxTolerance) { Train.Cars[j].Doors[0].AnticipatedReopen = true; } } if (Program.CurrentRoute.Stations[i].OpenRightDoors && !Train.Cars[j].Doors[1].AnticipatedReopen && Program.RandomNumberGenerator.NextDouble() < Program.CurrentRoute.Stations[i].ReopenDoor) { Train.Cars[j].Doors[1].ReopenLimit = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].ReopenStationLimit); Train.Cars[j].Doors[1].ReopenCounter = 0; Train.Cars[j].Doors[1].InterferingObjectRate = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].MaxInterferingObjectRate) * 0.01; if (Train.Cars[j].Doors[1].InterferingObjectRate * Train.Cars[j].Doors[1].Width >= Train.Cars[j].Doors[1].MaxTolerance) { Train.Cars[j].Doors[1].AnticipatedReopen = true; } } } } } } // detect departure bool left, right; if (!Program.CurrentRoute.Stations[i].OpenLeftDoors & !Program.CurrentRoute.Stations[i].OpenRightDoors) { left = true; right = true; } else { if (Program.CurrentRoute.Stations[i].OpenLeftDoors) { left = false; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { if (Train.Cars[j].Doors[k].State != 0.0) { left = true; break; } } if (left) { break; } } } else { left = false; } if (Program.CurrentRoute.Stations[i].OpenRightDoors) { right = false; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { if (Train.Cars[j].Doors[k].State != 0.0) { right = true; break; } } if (right) { break; } } } else { right = false; } } // departure sound if (!Train.StationDepartureSoundPlayed) { SoundBuffer buffer = (SoundBuffer)Program.CurrentRoute.Stations[i].DepartureSoundBuffer; if (buffer != null) { double dur = Program.Sounds.GetDuration(buffer); if (Program.CurrentRoute.SecondsSinceMidnight >= Train.StationDepartureTime - dur) { Program.Sounds.PlaySound(buffer, 1.0, 1.0, Program.CurrentRoute.Stations[i].SoundOrigin, false); Train.StationDepartureSoundPlayed = true; } } } for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[0].AnticipatedReopen && Train.Cars[j].Doors[0].State == Train.Cars[j].Doors[0].InterferingObjectRate) { if (Train.Cars[j].Doors[0].NextReopenTime == 0.0) { Train.Cars[j].Doors[0].NextReopenTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].InterferenceInDoor; } else if (Train.Cars[j].Doors[0].ReopenCounter < Train.Cars[j].Doors[0].ReopenLimit) { if (Program.CurrentRoute.SecondsSinceMidnight >= Train.Cars[j].Doors[0].NextReopenTime) { OpenTrainDoors(Train, j, true, false); } } else { Train.Cars[j].Doors[0].AnticipatedReopen = false; } } if (Train.Cars[j].Doors[1].AnticipatedReopen && Train.Cars[j].Doors[1].State == Train.Cars[j].Doors[1].InterferingObjectRate) { if (Train.Cars[j].Doors[1].NextReopenTime == 0.0) { Train.Cars[j].Doors[1].NextReopenTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].InterferenceInDoor; } else if (Train.Cars[j].Doors[1].ReopenCounter < Train.Cars[j].Doors[1].ReopenLimit) { if (Program.CurrentRoute.SecondsSinceMidnight >= Train.Cars[j].Doors[1].NextReopenTime) { OpenTrainDoors(Train, j, false, true); } } else { Train.Cars[j].Doors[1].AnticipatedReopen = false; } } } TrainDoorState doorState = GetDoorsState(Train, Program.CurrentRoute.Stations[i].OpenLeftDoors, Program.CurrentRoute.Stations[i].OpenRightDoors); if (left | right) { /* * Assume that passengers only board at a scheduled stop * If the player has opened the doors somewhere else (lineside?) * then passengers should not be boarding */ if (doorState != TrainDoorState.AllClosed && Interface.CurrentOptions.LoadingSway) { // passengers boarding for (int j = 0; j < Train.Cars.Length; j++) { if (!Train.Cars[j].EnableLoadingSway) { continue; } double r = 2.0 * Program.CurrentRoute.Stations[i].PassengerRatio * TimeElapsed; if (r >= Program.RandomNumberGenerator.NextDouble()) { int d = (int)Math.Floor(Program.RandomNumberGenerator.NextDouble() * (double)Train.Cars[j].Doors.Length); if (Train.Cars[j].Doors[d].State == 1.0) { Train.Cars[j].Specs.CurrentRollShakeDirection += (double)Train.Cars[j].Doors[d].Direction; } } } } } if (Train.Specs.DoorCloseMode == DoorMode.Manual || doorState == TrainDoorState.None || doorState == (TrainDoorState.Closed | TrainDoorState.AllClosed) || (Program.CurrentRoute.Stations[Train.Station].Type == StationType.ChangeEnds || Program.CurrentRoute.Stations[Train.Station].Type == StationType.Jump)) { if (left | right) { // departure message if (Program.CurrentRoute.SecondsSinceMidnight > Train.StationDepartureTime && (Program.CurrentRoute.Stations[i].Type != StationType.Terminal || Train != PlayerTrain)) { Train.StationState = TrainStopState.Completed; switch (Program.CurrentRoute.Stations[i].Type) { case StationType.Normal: if (!Train.IsPlayerTrain) { break; // Only trigger messages for the player train } if (!Program.CurrentRoute.Stations[i].OpenLeftDoors & !Program.CurrentRoute.Stations[i].OpenRightDoors | Train.Specs.DoorCloseMode != DoorMode.Manual) { MessageManager.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } else { MessageManager.AddMessage(Translations.GetInterfaceString("message_station_depart_closedoors"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } break; case StationType.ChangeEnds: // Change ends always jumps to the NEXT station JumpTrain(Train, i + 1); break; case StationType.Jump: // Jumps to an arbritrary station as defined in the routefile JumpTrain(Train, Program.CurrentRoute.Stations[i].JumpIndex); break; } } } else { Train.StationState = TrainStopState.Completed; if (Train.IsPlayerTrain & Program.CurrentRoute.Stations[i].Type == StationType.Normal) { MessageManager.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } } } } } else { if (Train.StationState != TrainStopState.Jumping) { Train.StationState = TrainStopState.Pending; } } // automatically close doors if (Train.Specs.DoorCloseMode != DoorMode.Manual & !Train.Specs.DoorClosureAttempted) { if (Train.Station == -1 | Train.StationState == TrainStopState.Completed) { if ((GetDoorsState(Train, true, true) & TrainDoorState.AllClosed) == 0) { CloseTrainDoors(Train, true, true); Train.Specs.DoorClosureAttempted = true; } } } }
/// <summary>Register the position to play microphone input.</summary> /// <param name="position">The position.</param> /// <param name="backwardTolerance">allowed tolerance in the backward direction</param> /// <param name="forwardTolerance">allowed tolerance in the forward direction</param> public void PlayMicSound(OpenBveApi.Math.Vector3 position, double backwardTolerance, double forwardTolerance) { MicSources.Add(new MicSource(OpenAlMic, MicStore, position, backwardTolerance, forwardTolerance)); }
/// <summary>Plays a sound.</summary> /// <param name="buffer">The sound buffer.</param> /// <param name="pitch">The pitch change factor.</param> /// <param name="volume">The volume change factor.</param> /// <param name="position">The position. If a train car is specified, the position is relative to the car, otherwise absolute.</param> /// <param name="parent">The parent object the sound is attached to, or a null reference.</param> /// <param name="looped">Whether to play the sound in a loop.</param> /// <returns>The sound source.</returns> public SoundSource PlaySound(SoundBuffer buffer, double pitch, double volume, OpenBveApi.Math.Vector3 position, object parent, bool looped) { if (Sources.Length == SourceCount) { Array.Resize(ref Sources, Sources.Length << 1); } Sources[SourceCount] = new SoundSource(buffer, buffer.Radius, pitch, volume, position, parent, looped); SourceCount++; return(Sources[SourceCount - 1]); }
// update grid leaf node internal static void UpdateGridLeafNode() { /* * Find the leaf node the camera is currently in. * */ ObjectGrid.GridLeafNode leaf; if (ObjectGrid.Grid.GetLeafNode(Camera.Position, out leaf)) { double x = 0.5 * (leaf.Rectangle.Left + leaf.Rectangle.Right); double z = 0.5 * (leaf.Rectangle.Near + leaf.Rectangle.Far); Camera.GridLeafNodeCenter = new OpenBveApi.Math.Vector3(x, 0.0, z); } else { leaf = null; Camera.GridLeafNodeCenter = new OpenBveApi.Math.Vector3(0.0, 0.0, 0.0); } /* * Check if the leaf node the camera is in has changed. * */ if (leaf != Camera.GridLeafNode) { if (leaf != null) { /* * The camera is within the bounds of a leaf node. * */ ObjectGrid.GridPopulatedLeafNode[] oldLeafNodes; if (Camera.GridLeafNode != null) { oldLeafNodes = Camera.GridLeafNode.VisibleLeafNodes; } else { oldLeafNodes = null; } ObjectGrid.GridPopulatedLeafNode[] newLeafNodes = leaf.VisibleLeafNodes; /* * Find leaf nodes that were visible before but are not any longer. * */ if (oldLeafNodes != null) { for (int i = 0; i < oldLeafNodes.Length; i++) { bool remove = true; for (int j = 0; j < newLeafNodes.Length; j++) { if (oldLeafNodes[i] == newLeafNodes[j]) { remove = false; break; } } if (remove) { /* * This leaf node is not visible any longer. Remove its * associated transparent faces from the renderer. * */ for (int j = 0; j < oldLeafNodes[i].TransparentFaceCount; j++) { Renderer.TransparentWorldFaces.Remove(oldLeafNodes[i].TransparentFaces[j]); } oldLeafNodes[i].TransparentFaceCount = 0; } } } /* * Find leaf nodes that are visible now but were not before. * */ for (int i = 0; i < newLeafNodes.Length; i++) { bool add = true; if (oldLeafNodes != null) { for (int j = 0; j < oldLeafNodes.Length; j++) { if (newLeafNodes[i] == oldLeafNodes[j]) { add = false; break; } } } if (add) { /* * This leaf node has become visible. Add all * its transparent faces to the renderer. * */ ObjectGrid.GridPopulatedLeafNode visibleLeaf = newLeafNodes[i]; if (visibleLeaf.TransparentFaceCount != 0) { throw new InvalidOperationException("#65517: A bug in the transparency management occured."); } visibleLeaf.LoadTextures(); double x = 0.5 * (visibleLeaf.Rectangle.Left + visibleLeaf.Rectangle.Right); double z = 0.5 * (visibleLeaf.Rectangle.Near + visibleLeaf.Rectangle.Far); OpenBveApi.Math.Vector3 offset = new OpenBveApi.Math.Vector3(x, 0.0, z); for (int j = 0; j < visibleLeaf.StaticOpaqueObjectCount; j++) { int libraryIndex = visibleLeaf.StaticOpaqueObjects[j].LibraryIndex; OpenBveApi.Geometry.FaceVertexMesh mesh = ObjectLibrary.Library.Objects[libraryIndex] as OpenBveApi.Geometry.FaceVertexMesh; if (mesh != null) { for (int k = 0; k < mesh.Faces.Length; k++) { add = false; if (mesh.Materials[mesh.Faces[k].Material].BlendMode == OpenBveApi.Geometry.BlendMode.Additive) { add = true; } if (!add) { Textures.ApiHandle apiHandle = mesh.Materials[mesh.Faces[k].Material].DaytimeTexture as Textures.ApiHandle; if (apiHandle != null) { Textures.TextureType type = Textures.RegisteredTextures[apiHandle.TextureIndex].Type; if (type == Textures.TextureType.Unknown) { throw new InvalidOperationException("#31596: A bug in the texture management occured."); } else if (type == Textures.TextureType.Alpha) { add = true; } } } if (!add) { for (int h = 0; h < mesh.Faces[k].Vertices.Length; h++) { if (mesh.Vertices[mesh.Faces[k].Vertices[h]].ReflectiveColor.A != 1.0f) { add = true; break; } } } if (add) { OpenBveApi.Math.Vector3 position = visibleLeaf.StaticOpaqueObjects[j].GridPosition + offset; OpenBveApi.Math.Orientation3 orientation = visibleLeaf.StaticOpaqueObjects[j].GridOrientation; if (visibleLeaf.TransparentFaceCount == visibleLeaf.TransparentFaces.Length) { Array.Resize<object>(ref visibleLeaf.TransparentFaces, visibleLeaf.TransparentFaces.Length << 1); } visibleLeaf.TransparentFaces[visibleLeaf.TransparentFaceCount] = Renderer.TransparentWorldFaces.Add(libraryIndex, k, position, orientation); visibleLeaf.TransparentFaceCount++; } } } } } } } else if (Camera.GridLeafNode != null) { /* * Before, the camera was inside the bounds of * a leaf node, but now, it is not anymore. * Remove the transparent faces associated * to the old leaf node from the renderer. * */ ObjectGrid.GridPopulatedLeafNode[] oldLeafNodes = Camera.GridLeafNode.VisibleLeafNodes; for (int i = 0; i < oldLeafNodes.Length; i++) { for (int j = 0; j < oldLeafNodes[i].TransparentFaceCount; j++) { Renderer.TransparentWorldFaces.Remove(oldLeafNodes[i].TransparentFaces[j]); } oldLeafNodes[i].TransparentFaceCount = 0; } } } /* * Apply the found leaf node. * */ Camera.GridLeafNode = leaf; }
/// <summary>Register the position to play microphone input.</summary> /// <param name="position">The position.</param> /// <param name="backwardTolerance">allowed tolerance in the backward direction</param> /// <param name="forwardTolerance">allowed tolerance in the forward direction</param> internal static void PlayMicSound(OpenBveApi.Math.Vector3 position, double backwardTolerance, double forwardTolerance) { MicSources.Add(new MicSource(position, backwardTolerance, forwardTolerance)); }
// --- functions --- internal static void InitializeLighting(OpenBveApi.Route.DirectionalLight light) { LightPosition = -light.LightDirection; LightAmbientColor = light.AmbientLight; LightDiffuseColor = light.DiffuseLight; }
/// <summary>Updates the sound component. Should be called every frame.</summary> /// <param name="timeElapsed">The time in seconds that elapsed since the last call to this function.</param> private static void UpdateInverseModel(double timeElapsed) { /* * Set up the listener. * */ OpenBveApi.Math.Vector3 listenerPosition = World.AbsoluteCameraPosition; OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); OpenBveApi.Math.Vector3 listenerVelocity; if (World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead | World.CameraMode == CameraViewMode.Exterior) { TrainManager.Car car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; OpenBveApi.Math.Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; if (diff.IsNullVector()) { listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Forward; } else { listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Normalize(diff); } } else { listenerVelocity = OpenBveApi.Math.Vector3.Zero; } AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); var Orientation = new float[] { (float)listenerOrientation.Z.X, (float)listenerOrientation.Z.Y, (float)listenerOrientation.Z.Z, -(float)listenerOrientation.Y.X, -(float)listenerOrientation.Y.Y, -(float)listenerOrientation.Y.Z }; AL.Listener(ALListenerfv.Orientation, ref Orientation); /* * Set up the atmospheric attributes. * */ double elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double airTemperature = Game.GetAirTemperature(elevation); double airPressure = Game.GetAirPressure(elevation, airTemperature); double speedOfSound = Game.GetSpeedOfSound(airPressure, airTemperature); try { AL.SpeedOfSound((float)speedOfSound); } catch { } /* * Collect all sounds that are to be played * and ensure that all others are stopped. * */ List <SoundSourceAttenuation> toBePlayed = new List <SoundSourceAttenuation>(); for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.StopPending) { /* * The sound is still playing but is to be stopped. * Stop the sound, then remove it from the list of * sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (Sources[i].State == SoundSourceState.Stopped) { /* * The sound was already stopped. Remove it from * the list of sound sources. * */ Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (GlobalMute) { /* * The sound is playing or about to be played, but * the global mute option is enabled. Stop the sound * sound if necessary, then remove it from the list * of sound sources if the sound is not looping. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * The sound is to be played or is already playing. * */ if (Sources[i].State == SoundSourceState.Playing) { int state; AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out state); if (state != (int)ALSourceState.Initial & state != (int)ALSourceState.Playing) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; continue; } } /* * Calculate the gain, then add the sound * to the list of sounds to be played. * */ OpenBveApi.Math.Vector3 position; if (Sources[i].Train != null) { OpenBveApi.Math.Vector3 direction; Sources[i].Train.Cars[Sources[i].Car].CreateWorldCoordinates(Sources[i].Position, out position, out direction); } else { position = Sources[i].Position; } OpenBveApi.Math.Vector3 positionDifference = position - listenerPosition; double distance = positionDifference.Norm(); double radius = Sources[i].Radius; if (World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { radius *= 0.5; } } double gain; if (distance < 2.0 * radius) { gain = 1.0 - distance * distance * (4.0 * radius - distance) / (16.0 * radius * radius * radius); } else { gain = radius / distance; } gain *= Sources[i].Volume; if (gain <= 0.0) { /* * The gain is too low. Stop the sound if playing, * but keep looping sounds pending. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * Add the source. * */ toBePlayed.Add(new SoundSourceAttenuation(Sources[i], gain, distance)); } } } /* * Now that we have the list of sounds that are to be played, * sort them by their gain so that we can determine and * adjust the clamp factor. * */ double clampFactor = Math.Exp(LogClampFactor); for (int i = 0; i < toBePlayed.Count; i++) { toBePlayed[i].Gain -= clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; } toBePlayed.Sort(); for (int i = 0; i < toBePlayed.Count; i++) { toBePlayed[i].Gain += clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; } double desiredLogClampFactor; int index = Interface.CurrentOptions.SoundNumber; if (toBePlayed.Count <= index) { desiredLogClampFactor = MinLogClampFactor; } else { double cutoffDistance = toBePlayed[index].Distance; if (cutoffDistance <= 0.0) { desiredLogClampFactor = MaxLogClampFactor; } else { double cutoffGain = toBePlayed[index].Gain; desiredLogClampFactor = Math.Log(cutoffGain / (cutoffDistance * cutoffDistance)); if (desiredLogClampFactor < MinLogClampFactor) { desiredLogClampFactor = MinLogClampFactor; } else if (desiredLogClampFactor > MaxLogClampFactor) { desiredLogClampFactor = MaxLogClampFactor; } } } const double rate = 3.0; if (LogClampFactor < desiredLogClampFactor) { LogClampFactor += timeElapsed * rate; if (LogClampFactor > desiredLogClampFactor) { LogClampFactor = desiredLogClampFactor; } } else if (LogClampFactor > desiredLogClampFactor) { LogClampFactor -= timeElapsed * rate; if (LogClampFactor < desiredLogClampFactor) { LogClampFactor = desiredLogClampFactor; } } /* * Play the sounds. * */ clampFactor = Math.Exp(LogClampFactor); for (int i = index; i < toBePlayed.Count; i++) { toBePlayed[i].Gain = 0.0; } for (int i = 0; i < toBePlayed.Count; i++) { SoundSource source = toBePlayed[i].Source; double gain = toBePlayed[i].Gain - clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; if (gain <= 0.0) { /* * Stop the sound. * */ if (source.State == SoundSourceState.Playing) { AL.DeleteSources(1, ref source.OpenAlSourceName); source.State = SoundSourceState.PlayPending; source.OpenAlSourceName = 0; } if (!source.Looped) { source.State = SoundSourceState.Stopped; source.OpenAlSourceName = 0; } } else { /* * Ensure the buffer is loaded, then play the sound. * */ if (source.State != SoundSourceState.Playing) { LoadBuffer(source.Buffer); if (source.Buffer.Loaded) { AL.GenSources(1, out source.OpenAlSourceName); AL.Source(source.OpenAlSourceName, ALSourcei.Buffer, source.Buffer.OpenAlBufferName); } else { /* * We cannot play the sound because * the buffer could not be loaded. * */ source.State = SoundSourceState.Stopped; continue; } } OpenBveApi.Math.Vector3 position; OpenBveApi.Math.Vector3 velocity; if (source.Train != null) { OpenBveApi.Math.Vector3 direction; source.Train.Cars[source.Car].CreateWorldCoordinates(source.Position, out position, out direction); velocity = source.Train.Cars[source.Car].Specs.CurrentSpeed * direction; } else { position = source.Position; velocity = OpenBveApi.Math.Vector3.Zero; } position -= listenerPosition; AL.Source(source.OpenAlSourceName, ALSource3f.Position, (float)position.X, (float)position.Y, (float)position.Z); AL.Source(source.OpenAlSourceName, ALSource3f.Velocity, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); AL.Source(source.OpenAlSourceName, ALSourcef.Pitch, (float)source.Pitch); AL.Source(source.OpenAlSourceName, ALSourcef.Gain, (float)gain); if (source.State != SoundSourceState.Playing) { AL.Source(source.OpenAlSourceName, ALSourceb.Looping, source.Looped); AL.SourcePlay(source.OpenAlSourceName); source.State = SoundSourceState.Playing; } } } }
/// <summary>Plays a sound.</summary> /// <param name="buffer">The sound buffer.</param> /// <param name="pitch">The pitch change factor.</param> /// <param name="volume">The volume change factor.</param> /// <param name="position">The position. If a train and car are specified, the position is relative to the car, otherwise absolute.</param> /// <param name="train">The train the sound is attached to, or a null reference.</param> /// <param name="car">The car in the train the sound is attached to.</param> /// <param name="looped">Whether to play the sound in a loop.</param> /// <returns>The sound source.</returns> internal static SoundSource PlaySound(SoundBuffer buffer, double pitch, double volume, OpenBveApi.Math.Vector3 position, TrainManager.Train train, int car, bool looped) { if (Sources.Length == SourceCount) { Array.Resize <SoundSource>(ref Sources, Sources.Length << 1); } Sources[SourceCount] = new SoundSource(buffer, buffer.Radius, pitch, volume, position, train, car, looped); SourceCount++; return(Sources[SourceCount - 1]); }
private static void PlaySound(ref int SoundSourceIndex, bool ReturnHandle, int SoundBufferIndex, TrainManager.Train Train, int CarIndex, Vector3 Position, Importance Important, bool Looped, double Pitch, double Gain) { if (OpenAlContext != ContextHandle.Zero) { if (Game.MinimalisticSimulation & Important == Importance.DontCare | SoundBufferIndex == -1) { return; } if (SoundSourceIndex >= 0) { StopSound(ref SoundSourceIndex); } int i; for (i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] == null) { break; } } if (i >= SoundSources.Length) { Array.Resize <SoundSource>(ref SoundSources, SoundSources.Length << 1); } SoundSources[i] = new SoundSource(); SoundSources[i].Position = Position; SoundSources[i].OpenAlPosition = new float[] { 0.0f, 0.0f, 0.0f }; SoundSources[i].OpenAlVelocity = new float[] { 0.0f, 0.0f, 0.0f }; SoundSources[i].SoundBufferIndex = SoundBufferIndex; SoundSources[i].Radius = SoundBuffers[SoundBufferIndex].Radius; SoundSources[i].Pitch = (float)Pitch; SoundSources[i].Gain = (float)Gain; SoundSources[i].Looped = Looped; SoundSources[i].Suppressed = true; SoundSources[i].FinishedPlaying = false; SoundSources[i].Train = Train; SoundSources[i].CarIndex = CarIndex; SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(0, false); SoundSources[i].HasHandle = ReturnHandle; SoundSourceIndex = i; } }