// 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.Normalize(ref World.AbsoluteCameraDirection.X, ref World.AbsoluteCameraDirection.Y, ref World.AbsoluteCameraDirection.Z); World.Normalize(ref World.AbsoluteCameraSide.X, ref World.AbsoluteCameraSide.Y, ref World.AbsoluteCameraSide.Z); 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; }
internal void ApplyShear(double dx, double dy, double dz, double sx, double sy, double sz, double r) { for (int j = 0; j < Mesh.Vertices.Length; j++) { double n = r * (dx * Mesh.Vertices[j].Coordinates.X + dy * Mesh.Vertices[j].Coordinates.Y + dz * Mesh.Vertices[j].Coordinates.Z); Mesh.Vertices[j].Coordinates.X += sx * n; Mesh.Vertices[j].Coordinates.Y += sy * n; Mesh.Vertices[j].Coordinates.Z += sz * n; } // ReSharper disable NotAccessedVariable double ux, uy, uz; // ReSharper restore NotAccessedVariable World.Cross(sx, sy, sz, dx, dy, dz, out ux, out uy, out uz); for (int j = 0; j < Mesh.Faces.Length; j++) { for (int k = 0; k < Mesh.Faces[j].Vertices.Length; k++) { if (Mesh.Faces[j].Vertices[k].Normal.X != 0.0f | Mesh.Faces[j].Vertices[k].Normal.Y != 0.0f | Mesh.Faces[j].Vertices[k].Normal.Z != 0.0f) { double nx = (double)Mesh.Faces[j].Vertices[k].Normal.X; double ny = (double)Mesh.Faces[j].Vertices[k].Normal.Y; double nz = (double)Mesh.Faces[j].Vertices[k].Normal.Z; double n = r * (sx * nx + sy * ny + sz * nz); nx -= dx * n; ny -= dy * n; nz -= dz * n; World.Normalize(ref nx, ref ny, ref nz); Mesh.Faces[j].Vertices[k].Normal.X = (float)nx; Mesh.Faces[j].Vertices[k].Normal.Y = (float)ny; Mesh.Faces[j].Vertices[k].Normal.Z = (float)nz; } } } }
/// <summary>Loads a Loksim3D object from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="LoadMode">The texture load mode.</param> /// <param name="Rotation">The rotation to be applied</param> /// <returns>The object loaded.</returns> internal static ObjectManager.StaticObject ReadObject(string FileName, ObjectManager.ObjectLoadMode LoadMode, Vector3 Rotation) { string BaseDir = System.IO.Path.GetDirectoryName(FileName); XmlDocument currentXML = new XmlDocument(); //Initialise the object ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Object.Mesh.Faces = new World.MeshFace[] { }; Object.Mesh.Materials = new World.MeshMaterial[] { }; Object.Mesh.Vertices = new World.Vertex[] { }; MeshBuilder Builder = new MeshBuilder(); Vector3[] Normals = new Vector3[4]; bool PropertiesFound = false; World.Vertex[] tempVertices = new World.Vertex[0]; Vector3[] tempNormals = new Vector3[0]; Color24 transparentColor = new Color24(); string tday = null; string tnight = null; string transtex = null; bool TransparencyUsed = false; bool TransparentTypSet = false; bool FirstPxTransparent = false; Color24 FirstPxColor = new Color24(); bool Face2 = false; int TextureWidth = 0; int TextureHeight = 0; if (File.Exists(FileName)) { try { currentXML.Load(FileName); } catch { return(null); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Loksim3D object " + FileName + " does not exist."); return(null); } //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/OBJECT"); //Check this file actually contains Loksim3D object nodes if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode node in outerNode.ChildNodes) { //I think there should only be one properties node?? //Need better format documentation if (node.Name == "Props" && PropertiesFound == false) { if (node.Attributes != null) { //Our node has child nodes, therefore this properties node should be valid //Needs better validation PropertiesFound = true; foreach (XmlAttribute attribute in node.Attributes) { switch (attribute.Name) { //Sets the texture //Loksim3D objects only support daytime textures case "Texture": tday = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory); if (!File.Exists(tday)) { Interface.AddMessage(Interface.MessageType.Warning, true, "Ls3d Texture file " + attribute.Value + " not found."); break; } try { using (Bitmap TextureInformation = new Bitmap(tday)) { TextureWidth = TextureInformation.Width; TextureHeight = TextureInformation.Height; Color color = TextureInformation.GetPixel(0, 0); FirstPxColor = new Color24(color.R, color.G, color.B); } } catch { Interface.AddMessage(Interface.MessageType.Error, true, "An error occured loading daytime texture " + tday + " in file " + FileName); tday = null; } break; //Defines whether the texture uses transparency //May be omitted case "Transparent": if (TransparentTypSet) { //Appears to be ignored with TransparentTyp set continue; } if (attribute.Value == "TRUE") { TransparencyUsed = true; transparentColor = new Color24(0, 0, 0); } break; case "TransTexture": if (string.IsNullOrEmpty(attribute.Value)) { //Empty.... continue; } transtex = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory); if (!File.Exists(transtex)) { Interface.AddMessage(Interface.MessageType.Error, true, "AlphaTexture " + transtex + " could not be found in file " + FileName); transtex = null; } break; //Sets the transparency type case "TransparentTyp": TransparentTypSet = true; switch (attribute.Value) { case "0": //Transparency is disabled TransparencyUsed = false; break; case "1": //Transparency is solid black TransparencyUsed = true; transparentColor = new Color24(0, 0, 0); FirstPxTransparent = false; break; case "2": //Transparency is the color at Pixel 1,1 TransparencyUsed = true; FirstPxTransparent = true; break; case "3": case "4": //This is used when transparency is used with an alpha bitmap TransparencyUsed = false; FirstPxTransparent = false; break; case "5": //Use the alpha channel from the image, so we don't need to do anything fancy //TODO: (Low priority) Check what happens in Loksim itself when an image uses the Alpha channel, but doesn't actually specify type 5 break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Unrecognised transparency type " + attribute.Value + " detected in " + attribute.Name + " in Loksim3D object file " + FileName); break; } break; //Sets whether the rears of the faces are to be drawn case "Drawrueckseiten": if (attribute.Value == "TRUE" || string.IsNullOrEmpty(attribute.Value)) { Face2 = true; } else { Face2 = false; } break; /* * MISSING PROPERTIES: * AutoRotate - Rotate with tracks?? LS3D presumably uses a 3D world system. * Beleuchtet- Translates as illuminated. Presume something to do with lighting? - What emissive color? * FileAuthor * FileInfo * FilePicture */ } } } } //The point command is eqivilant to a vertex else if (node.Name == "Point" && node.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { //Vertex double vx = 0.0, vy = 0.0, vz = 0.0; //Normals double nx = 0.0, ny = 0.0, nz = 0.0; foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { //Sets the vertex normals case "Normal": string[] NormalPoints = attribute.Value.Split(';'); if (!double.TryParse(NormalPoints[0], out nx)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nX in " + attribute.Name + " in Loksim3D object file " + FileName); } if (!double.TryParse(NormalPoints[1], out ny)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nY in " + attribute.Name + " in Loksim3D object file " + FileName); } if (!double.TryParse(NormalPoints[2], out nz)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nZ in " + attribute.Name + " in Loksim3D object file " + FileName); } break; //Sets the vertex 3D co-ordinates case "Vekt": string[] VertexPoints = attribute.Value.Split(';'); if (!double.TryParse(VertexPoints[0], out vx)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument vX in " + attribute.Name + " in Loksim3D object file " + FileName); } if (!double.TryParse(VertexPoints[1], out vy)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument yY in " + attribute.Name + " in Loksim3D object file " + FileName); } if (!double.TryParse(VertexPoints[2], out vz)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument vZ in " + attribute.Name + " in Loksim3D object file " + FileName); } break; } } World.Normalize(ref nx, ref ny, ref nz); //Resize temp arrays Array.Resize <World.Vertex>(ref tempVertices, tempVertices.Length + 1); Array.Resize <Vector3>(ref tempNormals, tempNormals.Length + 1); //Add vertex and normals to temp array tempVertices[tempVertices.Length - 1].Coordinates = new Vector3(vx, vy, vz); tempNormals[tempNormals.Length - 1] = new Vector3((float)nx, (float)ny, (float)nz); Array.Resize <World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); while (Builder.Vertices.Length >= Normals.Length) { Array.Resize <Vector3>(ref Normals, Normals.Length << 1); } Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new Vector3(vx, vy, vz); Normals[Builder.Vertices.Length - 1] = new Vector3((float)nx, (float)ny, (float)nz); } } } //The Flaeche command creates a face else if (node.Name == "Flaeche" && node.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { //Defines the verticies in this face //**NOTE**: A vertex may appear in multiple faces with different texture co-ordinates if (childNode.Attributes["Points"] != null) { string[] Verticies = childNode.Attributes["Points"].Value.Split(';'); int f = Builder.Faces.Length; //Add 1 to the length of the face array Array.Resize <World.MeshFace>(ref Builder.Faces, f + 1); Builder.Faces[f] = new World.MeshFace(); //Create the vertex array for the face Builder.Faces[f].Vertices = new World.MeshFaceVertex[Verticies.Length]; while (Builder.Vertices.Length > Normals.Length) { Array.Resize <Vector3>(ref Normals, Normals.Length << 1); } //Run through the vertices list and grab from the temp array int smallestX = TextureWidth; int smallestY = TextureHeight; for (int j = 0; j < Verticies.Length; j++) { //This is the position of the vertex in the temp array int currentVertex; if (!int.TryParse(Verticies[j], out currentVertex)) { Interface.AddMessage(Interface.MessageType.Error, false, Verticies[j] + " does not parse to a valid Vertex in " + node.Name + " in Loksim3D object file " + FileName); continue; } //Add one to the actual vertex array Array.Resize <World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); //Set coordinates Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = tempVertices[currentVertex].Coordinates; //Set the vertex index Builder.Faces[f].Vertices[j].Index = (ushort)(Builder.Vertices.Length - 1); //Set the normals Builder.Faces[f].Vertices[j].Normal = tempNormals[currentVertex]; //Now deal with the texture //Texture mapping points are in pixels X,Y and are relative to the face in question rather than the vertex if (childNode.Attributes["Texture"] != null) { string[] TextureCoords = childNode.Attributes["Texture"].Value.Split(';'); Vector2 currentCoords; float OpenBVEWidth; float OpenBVEHeight; string[] splitCoords = TextureCoords[j].Split(','); if (!float.TryParse(splitCoords[0], out OpenBVEWidth)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid texture width specified in " + node.Name + " in Loksim3D object file " + FileName); continue; } if (!float.TryParse(splitCoords[1], out OpenBVEHeight)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid texture height specified in " + node.Name + " in Loksim3D object file " + FileName); continue; } if (OpenBVEWidth <= smallestX && OpenBVEHeight <= smallestY) { //Clamp texture width and height smallestX = (int)OpenBVEWidth; smallestY = (int)OpenBVEHeight; } if (TextureWidth != 0 && TextureHeight != 0) { //Calculate openBVE co-ords currentCoords.X = (OpenBVEWidth / TextureWidth); currentCoords.Y = (OpenBVEHeight / TextureHeight); } else { //Invalid, so just return zero currentCoords.X = 0; currentCoords.Y = 0; } Builder.Vertices[Builder.Vertices.Length - 1].TextureCoordinates = currentCoords; } } if (Face2) { //Add face2 flag if required Builder.Faces[f].Flags = (byte)World.MeshFace.Face2Mask; } } } } } } } } } //Apply rotation /* * NOTES: * No rotation order is specified * The rotation string in a .l3dgrp file is ordered Y, Z, X ??? Can't find a good reason for this ??? * Rotations must still be performed in X,Y,Z order to produce correct results */ if (Rotation.Z != 0.0) { //Convert to radians Rotation.Z *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 1, 0, 0, Rotation.Z); } if (Rotation.X != 0.0) { //This is actually the Y-Axis rotation //Convert to radians Rotation.X *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 0, 1, 0, Rotation.X); } if (Rotation.Y != 0.0) { //This is actually the X-Axis rotation //Convert to radians Rotation.Y *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 0, 0, 1, Rotation.Y); } //These files appear to only have one texture defined //Therefore import later- May have to change if (File.Exists(tday)) { for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].DaytimeTexture = tday; Builder.Materials[j].NighttimeTexture = tnight; Builder.Materials[j].TransparencyTexture = transtex; } } if (TransparencyUsed == true) { for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].TransparentColor = FirstPxTransparent ? FirstPxColor : transparentColor; Builder.Materials[j].TransparentColorUsed = true; } } } ApplyMeshBuilder(ref Object, Builder, LoadMode, false, false); Object.Mesh.CreateNormals(); return(Object); }
/// <summary>Call this method to update a single track follower</summary> /// <param name="NewTrackPosition">The new track position of the follower</param> /// <param name="UpdateWorldCoordinates">Whether to update the world co-ordinates</param> /// <param name="AddTrackInaccurary">Whether to add track innacuracy</param> internal void Update(double NewTrackPosition, bool UpdateWorldCoordinates, bool AddTrackInaccurary) { if (CurrentTrack.Elements.Length == 0) { return; } int i = LastTrackElement; while (i >= 0 && NewTrackPosition < CurrentTrack.Elements[i].StartingTrackPosition) { double ta = TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double tb = -0.01; CheckEvents(i, -1, ta, tb); i--; } if (i >= 0) { while (i < CurrentTrack.Elements.Length - 1) { if (NewTrackPosition < CurrentTrack.Elements[i + 1].StartingTrackPosition) { break; } double ta = TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double tb = CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition + 0.01; CheckEvents(i, 1, ta, tb); i++; } } else { i = 0; } double da = TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double db = NewTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; // track if (UpdateWorldCoordinates) { if (db != 0.0) { if (CurrentTrack.Elements[i].CurveRadius != 0.0) { // curve double r = CurrentTrack.Elements[i].CurveRadius; double p = CurrentTrack.Elements[i].WorldDirection.Y / Math.Sqrt(CurrentTrack.Elements[i].WorldDirection.X * CurrentTrack.Elements[i].WorldDirection.X + CurrentTrack.Elements[i].WorldDirection.Z * CurrentTrack.Elements[i].WorldDirection.Z); double s = db / Math.Sqrt(1.0 + p * p); double h = s * p; double b = s / Math.Abs(r); double f = 2.0 * r * r * (1.0 - Math.Cos(b)); double c = (double)Math.Sign(db) * Math.Sqrt(f >= 0.0 ? f : 0.0); double a = 0.5 * (double)Math.Sign(r) * b; Vector3 D = new Vector3(CurrentTrack.Elements[i].WorldDirection.X, 0.0, CurrentTrack.Elements[i].WorldDirection.Z); World.Normalize(ref D.X, ref D.Y, ref D.Z); double cosa = Math.Cos(a); double sina = Math.Sin(a); World.Rotate(ref D, 0.0, 1.0, 0.0, cosa, sina); WorldPosition.X = CurrentTrack.Elements[i].WorldPosition.X + c * D.X; WorldPosition.Y = CurrentTrack.Elements[i].WorldPosition.Y + h; WorldPosition.Z = CurrentTrack.Elements[i].WorldPosition.Z + c * D.Z; World.Rotate(ref D, 0.0, 1.0, 0.0, cosa, sina); WorldDirection.X = D.X; WorldDirection.Y = p; WorldDirection.Z = D.Z; World.Normalize(ref WorldDirection.X, ref WorldDirection.Y, ref WorldDirection.Z); double cos2a = Math.Cos(2.0 * a); double sin2a = Math.Sin(2.0 * a); WorldSide = CurrentTrack.Elements[i].WorldSide; World.Rotate(ref WorldSide, 0.0, 1.0, 0.0, cos2a, sin2a); World.Cross(WorldDirection, WorldSide, out WorldUp); } else { // straight WorldPosition.X = CurrentTrack.Elements[i].WorldPosition.X + db * CurrentTrack.Elements[i].WorldDirection.X; WorldPosition.Y = CurrentTrack.Elements[i].WorldPosition.Y + db * CurrentTrack.Elements[i].WorldDirection.Y; WorldPosition.Z = CurrentTrack.Elements[i].WorldPosition.Z + db * CurrentTrack.Elements[i].WorldDirection.Z; WorldDirection = CurrentTrack.Elements[i].WorldDirection; WorldUp = CurrentTrack.Elements[i].WorldUp; WorldSide = CurrentTrack.Elements[i].WorldSide; CurveRadius = 0.0; } // cant if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double t2 = t * t; double t3 = t2 * t; CurveCant = (2.0 * t3 - 3.0 * t2 + 1.0) * CurrentTrack.Elements[i].CurveCant + (t3 - 2.0 * t2 + t) * CurrentTrack.Elements[i].CurveCantTangent + (-2.0 * t3 + 3.0 * t2) * CurrentTrack.Elements[i + 1].CurveCant + (t3 - t2) * CurrentTrack.Elements[i + 1].CurveCantTangent; CurveRadius = CurrentTrack.Elements[i].CurveRadius; } else { CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { WorldPosition = CurrentTrack.Elements[i].WorldPosition; WorldDirection = CurrentTrack.Elements[i].WorldDirection; WorldUp = CurrentTrack.Elements[i].WorldUp; WorldSide = CurrentTrack.Elements[i].WorldSide; CurveRadius = CurrentTrack.Elements[i].CurveRadius; CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { if (db != 0.0) { if (CurrentTrack.Elements[i].CurveRadius != 0.0) { CurveRadius = CurrentTrack.Elements[i].CurveRadius; } else { CurveRadius = 0.0; } if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double t2 = t * t; double t3 = t2 * t; CurveCant = (2.0 * t3 - 3.0 * t2 + 1.0) * CurrentTrack.Elements[i].CurveCant + (t3 - 2.0 * t2 + t) * CurrentTrack.Elements[i].CurveCantTangent + (-2.0 * t3 + 3.0 * t2) * CurrentTrack.Elements[i + 1].CurveCant + (t3 - t2) * CurrentTrack.Elements[i + 1].CurveCantTangent; } else { CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { CurveRadius = CurrentTrack.Elements[i].CurveRadius; CurveCant = CurrentTrack.Elements[i].CurveCant; } } AdhesionMultiplier = CurrentTrack.Elements[i].AdhesionMultiplier; //Pitch added for Plugin Data usage //Mutliply this by 1000 to get the original value Pitch = CurrentTrack.Elements[i].Pitch * 1000; // inaccuracy if (AddTrackInaccurary) { double x, y, c; if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double x1, y1, c1; double x2, y2, c2; GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i].CsvRwAccuracyLevel, out x1, out y1, out c1); GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i + 1].CsvRwAccuracyLevel, out x2, out y2, out c2); x = (1.0 - t) * x1 + t * x2; y = (1.0 - t) * y1 + t * y2; c = (1.0 - t) * c1 + t * c2; } else { GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i].CsvRwAccuracyLevel, out x, out y, out c); } WorldPosition.X += x * WorldSide.X + y * WorldUp.X; WorldPosition.Y += x * WorldSide.Y + y * WorldUp.Y; WorldPosition.Z += x * WorldSide.Z + y * WorldUp.Z; CurveCant += c; CantDueToInaccuracy = c; } else { CantDueToInaccuracy = 0.0; } // events CheckEvents(i, Math.Sign(db - da), da, db); //Update the odometer if (TrackPosition != NewTrackPosition) { //HACK: Reset the odometer if we've moved more than 10m this frame if (Math.Abs(NewTrackPosition - TrackPosition) > 10) { Odometer = 0; } else { Odometer += NewTrackPosition - TrackPosition; } } // finish TrackPosition = NewTrackPosition; LastTrackElement = i; }
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; 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); World.Rotate(ref World.AbsoluteCameraDirection.X, ref World.AbsoluteCameraDirection.Y, ref World.AbsoluteCameraDirection.Z, 0.0, 1.0, 0.0, cosa, sina); World.Rotate(ref World.AbsoluteCameraUp.X, ref World.AbsoluteCameraUp.Y, ref World.AbsoluteCameraUp.Z, 0.0, 1.0, 0.0, cosa, sina); World.Rotate(ref World.AbsoluteCameraSide.X, ref World.AbsoluteCameraSide.Y, ref World.AbsoluteCameraSide.Z, 0.0, 1.0, 0.0, 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); World.Rotate(ref World.AbsoluteCameraDirection.X, ref World.AbsoluteCameraDirection.Y, ref World.AbsoluteCameraDirection.Z, World.AbsoluteCameraSide.X, World.AbsoluteCameraSide.Y, World.AbsoluteCameraSide.Z, cosa, sina); World.Rotate(ref World.AbsoluteCameraUp.X, ref World.AbsoluteCameraUp.Y, ref World.AbsoluteCameraUp.Z, World.AbsoluteCameraSide.X, World.AbsoluteCameraSide.Y, World.AbsoluteCameraSide.Z, 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) { World.AbsoluteCameraPosition.X += MoveXSpeed * timeElapsed * World.AbsoluteCameraSide.X; World.AbsoluteCameraPosition.Y += MoveXSpeed * timeElapsed * World.AbsoluteCameraSide.Y; World.AbsoluteCameraPosition.Z += MoveXSpeed * timeElapsed * World.AbsoluteCameraSide.Z; 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) { World.AbsoluteCameraPosition.X += MoveYSpeed * timeElapsed * World.AbsoluteCameraUp.X; World.AbsoluteCameraPosition.Y += MoveYSpeed * timeElapsed * World.AbsoluteCameraUp.Y; World.AbsoluteCameraPosition.Z += MoveYSpeed * timeElapsed * World.AbsoluteCameraUp.Z; 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) { World.AbsoluteCameraPosition.X += MoveZSpeed * timeElapsed * World.AbsoluteCameraDirection.X; World.AbsoluteCameraPosition.Y += MoveZSpeed * timeElapsed * World.AbsoluteCameraDirection.Y; World.AbsoluteCameraPosition.Z += MoveZSpeed * timeElapsed * World.AbsoluteCameraDirection.Z; 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; World.AbsoluteCameraSide.Y = 0.0; World.Normalize(ref World.AbsoluteCameraSide.X, ref World.AbsoluteCameraSide.Y, ref World.AbsoluteCameraSide.Z); World.Normalize(ref World.AbsoluteCameraDirection.X, ref World.AbsoluteCameraDirection.Y, ref World.AbsoluteCameraDirection.Z); World.AbsoluteCameraUp = Vector3.Cross(World.AbsoluteCameraDirection, World.AbsoluteCameraSide); } else { ReducedModeEnteringTime -= timeElapsed; } } if (updatelight) { Renderer.OptionAmbientColor.R = (byte)Math.Round(32.0 + 128.0 * Program.LightingRelative * (2.0 - Program.LightingRelative)); Renderer.OptionAmbientColor.G = (byte)Math.Round(32.0 + 128.0 * 0.5 * (Program.LightingRelative + Program.LightingRelative * (2.0 - Program.LightingRelative))); Renderer.OptionAmbientColor.B = (byte)Math.Round(32.0 + 128.0 * Program.LightingRelative); Renderer.OptionDiffuseColor.R = (byte)Math.Round(32.0 + 128.0 * Program.LightingRelative); Renderer.OptionDiffuseColor.G = (byte)Math.Round(32.0 + 128.0 * Program.LightingRelative); Renderer.OptionDiffuseColor.B = (byte)Math.Round(32.0 + 128.0 * Math.Sqrt(Program.LightingRelative)); Renderer.InitializeLighting(); } Renderer.RenderScene(); SwapBuffers(); }
internal static void UpdateTrackFollower(ref TrackFollower Follower, double NewTrackPosition, bool UpdateWorldCoordinates, bool AddTrackInaccurary) { if (CurrentTrack.Elements.Length == 0) { return; } int i = Follower.LastTrackElement; while (i >= 0 && NewTrackPosition < CurrentTrack.Elements[i].StartingTrackPosition) { double ta = Follower.TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double tb = -0.01; CheckEvents(ref Follower, i, -1, ta, tb); i--; } if (i >= 0) { while (i < CurrentTrack.Elements.Length - 1) { if (NewTrackPosition < CurrentTrack.Elements[i + 1].StartingTrackPosition) { break; } double ta = Follower.TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double tb = CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition + 0.01; CheckEvents(ref Follower, i, 1, ta, tb); i++; } } else { i = 0; } double da = Follower.TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double db = NewTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; // track if (UpdateWorldCoordinates) { if (db != 0.0) { if (CurrentTrack.Elements[i].CurveRadius != 0.0) { // curve double r = CurrentTrack.Elements[i].CurveRadius; double p = CurrentTrack.Elements[i].WorldDirection.Y / Math.Sqrt(CurrentTrack.Elements[i].WorldDirection.X * CurrentTrack.Elements[i].WorldDirection.X + CurrentTrack.Elements[i].WorldDirection.Z * CurrentTrack.Elements[i].WorldDirection.Z); double s = db / Math.Sqrt(1.0 + p * p); double h = s * p; double b = s / Math.Abs(r); double f = 2.0 * r * r * (1.0 - Math.Cos(b)); double c = (double)Math.Sign(db) * Math.Sqrt(f >= 0.0 ? f : 0.0); double a = 0.5 * (double)Math.Sign(r) * b; Vector3 D = new Vector3(CurrentTrack.Elements[i].WorldDirection.X, 0.0, CurrentTrack.Elements[i].WorldDirection.Z); World.Normalize(ref D.X, ref D.Y, ref D.Z); double cosa = Math.Cos(a); double sina = Math.Sin(a); World.Rotate(ref D.X, ref D.Y, ref D.Z, 0.0, 1.0, 0.0, cosa, sina); Follower.WorldPosition.X = CurrentTrack.Elements[i].WorldPosition.X + c * D.X; Follower.WorldPosition.Y = CurrentTrack.Elements[i].WorldPosition.Y + h; Follower.WorldPosition.Z = CurrentTrack.Elements[i].WorldPosition.Z + c * D.Z; World.Rotate(ref D.X, ref D.Y, ref D.Z, 0.0, 1.0, 0.0, cosa, sina); Follower.WorldDirection.X = D.X; Follower.WorldDirection.Y = p; Follower.WorldDirection.Z = D.Z; World.Normalize(ref Follower.WorldDirection.X, ref Follower.WorldDirection.Y, ref Follower.WorldDirection.Z); double cos2a = Math.Cos(2.0 * a); double sin2a = Math.Sin(2.0 * a); Follower.WorldSide = CurrentTrack.Elements[i].WorldSide; World.Rotate(ref Follower.WorldSide.X, ref Follower.WorldSide.Y, ref Follower.WorldSide.Z, 0.0, 1.0, 0.0, cos2a, sin2a); Follower.WorldUp = Vector3.Cross(Follower.WorldDirection, Follower.WorldSide); Follower.CurveRadius = CurrentTrack.Elements[i].CurveRadius; } else { // straight Follower.WorldPosition.X = CurrentTrack.Elements[i].WorldPosition.X + db * CurrentTrack.Elements[i].WorldDirection.X; Follower.WorldPosition.Y = CurrentTrack.Elements[i].WorldPosition.Y + db * CurrentTrack.Elements[i].WorldDirection.Y; Follower.WorldPosition.Z = CurrentTrack.Elements[i].WorldPosition.Z + db * CurrentTrack.Elements[i].WorldDirection.Z; Follower.WorldDirection = CurrentTrack.Elements[i].WorldDirection; Follower.WorldUp = CurrentTrack.Elements[i].WorldUp; Follower.WorldSide = CurrentTrack.Elements[i].WorldSide; Follower.CurveRadius = 0.0; } // cant if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double t2 = t * t; double t3 = t2 * t; Follower.CurveCant = (2.0 * t3 - 3.0 * t2 + 1.0) * CurrentTrack.Elements[i].CurveCant + (t3 - 2.0 * t2 + t) * CurrentTrack.Elements[i].CurveCantTangent + (-2.0 * t3 + 3.0 * t2) * CurrentTrack.Elements[i + 1].CurveCant + (t3 - t2) * CurrentTrack.Elements[i + 1].CurveCantTangent; } else { Follower.CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { Follower.WorldPosition = CurrentTrack.Elements[i].WorldPosition; Follower.WorldDirection = CurrentTrack.Elements[i].WorldDirection; Follower.WorldUp = CurrentTrack.Elements[i].WorldUp; Follower.WorldSide = CurrentTrack.Elements[i].WorldSide; Follower.CurveRadius = CurrentTrack.Elements[i].CurveRadius; Follower.CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { if (db != 0.0) { if (CurrentTrack.Elements[i].CurveRadius != 0.0) { Follower.CurveRadius = CurrentTrack.Elements[i].CurveRadius; } else { Follower.CurveRadius = 0.0; } if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double t2 = t * t; double t3 = t2 * t; Follower.CurveCant = (2.0 * t3 - 3.0 * t2 + 1.0) * CurrentTrack.Elements[i].CurveCant + (t3 - 2.0 * t2 + t) * CurrentTrack.Elements[i].CurveCantTangent + (-2.0 * t3 + 3.0 * t2) * CurrentTrack.Elements[i + 1].CurveCant + (t3 - t2) * CurrentTrack.Elements[i + 1].CurveCantTangent; } else { Follower.CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { Follower.CurveRadius = CurrentTrack.Elements[i].CurveRadius; Follower.CurveCant = CurrentTrack.Elements[i].CurveCant; } } Follower.AdhesionMultiplier = CurrentTrack.Elements[i].AdhesionMultiplier; // inaccuracy if (AddTrackInaccurary) { double x, y, c; if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double x1, y1, c1; double x2, y2, c2; GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i].CsvRwAccuracyLevel, out x1, out y1, out c1); GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i + 1].CsvRwAccuracyLevel, out x2, out y2, out c2); x = (1.0 - t) * x1 + t * x2; y = (1.0 - t) * y1 + t * y2; c = (1.0 - t) * c1 + t * c2; } else { GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i].CsvRwAccuracyLevel, out x, out y, out c); } Follower.WorldPosition.X += x * Follower.WorldSide.X + y * Follower.WorldUp.X; Follower.WorldPosition.Y += x * Follower.WorldSide.Y + y * Follower.WorldUp.Y; Follower.WorldPosition.Z += x * Follower.WorldSide.Z + y * Follower.WorldUp.Z; Follower.CurveCant += c; Follower.CantDueToInaccuracy = c; } else { Follower.CantDueToInaccuracy = 0.0; } // events CheckEvents(ref Follower, i, Math.Sign(db - da), da, db); // finish Follower.TrackPosition = NewTrackPosition; Follower.LastTrackElement = i; }
internal static void Main(string[] args) { // platform and mono int p = (int)Environment.OSVersion.Platform; if (p == 4 | p == 128) { // general Unix CurrentPlatform = Platform.Linux; } else if (p == 6) { // Mac CurrentPlatform = Platform.Mac; } else { // non-Unix CurrentPlatform = Platform.Windows; } CurrentlyRunOnMono = Type.GetType("Mono.Runtime") != null; // file system FileSystem = FileSystem.FromCommandLineArgs(args); FileSystem.CreateFileSystem(); SetPackageLookupDirectories(); // command line arguments bool[] SkipArgs = new bool[args.Length]; if (args.Length != 0) { string File = System.IO.Path.Combine(Application.StartupPath, "RouteViewer.exe"); if (System.IO.File.Exists(File)) { int Skips = 0; System.Text.StringBuilder NewArgs = new System.Text.StringBuilder(); for (int i = 0; i < args.Length; i++) { if (System.IO.File.Exists(args[i])) { if (System.IO.Path.GetExtension(args[i]).Equals(".csv", StringComparison.OrdinalIgnoreCase)) { string Text = System.IO.File.ReadAllText(args[i], System.Text.Encoding.UTF8); if (Text.Length != -1 && Text.IndexOf("CreateMeshBuilder", StringComparison.OrdinalIgnoreCase) == -1) { if (NewArgs.Length != 0) { NewArgs.Append(" "); } NewArgs.Append("\"" + args[i] + "\""); SkipArgs[i] = true; Skips++; } } } else { SkipArgs[i] = true; Skips++; } } if (NewArgs.Length != 0) { System.Diagnostics.Process.Start(File, NewArgs.ToString()); } if (Skips == args.Length) { return; } } } // application Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) != 0) { MessageBox.Show("SDL failed to initialize the video subsystem.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } Interface.CurrentOptions.ObjectOptimizationBasicThreshold = 1000; Interface.CurrentOptions.ObjectOptimizationFullThreshold = 250; // initialize sdl window SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DEPTH_SIZE, 16); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, 8); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_GREEN_SIZE, 8); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_BLUE_SIZE, 8); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_ALPHA_SIZE, 8); SDL.SDL_ShowCursor(1); // initialize camera ResetCamera(); // create window Renderer.ScreenWidth = 960; Renderer.ScreenHeight = 600; // int Bits = 32; //Sdl.SDL_SetVideoMode(Renderer.ScreenWidth, Renderer.ScreenHeight, Bits, Sdl.SDL_OPENGL | Sdl.SDL_DOUBLEBUF); SDLWindow = SDL.SDL_CreateWindow(Application.ProductName, SDL.SDL_WINDOWPOS_UNDEFINED, SDL.SDL_WINDOWPOS_UNDEFINED, Renderer.ScreenWidth, Renderer.ScreenHeight, SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE); if (SDLWindow != IntPtr.Zero) { // create window string File = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder(), "icon.png"); if (System.IO.File.Exists(File)) { // set up icon iconBmp = new Bitmap(File); // load file iconData = iconBmp.LockBits(new Rectangle(0, 0, iconBmp.Width, iconBmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); // lock data iconSurface = SDL.SDL_CreateRGBSurfaceFrom(iconData.Scan0, iconBmp.Width, iconBmp.Height, 32, iconData.Stride, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); // upload to sdl SDL.SDL_SetWindowIcon(SDLWindow, iconSurface); // use icon // free is on the end of the program /* * IntPtr bitmap = SDL.SDL_LoadBMP(File); * if (bitmap != IntPtr.Zero) { * SDL.SDL_Surface Surface = (SDL.SDL_Surface)System.Runtime.InteropServices.Marshal.PtrToStructure(bitmap, typeof(SDL.SDL_Surface)); * uint ColorKey = SDL.SDL_MapRGB(Surface.format, 0, 0, 255); * SDL.SDL_SetColorKey(bitmap, 1, ColorKey); * SDL.SDL_SetWindowIcon(SDLWindow,bitmap); * } */ } GLContext = SDL.SDL_GL_CreateContext(SDLWindow); tkContext = new GraphicsContext(new ContextHandle(GLContext), SDL.SDL_GL_GetProcAddress, () => new ContextHandle(SDL.SDL_GL_GetCurrentContext())); // anisotropic filtering string[] extensions = GL.GetString(StringName.Extensions).Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries); Interface.CurrentOptions.AnisotropicFilteringMaximum = 0; for (int i = 0; i < extensions.Length; i++) { if (extensions[i] == "GL_EXT_texture_filter_anisotropic") { float n; GL.GetFloat((GetPName)ExtTextureFilterAnisotropic.MaxTextureMaxAnisotropyExt, out n); Interface.CurrentOptions.AnisotropicFilteringMaximum = (int)Math.Round((double)n); break; } } if (Interface.CurrentOptions.AnisotropicFilteringMaximum <= 0) { Interface.CurrentOptions.AnisotropicFilteringMaximum = 0; Interface.CurrentOptions.AnisotropicFilteringLevel = 0; Interface.CurrentOptions.Interpolation = TextureManager.InterpolationMode.AnisotropicFiltering; } else { Interface.CurrentOptions.AnisotropicFilteringLevel = Interface.CurrentOptions.AnisotropicFilteringMaximum; Interface.CurrentOptions.Interpolation = TextureManager.InterpolationMode.TrilinearMipmapped; } // module initialization Renderer.Initialize(); Renderer.InitializeLighting(); SDL.SDL_GL_SwapWindow(SDLWindow); Fonts.Initialize(); UpdateViewport(); // command line arguments for (int i = 0; i < args.Length; i++) { if (!SkipArgs[i] && System.IO.File.Exists(args[i])) { try { ObjectManager.UnifiedObject o = ObjectManager.LoadObject(args[i], System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, false); ObjectManager.CreateObject(o, new World.Vector3D(0.0, 0.0, 0.0), new World.Transformation(0.0, 0.0, 0.0), new World.Transformation(0.0, 0.0, 0.0), true, 0.0, 0.0, 25.0, 0.0); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Critical, false, "Unhandled error (" + ex.Message + ") encountered while processing the file " + args[i] + "."); } Array.Resize <string>(ref Files, Files.Length + 1); Files[Files.Length - 1] = args[i]; } } ObjectManager.InitializeVisibility(); ObjectManager.FinishCreatingObjects(); ObjectManager.UpdateVisibility(0.0, true); ObjectManager.UpdateAnimatedWorldObjects(0.01, true); UpdateCaption(); Stopwatch timer = new Stopwatch(); timer.Start(); LastTicks = timer.ElapsedMilliseconds; // loop while (!Quit) { long ticks = timer.ElapsedMilliseconds; double timeElapsed = 0.001 * (double)(ticks - LastTicks); if (timeElapsed < 0.0001) { timeElapsed = 0.0001; } LastTicks = ticks; DateTime time = DateTime.Now; Game.SecondsSinceMidnight = (double)(3600 * time.Hour + 60 * time.Minute + time.Second) + 0.001 * (double)time.Millisecond; ObjectManager.UpdateAnimatedWorldObjects(timeElapsed, false); ProcessEvents(); if (ReducedMode) { System.Threading.Thread.Sleep(125); } else { System.Threading.Thread.Sleep(1); } bool updatelight = false; bool keep = false; // rotate x if (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)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); World.Rotate(ref World.AbsoluteCameraDirection.X, ref World.AbsoluteCameraDirection.Y, ref World.AbsoluteCameraDirection.Z, 0.0, 1.0, 0.0, cosa, sina); World.Rotate(ref World.AbsoluteCameraUp.X, ref World.AbsoluteCameraUp.Y, ref World.AbsoluteCameraUp.Z, 0.0, 1.0, 0.0, cosa, sina); World.Rotate(ref World.AbsoluteCameraSide.X, ref World.AbsoluteCameraSide.Y, ref World.AbsoluteCameraSide.Z, 0.0, 1.0, 0.0, cosa, sina); keep = true; } // rotate y if (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)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); World.Rotate(ref World.AbsoluteCameraDirection.X, ref World.AbsoluteCameraDirection.Y, ref World.AbsoluteCameraDirection.Z, World.AbsoluteCameraSide.X, World.AbsoluteCameraSide.Y, World.AbsoluteCameraSide.Z, cosa, sina); World.Rotate(ref World.AbsoluteCameraUp.X, ref World.AbsoluteCameraUp.Y, ref World.AbsoluteCameraUp.Z, World.AbsoluteCameraSide.X, World.AbsoluteCameraSide.Y, World.AbsoluteCameraSide.Z, cosa, sina); keep = true; } // move x if (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)MoveX * d; if (MoveXSpeed < -m) { MoveXSpeed = -m; } else if (MoveXSpeed > m) { MoveXSpeed = m; } } if (MoveXSpeed != 0.0) { World.AbsoluteCameraPosition.X += MoveXSpeed * timeElapsed * World.AbsoluteCameraSide.X; World.AbsoluteCameraPosition.Y += MoveXSpeed * timeElapsed * World.AbsoluteCameraSide.Y; World.AbsoluteCameraPosition.Z += MoveXSpeed * timeElapsed * World.AbsoluteCameraSide.Z; keep = true; } // move y if (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)MoveY * d; if (MoveYSpeed < -m) { MoveYSpeed = -m; } else if (MoveYSpeed > m) { MoveYSpeed = m; } } if (MoveYSpeed != 0.0) { World.AbsoluteCameraPosition.X += MoveYSpeed * timeElapsed * World.AbsoluteCameraUp.X; World.AbsoluteCameraPosition.Y += MoveYSpeed * timeElapsed * World.AbsoluteCameraUp.Y; World.AbsoluteCameraPosition.Z += MoveYSpeed * timeElapsed * World.AbsoluteCameraUp.Z; keep = true; } // move z if (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)MoveZ * d; if (MoveZSpeed < -m) { MoveZSpeed = -m; } else if (MoveZSpeed > m) { MoveZSpeed = m; } } if (MoveZSpeed != 0.0) { World.AbsoluteCameraPosition.X += MoveZSpeed * timeElapsed * World.AbsoluteCameraDirection.X; World.AbsoluteCameraPosition.Y += MoveZSpeed * timeElapsed * World.AbsoluteCameraDirection.Y; World.AbsoluteCameraPosition.Z += MoveZSpeed * timeElapsed * World.AbsoluteCameraDirection.Z; keep = true; } // lighting if (LightingRelative == -1) { LightingRelative = (double)LightingTarget; updatelight = true; } if (LightingTarget == 0) { if (LightingRelative != 0.0) { LightingRelative -= 0.5 * timeElapsed; if (LightingRelative < 0.0) { LightingRelative = 0.0; } updatelight = true; keep = true; } } else { if (LightingRelative != 1.0) { LightingRelative += 0.5 * timeElapsed; if (LightingRelative > 1.0) { LightingRelative = 1.0; } updatelight = true; keep = true; } } // continue if (ReducedMode) { ReducedModeEnteringTime = (int)(ticks + 3000); } else { if (keep) { ReducedModeEnteringTime = (int)(ticks + 3000); } else if (ticks > ReducedModeEnteringTime) { ReducedMode = true; World.AbsoluteCameraSide.Y = 0.0; World.Normalize(ref World.AbsoluteCameraSide.X, ref World.AbsoluteCameraSide.Y, ref World.AbsoluteCameraSide.Z); World.Normalize(ref World.AbsoluteCameraDirection.X, ref World.AbsoluteCameraDirection.Y, ref World.AbsoluteCameraDirection.Z); World.AbsoluteCameraUp = World.Cross(World.AbsoluteCameraDirection, World.AbsoluteCameraSide); } } if (updatelight) { Renderer.OptionAmbientColor.R = (byte)Math.Round(32.0 + 128.0 * LightingRelative * (2.0 - LightingRelative)); Renderer.OptionAmbientColor.G = (byte)Math.Round(32.0 + 128.0 * 0.5 * (LightingRelative + LightingRelative * (2.0 - LightingRelative))); Renderer.OptionAmbientColor.B = (byte)Math.Round(32.0 + 128.0 * LightingRelative); Renderer.OptionDiffuseColor.R = (byte)Math.Round(32.0 + 128.0 * LightingRelative); Renderer.OptionDiffuseColor.G = (byte)Math.Round(32.0 + 128.0 * LightingRelative); Renderer.OptionDiffuseColor.B = (byte)Math.Round(32.0 + 128.0 * Math.Sqrt(LightingRelative)); Renderer.InitializeLighting(); } Renderer.RenderScene(); SDL.SDL_GL_SwapWindow(SDLWindow); } // quit TextureManager.UnuseAllTextures(); if (iconSurface != IntPtr.Zero) { SDL.SDL_FreeSurface(iconSurface); // free surface } if (iconBmp != null && iconData != null) { iconBmp.UnlockBits(iconData); // free pixels iconBmp.Dispose(); } SDL.SDL_GL_DeleteContext(GLContext); SDL.SDL_DestroyWindow(SDLWindow); SDL.SDL_Quit(); } else { MessageBox.Show("SDL failed to create the window.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } }
// read object /// <summary>Loads a Loksim3D object from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <param name="ForceTextureRepeatX">Whether to force TextureWrapMode.Repeat for the X-axis</param> /// /// <param name="ForceTextureRepeatY">Whether to force TextureWrapMode.Repeat for the Y-axis</param> /// <returns>The object loaded.</returns> internal static ObjectManager.StaticObject ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY, double RotationX, double RotationY, double RotationZ) { XmlDocument currentXML = new XmlDocument(); //May need to be changed to use de-DE System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; //Initialise the object ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Object.Mesh.Faces = new World.MeshFace[] { }; Object.Mesh.Materials = new World.MeshMaterial[] { }; Object.Mesh.Vertices = new World.Vertex[] { }; MeshBuilder Builder = new MeshBuilder(); World.Vector3Df[] Normals = new World.Vector3Df[4]; bool PropertiesFound = false; World.Vertex[] tempVertices = new World.Vertex[0]; World.Vector3Df[] tempNormals = new World.Vector3Df[0]; World.ColorRGB transparentColor = new World.ColorRGB(); string tday = null; string tnight = null; bool TransparencyUsed = false; bool Face2 = false; int TextureWidth = 0; int TextureHeight = 0; if (File.Exists(FileName)) { currentXML.Load(FileName); } else { return(null); } //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/OBJECT"); //Check this file actually contains Loksim3D object nodes if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.HasChildNodes) { foreach (XmlNode node in outerNode.ChildNodes) { //I think there should only be one properties node?? //Need better format documentation if (node.Name == "Props" && PropertiesFound == false) { if (node.Attributes != null) { //Our node has child nodes, therefore this properties node should be valid //Needs better validation PropertiesFound = true; foreach (XmlAttribute attribute in node.Attributes) { switch (attribute.Name) { //Sets the texture //Loksim3D objects only support daytime textures case "Texture": tday = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), attribute.Value); if (File.Exists(tday)) { try { using (Bitmap TextureInformation = new Bitmap(tday)) { TextureWidth = TextureInformation.Width; TextureHeight = TextureInformation.Height; Color color = TextureInformation.GetPixel(1, 1); transparentColor = new World.ColorRGB((byte)color.R, (byte)color.G, (byte)color.B); } } catch { Interface.AddMessage(Interface.MessageType.Error, true, "An error occured loading daytime texture " + tday + " in file " + FileName); tday = null; } } else { Interface.AddMessage(Interface.MessageType.Error, true, "DaytimeTexture " + tday + " could not be found in file " + FileName); } break; //Defines whether the texture uses transparency //May be omitted case "Transparent": if (attribute.Value == "TRUE") { TransparencyUsed = true; } else { TransparencyUsed = false; } break; //Sets the transparency type case "TransparentTyp": switch (attribute.Value) { case "0": //Transparency is disabled TransparencyUsed = false; break; case "1": //Transparency is solid black TransparencyUsed = true; transparentColor = new World.ColorRGB(0, 0, 0); break; case "2": //Transparency is the color at Pixel 1,1 TransparencyUsed = true; break; case "3": //This is used when transparency is used with an alpha bitmap //Not currently supported TransparencyUsed = false; break; } break; //Sets whether the rears of the faces are to be drawn case "Drawrueckseiten": Face2 = true; break; /* * MISSING PROPERTIES: * AutoRotate - Rotate with tracks?? LS3D presumably uses a 3D world system. * Beleuchtet- Translates as illuminated. Presume something to do with lighting? - What emissive color? * FileAuthor * FileInfo * FilePicture */ } } } } //The point command is eqivilant to a vertex else if (node.Name == "Point" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { //Vertex double vx = 0.0, vy = 0.0, vz = 0.0; //Normals double nx = 0.0, ny = 0.0, nz = 0.0; foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { //Sets the vertex normals case "Normal": string[] NormalPoints = attribute.Value.Split(';'); double.TryParse(NormalPoints[0], out nx); double.TryParse(NormalPoints[1], out ny); double.TryParse(NormalPoints[2], out nz); break; //Sets the vertex 3D co-ordinates case "Vekt": string[] VertexPoints = attribute.Value.Split(';'); double.TryParse(VertexPoints[0], out vx); double.TryParse(VertexPoints[1], out vy); double.TryParse(VertexPoints[2], out vz); break; } } World.Normalize(ref nx, ref ny, ref nz); { //Resize temp arrays Array.Resize <World.Vertex>(ref tempVertices, tempVertices.Length + 1); Array.Resize <World.Vector3Df>(ref tempNormals, tempNormals.Length + 1); //Add vertex and normals to temp array tempVertices[tempVertices.Length - 1].Coordinates = new World.Vector3D(vx, vy, vz); tempNormals[tempNormals.Length - 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); } Array.Resize <World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); while (Builder.Vertices.Length >= Normals.Length) { Array.Resize <World.Vector3Df>(ref Normals, Normals.Length << 1); } Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new World.Vector3D(vx, vy, vz); Normals[Builder.Vertices.Length - 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); } } } //The Flaeche command creates a face else if (node.Name == "Flaeche" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { //Defines the verticies in this face //**NOTE**: A vertex may appear in multiple faces with different texture co-ordinates if (childNode.Attributes["Points"] != null) { string[] Verticies = childNode.Attributes["Points"].Value.Split(';'); int f = Builder.Faces.Length; //Add 1 to the length of the face array Array.Resize <World.MeshFace>(ref Builder.Faces, f + 1); Builder.Faces[f] = new World.MeshFace(); //Create the vertex array for the face Builder.Faces[f].Vertices = new World.MeshFaceVertex[Verticies.Length]; while (Builder.Vertices.Length > Normals.Length) { Array.Resize <World.Vector3Df>(ref Normals, Normals.Length << 1); } //Run through the vertices list and grab from the temp array for (int j = 0; j < Verticies.Length; j++) { //This is the position of the vertex in the temp array int currentVertex; int.TryParse(Verticies[j], out currentVertex); //Add one to the actual vertex array Array.Resize <World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); //Set coordinates Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = tempVertices[currentVertex].Coordinates; //Set the vertex index Builder.Faces[f].Vertices[j].Index = (ushort)(Builder.Vertices.Length - 1); //Set the normals Builder.Faces[f].Vertices[j].Normal = tempNormals[currentVertex]; //Now deal with the texture //Texture mapping points are in pixels X,Y and are relative to the face in question rather than the vertex if (childNode.Attributes["Texture"] != null) { string[] TextureCoords = childNode.Attributes["Texture"].Value.Split(';'); World.Vector2Df currentCoords; float OpenBVEWidth; float OpenBVEHeight; string[] splitCoords = TextureCoords[j].Split(','); float.TryParse(splitCoords[0], out OpenBVEWidth); float.TryParse(splitCoords[1], out OpenBVEHeight); if (TextureWidth != 0 && TextureHeight != 0) { currentCoords.X = (OpenBVEWidth / TextureWidth); currentCoords.Y = (OpenBVEHeight / TextureHeight); } else { currentCoords.X = 0; currentCoords.Y = 0; } Builder.Vertices[Builder.Vertices.Length - 1].TextureCoordinates = currentCoords; } if (Face2) { Builder.Faces[f].Flags = (byte)World.MeshFace.Face2Mask; } } } } } } } } } } //Apply rotation /* * NOTES: * No rotation order is specified * The rotation string in a .l3dgrp file is ordered Y, X, Z ??? Can't find a good reason for this ??? * Rotations must still be performed in X,Y,Z order to produce correct results */ if (RotationX != 0.0) { //This is actually the Y-Axis rotation //Convert to radians RotationX *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 0, 1, 0, RotationX); } if (RotationY != 0.0) { //This is actually the X-Axis rotation //Convert to radians RotationY *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 1, 0, 0, RotationY); } if (RotationZ != 0.0) { //Convert to radians RotationZ *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 0, 0, 1, RotationZ); } //These files appear to only have one texture defined //Therefore import later- May have to change if (File.Exists(tday)) { for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].DaytimeTexture = tday; Builder.Materials[j].NighttimeTexture = tnight; } } if (TransparencyUsed == true) { for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].TransparentColor = transparentColor; Builder.Materials[j].TransparentColorUsed = true; } } } ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); World.CreateNormals(ref Object.Mesh); return(Object); }