/// <summary>Creates a mirrored copy of the prototype object (Animated objects)</summary> /// <param name="Prototype">The prototype</param> /// <returns>The mirrored copy</returns> private static UnifiedObject GetMirroredObject(UnifiedObject Prototype) { if (Prototype is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)Prototype; return(GetMirroredStaticObject(s)); } if (Prototype is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)Prototype; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection { Objects = new ObjectManager.AnimatedObject[a.Objects.Length] }; for (int i = 0; i < a.Objects.Length; i++) { Result.Objects[i] = a.Objects[i].Clone(); for (int j = 0; j < a.Objects[i].States.Length; j++) { Result.Objects[i].States[j].Object = GetMirroredStaticObject(a.Objects[i].States[j].Object); } Result.Objects[i].TranslateXDirection.X *= -1.0; Result.Objects[i].TranslateYDirection.X *= -1.0; Result.Objects[i].TranslateZDirection.X *= -1.0; Result.Objects[i].RotateXDirection.X *= -1.0; Result.Objects[i].RotateYDirection.X *= -1.0; Result.Objects[i].RotateZDirection.X *= -1.0; } return(Result); } return(null); }
internal void LoadCarSections(UnifiedObject currentObject) { int j = CarSections.Length; Array.Resize(ref CarSections, j + 1); CarSections[j] = new CarSection(); if (currentObject is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)currentObject; CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; CarSections[j].Elements[0] = new ObjectManager.AnimatedObject { States = new ObjectManager.AnimatedObjectState[1] }; CarSections[j].Elements[0].States[0].Position = Vector3.Zero; CarSections[j].Elements[0].States[0].Object = s; CarSections[j].Elements[0].CurrentState = 0; CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (currentObject is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)currentObject; CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { CarSections[j].Elements[h] = a.Objects[h].Clone(); CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } }
internal void Apply(out ObjectManager.StaticObject Object) { Object = new ObjectManager.StaticObject(); if (faces.Count != 0) { int mf = Object.Mesh.Faces.Length; int mm = Object.Mesh.Materials.Length; int mv = Object.Mesh.Vertices.Length; Array.Resize(ref Object.Mesh.Faces, mf + faces.Count); Array.Resize(ref Object.Mesh.Materials, mm + materials.Count); Array.Resize(ref Object.Mesh.Vertices, mv + verticies.Count); for (int i = 0; i < verticies.Count; i++) { Object.Mesh.Vertices[mv + i] = new OpenBveApi.Objects.Vertex(verticies[i].Coordinates, verticies[i].TextureCoordinates); } for (int i = 0; i < faces.Count; i++) { Object.Mesh.Faces[i] = new MeshFace(faces[i].Vertices); Object.Mesh.Faces[i].Material = (ushort)faces[i].Material; for (int k = 0; k < faces[i].Vertices.Length; k++) { Object.Mesh.Faces[i].Vertices[k].Normal = verticies[faces[i].Vertices[k]].Normal; } for (int j = 0; j < Object.Mesh.Faces[mf + i].Vertices.Length; j++) { Object.Mesh.Faces[mf + i].Vertices[j].Index += (ushort)mv; } Object.Mesh.Faces[mf + i].Material += (ushort)mm; } for (int i = 0; i < materials.Count; i++) { Object.Mesh.Materials[mm + i].Flags = 0; Object.Mesh.Materials[mm + i].Color = materials[i].Color; Object.Mesh.Materials[mm + i].TransparentColor = Color24.Black; Object.Mesh.Materials[mm + i].BlendMode = MeshMaterialBlendMode.Normal; if (materials[i].DaytimeTexture != null) { OpenBveApi.Textures.Texture tday; Textures.RegisterTexture(materials[i].DaytimeTexture, out tday); Object.Mesh.Materials[mm + i].DaytimeTexture = tday; } else { Object.Mesh.Materials[mm + i].DaytimeTexture = null; } Object.Mesh.Materials[mm + i].EmissiveColor = materials[i].EmissiveColor; Object.Mesh.Materials[mm + i].NighttimeTexture = null; Object.Mesh.Materials[mm + i].DaytimeNighttimeBlend = 0; Object.Mesh.Materials[mm + i].GlowAttenuationData = materials[i].GlowAttenuationData; Object.Mesh.Materials[mm + i].WrapMode = materials[i].WrapMode; } } }
/// <summary>Checks whether the camera can move in the selected direction, due to a bounding box.</summary> /// <returns>True if we are able to move the camera, false otherwise</returns> internal static bool PerformBoundingBoxTest(ref ObjectManager.StaticObject bounding, ref Vector3 cameraLocation) { if (cameraLocation.X < bounding.Mesh.BoundingBox[0].X || cameraLocation.X > bounding.Mesh.BoundingBox[1].X || cameraLocation.Y <bounding.Mesh.BoundingBox[0].Y || cameraLocation.Y> bounding.Mesh.BoundingBox[1].Y || cameraLocation.Z <bounding.Mesh.BoundingBox[0].Z || cameraLocation.Z> bounding.Mesh.BoundingBox[1].Z) { //Our bounding boxes do not intersect return(true); } return(false); }
/// <summary>Adds a new Static Object to the dictionary</summary> /// <param name="key">The object index</param> /// <param name="staticObject">The object</param> /// <param name="Type">The object type</param> internal void Add(int key, ObjectManager.StaticObject staticObject, string Type) { if (this.ContainsKey(key)) { this[key] = staticObject; Interface.AddMessage(Interface.MessageType.Warning, false, "The " + Type + " with an index of " + key + " has been declared twice: The most recent declaration will be used."); } else { this.Add(key, staticObject); } }
private static void ChildrenNode(ref ObjectManager.StaticObject obj, ref MeshBuilder builder, Node child) { builder.TransformMatrix = ConvertMatrix(child.TrafoMatrix); foreach (var mesh in child.Meshes) { MeshBuilder(ref obj, ref builder, mesh); } foreach (var grandchild in child.Children) { ChildrenNode(ref obj, ref builder, grandchild); } }
/// <summary>Creates a mirrored copy of the prototype object</summary> /// <param name="Prototype">The prototype</param> /// <returns>The mirrored copy</returns> private static ObjectManager.StaticObject GetMirroredStaticObject(ObjectManager.StaticObject Prototype) { ObjectManager.StaticObject Result = Prototype.Clone(); for (int i = 0; i < Result.Mesh.Vertices.Length; i++) { Result.Mesh.Vertices[i].Coordinates.X = -Result.Mesh.Vertices[i].Coordinates.X; } for (int i = 0; i < Result.Mesh.Faces.Length; i++) { for (int k = 0; k < Result.Mesh.Faces[i].Vertices.Length; k++) { Result.Mesh.Faces[i].Vertices[k].Normal.X = -Result.Mesh.Faces[i].Vertices[k].Normal.X; } Result.Mesh.Faces[i].Flip(); } return(Result); }
private static ObjectManager.StaticObject LoadBinaryX(byte[] Data, int FloatingPointSize) { Block block = new BinaryBlock(Data, FloatingPointSize); block.FloatingPointSize = FloatingPointSize; ObjectManager.StaticObject obj = new ObjectManager.StaticObject(); MeshBuilder builder = new MeshBuilder(); Material material = new Material(); while (block.Position() < block.Length()) { Block subBlock = block.ReadSubBlock(); ParseSubBlock(subBlock, ref obj, ref builder, ref material); } builder.Apply(ref obj); obj.Mesh.CreateNormals(); return(obj); }
/// <summary>Creates a new background object</summary> /// <param name="Object">The object to use for the background</param> internal BackgroundObject(ObjectManager.StaticObject Object) { this.ObjectBackground = Object; //As we are using an object based background, calculate the minimum clip distance for (int i = 0; i > Object.Mesh.Vertices.Length; i++) { double X = Math.Abs(Object.Mesh.Vertices[i].Coordinates.X); double Z = Math.Abs(Object.Mesh.Vertices[i].Coordinates.Z); if (X < ClipDistance) { ClipDistance = X; } if (Z < ClipDistance) { ClipDistance = Z; } } }
private static ObjectManager.StaticObject LoadTextualX(string Text) { Text = Text.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " ").Trim(); ObjectManager.StaticObject obj = new ObjectManager.StaticObject(); MeshBuilder builder = new MeshBuilder(); Material material = new Material(); Block block = new TextualBlock(Text); while (block.Position() < block.Length() - 5) { Block subBlock = block.ReadSubBlock(); ParseSubBlock(subBlock, ref obj, ref builder, ref material); } builder.Apply(ref obj); obj.Mesh.CreateNormals(); if (rootMatrix != Matrix4D.NoTransformation) { for (int i = 0; i < obj.Mesh.Vertices.Length; i++) { obj.Mesh.Vertices[i].Coordinates.Transform(rootMatrix); } } return(obj); }
internal static void LoadAutoGeneratedObjects(string CompatibilityFolder, out string LimitGraphicsPath) { string SignalPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Signals"); SignalPost = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalPath, "signal_post.csv"), System.Text.Encoding.UTF8, false); string LimitPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Limits"); LimitGraphicsPath = OpenBveApi.Path.CombineDirectory(LimitPath, "Graphics"); LimitPostStraight = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_straight.csv"), System.Text.Encoding.UTF8, false); LimitPostLeft = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_left.csv"), System.Text.Encoding.UTF8, false); LimitPostRight = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_right.csv"), System.Text.Encoding.UTF8, false); LimitPostInfinite = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_infinite.csv"), System.Text.Encoding.UTF8, false); LimitOneDigit = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_1_digit.csv"), System.Text.Encoding.UTF8, false); LimitTwoDigits = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_2_digits.csv"), System.Text.Encoding.UTF8, false); LimitThreeDigits = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_3_digits.csv"), System.Text.Encoding.UTF8, false); StopPost = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(CompatibilityFolder, "stop.csv"), System.Text.Encoding.UTF8, false); string TransponderPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Transponders"); TransponderS = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "s.csv"), System.Text.Encoding.UTF8, false); TransponderSN = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "sn.csv"), System.Text.Encoding.UTF8, false); TransponderFalseStart = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "falsestart.csv"), System.Text.Encoding.UTF8, false); TransponderPOrigin = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "porigin.csv"), System.Text.Encoding.UTF8, false); TransponderPStop = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "pstop.csv"), System.Text.Encoding.UTF8, false); }
private static void ApplyMeshBuilder(ref ObjectManager.StaticObject Object, MeshBuilder Builder) { if (Builder.Faces.Count != 0) { int mf = Object.Mesh.Faces.Length; int mm = Object.Mesh.Materials.Length; int mv = Object.Mesh.Vertices.Length; Array.Resize <World.MeshFace>(ref Object.Mesh.Faces, mf + Builder.Faces.Count); if (mm == 0) { if (Object.Mesh.Materials.Length == 0) { /* * If the object has no materials defined at all, we need to add one */ Array.Resize(ref Object.Mesh.Materials, 1); Object.Mesh.Materials[0] = new World.MeshMaterial(); Object.Mesh.Materials[0].Color = new Color32(255, 255, 255); Object.Mesh.Materials[0].Flags = (byte)(0 | 0); Object.Mesh.Materials[0].DaytimeTexture = null; Object.Mesh.Materials[0].NighttimeTexture = null; mm++; } } if (Builder.Materials.Length > 0) { Array.Resize <World.MeshMaterial>(ref Object.Mesh.Materials, mm + Builder.Materials.Length); } else { /* * If no materials have been defined for this face group, use the last material */ mm -= 1; } Array.Resize <VertexTemplate>(ref Object.Mesh.Vertices, mv + Builder.Vertices.Count); for (int i = 0; i < Builder.Vertices.Count; i++) { Object.Mesh.Vertices[mv + i] = new Vertex((Vertex)Builder.Vertices[i]); } for (int i = 0; i < Builder.Faces.Count; i++) { Object.Mesh.Faces[mf + i] = Builder.Faces[i]; for (int j = 0; j < Object.Mesh.Faces[mf + i].Vertices.Length; j++) { Object.Mesh.Faces[mf + i].Vertices[j].Index += (ushort)mv; } Object.Mesh.Faces[mf + i].Material += (ushort)mm; } for (int i = 0; i < Builder.Materials.Length; i++) { Object.Mesh.Materials[mm + i].Flags = (byte)((Builder.Materials[i].EmissiveColorUsed ? World.MeshMaterial.EmissiveColorMask : 0) | (Builder.Materials[i].TransparentColorUsed ? World.MeshMaterial.TransparentColorMask : 0)); Object.Mesh.Materials[mm + i].Color = Builder.Materials[i].Color; Object.Mesh.Materials[mm + i].TransparentColor = Builder.Materials[i].TransparentColor; if (Builder.Materials[i].DaytimeTexture != null) { Textures.Texture tday; if (Builder.Materials[i].TransparentColorUsed) { Textures.RegisterTexture(Builder.Materials[i].DaytimeTexture, new OpenBveApi.Textures.TextureParameters(null, new Color24(Builder.Materials[i].TransparentColor.R, Builder.Materials[i].TransparentColor.G, Builder.Materials[i].TransparentColor.B)), out tday); } else { Textures.RegisterTexture(Builder.Materials[i].DaytimeTexture, out tday); } Object.Mesh.Materials[mm + i].DaytimeTexture = tday; } else { Object.Mesh.Materials[mm + i].DaytimeTexture = null; } Object.Mesh.Materials[mm + i].EmissiveColor = Builder.Materials[i].EmissiveColor; if (Builder.Materials[i].NighttimeTexture != null) { Textures.Texture tnight; if (Builder.Materials[i].TransparentColorUsed) { Textures.RegisterTexture(Builder.Materials[i].NighttimeTexture, new OpenBveApi.Textures.TextureParameters(null, new Color24(Builder.Materials[i].TransparentColor.R, Builder.Materials[i].TransparentColor.G, Builder.Materials[i].TransparentColor.B)), out tnight); } else { Textures.RegisterTexture(Builder.Materials[i].NighttimeTexture, out tnight); } Object.Mesh.Materials[mm + i].NighttimeTexture = tnight; } else { Object.Mesh.Materials[mm + i].NighttimeTexture = null; } Object.Mesh.Materials[mm + i].DaytimeNighttimeBlend = 0; Object.Mesh.Materials[mm + i].BlendMode = Builder.Materials[i].BlendMode; Object.Mesh.Materials[mm + i].GlowAttenuationData = Builder.Materials[i].GlowAttenuationData; Object.Mesh.Materials[mm + i].WrapMode = Textures.OpenGlTextureWrapMode.RepeatRepeat; } } }
private static void ApplyMeshBuilder(ref ObjectManager.StaticObject Object, MeshBuilder Builder, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { if (Builder.Faces.Count != 0) { int mf = Object.Mesh.Faces.Length; int mm = Object.Mesh.Materials.Length; int mv = Object.Mesh.Vertices.Length; Array.Resize <World.MeshFace>(ref Object.Mesh.Faces, mf + Builder.Faces.Count); if (mm == 0) { if (Object.Mesh.Materials.Length == 0) { /* * If the object has no materials defined at all, we need to add one */ Array.Resize(ref Object.Mesh.Materials, 1); Object.Mesh.Materials[0] = new World.MeshMaterial(); Object.Mesh.Materials[0].Color = new Color32(255, 255, 255); Object.Mesh.Materials[0].Flags = (byte)(0 | 0); Object.Mesh.Materials[0].DaytimeTextureIndex = -1; Object.Mesh.Materials[0].NighttimeTextureIndex = -1; mm++; } } if (Builder.Materials.Length > 0) { Array.Resize <World.MeshMaterial>(ref Object.Mesh.Materials, mm + Builder.Materials.Length); } else { /* * If no materials have been defined for this face group, use the last material */ mm -= 1; } Array.Resize <VertexTemplate>(ref Object.Mesh.Vertices, mv + Builder.Vertices.Count); for (int i = 0; i < Builder.Vertices.Count; i++) { Object.Mesh.Vertices[mv + i] = new Vertex((Vertex)Builder.Vertices[i]); } for (int i = 0; i < Builder.Faces.Count; i++) { Object.Mesh.Faces[mf + i] = Builder.Faces[i]; for (int j = 0; j < Object.Mesh.Faces[mf + i].Vertices.Length; j++) { Object.Mesh.Faces[mf + i].Vertices[j].Index += (ushort)mv; } Object.Mesh.Faces[mf + i].Material += (ushort)mm; } for (int i = 0; i < Builder.Materials.Length; i++) { Object.Mesh.Materials[mm + i].Flags = (byte)((Builder.Materials[i].EmissiveColorUsed ? World.MeshMaterial.EmissiveColorMask : 0) | (Builder.Materials[i].TransparentColorUsed ? World.MeshMaterial.TransparentColorMask : 0)); Object.Mesh.Materials[mm + i].Color = Builder.Materials[i].Color; Object.Mesh.Materials[mm + i].TransparentColor = Builder.Materials[i].TransparentColor; TextureManager.TextureWrapMode WrapX, WrapY; if (ForceTextureRepeatX) { WrapX = TextureManager.TextureWrapMode.Repeat; } else { WrapX = TextureManager.TextureWrapMode.ClampToEdge; } if (ForceTextureRepeatY) { WrapY = TextureManager.TextureWrapMode.Repeat; } else { WrapY = TextureManager.TextureWrapMode.ClampToEdge; } if (WrapX != TextureManager.TextureWrapMode.Repeat | WrapY != TextureManager.TextureWrapMode.Repeat) { for (int j = 0; j < Builder.Vertices.Count; j++) { if (Builder.Vertices[j].TextureCoordinates.X <0.0 | Builder.Vertices[j].TextureCoordinates.X> 1.0) { WrapX = TextureManager.TextureWrapMode.Repeat; } if (Builder.Vertices[j].TextureCoordinates.Y <0.0 | Builder.Vertices[j].TextureCoordinates.Y> 1.0) { WrapY = TextureManager.TextureWrapMode.Repeat; } } } if (Builder.Materials[i].DaytimeTexture != null) { int tday = TextureManager.RegisterTexture(Builder.Materials[i].DaytimeTexture, Builder.Materials[i].TransparentColor, Builder.Materials[i].TransparentColorUsed ? (byte)1 : (byte)0, WrapX, WrapY, LoadMode != ObjectManager.ObjectLoadMode.Normal); Object.Mesh.Materials[mm + i].DaytimeTextureIndex = tday; } else { Object.Mesh.Materials[mm + i].DaytimeTextureIndex = -1; } Object.Mesh.Materials[mm + i].EmissiveColor = Builder.Materials[i].EmissiveColor; if (Builder.Materials[i].NighttimeTexture != null) { int tnight = TextureManager.RegisterTexture(Builder.Materials[i].NighttimeTexture, Builder.Materials[i].TransparentColor, Builder.Materials[i].TransparentColorUsed ? (byte)1 : (byte)0, WrapX, WrapY, LoadMode != ObjectManager.ObjectLoadMode.Normal); Object.Mesh.Materials[mm + i].NighttimeTextureIndex = tnight; } else { Object.Mesh.Materials[mm + i].NighttimeTextureIndex = -1; } Object.Mesh.Materials[mm + i].DaytimeNighttimeBlend = 0; Object.Mesh.Materials[mm + i].BlendMode = Builder.Materials[i].BlendMode; Object.Mesh.Materials[mm + i].GlowAttenuationData = Builder.Materials[i].GlowAttenuationData; } } }
internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, Vector3 Rotation) { XmlDocument currentXML = new XmlDocument(); ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection { Objects = new ObjectManager.AnimatedObject[0] }; try { currentXML.Load(FileName); } catch (Exception ex) { //The XML is not strictly valid string[] Lines = File.ReadAllLines(FileName); using (var stringReader = new StringReader(Lines[0])) { var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment }; using (var xmlReader = XmlReader.Create(stringReader, settings)) { if (xmlReader.Read()) { //Attempt to find the text encoding and re-read the file var result = xmlReader.GetAttribute("encoding"); if (result != null) { var e = System.Text.Encoding.GetEncoding(result); Lines = File.ReadAllLines(FileName, e); //Turf out the old encoding, as our string array should now be UTF-8 Lines[0] = "<?xml version=\"1.0\"?>"; } } } } for (int i = 0; i < Lines.Length; i++) { while (Lines[i].IndexOf("\"\"", StringComparison.Ordinal) != -1) { //Loksim parser tolerates multiple quotes, strict XML does not Lines[i] = Lines[i].Replace("\"\"", "\""); } while (Lines[i].IndexOf(" ", StringComparison.Ordinal) != -1) { //Replace double-spaces with singles Lines[i] = Lines[i].Replace(" ", " "); } } bool tryLoad = false; try { //Horrible hack: Write out our string array to a new memory stream, then load from this stream //Why can't XmlDocument.Load() just take a string array...... using (var stream = new MemoryStream()) { var sw = new StreamWriter(stream); foreach (var line in Lines) { sw.Write(line); sw.Flush(); } sw.Flush(); stream.Position = 0; currentXML.Load(stream); tryLoad = true; } } catch { //Generic catch-all clause } if (!tryLoad) { //Pass out the *original* XML error, not anything generated when we've tried to correct it Interface.AddMessage(Interface.MessageType.Error, false, "Error parsing Loksim3D XML: " + ex.Message); return(null); } } string BaseDir = System.IO.Path.GetDirectoryName(FileName); GruppenObject[] CurrentObjects = new GruppenObject[0]; //Check for null if (currentXML.DocumentElement != null) { ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[0]; XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT"); if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode node in outerNode.ChildNodes) { if (node.Name == "Object" && node.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { GruppenObject Object = new GruppenObject { Rotation = Rotation }; foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { case "Name": string ObjectFile = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory); if (!File.Exists(ObjectFile)) { Object.Name = null; Interface.AddMessage(Interface.MessageType.Warning, true, "Ls3d Object file " + attribute.Value + " not found."); } else { Object.Name = ObjectFile; } break; case "Position": string[] SplitPosition = attribute.Value.Split(';'); double.TryParse(SplitPosition[0], out Object.Position.X); double.TryParse(SplitPosition[1], out Object.Position.Y); double.TryParse(SplitPosition[2], out Object.Position.Z); break; case "Rotation": string[] SplitRotation = attribute.Value.Split(';'); Vector3 r; double.TryParse(SplitRotation[0], out r.X); double.TryParse(SplitRotation[1], out r.Y); double.TryParse(SplitRotation[2], out r.Z); Object.Rotation += r; break; case "ShowOn": //Defines when the object should be shown Object.FunctionScript = FunctionScripts.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, false)); break; case "HideOn": //Defines when the object should be hidden Object.FunctionScript = FunctionScripts.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, true)); break; } } if (Object.Name != null) { Array.Resize <GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1); CurrentObjects[CurrentObjects.Length - 1] = Object; } } } } } } } //We've loaded the XML references, now load the objects into memory //Single mesh object, containing all static components of the LS3D object //If we use multiples, the Z-sorting throws a wobbly ObjectManager.StaticObject staticObject = new ObjectManager.StaticObject { Mesh = new World.Mesh { Vertices = new World.Vertex[0], Faces = new World.MeshFace[0], Materials = new World.MeshMaterial[0] } }; for (int i = 0; i < CurrentObjects.Length; i++) { if (CurrentObjects[i] == null || string.IsNullOrEmpty(CurrentObjects[i].Name)) { continue; } ObjectManager.StaticObject Object = null; ObjectManager.AnimatedObjectCollection AnimatedObject = null; try { if (CurrentObjects[i].Name.ToLowerInvariant().EndsWith(".l3dgrp")) { AnimatedObject = ReadObject(CurrentObjects[i].Name, Encoding, LoadMode, CurrentObjects[i].Rotation); } else if (CurrentObjects[i].Name.ToLowerInvariant().EndsWith(".l3dobj")) { Object = Ls3DObjectParser.ReadObject(CurrentObjects[i].Name, LoadMode, CurrentObjects[i].Rotation); } else { throw new Exception("Format " + System.IO.Path.GetExtension(CurrentObjects[i].Name) + " is not currently supported by the Loksim3D object parser"); } } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message); } if (Object != null) { if (!string.IsNullOrEmpty(CurrentObjects[i].FunctionScript)) { //If the function script is not empty, this is a new animated object bit Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length + 1); obj[obj.Length - 1] = Object; int aL = Result.Objects.Length; Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, aL + 1); ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = Object, Position = CurrentObjects[i].Position, }; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[aL] = a; Result.Objects[aL].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(CurrentObjects[i].FunctionScript + " 1 == --"); } else { //Otherwise, join to the main static mesh & update co-ords for (int j = 0; j < Object.Mesh.Vertices.Length; j++) { Object.Mesh.Vertices[j].Coordinates += CurrentObjects[i].Position; } staticObject.JoinObjects(Object); } } else if (AnimatedObject != null) { int rl = Result.Objects.Length; int l = AnimatedObject.Objects.Length; Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + l); for (int o = rl; o < rl + l; o++) { if (AnimatedObject.Objects[o - rl] != null) { Result.Objects[o] = AnimatedObject.Objects[o - rl].Clone(); for (int si = 0; si < Result.Objects[o].States.Length; si++) { Result.Objects[o].States[si].Position += CurrentObjects[i].Position; } } else { Result.Objects[o] = new ObjectManager.AnimatedObject(); Result.Objects[o].States = new ObjectManager.AnimatedObjectState[0]; } } } } if (staticObject != null) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1); ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = staticObject, }; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[Result.Objects.Length - 1] = a; } } return(Result); } //Didn't find an acceptable XML object //Probably will cause things to throw an absolute wobbly somewhere.... return(null); }
// parse extensions config internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, out ObjectManager.UnifiedObject[] CarObjects, TrainManager.Train Train) { CarObjects = new ObjectManager.UnifiedObject[Train.Cars.Length]; bool[] CarObjectsReversed = new bool[Train.Cars.Length]; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "extensions.cfg"); if (System.IO.File.Exists(FileName)) { // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; default: if (Lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(4, Lines[i].Length - 5); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { bool DefinedLength = false; bool DefinedAxles = false; i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { double m; if (double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out m)) { if (m > 0.0) { Train.Cars[n].Length = m; Train.Cars[n].BeaconReceiverPosition = 0.5 * m; DefinedLength = true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(Interface.MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Cars[n].RearAxlePosition = rear; Train.Cars[n].FrontAxlePosition = front; DefinedAxles = true; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": CarObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; if (DefinedLength & !DefinedAxles) { double AxleDistance = 0.4 * Train.Cars[n].Length; Train.Cars[n].RearAxlePosition = -AxleDistance; Train.Cars[n].FrontAxlePosition = AxleDistance; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // coupler string t = Lines[i].Substring(8, Lines[i].Length - 9); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Couplers.Length) { i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "distances": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double min, max; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min)) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out max)) { Interface.AddMessage(Interface.MessageType.Error, false, "Maximum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (min > max) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Couplers[n].MinimumDistanceBetweenCars = min; Train.Couplers[n].MaximumDistanceBetweenCars = max; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index " + t + " does not reference an existing coupler at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { // default Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; } } } // check for car objects and reverse if necessary int carObjects = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] != null) { carObjects++; if (CarObjectsReversed[i]) { { // reverse axle positions double temp = Train.Cars[i].FrontAxlePosition; Train.Cars[i].FrontAxlePosition = -Train.Cars[i].RearAxlePosition; Train.Cars[i].RearAxlePosition = -temp; } if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i]; CsvB3dObjectParser.ApplyScale(obj, -1.0, 1.0, -1.0); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { CsvB3dObjectParser.ApplyScale(obj.Objects[j].States[h].Object, -1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjects > 0 & carObjects < Train.Cars.Length) { Interface.AddMessage(Interface.MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName); } } }
private static void LoadEverythingThreaded() { string RailwayFolder = GetRailwayFolder(CurrentRouteFile); // if (RailwayFolder == null) { // Interface.AddMessage(Interface.MessageType.Critical, false, "The Railway folder could not be found. Please check your folder structure."); // return; // } string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); // reset Game.Reset(true); Game.MinimalisticSimulation = true; // screen World.CameraTrackFollower = new TrackManager.TrackFollower(); World.CameraTrackFollower.Train = null; World.CameraTrackFollower.CarIndex = -1; World.CameraMode = World.CameraViewMode.Interior; // load route bool IsRW = string.Equals(System.IO.Path.GetExtension(CurrentRouteFile), ".rw", StringComparison.OrdinalIgnoreCase); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Game.CalculateSeaLevelConstants(); if (Game.BogusPretrainInstructions.Length != 0) { double t = Game.BogusPretrainInstructions[0].Time; double p = Game.BogusPretrainInstructions[0].TrackPosition; for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++) { if (Game.BogusPretrainInstructions[i].Time > t) { t = Game.BogusPretrainInstructions[i].Time; } else { t += 1.0; Game.BogusPretrainInstructions[i].Time = t; } if (Game.BogusPretrainInstructions[i].TrackPosition > p) { p = Game.BogusPretrainInstructions[i].TrackPosition; } else { p += 1.0; Game.BogusPretrainInstructions[i].TrackPosition = p; } } } RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { TrainManager.Trains[k] = new TrainManager.Train(); TrainManager.Trains[k].TrainIndex = k; if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0) { TrainManager.Trains[k].State = TrainManager.TrainState.Bogus; } else { TrainManager.Trains[k].State = TrainManager.TrainState.Pending; } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus) { // bogus train string Folder = Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"); TrainDatParser.ParseTrainData(Folder, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } SoundCfgParser.LoadNoSound(TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { // real train TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; TrainDatParser.ParseTrainData(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].BeaconReceiver.Train = TrainManager.Trains[k]; } // add panel section if (k == TrainManager.PlayerTrain.TrainIndex) { TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections = new TrainManager.CarSection[1]; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { }; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay = true; TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } } // add exterior section if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { ObjectManager.UnifiedObject[] CarObjects; ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; CsvB3dObjectParser.ApplyScale(so, sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object int j = TrainManager.Trains[k].Cars[i].CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1); if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } } } // place cars { double z = 0.0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxlePosition; TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxlePosition; TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition; z -= TrainManager.Trains[k].Cars[i].Length; if (i < TrainManager.Trains[k].Cars.Length - 1) { z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars); } } } // configure ai / timetable if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } ObjectManager.FinishCreatingObjects(); // update sections if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus) { if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding)) { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } else { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } } }
internal static void ParseExtensionsConfig(string filePath, Encoding encoding, out UnifiedObject[] carObjects, out UnifiedObject[] bogieObjects, out double[] axleLocations, out TrainManager.Train train, bool loadObjects) { CultureInfo Culture = CultureInfo.InvariantCulture; carObjects = new UnifiedObject[] { }; bogieObjects = new UnifiedObject[] { }; axleLocations = new double[] { }; train = new TrainManager.Train(); if (!System.IO.File.Exists(filePath)) { return; } train.Cars = new TrainManager.Car[] { }; bool[] carObjectsReversed = new bool[train.Cars.Length]; bool[] bogieObjectsReversed = new bool[train.Cars.Length * 2]; bool[] carsDefined = new bool[train.Cars.Length]; bool[] bogiesDefined = new bool[train.Cars.Length * 2]; axleLocations = new double[train.Cars.Length * 2]; string trainPath = System.IO.Path.GetDirectoryName(filePath); System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.InvariantCulture; TextEncoding.Encoding newEncoding = TextEncoding.GetEncodingFromFile(filePath); if (newEncoding != TextEncoding.Encoding.Unknown) { switch (newEncoding) { case TextEncoding.Encoding.Utf7: encoding = Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: encoding = Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: encoding = Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: encoding = Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: encoding = Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: encoding = Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: encoding = Encoding.GetEncoding(932); break; } } string[] lines = System.IO.File.ReadAllLines(filePath, encoding); for (int i = 0; i < lines.Length; i++) { int j = lines[i].IndexOf(';'); if (j >= 0) { lines[i] = lines[i].Substring(0, j).Trim(); } else { lines[i] = lines[i].Trim(); } } for (int i = 0; i < lines.Length; i++) { if (lines[i].Length != 0) { switch (lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, culture, out n)) { if (n >= 0) { if (n >= train.Cars.Length) { Array.Resize(ref train.Cars, n + 1); Array.Resize(ref carObjects, n + 1); Array.Resize(ref bogieObjects, (n + 1) * 2); Array.Resize(ref carObjectsReversed, n + 1); Array.Resize(ref bogieObjectsReversed, (n + 1) * 2); Array.Resize(ref carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath); } else { string file = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { carObjects[n] = ObjectManager.LoadObject(file, encoding, false, false, false); } } else { Interface.AddMessage(MessageType.Error, true, "The car object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath); } } } else { Interface.AddMessage(MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); } } i++; } i--; break; default: if (lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = lines[i].Substring(4, lines[i].Length - 5); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, culture, out n)) { if (n >= 0) { if (n >= train.Cars.Length) { Array.Resize(ref train.Cars, n + 1); Array.Resize(ref carObjects, n + 1); Array.Resize(ref bogieObjects, (n + 1) * 2); Array.Resize(ref carObjectsReversed, n + 1); Array.Resize(ref bogieObjectsReversed, (n + 1) * 2); Array.Resize(ref carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); Array.Resize(ref axleLocations, (n + 1) * 2); } if (carsDefined[n]) { Interface.AddMessage(MessageType.Error, false, "Car " + n.ToString(culture) + " has already been declared at line " + (i + 1).ToString(culture) + " in file " + filePath); } carsDefined[n] = true; i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, "An empty car object was supplied at line " + (i + 1).ToString(culture) + " in file " + filePath); break; } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath); } else { string file = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { carObjects[n] = ObjectManager.LoadObject(file, encoding, false, false, false); } } else { Interface.AddMessage(MessageType.Error, true, "The car object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath); } } break; case "length": { double m; if (double.TryParse(b, System.Globalization.NumberStyles.Float, culture, out m)) { if (m > 0.0) { train.Cars[n].Length = m; } else { Interface.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(culture) + " in file " + filePath); } } break; case "reversed": carObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "axles": int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath); } else if (rear >= front) { Interface.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath); } else { axleLocations[n] = rear; axleLocations[n + 1] = front; } } else { Interface.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath); } break; default: Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); break; } } else { Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else if (lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = lines[i].Substring(6, lines[i].Length - 7); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, culture, out n)) { if (n >= train.Cars.Length * 2) { Array.Resize(ref train.Cars, n / 2 + 1); Array.Resize(ref carObjects, n / 2 + 1); Array.Resize(ref bogieObjects, n + 2); Array.Resize(ref carObjectsReversed, n / 2 + 1); Array.Resize(ref bogieObjectsReversed, n + 2); Array.Resize(ref carsDefined, n / 2 + 1); Array.Resize(ref bogiesDefined, n + 2); } if (n > bogiesDefined.Length - 1) { continue; } if (bogiesDefined[n]) { Interface.AddMessage(MessageType.Error, false, "Bogie " + n.ToString(culture) + " has already been declared at line " + (i + 1).ToString(culture) + " in file " + filePath); } bogiesDefined[n] = true; //Assuming that there are two bogies per car if (n >= 0 & n < train.Cars.Length * 2) { i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath); } else { if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, "An empty bogie object was supplied at line " + (i + 1).ToString(culture) + " in file " + filePath); break; } string file = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { bogieObjects[n] = ObjectManager.LoadObject(file, encoding, false, false, false); } } else { Interface.AddMessage(MessageType.Error, true, "The bogie object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath); } } break; case "length": { Interface.AddMessage(MessageType.Error, false, "A defined length is not supported for bogies at line " + (i + 1).ToString(culture) + " in file " + filePath); } break; case "reversed": bogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "axles": //Axles aren't used in bogie positioning, just in rotation break; default: Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); break; } } else { Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else if (lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { /* * Coupler statments are currently not supported in Object Viewer */ i++; } i--; } else { // default if (lines.Length == 1 && encoding.Equals(Encoding.Unicode)) { /* * If only one line, there's a good possibility that our file is NOT Unicode at all * and that the misdetection has turned it into garbage * * Try again with ASCII instead */ ParseExtensionsConfig(filePath, Encoding.GetEncoding(1252), out carObjects, out bogieObjects, out axleLocations, out train, loadObjects); return; } Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); } break; } } } // check for car objects and reverse if necessary int carObjectsCount = 0; for (int i = 0; i < train.Cars.Length; i++) { if (carObjects[i] != null) { carObjectsCount++; if (carObjectsReversed[i] && loadObjects) { if (carObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)carObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (carObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)carObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjectsCount = 0; for (int i = 0; i < train.Cars.Length * 2; i++) { if (bogieObjects[i] != null) { bogieObjectsCount++; if (bogieObjectsReversed[i] && loadObjects) { if (bogieObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)bogieObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (bogieObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)bogieObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjectsCount > 0 & carObjectsCount < train.Cars.Length) { Interface.AddMessage(MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + filePath); } if (bogieObjectsCount > 0 & bogieObjectsCount < train.Cars.Length * 2) { Interface.AddMessage(MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + filePath); } }
private static void ApplyMeshBuilder(ref ObjectManager.StaticObject Object, MeshBuilder Builder, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { if (Builder.Faces.Length != 0) { int mf = Object.Mesh.Faces.Length; int mm = Object.Mesh.Materials.Length; int mv = Object.Mesh.Vertices.Length; Array.Resize <World.MeshFace>(ref Object.Mesh.Faces, mf + Builder.Faces.Length); Array.Resize <World.MeshMaterial>(ref Object.Mesh.Materials, mm + Builder.Materials.Length); Array.Resize <World.Vertex>(ref Object.Mesh.Vertices, mv + Builder.Vertices.Length); for (int i = 0; i < Builder.Vertices.Length; i++) { Object.Mesh.Vertices[mv + i] = Builder.Vertices[i]; } for (int i = 0; i < Builder.Faces.Length; i++) { Object.Mesh.Faces[mf + i] = Builder.Faces[i]; for (int j = 0; j < Object.Mesh.Faces[mf + i].Vertices.Length; j++) { Object.Mesh.Faces[mf + i].Vertices[j].Index += (ushort)mv; } Object.Mesh.Faces[mf + i].Material += (ushort)mm; } for (int i = 0; i < Builder.Materials.Length; i++) { Object.Mesh.Materials[mm + i].Flags = (byte)((Builder.Materials[i].EmissiveColorUsed ? World.MeshMaterial.EmissiveColorMask : 0) | (Builder.Materials[i].TransparentColorUsed ? World.MeshMaterial.TransparentColorMask : 0)); Object.Mesh.Materials[mm + i].Color = Builder.Materials[i].Color; Object.Mesh.Materials[mm + i].TransparentColor = Builder.Materials[i].TransparentColor; Textures.OpenGlTextureWrapMode WrapX, WrapY; if (ForceTextureRepeatX) { WrapX = Textures.OpenGlTextureWrapMode.RepeatRepeat; } else { WrapX = Textures.OpenGlTextureWrapMode.ClampClamp; } if (ForceTextureRepeatY) { WrapY = Textures.OpenGlTextureWrapMode.RepeatRepeat; } else { WrapY = Textures.OpenGlTextureWrapMode.ClampClamp; } if (WrapX != Textures.OpenGlTextureWrapMode.RepeatRepeat | WrapY != Textures.OpenGlTextureWrapMode.RepeatRepeat) { for (int j = 0; j < Builder.Vertices.Length; j++) { if (Builder.Vertices[j].TextureCoordinates.X <0.0 | Builder.Vertices[j].TextureCoordinates.X> 1.0) { WrapX = Textures.OpenGlTextureWrapMode.RepeatRepeat; } if (Builder.Vertices[j].TextureCoordinates.Y <0.0 | Builder.Vertices[j].TextureCoordinates.Y> 1.0) { WrapY = Textures.OpenGlTextureWrapMode.RepeatRepeat; } } } if (Builder.Materials[i].DaytimeTexture != null) { Textures.Texture tday; if (Builder.Materials[i].TransparencyTexture != null) { Bitmap Main = new Bitmap(Builder.Materials[i].DaytimeTexture); Main = ResizeImage(Main, Main.Size.Width, Main.Size.Height); Bitmap Alpha = new Bitmap(Builder.Materials[i].TransparencyTexture); if (Alpha.Size != Main.Size) { Alpha = ResizeImage(Alpha, Main.Size.Width, Main.Size.Height); } Bitmap texture = MergeAlphaBitmap(Main, Alpha); //Dispose of both main and alpha Main.Dispose(); Alpha.Dispose(); tday = Textures.RegisterTexture(texture); } else { Textures.RegisterTexture(Builder.Materials[i].DaytimeTexture, new TextureParameters(null, Builder.Materials[i].TransparentColor), out tday); } Object.Mesh.Materials[mm + i].DaytimeTexture = tday; } else { Object.Mesh.Materials[mm + i].DaytimeTexture = null; } Object.Mesh.Materials[mm + i].EmissiveColor = Builder.Materials[i].EmissiveColor; if (Builder.Materials[i].NighttimeTexture != null) { Textures.Texture tnight; Textures.RegisterTexture(Builder.Materials[i].NighttimeTexture, new TextureParameters(null, Builder.Materials[i].TransparentColor), out tnight); Object.Mesh.Materials[mm + i].DaytimeTexture = tnight; } else { Object.Mesh.Materials[mm + i].NighttimeTexture = null; } Object.Mesh.Materials[mm + i].DaytimeNighttimeBlend = 0; Object.Mesh.Materials[mm + i].BlendMode = Builder.Materials[i].BlendMode; Object.Mesh.Materials[mm + i].GlowAttenuationData = Builder.Materials[i].GlowAttenuationData; } } }
// 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; }
// parse extensions config internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, ref ObjectManager.UnifiedObject[] CarObjects, ref ObjectManager.UnifiedObject[] BogieObjects, TrainManager.Train Train, bool LoadObjects) { bool[] CarObjectsReversed = new bool[Train.Cars.Length]; bool[] BogieObjectsReversed = new bool[Train.Cars.Length * 2]; bool[] CarsDefined = new bool[Train.Cars.Length]; bool[] BogiesDefined = new bool[Train.Cars.Length * 2]; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "extensions.cfg"); if (System.IO.File.Exists(FileName)) { TextEncoding.Encoding newEncoding = TextEncoding.GetEncodingFromFile(FileName); if (newEncoding != TextEncoding.Encoding.Unknown) { switch (newEncoding) { case TextEncoding.Encoding.Utf7: Encoding = System.Text.Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: Encoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Encoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Encoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Encoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Encoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Encoding = System.Text.Encoding.GetEncoding(932); break; } } string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { if (LoadObjects) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; default: if (Lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(4, Lines[i].Length - 5); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (CarsDefined[n]) { Interface.AddMessage(Interface.MessageType.Error, false, "Car " + n.ToString(Culture) + " has already been declared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } CarsDefined[n] = true; bool DefinedLength = false; bool DefinedAxles = false; i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(Interface.MessageType.Error, true, "An empty car object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { if (LoadObjects) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { double m; if (double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out m)) { if (m > 0.0) { Train.Cars[n].Length = m; Train.Cars[n].BeaconReceiverPosition = 0.5 * m; DefinedLength = true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(Interface.MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Cars[n].RearAxle.Position = rear; Train.Cars[n].FrontAxle.Position = front; DefinedAxles = true; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": CarObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; if (DefinedLength & !DefinedAxles) { double AxleDistance = 0.4 * Train.Cars[n].Length; Train.Cars[n].RearAxle.Position = -AxleDistance; Train.Cars[n].FrontAxle.Position = AxleDistance; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // coupler string t = Lines[i].Substring(8, Lines[i].Length - 9); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Couplers.Length) { i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "distances": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double min, max; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min)) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out max)) { Interface.AddMessage(Interface.MessageType.Error, false, "Maximum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (min > max) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Couplers[n].MinimumDistanceBetweenCars = min; Train.Couplers[n].MaximumDistanceBetweenCars = max; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index " + t + " does not reference an existing coupler at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(6, Lines[i].Length - 7); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n > BogiesDefined.Length - 1) { continue; } if (BogiesDefined[n]) { Interface.AddMessage(Interface.MessageType.Error, false, "Bogie " + n.ToString(Culture) + " has already been declared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } BogiesDefined[n] = true; //Assuming that there are two bogies per car bool IsOdd = (n % 2 != 0); int CarIndex = n / 2; if (n >= 0 & n < Train.Cars.Length * 2) { bool DefinedAxles = false; i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (string.IsNullOrEmpty(b)) { Interface.AddMessage(Interface.MessageType.Error, true, "An empty bogie object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { if (LoadObjects) { BogieObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } else { Interface.AddMessage(Interface.MessageType.Error, true, "The bogie object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { Interface.AddMessage(Interface.MessageType.Error, false, "A defined length is not supported for bogies at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(Interface.MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (IsOdd) { Train.Cars[CarIndex].FrontBogie.RearAxle.Position = rear; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = front; } else { Train.Cars[CarIndex].RearBogie.RearAxle.Position = rear; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = front; } DefinedAxles = true; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": BogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; if (!DefinedAxles) { if (IsOdd) { double AxleDistance = 0.4 * Train.Cars[CarIndex].FrontBogie.Length; Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -AxleDistance; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = AxleDistance; } else { double AxleDistance = 0.4 * Train.Cars[CarIndex].RearBogie.Length; Train.Cars[CarIndex].RearBogie.RearAxle.Position = -AxleDistance; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = AxleDistance; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { // default Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; } } } // check for car objects and reverse if necessary int carObjects = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] != null) { carObjects++; if (CarObjectsReversed[i] && LoadObjects) { { // reverse axle positions double temp = Train.Cars[i].FrontAxle.Position; Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position; Train.Cars[i].RearAxle.Position = -temp; } if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjects = 0; for (int i = 0; i < Train.Cars.Length * 2; i++) { bool IsOdd = (i % 2 != 0); int CarIndex = i / 2; if (BogieObjects[i] != null) { bogieObjects++; if (BogieObjectsReversed[i] && LoadObjects) { { // reverse axle positions if (IsOdd) { double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position; Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -temp; } else { double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position; Train.Cars[CarIndex].RearBogie.RearAxle.Position = -temp; } } if (BogieObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)BogieObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (BogieObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)BogieObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjects > 0 & carObjects < Train.Cars.Length) { Interface.AddMessage(Interface.MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName); } if (bogieObjects > 0 & bogieObjects < Train.Cars.Length * 2) { Interface.AddMessage(Interface.MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + FileName); } } }
//The XML Parser Class will allow loading of an object with more advanced //properties than are currently available with the CSV and B3D formats, whilst //not requiring backwards incompatible changes public static UnifiedObject ReadObject(string fileName, Encoding encoding) { //The current XML file to load XmlDocument currentXML = new XmlDocument(); ObjectManager.StaticObject Object = null; //Load the object's XML file currentXML.Load(fileName); //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openbve/object"); //Check this file actually contains OpenBVE object nodes if (DocumentNodes != null) { foreach (XmlNode node in DocumentNodes) { string objectPath; try { var fn = System.IO.Path.GetDirectoryName(fileName); var InnerNode = node.SelectSingleNode("filename").InnerText; InnerNode = InnerNode.Trim(); objectPath = OpenBveApi.Path.CombineFile(fn, InnerNode); } catch (Exception) { Interface.AddMessage(MessageType.Error, false, "The XML does not contain a valid object path: " + fileName); return(null); } if (objectPath != null && System.IO.File.Exists(objectPath)) { switch (System.IO.Path.GetExtension(objectPath).ToLowerInvariant()) { case ".csv": case ".b3d": Object = CsvB3dObjectParser.ReadObject(objectPath, encoding); break; case ".x": Object = XObjectParser.ReadObject(objectPath, encoding); break; case ".animated": //Not currently working. //Object = AnimatedObjectParser.ReadObject(objectPath, encoding, LoadMode); break; } try { var BoundingBoxUpper = node.SelectSingleNode("boundingboxupper").InnerText; var BoundingBoxLower = node.SelectSingleNode("boundingboxlower").InnerText; Object.Mesh.BoundingBox = new Vector3[2]; var splitStrings = BoundingBoxUpper.Split(','); if (splitStrings.Length != 3) { //Throw exception, as this isn't a valid 3D point throw new Exception(); } Object.Mesh.BoundingBox[0].X = Double.Parse(splitStrings[0]); Object.Mesh.BoundingBox[0].Y = Double.Parse(splitStrings[1]); Object.Mesh.BoundingBox[0].Z = Double.Parse(splitStrings[2]); splitStrings = BoundingBoxLower.Split(','); if (splitStrings.Length != 3) { //Throw exception, as this isn't a valid 3D point throw new Exception(); } Object.Mesh.BoundingBox[1].X = Double.Parse(splitStrings[0]); Object.Mesh.BoundingBox[1].Y = Double.Parse(splitStrings[1]); Object.Mesh.BoundingBox[1].Y = Double.Parse(splitStrings[2]); } catch (Exception) { Interface.AddMessage(MessageType.Error, false, "The XML contained an invalid bounding box entry: " + fileName); } var selectSingleNode = node.SelectSingleNode("author"); if (selectSingleNode != null) { //Attempt to load author information from XML Object.Author = selectSingleNode.InnerText.Trim(); } selectSingleNode = node.SelectSingleNode("copyright"); if (selectSingleNode != null) { //Attempt to load copyright information from XML Object.Copyright = selectSingleNode.InnerText.Trim(); } return(Object); } Interface.AddMessage(MessageType.Error, false, "The file extension is not supported: " + objectPath); return(null); } } } //We couldn't find any valid XML, so return a null object return(null); }
internal static ObjectManager.StaticObject ReadObject(string FileName) { currentFolder = System.IO.Path.GetDirectoryName(FileName); currentFile = FileName; #if !DEBUG try { #endif ObjFileParser parser = new ObjFileParser(System.IO.File.ReadAllLines(currentFile), null, System.IO.Path.GetFileNameWithoutExtension(currentFile), currentFile); Model model = parser.GetModel(); ObjectManager.StaticObject obj = new ObjectManager.StaticObject(); MeshBuilder builder = new MeshBuilder(); List <Vertex> allVertices = new List <Vertex>(); foreach (var vertex in model.Vertices) { allVertices.Add(new Vertex(vertex.X, vertex.Y, vertex.Z)); } List <Vector2> allTexCoords = new List <Vector2>(); foreach (var texCoord in model.TextureCoord) { allTexCoords.Add(new Vector2(texCoord.X, texCoord.Y)); } List <Vector3> allNormals = new List <Vector3>(); foreach (var normal in model.Normals) { allNormals.Add(new Vector3(normal.X, normal.Y, normal.Z)); } foreach (AssimpNET.Obj.Mesh mesh in model.Meshes) { foreach (Face face in mesh.Faces) { int nVerts = face.Vertices.Count; if (nVerts == 0) { throw new Exception("nVertices must be greater than zero"); } int v = builder.Vertices.Length; Array.Resize(ref builder.Vertices, v + nVerts); for (int i = 0; i < nVerts; i++) { builder.Vertices[v + i] = allVertices[(int)face.Vertices[i]]; } int f = builder.Faces.Length; Array.Resize(ref builder.Faces, f + 1); builder.Faces[f] = new MeshFace(); builder.Faces[f].Vertices = new MeshFaceVertex[nVerts]; for (int i = 0; i < nVerts; i++) { builder.Faces[f].Vertices[i].Index = (ushort)i; } builder.Faces[f].Material = 1; int m = builder.Materials.Length; Array.Resize(ref builder.Materials, m + 1); builder.Materials[m] = new Material(); uint materialIndex = mesh.MaterialIndex; if (materialIndex != AssimpNET.Obj.Mesh.NoMaterial) { AssimpNET.Obj.Material material = model.MaterialMap[model.MaterialLib[(int)materialIndex]]; builder.Materials[m].Color = new Color32((byte)(255 * material.Diffuse.R), (byte)(255 * material.Diffuse.G), (byte)(255 * material.Diffuse.B), (byte)(255 * material.Diffuse.A)); Color24 mSpecular = new Color24((byte)material.Specular.R, (byte)material.Specular.G, (byte)material.Specular.B); builder.Materials[m].EmissiveColor = new Color24((byte)(255 * material.Emissive.R), (byte)(255 * material.Emissive.G), (byte)(255 * material.Emissive.B)); builder.Materials[m].EmissiveColorUsed = true; //TODO: Check exact behaviour builder.Materials[m].TransparentColor = new Color24((byte)(255 * material.Transparent.R), (byte)(255 * material.Transparent.G), (byte)(255 * material.Transparent.B)); builder.Materials[m].TransparentColorUsed = true; if (material.Texture != null) { builder.Materials[m].DaytimeTexture = OpenBveApi.Path.CombineFile(currentFolder, material.Texture); if (!System.IO.File.Exists(builder.Materials[m].DaytimeTexture)) { Interface.AddMessage(MessageType.Error, true, "Texure " + builder.Materials[m].DaytimeTexture + " was not found in file " + currentFile); builder.Materials[m].DaytimeTexture = null; } } } int nCoords = face.TexturCoords.Count; for (int i = 0; i < nCoords; i++) { builder.Vertices[i].TextureCoordinates = allTexCoords[(int)face.TexturCoords[i]]; } int nNormals = face.Normals.Count; Vector3[] normals = new Vector3[nNormals]; for (int i = 0; i < nNormals; i++) { normals[i] = allNormals[(int)face.Normals[i]]; normals[i].Normalize(); } for (int i = 0; i < nNormals; i++) { builder.Faces[0].Vertices[i].Normal = normals[i]; } builder.Apply(ref obj); builder = new MeshBuilder(); } } obj.Mesh.CreateNormals(); return(obj); #if !DEBUG } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, e.Message + " in " + FileName); return(null); } #endif }
private static void LoadEverythingThreaded() { Program.FileSystem.AppendToLogFile("Loading route file: " + CurrentRouteFile); string RailwayFolder = GetRailwayFolder(CurrentRouteFile); string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); // reset Game.Reset(true); Game.MinimalisticSimulation = true; // screen World.CameraTrackFollower = new TrackManager.TrackFollower { Train = null, CarIndex = -1 }; World.CameraMode = CameraViewMode.Interior; //First, check the format of the route file //RW routes were written for BVE1 / 2, and have a different command syntax bool IsRW = CsvRwRouteParser.isRWFile(CurrentRouteFile); Program.FileSystem.AppendToLogFile("Route file format is: " + (IsRW ? "RW" : "CSV")); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); Thread createIllustrations = new Thread(Game.RouteInformation.LoadInformation) { IsBackground = true }; createIllustrations.Start(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Game.CalculateSeaLevelConstants(); if (Game.BogusPretrainInstructions.Length != 0) { double t = Game.BogusPretrainInstructions[0].Time; double p = Game.BogusPretrainInstructions[0].TrackPosition; for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++) { if (Game.BogusPretrainInstructions[i].Time > t) { t = Game.BogusPretrainInstructions[i].Time; } else { t += 1.0; Game.BogusPretrainInstructions[i].Time = t; } if (Game.BogusPretrainInstructions[i].TrackPosition > p) { p = Game.BogusPretrainInstructions[i].TrackPosition; } else { p += 1.0; Game.BogusPretrainInstructions[i].TrackPosition = p; } } } if (Game.Stations.Length == 1) { //Log the fact that only a single station is present, as this is probably not right Program.FileSystem.AppendToLogFile("The processed route file only contains a single station."); } Program.FileSystem.AppendToLogFile("Route file loaded successfully."); RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0) { TrainManager.Trains[k] = new TrainManager.Train(TrainManager.TrainState.Bogus); } else { TrainManager.Trains[k] = new TrainManager.Train(TrainManager.TrainState.Pending); } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; UnifiedObject[] CarObjects = null; UnifiedObject[] BogieObjects = null; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { //Sleep for 20ms to allow route loading locks to release Thread.Sleep(20); if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus) { // bogus train string TrainData = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat"); TrainDatParser.ParseTrainData(TrainData, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains[k].InitializeCarSounds(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { TrainManager.Trains[k].TrainFolder = CurrentTrainFolder; // real train if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { Program.FileSystem.AppendToLogFile("Loading player train: " + TrainManager.Trains[k].TrainFolder); } else { Program.FileSystem.AppendToLogFile("Loading AI train: " + TrainManager.Trains[k].TrainFolder); } TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; string TrainData = OpenBveApi.Path.CombineFile(TrainManager.Trains[k].TrainFolder, "train.dat"); TrainDatParser.ParseTrainData(TrainData, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer != null & TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer); double a = TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer); double a = TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer); double b = TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer != null & TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer); double a = TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer); double a = TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer); double b = TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } // add panel section if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } Program.FileSystem.AppendToLogFile("Train panel loaded sucessfully."); } // add exterior section if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { bool LoadObjects = false; if (CarObjects == null) { CarObjects = new UnifiedObject[TrainManager.Trains[k].Cars.Length]; BogieObjects = new UnifiedObject[TrainManager.Trains[k].Cars.Length * 2]; LoadObjects = true; } string tXml = OpenBveApi.Path.CombineFile(TrainManager.Trains[k].TrainFolder, "train.xml"); if (System.IO.File.Exists(tXml)) { TrainXmlParser.Parse(tXml, TrainManager.Trains[k], ref CarObjects, ref BogieObjects); } else { ExtensionsCfgParser.ParseExtensionsConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, ref CarObjects, ref BogieObjects, TrainManager.Trains[k], LoadObjects); } World.CameraCar = TrainManager.Trains[k].DriverCar; System.Threading.Thread.Sleep(1); if (Cancel) { return; } //Stores the current array index of the bogie object to add //Required as there are two bogies per car, and we're using a simple linear array.... int currentBogieObject = 0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, false, false, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; so.ApplyScale(sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object TrainManager.Trains[k].Cars[i].LoadCarSections(CarObjects[i]); } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { TrainManager.Trains[k].Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject]); } currentBogieObject++; if (BogieObjects[currentBogieObject] != null) { TrainManager.Trains[k].Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject]); } currentBogieObject++; } } // place cars TrainManager.Trains[k].PlaceCars(0.0); // configure ai / timetable if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } Array.Resize(ref ObjectManager.Objects, ObjectManager.ObjectsUsed); Array.Resize(ref ObjectManager.AnimatedWorldObjects, ObjectManager.AnimatedWorldObjectsUsed); // update sections if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus) { if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], TrainManager.Trains[i].TrainFolder, CurrentTrainEncoding)) { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], TrainManager.Trains[i].TrainFolder); } } else { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], TrainManager.Trains[i].TrainFolder); } } } }
// process structure private static bool ProcessStructure(string FileName, Structure Structure, out ObjectManager.StaticObject Object, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; Object = new ObjectManager.StaticObject(); Object.Mesh.Faces = new World.MeshFace[] { }; Object.Mesh.Materials = new World.MeshMaterial[] { }; Object.Mesh.Vertices = new World.Vertex[] { }; // file for (int i = 0; i < Structure.Data.Length; i++) { Structure f = Structure.Data[i] as Structure; if (f == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Top-level inlined arguments are invalid in x object file " + FileName); return false; } switch (f.Name) { case "Mesh": { // mesh if (f.Data.Length < 4) { Interface.AddMessage(Interface.MessageType.Error, false, "Mesh is expected to have at least 4 arguments in x object file " + FileName); return false; } else if (!(f.Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nVertices is expected to be a DWORD in Mesh in x object file " + FileName); return false; } else if (!(f.Data[1] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "vertices[nVertices] is expected to be a Vector array in Mesh in x object file " + FileName); return false; } else if (!(f.Data[2] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaces is expected to be a DWORD in Mesh in x object file " + FileName); return false; } else if (!(f.Data[3] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faces[nFaces] is expected to be a MeshFace array in Mesh in x object file " + FileName); return false; } int nVertices = (int)f.Data[0]; if (nVertices < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nVertices is expected to be non-negative in Mesh in x object file " + FileName); return false; } Structure[] vertices = (Structure[])f.Data[1]; if (nVertices != vertices.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nVertices does not match with the length of array vertices in Mesh in x object file " + FileName); return false; } int nFaces = (int)f.Data[2]; if (nFaces < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaces is expected to be non-negative in Mesh in x object file " + FileName); return false; } Structure[] faces = (Structure[])f.Data[3]; if (nFaces != faces.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaces does not match with the length of array faces in Mesh in x object file " + FileName); return false; } // collect vertices World.Vertex[] Vertices = new World.Vertex[nVertices]; for (int j = 0; j < nVertices; j++) { if (vertices[j].Name != "Vector") { Interface.AddMessage(Interface.MessageType.Error, false, "vertices[" + j.ToString(Culture) + "] is expected to be of template Vertex in Mesh in x object file " + FileName); return false; } else if (vertices[j].Data.Length != 3) { Interface.AddMessage(Interface.MessageType.Error, false, "vertices[" + j.ToString(Culture) + "] is expected to have 3 arguments in Mesh in x object file " + FileName); return false; } else if (!(vertices[j].Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "x is expected to be a float in vertices[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } else if (!(vertices[j].Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "y is expected to be a float in vertices[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } else if (!(vertices[j].Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "z is expected to be a float in vertices[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } double x = (double)vertices[j].Data[0]; double y = (double)vertices[j].Data[1]; double z = (double)vertices[j].Data[2]; Vertices[j].Coordinates = new World.Vector3D(x, y, z); } // collect faces int[][] Faces = new int[nFaces][]; World.Vector3Df[][] FaceNormals = new World.Vector3Df[nFaces][]; int[] FaceMaterials = new int[nFaces]; for (int j = 0; j < nFaces; j++) { FaceMaterials[j] = -1; } for (int j = 0; j < nFaces; j++) { if (faces[j].Name != "MeshFace") { Interface.AddMessage(Interface.MessageType.Error, false, "faces[" + j.ToString(Culture) + "] is expected to be of template MeshFace in Mesh in x object file " + FileName); return false; } else if (faces[j].Data.Length != 2) { Interface.AddMessage(Interface.MessageType.Error, false, "face[" + j.ToString(Culture) + "] is expected to have 2 arguments in Mesh in x object file " + FileName); return false; } else if (!(faces[j].Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices is expected to be a DWORD in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } else if (!(faces[j].Data[1] is int[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faceVertexIndices[nFaceVertexIndices] is expected to be a DWORD array in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } int nFaceVertexIndices = (int)faces[j].Data[0]; if (nFaceVertexIndices < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices is expected to be non-negative in MeshFace in Mesh in x object file " + FileName); return false; } int[] faceVertexIndices = (int[])faces[j].Data[1]; if (nFaceVertexIndices != faceVertexIndices.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices does not match with the length of array faceVertexIndices in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } Faces[j] = new int[nFaceVertexIndices]; FaceNormals[j] = new World.Vector3Df[nFaceVertexIndices]; for (int k = 0; k < nFaceVertexIndices; k++) { if (faceVertexIndices[k] < 0 | faceVertexIndices[k] >= nVertices) { Interface.AddMessage(Interface.MessageType.Error, false, "faceVertexIndices[" + k.ToString(Culture) + "] does not reference a valid vertex in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } Faces[j][k] = faceVertexIndices[k]; FaceNormals[j][k] = new World.Vector3Df(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) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected inlined argument encountered in Mesh in x object file " + FileName); return false; } switch (g.Name) { case "MeshMaterialList": { // meshmateriallist if (g.Data.Length < 3) { Interface.AddMessage(Interface.MessageType.Error, false, "MeshMaterialList is expected to have at least 3 arguments in Mesh in x object file " + FileName); return false; } else if (!(g.Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nMaterials is expected to be a DWORD in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(g.Data[1] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceIndexes is expected to be a DWORD in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(g.Data[2] is int[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faceIndexes[nFaceIndexes] is expected to be a DWORD array in MeshMaterialList in Mesh in x object file " + FileName); return false; } int nMaterials = (int)g.Data[0]; if (nMaterials < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nMaterials is expected to be non-negative in MeshMaterialList in Mesh in x object file " + FileName); return false; } int nFaceIndexes = (int)g.Data[1]; if (nFaceIndexes < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceIndexes is expected to be non-negative in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (nFaceIndexes > nFaces) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceIndexes does not reference valid faces in MeshMaterialList in Mesh in x object file " + FileName); return false; } int[] faceIndexes = (int[])g.Data[2]; if (nFaceIndexes != faceIndexes.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceIndexes does not match with the length of array faceIndexes in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } for (int k = 0; k < nFaceIndexes; k++) { if (faceIndexes[k] < 0 | faceIndexes[k] >= nMaterials) { Interface.AddMessage(Interface.MessageType.Error, false, "faceIndexes[" + k.ToString(Culture) + "] does not reference a valid Material template in MeshMaterialList in Mesh in x object file " + FileName); 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 World.ColorRGBA(255, 255, 255, 255); Materials[mn + k].specularColor = new World.ColorRGB(0, 0, 0); Materials[mn + k].emissiveColor = new World.ColorRGB(0, 0, 0); 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) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected inlined argument encountered in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (h.Name != "Material") { Interface.AddMessage(Interface.MessageType.Error, false, "Material template expected in MeshMaterialList in Mesh in x object file " + FileName); return false; } else { // material if (h.Data.Length < 4) { Interface.AddMessage(Interface.MessageType.Error, false, "Material is expected to have at least 4 arguments in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(h.Data[0] is Structure)) { Interface.AddMessage(Interface.MessageType.Error, false, "faceColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(h.Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "power is expected to be a float in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(h.Data[2] is Structure)) { Interface.AddMessage(Interface.MessageType.Error, false, "specularColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(h.Data[3] is Structure)) { Interface.AddMessage(Interface.MessageType.Error, false, "emissiveColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh in x object file " + FileName); 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") { Interface.AddMessage(Interface.MessageType.Error, false, "faceColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (faceColor.Data.Length != 4) { Interface.AddMessage(Interface.MessageType.Error, false, "faceColor is expected to have 4 arguments in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(faceColor.Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be a float in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(faceColor.Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be a float in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(faceColor.Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be a float in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(faceColor.Data[3] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "alpha is expected to be a float in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); 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) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); blue = blue < 0.5 ? 0.0 : 1.0; } if (alpha < 0.0 | alpha > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "alpha is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); alpha = alpha < 0.5 ? 0.0 : 1.0; } Materials[MaterialIndex].faceColor = new World.ColorRGBA((byte)Math.Round(255.0 * red), (byte)Math.Round(255.0 * green), (byte)Math.Round(255.0 * blue), (byte)Math.Round(255.0 * alpha)); // collect specular color if (specularColor.Name != "ColorRGB") { Interface.AddMessage(Interface.MessageType.Error, false, "specularColor is expected to be a ColorRGB in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (specularColor.Data.Length != 3) { Interface.AddMessage(Interface.MessageType.Error, false, "specularColor is expected to have 3 arguments in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(specularColor.Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be a float in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(specularColor.Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be a float in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(specularColor.Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be a float in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } red = (double)specularColor.Data[0]; green = (double)specularColor.Data[1]; blue = (double)specularColor.Data[2]; if (red < 0.0 | red > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); blue = blue < 0.5 ? 0.0 : 1.0; } Materials[MaterialIndex].specularColor = new World.ColorRGB((byte)Math.Round(255.0 * red), (byte)Math.Round(255.0 * green), (byte)Math.Round(255.0 * blue)); // collect emissive color if (emissiveColor.Name != "ColorRGB") { Interface.AddMessage(Interface.MessageType.Error, false, "emissiveColor is expected to be a ColorRGBA in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (emissiveColor.Data.Length != 3) { Interface.AddMessage(Interface.MessageType.Error, false, "emissiveColor is expected to have 3 arguments in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(emissiveColor.Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(emissiveColor.Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(emissiveColor.Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } red = (double)emissiveColor.Data[0]; green = (double)emissiveColor.Data[1]; blue = (double)emissiveColor.Data[2]; if (red < 0.0 | red > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); blue = blue < 0.5 ? 0.0 : 1.0; } Materials[MaterialIndex].emissiveColor = new World.ColorRGB((byte)Math.Round(255.0 * red), (byte)Math.Round(255.0 * green), (byte)Math.Round(255.0 * blue)); // collect additional templates for (int l = 4; l < h.Data.Length; l++) { Structure e = h.Data[l] as Structure; if (e == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected inlined argument encountered in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } switch (e.Name) { case "TextureFilename": { // texturefilename if (e.Data.Length != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "filename is expected to have 1 argument in TextureFilename in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(e.Data[0] is string)) { Interface.AddMessage(Interface.MessageType.Error, false, "filename is expected to be a string in TextureFilename in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } string filename = (string)e.Data[0]; if (Interface.ContainsInvalidPathChars(filename)) { Interface.AddMessage(Interface.MessageType.Error, false, "filename contains illegal characters in TextureFilename in Material in MeshMaterialList in Mesh in x object file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), filename); if (System.IO.File.Exists(File)) { Materials[MaterialIndex].TextureFilename = File; } else { Interface.AddMessage(Interface.MessageType.Error, true, "The texture file " + File + " could not be found in TextureFilename in Material in MeshMaterialList in Mesh in x object file " + FileName); } } } break; default: // unknown Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported template " + e.Name + " encountered in MeshMaterialList in Mesh in x object file " + FileName); break; } } // finish MaterialIndex++; } } if (MaterialIndex != mn + nMaterials) { Interface.AddMessage(Interface.MessageType.Error, false, "nMaterials does not match the number of Material templates encountered in Material in MeshMaterialList in Mesh in x object file " + FileName); 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; } } } } break; case "MeshTextureCoords": { // meshtexturecoords if (g.Data.Length != 2) { Interface.AddMessage(Interface.MessageType.Error, false, "MeshTextureCoords is expected to have 2 arguments in Mesh in x object file " + FileName); return false; } else if (!(g.Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nTextureCoords is expected to be a DWORD in MeshTextureCoords in Mesh in x object file " + FileName); return false; } else if (!(g.Data[1] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "textureCoords[nTextureCoords] is expected to be a Coords2d array in MeshTextureCoords in Mesh in x object file " + FileName); return false; } int nTextureCoords = (int)g.Data[0]; Structure[] textureCoords = (Structure[])g.Data[1]; if (nTextureCoords < 0 | nTextureCoords > nVertices) { Interface.AddMessage(Interface.MessageType.Error, false, "nTextureCoords does not reference valid vertices in MeshTextureCoords in Mesh in x object file " + FileName); return false; } for (int k = 0; k < nTextureCoords; k++) { if (textureCoords[k].Name != "Coords2d") { Interface.AddMessage(Interface.MessageType.Error, false, "textureCoords[" + k.ToString(Culture) + "] is expected to be a Coords2d in MeshTextureCoords in Mesh in x object file " + FileName); return false; } else if (textureCoords[k].Data.Length != 2) { Interface.AddMessage(Interface.MessageType.Error, false, "textureCoords[" + k.ToString(Culture) + "] is expected to have 2 arguments in MeshTextureCoords in Mesh in x object file " + FileName); return false; } else if (!(textureCoords[k].Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "u is expected to be a float in textureCoords[" + k.ToString(Culture) + "] in MeshTextureCoords in Mesh in x object file " + FileName); return false; } else if (!(textureCoords[k].Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "v is expected to be a float in textureCoords[" + k.ToString(Culture) + "] in MeshTextureCoords in Mesh in x object file " + FileName); return false; } double u = (double)textureCoords[k].Data[0]; double v = (double)textureCoords[k].Data[1]; Vertices[k].TextureCoordinates = new World.Vector2Df((float)u, (float)v); } } break; case "MeshNormals": { // meshnormals if (g.Data.Length != 4) { Interface.AddMessage(Interface.MessageType.Error, false, "MeshNormals is expected to have 4 arguments in Mesh in x object file " + FileName); return false; } else if (!(g.Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nNormals is expected to be a DWORD in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(g.Data[1] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "normals is expected to be a Vector array in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(g.Data[2] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceNormals is expected to be a DWORD in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(g.Data[3] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faceNormals is expected to be a MeshFace array in MeshNormals in Mesh in x object file " + FileName); return false; } int nNormals = (int)g.Data[0]; if (nNormals < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nNormals is expected to be non-negative in MeshNormals in Mesh in x object file " + FileName); return false; } Structure[] normals = (Structure[])g.Data[1]; if (nNormals != normals.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nNormals does not match with the length of array normals in MeshNormals in Mesh in x object file " + FileName); return false; } int nFaceNormals = (int)g.Data[2]; if (nFaceNormals < 0 | nFaceNormals > nFaces) { Interface.AddMessage(Interface.MessageType.Error, false, "nNormals does not reference valid vertices in MeshNormals in Mesh in x object file " + FileName); return false; } Structure[] faceNormals = (Structure[])g.Data[3]; if (nFaceNormals != faceNormals.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceNormals does not match with the length of array faceNormals in MeshNormals in Mesh in x object file " + FileName); return false; } // collect normals World.Vector3Df[] Normals = new World.Vector3Df[nNormals]; for (int k = 0; k < nNormals; k++) { if (normals[k].Name != "Vector") { Interface.AddMessage(Interface.MessageType.Error, false, "normals[" + k.ToString(Culture) + "] is expected to be of template Vertex in MeshNormals in Mesh in x object file " + FileName); return false; } else if (normals[k].Data.Length != 3) { Interface.AddMessage(Interface.MessageType.Error, false, "normals[" + k.ToString(Culture) + "] is expected to have 3 arguments in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(normals[k].Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "x is expected to be a float in normals[" + k.ToString(Culture) + "] in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(normals[k].Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "y is expected to be a float in normals[" + k.ToString(Culture) + " ]in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(normals[k].Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "z is expected to be a float in normals[" + k.ToString(Culture) + "] in MeshNormals in Mesh in x object file " + FileName); return false; } double x = (double)normals[k].Data[0]; double y = (double)normals[k].Data[1]; double z = (double)normals[k].Data[2]; World.Normalize(ref x, ref y, ref z); Normals[k] = new World.Vector3Df((float)x, (float)y, (float)z); } // collect faces for (int k = 0; k < nFaceNormals; k++) { if (faceNormals[k].Name != "MeshFace") { Interface.AddMessage(Interface.MessageType.Error, false, "faceNormals[" + k.ToString(Culture) + "] is expected to be of template MeshFace in MeshNormals in Mesh in x object file " + FileName); return false; } else if (faceNormals[k].Data.Length != 2) { Interface.AddMessage(Interface.MessageType.Error, false, "faceNormals[" + k.ToString(Culture) + "] is expected to have 2 arguments in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(faceNormals[k].Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices is expected to be a DWORD in faceNormals[" + k.ToString(Culture) + "] in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(faceNormals[k].Data[1] is int[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faceVertexIndices[nFaceVertexIndices] is expected to be a DWORD array in faceNormals[" + k.ToString(Culture) + "] in MeshNormals in Mesh in x object file " + FileName); return false; } int nFaceVertexIndices = (int)faceNormals[k].Data[0]; if (nFaceVertexIndices < 0 | nFaceVertexIndices > Faces[k].Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices does not reference a valid vertex in MeshFace in MeshNormals in Mesh in x object file " + FileName); return false; } int[] faceVertexIndices = (int[])faceNormals[k].Data[1]; if (nFaceVertexIndices != faceVertexIndices.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices does not match with the length of array faceVertexIndices in faceNormals[" + k.ToString(Culture) + "] in MeshFace in MeshNormals in Mesh in x object file " + FileName); return false; } for (int l = 0; l < nFaceVertexIndices; l++) { if (faceVertexIndices[l] < 0 | faceVertexIndices[l] >= nNormals) { Interface.AddMessage(Interface.MessageType.Error, false, "faceVertexIndices[" + l.ToString(Culture) + "] does not reference a valid normal in faceNormals[" + k.ToString(Culture) + "] in MeshFace in MeshNormals in Mesh in x object file " + FileName); return false; } FaceNormals[k][l] = Normals[faceVertexIndices[l]]; } } } break; default: // unknown Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported template " + g.Name + " encountered in Mesh in x object file " + FileName); break; } } // default material if (Materials.Length == 0) { Materials = new Material[1]; Materials[0].faceColor = new World.ColorRGBA(255, 255, 255, 255); Materials[0].emissiveColor = new World.ColorRGB(0, 0, 0); Materials[0].specularColor = new World.ColorRGB(0, 0, 0); Materials[0].TextureFilename = null; for (int j = 0; j < nFaces; j++) { FaceMaterials[j] = 0; } } // create mesh int mf = Object.Mesh.Faces.Length; int mm = Object.Mesh.Materials.Length; int mv = Object.Mesh.Vertices.Length; Array.Resize<World.MeshFace>(ref Object.Mesh.Faces, mf + nFaces); Array.Resize<World.MeshMaterial>(ref Object.Mesh.Materials, mm + Materials.Length); Array.Resize<World.Vertex>(ref Object.Mesh.Vertices, mv + Vertices.Length); for (int j = 0; j < Materials.Length; j++) { bool emissive = Materials[j].emissiveColor.R != 0 | Materials[j].emissiveColor.G != 0 | Materials[j].emissiveColor.B != 0; bool transparent; if (Materials[j].TextureFilename != null) { TextureManager.TextureWrapMode WrapX, WrapY; if (ForceTextureRepeatX) { WrapX = TextureManager.TextureWrapMode.Repeat; } else { WrapX = TextureManager.TextureWrapMode.ClampToEdge; } if (ForceTextureRepeatY) { WrapY = TextureManager.TextureWrapMode.Repeat; } else { WrapY = TextureManager.TextureWrapMode.ClampToEdge; } if (WrapX != TextureManager.TextureWrapMode.Repeat | WrapY != TextureManager.TextureWrapMode.Repeat) { for (int k = 0; k < nFaces; k++) { for (int h = 0; h < Faces[k].Length; h++) { if (Vertices[Faces[k][h]].TextureCoordinates.X < 0.0 | Vertices[Faces[k][h]].TextureCoordinates.X > 1.0) { WrapX = TextureManager.TextureWrapMode.Repeat; } if (Vertices[Faces[k][h]].TextureCoordinates.Y < 0.0 | Vertices[Faces[k][h]].TextureCoordinates.Y > 1.0) { WrapY = TextureManager.TextureWrapMode.Repeat; } } } } int tday = TextureManager.RegisterTexture(Materials[j].TextureFilename, new World.ColorRGB(0, 0, 0), 1, TextureManager.TextureLoadMode.Normal, WrapX, WrapY, LoadMode != ObjectManager.ObjectLoadMode.Normal, 0, 0, 0, 0); Object.Mesh.Materials[mm + j].DaytimeTextureIndex = tday; transparent = true; } else { Object.Mesh.Materials[mm + j].DaytimeTextureIndex = -1; transparent = false; } Object.Mesh.Materials[mm + j].Flags = (byte)((transparent ? World.MeshMaterial.TransparentColorMask : 0) | (emissive ? World.MeshMaterial.EmissiveColorMask : 0)); Object.Mesh.Materials[mm + j].Color = Materials[j].faceColor; Object.Mesh.Materials[mm + j].TransparentColor = new World.ColorRGB(0, 0, 0); Object.Mesh.Materials[mm + j].EmissiveColor = Materials[j].emissiveColor; Object.Mesh.Materials[mm + j].NighttimeTextureIndex = -1; Object.Mesh.Materials[mm + j].BlendMode = World.MeshMaterialBlendMode.Normal; Object.Mesh.Materials[mm + j].GlowAttenuationData = 0; } for (int j = 0; j < nFaces; j++) { Object.Mesh.Faces[mf + j].Material = (ushort)FaceMaterials[j]; Object.Mesh.Faces[mf + j].Vertices = new World.MeshFaceVertex[Faces[j].Length]; for (int k = 0; k < Faces[j].Length; k++) { Object.Mesh.Faces[mf + j].Vertices[mv + k] = new World.MeshFaceVertex(mv + Faces[j][k], FaceNormals[j][k]); } } for (int j = 0; j < Vertices.Length; j++) { Object.Mesh.Vertices[mv + j] = Vertices[j]; } break; } case "Header": break; default: // unknown Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported template " + f.Name + " encountered in x object file " + FileName); break; } } // return World.CreateNormals(ref Object.Mesh); return true; }
// create element private static int CreateElement(TrainManager.Train Train, double Left, double Top, double Width, double Height, double FullWidth, double FullHeight, double WorldLeft, double WorldTop, double WorldWidth, double WorldHeight, double WorldZ, double DriverX, double DriverY, double DriverZ, Textures.Texture Texture, Color32 Color, bool AddStateToLastElement) { // create object ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Vector3[] v = new Vector3[4]; double sx = 0.5 * WorldWidth * Width / FullWidth; double sy = 0.5 * WorldHeight * Height / FullHeight; v[0] = new Vector3(-sx, -sy, 0); v[1] = new Vector3(-sx, sy, 0); v[2] = new Vector3(sx, sy, 0); v[3] = new Vector3(sx, -sy, 0); World.Vertex t0 = new World.Vertex(v[0], new Vector2(0.0f, 1.0f)); World.Vertex t1 = new World.Vertex(v[1], new Vector2(0.0f, 0.0f)); World.Vertex t2 = new World.Vertex(v[2], new Vector2(1.0f, 0.0f)); World.Vertex t3 = new World.Vertex(v[3], new Vector2(1.0f, 1.0f)); Object.Mesh.Vertices = new World.Vertex[] { t0, t1, t2, t3 }; Object.Mesh.Faces = new World.MeshFace[] { new World.MeshFace(new int[] { 0, 1, 2, 3 }) }; Object.Mesh.Materials = new World.MeshMaterial[1]; Object.Mesh.Materials[0].Flags = Texture != null ? (byte)World.MeshMaterial.TransparentColorMask : (byte)0; Object.Mesh.Materials[0].Color = Color; Object.Mesh.Materials[0].TransparentColor = new Color24(0, 0, 255); Object.Mesh.Materials[0].DaytimeTexture = Texture; Object.Mesh.Materials[0].NighttimeTexture = null; Object.Dynamic = true; // calculate offset Vector3 o; o.X = WorldLeft + sx + WorldWidth * Left / FullWidth; o.Y = WorldTop - sy - WorldHeight * Top / FullHeight; o.Z = WorldZ; // add object if (AddStateToLastElement) { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length - 1; int j = Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States.Length; Array.Resize<ObjectManager.AnimatedObjectState>(ref Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States, j + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Object = Object; return n; } else { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length; Array.Resize<ObjectManager.AnimatedObject>(ref Train.Cars[Train.DriverCar].CarSections[0].Elements, n + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n] = new ObjectManager.AnimatedObject(); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States = new ObjectManager.AnimatedObjectState[1]; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Object = Object; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].CurrentState = 0; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex = ObjectManager.CreateDynamicObject(); ObjectManager.Objects[Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex] = ObjectManager.CloneObject(Object); return n; } }
private static void LoadEverythingThreaded() { Program.AppendToLogFile("Loading route file: " + CurrentRouteFile); string RailwayFolder = GetRailwayFolder(CurrentRouteFile); string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); // reset Game.Reset(true); Game.MinimalisticSimulation = true; // screen World.CameraTrackFollower = new TrackManager.TrackFollower { Train = null, CarIndex = -1 }; World.CameraMode = World.CameraViewMode.Interior; //First, check the format of the route file //RW routes were written for BVE1 / 2, and have a different command syntax bool IsRW = CsvRwRouteParser.isRWFile(CurrentRouteFile); Program.AppendToLogFile("Route file format is: " + (IsRW ? "RW" : "CSV")); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); Thread createIllustrations = new Thread(Game.RouteInformation.LoadInformation) { IsBackground = true }; createIllustrations.Start(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Game.CalculateSeaLevelConstants(); if (Game.BogusPretrainInstructions.Length != 0) { double t = Game.BogusPretrainInstructions[0].Time; double p = Game.BogusPretrainInstructions[0].TrackPosition; for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++) { if (Game.BogusPretrainInstructions[i].Time > t) { t = Game.BogusPretrainInstructions[i].Time; } else { t += 1.0; Game.BogusPretrainInstructions[i].Time = t; } if (Game.BogusPretrainInstructions[i].TrackPosition > p) { p = Game.BogusPretrainInstructions[i].TrackPosition; } else { p += 1.0; Game.BogusPretrainInstructions[i].TrackPosition = p; } } } if (Game.Stations.Length == 1) { //Log the fact that only a single station is present, as this is probably not right Program.AppendToLogFile("The processed route file only contains a single station."); } Program.AppendToLogFile("Route file loaded successfully."); RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { TrainManager.Trains[k] = new TrainManager.Train { TrainIndex = k }; if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0) { TrainManager.Trains[k].State = TrainManager.TrainState.Bogus; } else { TrainManager.Trains[k].State = TrainManager.TrainState.Pending; } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { //Sleep for 10ms to allow route loading locks to release Thread.Sleep(20); if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus) { // bogus train string TrainData = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat"); TrainDatParser.ParseTrainData(TrainData, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains[k].InitializeCarSounds(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { // real train Program.AppendToLogFile("Loading player train: " + CurrentTrainFolder); TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; string TrainData = OpenBveApi.Path.CombineFile(CurrentTrainFolder, "train.dat"); TrainDatParser.ParseTrainData(TrainData, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].BeaconReceiver.Train = TrainManager.Trains[k]; } // add panel section if (k == TrainManager.PlayerTrain.TrainIndex) { TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections = new TrainManager.CarSection[1]; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { }; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay = true; TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } Program.AppendToLogFile("Train panel loaded sucessfully."); } // add exterior section if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { ObjectManager.UnifiedObject[] CarObjects; ObjectManager.UnifiedObject[] BogieObjects; ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, out BogieObjects, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } //Stores the current array index of the bogie object to add //Required as there are two bogies per car, and we're using a simple linear array.... int currentBogieObject = 0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; CsvB3dObjectParser.ApplyScale(so, sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object int j = TrainManager.Trains[k].Cars[i].CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1); if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { int j = TrainManager.Trains[k].Cars[i].FrontBogie.CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].FrontBogie.CarSections, j + 1); if (BogieObjects[currentBogieObject] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (BogieObjects[currentBogieObject] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } currentBogieObject++; //Can't think of a better way to do this than two functions...... if (BogieObjects[currentBogieObject] != null) { int j = TrainManager.Trains[k].Cars[i].RearBogie.CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].RearBogie.CarSections, j + 1); if (BogieObjects[currentBogieObject] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (BogieObjects[currentBogieObject] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } currentBogieObject++; } } // place cars { double z = 0.0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { //Front axle track position TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxle.Position; //Bogie for front axle TrainManager.Trains[k].Cars[i].FrontBogie.FrontAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].FrontBogie.Length + TrainManager.Trains[k].Cars[i].FrontBogie.FrontAxle.Position; TrainManager.Trains[k].Cars[i].FrontBogie.RearAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].FrontBogie.Length + TrainManager.Trains[k].Cars[i].FrontBogie.RearAxle.Position; //Rear axle track position TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxle.Position; //Bogie for rear axle TrainManager.Trains[k].Cars[i].RearBogie.FrontAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].RearBogie.Length + TrainManager.Trains[k].Cars[i].RearBogie.FrontAxle.Position; TrainManager.Trains[k].Cars[i].RearBogie.RearAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].RearBogie.Length + TrainManager.Trains[k].Cars[i].RearBogie.RearAxle.Position; //Beacon reciever (AWS, ATC etc.) TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition; z -= TrainManager.Trains[k].Cars[i].Length; if (i < TrainManager.Trains[k].Cars.Length - 1) { z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars); } } } // configure ai / timetable if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } ObjectManager.FinishCreatingObjects(); // update sections if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus) { if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding)) { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } else { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } } }
/// <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>Creates a transformed copy of the provided prototype object (e.g. Platform top, roof etc.)</summary> /// <param name="Prototype">The prototype</param> /// /// <param name="NearDistance">The object's width at the start of the block</param> /// /// <param name="FarDistance">The object's width at the end of the block</param> /// <returns>The transformed copy</returns> private static ObjectManager.StaticObject GetTransformedStaticObject(ObjectManager.StaticObject Prototype, double NearDistance, double FarDistance) { ObjectManager.StaticObject Result = Prototype.Clone(); int n = 0; double x2 = 0.0, x3 = 0.0, x6 = 0.0, x7 = 0.0; for (int i = 0; i < Result.Mesh.Vertices.Length; i++) { if (n == 2) { x2 = Result.Mesh.Vertices[i].Coordinates.X; } else if (n == 3) { x3 = Result.Mesh.Vertices[i].Coordinates.X; } else if (n == 6) { x6 = Result.Mesh.Vertices[i].Coordinates.X; } else if (n == 7) { x7 = Result.Mesh.Vertices[i].Coordinates.X; } n++; if (n == 8) { break; } } if (n >= 4) { int m = 0; for (int i = 0; i < Result.Mesh.Vertices.Length; i++) { if (m == 0) { Result.Mesh.Vertices[i].Coordinates.X = NearDistance - x3; } else if (m == 1) { Result.Mesh.Vertices[i].Coordinates.X = FarDistance - x2; if (n < 8) { break; } } else if (m == 4) { Result.Mesh.Vertices[i].Coordinates.X = NearDistance - x7; } else if (m == 5) { Result.Mesh.Vertices[i].Coordinates.X = NearDistance - x6; break; } m++; if (m == 8) { break; } } } return(Result); }
// parse animated object config /// <summary>Loads a collection of animated objects 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> /// <returns>The collection of animated objects.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection { Objects = new ObjectManager.AnimatedObject[4] }; int ObjectCount = 0; // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); bool rpnUsed = false; for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } if (Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0) { rpnUsed = true; } } if (rpnUsed) { Interface.AddMessage(Interface.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName); } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[include]": { i++; Vector3 position = new Vector3(0.0, 0.0, 0.0); ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[4]; int objCount = 0; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { position = new Vector3(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { string Folder = System.IO.Path.GetDirectoryName(FileName); if (Path.ContainsInvalidChars(Lines[i])) { Interface.AddMessage(Interface.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]); if (System.IO.File.Exists(file)) { if (obj.Length == objCount) { Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length << 1); } obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false); objCount++; } else { Interface.AddMessage(Interface.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; for (int j = 0; j < objCount; j++) { if (obj[j] != null) { if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = true; if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = s, Position = position }; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[ObjectCount] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += position.X; a.Objects[k].States[h].Position.Y += position.Y; a.Objects[k].States[h].Position.Z += position.Z; } Result.Objects[ObjectCount] = a.Objects[k]; ObjectCount++; } } } } } break; case "[object]": { i++; if (Result.Objects.Length == ObjectCount) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject { States = new ObjectManager.AnimatedObjectState[] {}, CurrentState = -1, TranslateXDirection = new Vector3(1.0, 0.0, 0.0), TranslateYDirection = new Vector3(0.0, 1.0, 0.0), TranslateZDirection = new Vector3(0.0, 0.0, 1.0), RotateXDirection = new Vector3(1.0, 0.0, 0.0), RotateYDirection = new Vector3(0.0, 1.0, 0.0), RotateZDirection = new Vector3(0.0, 0.0, 1.0), TextureShiftXDirection = new Vector2(1.0, 0.0), TextureShiftYDirection = new Vector2(0.0, 1.0), RefreshRate = 0.0, ObjectIndex = -1 }; Vector3 Position = new Vector3(0.0, 0.0, 0.0); bool timetableUsed = false; string[] StateFiles = null; string StateFunctionRpn = null; int StateFunctionLine = -1; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Position = new Vector3(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "states": { string[] s = b.Split(','); if (s.Length >= 1) { string Folder = System.IO.Path.GetDirectoryName(FileName); StateFiles = new string[s.Length]; for (int k = 0; k < s.Length; k++) { s[k] = s[k].Trim(); if (s[k].Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " is an empty string - did you mean something else? - in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else if (Path.ContainsInvalidChars(s[k])) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " contains illegal characters in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else { StateFiles[k] = OpenBveApi.Path.CombineFile(Folder, s[k]); if (!System.IO.File.Exists(StateFiles[k])) { Interface.AddMessage(Interface.MessageType.Error, true, "File " + StateFiles[k] + " not found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } break; case "statefunction": try { StateFunctionLine = i; StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "statefunctionrpn": { StateFunctionLine = i; StateFunctionRpn = b; } break; case "translatexdirection": case "translateydirection": case "translatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "translatexdirection": Result.Objects[ObjectCount].TranslateXDirection = new Vector3(x, y, z); break; case "translateydirection": Result.Objects[ObjectCount].TranslateYDirection = new Vector3(x, y, z); break; case "translatezdirection": Result.Objects[ObjectCount].TranslateZDirection = new Vector3(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "translatexfunction": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunction": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunction": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatexfunctionrpn": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunctionrpn": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunctionrpn": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdirection": case "rotateydirection": case "rotatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (x == 0.0 & y == 0.0 & z == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "The direction indicated by X, Y and Z is expected to be non-zero in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdirection": Result.Objects[ObjectCount].RotateXDirection = new Vector3(x, y, z); break; case "rotateydirection": Result.Objects[ObjectCount].RotateYDirection = new Vector3(x, y, z); break; case "rotatezdirection": Result.Objects[ObjectCount].RotateZDirection = new Vector3(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "rotatexfunction": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunction": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunction": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexfunctionrpn": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunctionrpn": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunctionrpn": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdamping": case "rotateydamping": case "rotatezdamping": { string[] s = b.Split(','); if (s.Length == 2) { double nf, dr; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out nf)) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out dr)) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (nf <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (dr <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdamping": Result.Objects[ObjectCount].RotateXDamping = new ObjectManager.Damping(nf, dr); break; case "rotateydamping": Result.Objects[ObjectCount].RotateYDamping = new ObjectManager.Damping(nf, dr); break; case "rotatezdamping": Result.Objects[ObjectCount].RotateZDamping = new ObjectManager.Damping(nf, dr); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxdirection": case "textureshiftydirection": { string[] s = b.Split(','); if (s.Length == 2) { double x, y; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "textureshiftxdirection": Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2(x, y); break; case "textureshiftydirection": Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2(x, y); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxfunction": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunction": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftxfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureoverride": switch (b.ToLowerInvariant()) { case "none": break; case "timetable": if (!timetableUsed) { Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]); timetableUsed = true; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Unrecognized value in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } break; case "refreshrate": { double r; if (!double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (r < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Result.Objects[ObjectCount].RefreshRate = r; } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } i++; } i--; if (StateFiles != null) { // create the object if (timetableUsed) { if (StateFunctionRpn != null) { StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?"; } else { StateFunctionRpn = "timetable"; } } if (StateFunctionRpn != null) { try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName); } } Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length]; bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.X != 0.0; bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.Y != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; for (int k = 0; k < StateFiles.Length; k++) { Result.Objects[ObjectCount].States[k].Position = new Vector3(0.0, 0.0, 0.0); if (StateFiles[k] != null) { Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY); if (Result.Objects[ObjectCount].States[k].Object != null) { Result.Objects[ObjectCount].States[k].Object.Dynamic = true; } } else { Result.Objects[ObjectCount].States[k].Object = null; } for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) { Result.Objects[ObjectCount].States[j].Position = Position; } } } else { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; } ObjectCount++; } break; case "[sound]": case "[statechangesound]": //Only show the sound nag once per route, otherwise this could cause spam... if (!Program.SoundError) { Interface.AddMessage(Interface.MessageType.Information, false, "Animated objects containing sounds are only supported in openBVE v1.5.2.4+"); Interface.AddMessage(Interface.MessageType.Information, false, "Object Viewer does not support sounds. Please use the main game to test these!"); Program.SoundError = true; } i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { i++; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } } Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount); return(Result); }
internal static ObjectManager.StaticObject ReadObject(string FileName) { currentFolder = System.IO.Path.GetDirectoryName(FileName); currentFile = FileName; rootMatrix = Matrix4D.NoTransformation; #if !DEBUG try { #endif XFileParser parser = new XFileParser(System.IO.File.ReadAllBytes(FileName)); Scene scene = parser.GetImportedData(); ObjectManager.StaticObject obj = new ObjectManager.StaticObject(); MeshBuilder builder = new MeshBuilder(); // Global foreach (var mesh in scene.GlobalMeshes) { MeshBuilder(ref obj, ref builder, mesh); } if (scene.RootNode != null) { // Root Node if (scene.RootNode.TrafoMatrix != OpenTK.Matrix4.Zero) { rootMatrix = ConvertMatrix(scene.RootNode.TrafoMatrix); } foreach (var mesh in scene.RootNode.Meshes) { MeshBuilder(ref obj, ref builder, mesh); } // Children Node foreach (var node in scene.RootNode.Children) { ChildrenNode(ref obj, ref builder, node); } } builder.Apply(ref obj); obj.Mesh.CreateNormals(); if (rootMatrix != Matrix4D.NoTransformation) { for (int i = 0; i < obj.Mesh.Vertices.Length; i++) { obj.Mesh.Vertices[i].Coordinates.Transform(rootMatrix); } } return(obj); #if !DEBUG } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, e.Message + " in " + FileName); return(null); } #endif }
/// <summary>Loads a Wavefront 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 referenced textures on the X-axis</param> /// <param name="ForceTextureRepeatY">Whether to force TextureWrapMode.Repeat for referenced textures on 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) { ObjectManager.StaticObject Object = new ObjectManager.StaticObject { Mesh = { Faces = new World.MeshFace[] { }, Materials = new World.MeshMaterial[] { }, Vertices = new VertexTemplate[] { } } }; MeshBuilder Builder = new MeshBuilder(); /* * Temporary arrays */ List <Vector3> tempVertices = new List <Vector3>(); List <Vector3> tempNormals = new List <Vector3>(); List <Vector2> tempCoords = new List <Vector2>(); Material[] TempMaterials = new Material[0]; //Stores the current material int currentMaterial = -1; //Read the contents of the file string[] Lines = File.ReadAllLines(FileName); //Preprocess for (int i = 0; i < Lines.Length; i++) { // Strip hash comments int c = Lines[i].IndexOf("#", StringComparison.Ordinal); if (c >= 0) { Lines[i] = Lines[i].Substring(0, c); } // collect arguments List <string> Arguments = new List <string>(Lines[i].Split(new char[] { ' ', '\t' }, StringSplitOptions.None)); for (int j = Arguments.Count - 1; j >= 0; j--) { Arguments[j] = Arguments[j].Trim(); if (Arguments[j] == string.Empty) { Arguments.RemoveAt(j); } } if (Arguments.Count == 0) { continue; } switch (Arguments[0].ToLowerInvariant()) { case "v": //Vertex Vector3 vertex = new Vector3(); if (!double.TryParse(Arguments[1], out vertex.X)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid X co-ordinate in Vertex at Line " + i); } if (!double.TryParse(Arguments[2], out vertex.Y)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Y co-ordinate in Vertex at Line " + i); } if (!double.TryParse(Arguments[3], out vertex.Z)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Z co-ordinate in Vertex at Line " + i); } tempVertices.Add(vertex); break; case "vt": //Vertex texture co-ords Vector2 coords = new Vector2(); if (!double.TryParse(Arguments[1], out coords.X)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-ordinates at Line " + i); } if (!double.TryParse(Arguments[2], out coords.Y)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-Ordinates at Line " + i); } tempCoords.Add(coords); break; case "vn": Vector3 normal = new Vector3(); if (!double.TryParse(Arguments[1], out normal.X)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid X co-ordinate in Vertex Normal at Line " + i); } if (!double.TryParse(Arguments[2], out normal.Y)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Y co-ordinate in Vertex Normal at Line " + i); } if (!double.TryParse(Arguments[3], out normal.Z)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Z co-ordinate in Vertex Normal at Line " + i); } tempNormals.Add(normal); //Vertex normals break; case "vp": //Parameter space verticies, not supported throw new NotSupportedException("Parameter space verticies are not supported by this parser"); case "f": //Creates a new face //Create the temp list to hook out the vertices List <VertexTemplate> vertices = new List <VertexTemplate>(); List <Vector3> normals = new List <Vector3>(); for (int f = 1; f < Arguments.Count; f++) { Vertex newVertex = new Vertex(); string[] faceArguments = Arguments[f].Split(new char[] { '/' }, StringSplitOptions.None); int idx; if (!int.TryParse(faceArguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Vertex index in Face " + f + " at Line " + i); continue; } int currentVertex = tempVertices.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentVertex++; currentVertex += idx; } else { currentVertex = idx; } if (currentVertex > tempVertices.Count) { Interface.AddMessage(Interface.MessageType.Warning, false, "Vertex index " + idx + " was greater than the available number of vertices in Face " + f + " at Line " + i); continue; } newVertex.Coordinates = tempVertices[currentVertex - 1]; if (faceArguments.Length <= 1) { normals.Add(new Vector3()); } else { if (!int.TryParse(faceArguments[1], out idx)) { if (!string.IsNullOrEmpty(faceArguments[1])) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Texture Co-ordinate index in Face " + f + " at Line " + i); } newVertex.TextureCoordinates = new Vector2(); } else { int currentCoord = tempCoords.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentCoord++; currentCoord += idx; } else { currentCoord = idx; } if (currentCoord > tempCoords.Count) { Interface.AddMessage(Interface.MessageType.Warning, false, "Texture Co-ordinate index " + currentCoord + " was greater than the available number of texture co-ordinates in Face " + f + " at Line " + i); } else { newVertex.TextureCoordinates = tempCoords[currentCoord - 1]; } } } if (faceArguments.Length <= 2) { normals.Add(new Vector3()); } else { if (!int.TryParse(faceArguments[2], out idx)) { if (!string.IsNullOrEmpty(faceArguments[2])) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Vertex Normal index in Face " + f + " at Line " + i); } normals.Add(new Vector3()); } else { int currentNormal = tempNormals.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentNormal++; currentNormal += idx; } else { currentNormal = idx; } if (currentNormal > tempNormals.Count) { Interface.AddMessage(Interface.MessageType.Warning, false, "Vertex Normal index " + currentNormal + " was greater than the available number of normals in Face " + f + " at Line " + i); normals.Add(new Vector3()); } else { normals.Add(tempNormals[currentNormal - 1]); } } } vertices.Add(newVertex); } World.MeshFaceVertex[] Vertices = new World.MeshFaceVertex[vertices.Count]; for (int k = 0; k < vertices.Count; k++) { Builder.Vertices.Add(vertices[k]); Vertices[k].Index = (ushort)(Builder.Vertices.Count - 1); Vertices[k].Normal = normals[k]; } Builder.Faces.Add(currentMaterial == -1 ? new World.MeshFace(Vertices, 0) : new World.MeshFace(Vertices, (ushort)currentMaterial)); break; case "g": //Starts a new face group and (normally) applies a new texture ApplyMeshBuilder(ref Object, Builder); Builder = new MeshBuilder(); break; case "s": /* * Changes the smoothing group applied to these vertexes: * 0- Disabled (Overriden by Vertex normals) * Otherwise appears to be a bitmask (32 available groups) * whereby faces within the same groups have their normals averaged * to appear smooth joins * * Not really supported at the minute, probably requires the engine * twiddling to deliberately support specifiying the shading type for a face * */ break; case "mtllib": //Loads the library of materials used by this file string MaterialsPath = OpenBveApi.Path.CombineFile(Path.GetDirectoryName(FileName), Arguments[1]); if (File.Exists(MaterialsPath)) { LoadMaterials(MaterialsPath, ref TempMaterials); } break; case "usemtl": for (int m = 0; m < TempMaterials.Length; m++) { if (TempMaterials[m].Key.ToLowerInvariant() == Arguments[1].ToLowerInvariant()) { bool mf = false; for (int k = 0; k < Builder.Materials.Length; k++) { if (Builder.Materials[k].Key.ToLowerInvariant() == Arguments[1].ToLowerInvariant()) { mf = true; currentMaterial = k; break; } } if (!mf) { Array.Resize(ref Builder.Materials, Builder.Materials.Length + 1); Builder.Materials[Builder.Materials.Length - 1] = TempMaterials[m]; currentMaterial = Builder.Materials.Length - 1; } break; } if (m == TempMaterials.Length) { Interface.AddMessage(Interface.MessageType.Error, true, "Material " + Arguments[1] + " was not found."); currentMaterial = -1; } } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unrecognised command " + Arguments[0]); break; } } ApplyMeshBuilder(ref Object, Builder); Object.Mesh.CreateNormals(); return(Object); }
private static void MeshBuilder(ref ObjectManager.StaticObject obj, ref MeshBuilder builder, AssimpNET.X.Mesh mesh) { if (builder.Vertices.Length != 0) { builder.Apply(ref obj); builder = new MeshBuilder(); } int nVerts = mesh.Positions.Count; if (nVerts == 0) { //Some null objects contain an empty mesh Interface.AddMessage(MessageType.Warning, false, "nVertices should be greater than zero in Mesh " + mesh.Name); } int v = builder.Vertices.Length; Array.Resize(ref builder.Vertices, v + nVerts); for (int i = 0; i < nVerts; i++) { builder.Vertices[v + i] = new Vertex(mesh.Positions[i].X, mesh.Positions[i].Y, mesh.Positions[i].Z); } int nFaces = mesh.PosFaces.Count; int f = builder.Faces.Length; Array.Resize(ref builder.Faces, f + nFaces); for (int i = 0; i < nFaces; i++) { int fVerts = mesh.PosFaces[i].Indices.Count; if (nFaces == 0) { throw new Exception("fVerts must be greater than zero"); } builder.Faces[f + i] = new MeshFace(); builder.Faces[f + i].Vertices = new MeshFaceVertex[fVerts]; for (int j = 0; j < fVerts; j++) { builder.Faces[f + i].Vertices[j].Index = (ushort)mesh.PosFaces[i].Indices[j]; } } int nMaterials = mesh.Materials.Count; int nFaceIndices = mesh.FaceMaterials.Count; for (int i = 0; i < nFaceIndices; i++) { int fMaterial = (int)mesh.FaceMaterials[i]; builder.Faces[i].Material = (ushort)(fMaterial + 1); } for (int i = 0; i < nMaterials; i++) { int m = builder.Materials.Length; Array.Resize(ref builder.Materials, m + 1); builder.Materials[m] = new Material(); builder.Materials[m].Color = new Color32((byte)(255 * mesh.Materials[i].Diffuse.R), (byte)(255 * mesh.Materials[i].Diffuse.G), (byte)(255 * mesh.Materials[i].Diffuse.B), (byte)(255 * mesh.Materials[i].Diffuse.A)); double mPower = mesh.Materials[i].SpecularExponent; //TODO: Unsure what this does... Color24 mSpecular = new Color24((byte)mesh.Materials[i].Specular.R, (byte)mesh.Materials[i].Specular.G, (byte)mesh.Materials[i].Specular.B); builder.Materials[m].EmissiveColor = new Color24((byte)(255 * mesh.Materials[i].Emissive.R), (byte)(255 * mesh.Materials[i].Emissive.G), (byte)(255 * mesh.Materials[i].Emissive.B)); builder.Materials[m].EmissiveColorUsed = true; //TODO: Check exact behaviour builder.Materials[m].TransparentColor = Color24.Black; //TODO: Check, also can we optimise which faces have the transparent color set? builder.Materials[m].TransparentColorUsed = true; if (mesh.Materials[i].Textures.Count > 0) { builder.Materials[m].DaytimeTexture = OpenBveApi.Path.CombineFile(currentFolder, mesh.Materials[i].Textures[0].Name); if (!System.IO.File.Exists(builder.Materials[m].DaytimeTexture)) { Interface.AddMessage(MessageType.Error, true, "Texure " + builder.Materials[m].DaytimeTexture + " was not found in file " + currentFile); builder.Materials[m].DaytimeTexture = null; } } } if (mesh.TexCoords.Length > 0 && mesh.TexCoords[0] != null) { int nCoords = mesh.TexCoords[0].Count; for (int i = 0; i < nCoords; i++) { builder.Vertices[i].TextureCoordinates = new Vector2(mesh.TexCoords[0][i].X, mesh.TexCoords[0][i].Y); } } int nNormals = mesh.Normals.Count; Vector3[] normals = new Vector3[nNormals]; for (int i = 0; i < nNormals; i++) { normals[i] = new Vector3(mesh.Normals[i].X, mesh.Normals[i].Y, mesh.Normals[i].Z); normals[i].Normalize(); } int nFaceNormals = mesh.NormFaces.Count; if (nFaceNormals > builder.Faces.Length) { throw new Exception("nFaceNormals must match the number of faces in the mesh"); } for (int i = 0; i < nFaceNormals; i++) { int nVertexNormals = mesh.NormFaces[i].Indices.Count; if (nVertexNormals > builder.Faces[i].Vertices.Length) { throw new Exception("nVertexNormals must match the number of verticies in the face"); } for (int j = 0; j < nVertexNormals; j++) { builder.Faces[i].Vertices[j].Normal = normals[(int)mesh.NormFaces[i].Indices[j]]; } } int nVertexColors = (int)mesh.NumColorSets; for (int i = 0; i < nVertexColors; i++) { builder.Vertices[i] = new ColoredVertex((Vertex)builder.Vertices[i], new Color128(mesh.Colors[0][i].R, mesh.Colors[0][i].G, mesh.Colors[0][i].B, mesh.Colors[0][i].A)); } }
internal void Apply(ref ObjectManager.StaticObject Object) { if (TransformMatrix != Matrix4D.NoTransformation) { for (int i = 0; i < Vertices.Length; i++) { Vertices[i].Coordinates.Transform(TransformMatrix); } } if (Faces.Length != 0) { int mf = Object.Mesh.Faces.Length; int mm = Object.Mesh.Materials.Length; int mv = Object.Mesh.Vertices.Length; Array.Resize <MeshFace>(ref Object.Mesh.Faces, mf + Faces.Length); Array.Resize <MeshMaterial>(ref Object.Mesh.Materials, mm + Materials.Length); Array.Resize <VertexTemplate>(ref Object.Mesh.Vertices, mv + Vertices.Length); for (int i = 0; i < Vertices.Length; i++) { Object.Mesh.Vertices[mv + i] = Vertices[i]; } for (int i = 0; i < Faces.Length; i++) { Object.Mesh.Faces[mf + i] = Faces[i]; for (int j = 0; j < Object.Mesh.Faces[mf + i].Vertices.Length; j++) { Object.Mesh.Faces[mf + i].Vertices[j].Index += (ushort)mv; } Object.Mesh.Faces[mf + i].Material += (ushort)mm; } for (int i = 0; i < Materials.Length; i++) { Object.Mesh.Materials[mm + i].Flags = (byte)((Materials[i].EmissiveColorUsed ? MeshMaterial.EmissiveColorMask : 0) | (Materials[i].TransparentColorUsed ? MeshMaterial.TransparentColorMask : 0)); Object.Mesh.Materials[mm + i].Color = Materials[i].Color; Object.Mesh.Materials[mm + i].TransparentColor = Materials[i].TransparentColor; if (Materials[i].DaytimeTexture != null || Materials[i].Text != null) { Texture tday; if (Materials[i].Text != null) { Bitmap bitmap = null; if (Materials[i].DaytimeTexture != null) { bitmap = new Bitmap(Materials[i].DaytimeTexture); } Bitmap texture = TextOverlay.AddTextToBitmap(bitmap, Materials[i].Text, Materials[i].Font, 12, Materials[i].BackgroundColor, Materials[i].TextColor, Materials[i].TextPadding); tday = Textures.RegisterTexture(texture, new TextureParameters(null, new Color24(Materials[i].TransparentColor.R, Materials[i].TransparentColor.G, Materials[i].TransparentColor.B))); } else { if (Materials[i].TransparentColorUsed) { Textures.RegisterTexture(Materials[i].DaytimeTexture, new TextureParameters(null, new Color24(Materials[i].TransparentColor.R, Materials[i].TransparentColor.G, Materials[i].TransparentColor.B)), out tday); } else { Textures.RegisterTexture(Materials[i].DaytimeTexture, out tday); } } Object.Mesh.Materials[mm + i].DaytimeTexture = tday; } else { Object.Mesh.Materials[mm + i].DaytimeTexture = null; } Object.Mesh.Materials[mm + i].EmissiveColor = Materials[i].EmissiveColor; if (Materials[i].NighttimeTexture != null) { Texture tnight; if (Materials[i].TransparentColorUsed) { Textures.RegisterTexture(Materials[i].NighttimeTexture, new TextureParameters(null, new Color24(Materials[i].TransparentColor.R, Materials[i].TransparentColor.G, Materials[i].TransparentColor.B)), out tnight); } else { Textures.RegisterTexture(Materials[i].NighttimeTexture, out tnight); } Object.Mesh.Materials[mm + i].NighttimeTexture = tnight; } else { Object.Mesh.Materials[mm + i].NighttimeTexture = null; } Object.Mesh.Materials[mm + i].DaytimeNighttimeBlend = 0; Object.Mesh.Materials[mm + i].BlendMode = Materials[i].BlendMode; Object.Mesh.Materials[mm + i].GlowAttenuationData = Materials[i].GlowAttenuationData; Object.Mesh.Materials[mm + i].WrapMode = Materials[i].WrapMode; } } }
private static int CreateElement(TrainManager.Train Train, double Left, double Top, double Width, double Height, double RelativeRotationCenterX, double RelativeRotationCenterY, double Distance, double PanelResolution, double PanelLeft, double PanelRight, double PanelTop, double PanelBottom, double PanelBitmapWidth, double PanelBitmapHeight, double PanelCenterX, double PanelCenterY, double PanelOriginX, double PanelOriginY, double DriverX, double DriverY, double DriverZ, Textures.Texture DaytimeTexture, Textures.Texture NighttimeTexture, Color32 Color, bool AddStateToLastElement) { double WorldWidth, WorldHeight; if (Screen.Width >= Screen.Height) { WorldWidth = 2.0 * Math.Tan(0.5 * World.HorizontalViewingAngle) * EyeDistance; WorldHeight = WorldWidth / World.AspectRatio; } else { WorldHeight = 2.0 * Math.Tan(0.5 * World.VerticalViewingAngle) * EyeDistance / World.AspectRatio; WorldWidth = WorldHeight * World.AspectRatio; } double x0 = Left / PanelResolution; double x1 = (Left + Width) / PanelResolution; double y0 = (PanelBottom - Top) / PanelResolution * World.AspectRatio; double y1 = (PanelBottom - (Top + Height)) / PanelResolution * World.AspectRatio; double xd = 0.5 - PanelCenterX / PanelResolution; x0 += xd; x1 += xd; double yt = PanelBottom - PanelResolution / World.AspectRatio; double yd = (PanelCenterY - yt) / (PanelBottom - yt) - 0.5; y0 += yd; y1 += yd; x0 = (x0 - 0.5) * WorldWidth; x1 = (x1 - 0.5) * WorldWidth; y0 = (y0 - 0.5) * WorldHeight; y1 = (y1 - 0.5) * WorldHeight; double xm = x0 * (1.0 - RelativeRotationCenterX) + x1 * RelativeRotationCenterX; double ym = y0 * (1.0 - RelativeRotationCenterY) + y1 * RelativeRotationCenterY; Vector3[] v = new Vector3[4]; v[0] = new Vector3(x0 - xm, y1 - ym, 0); v[1] = new Vector3(x0 - xm, y0 - ym, 0); v[2] = new Vector3(x1 - xm, y0 - ym, 0); v[3] = new Vector3(x1 - xm, y1 - ym, 0); World.Vertex t0 = new World.Vertex(v[0], new Vector2(0.0f, 1.0f)); World.Vertex t1 = new World.Vertex(v[1], new Vector2(0.0f, 0.0f)); World.Vertex t2 = new World.Vertex(v[2], new Vector2(1.0f, 0.0f)); World.Vertex t3 = new World.Vertex(v[3], new Vector2(1.0f, 1.0f)); ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Object.Mesh.Vertices = new World.Vertex[] { t0, t1, t2, t3 }; Object.Mesh.Faces = new World.MeshFace[] { new World.MeshFace(new int[] { 0, 1, 2, 3 }) }; Object.Mesh.Materials = new World.MeshMaterial[1]; Object.Mesh.Materials[0].Flags = (byte)(DaytimeTexture != null ? World.MeshMaterial.TransparentColorMask : 0); Object.Mesh.Materials[0].Color = Color; Object.Mesh.Materials[0].TransparentColor = new Color24(0, 0, 255); Object.Mesh.Materials[0].DaytimeTexture = DaytimeTexture; Object.Mesh.Materials[0].NighttimeTexture = NighttimeTexture; Object.Dynamic = true; // calculate offset Vector3 o; o.X = xm + DriverX; o.Y = ym + DriverY; o.Z = EyeDistance - Distance + DriverZ; // add object if (AddStateToLastElement) { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length - 1; int j = Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States.Length; Array.Resize<ObjectManager.AnimatedObjectState>(ref Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States, j + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Object = Object; return n; } else { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length; Array.Resize<ObjectManager.AnimatedObject>(ref Train.Cars[Train.DriverCar].CarSections[0].Elements, n + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n] = new ObjectManager.AnimatedObject(); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States = new ObjectManager.AnimatedObjectState[1]; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Object = Object; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].CurrentState = 0; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex = ObjectManager.CreateDynamicObject(); ObjectManager.Objects[Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex] = ObjectManager.CloneObject(Object); return n; } }
// read object /// <summary>Loads a CSV or B3D 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 X axis of referenced textures.</param> /// <param name="ForceTextureRepeatY">Whether to force TextureWrapMode.Repeat for Y axis of referenced textures.</param> /// <returns>The object loaded.</returns> internal static ObjectManager.StaticObject ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; bool IsB3D = string.Equals(System.IO.Path.GetExtension(FileName), ".b3d", StringComparison.OrdinalIgnoreCase); // initialize 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[] { }; // read lines string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); // parse lines MeshBuilder Builder = new MeshBuilder(); Vector3f[] Normals = new Vector3f[4]; for (int i = 0; i < Lines.Length; i++) { { // strip away comments int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j); } } // collect arguments string[] Arguments = Lines[i].Split(new char[] { ',' }, StringSplitOptions.None); for (int j = 0; j < Arguments.Length; j++) { Arguments[j] = Arguments[j].Trim(); } { // remove unused arguments at the end of the chain int j; for (j = Arguments.Length - 1; j >= 0; j--) { if (Arguments[j].Length != 0) break; } Array.Resize<string>(ref Arguments, j + 1); } // style string Command; if (IsB3D & Arguments.Length != 0) { // b3d int space = Arguments[0].IndexOf(' '); if (space >= 0) { Command = Arguments[0].Substring(0, space).TrimEnd(); Arguments[0] = Arguments[0].Substring(space + 1).TrimStart(); } else { Command = Arguments[0]; if (Arguments.Length != 1) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid syntax at line " + (i + 1).ToString(Culture) + " in file " + FileName); } Arguments = new string[] { }; } } else if (Arguments.Length != 0) { // csv Command = Arguments[0]; for (int j = 0; j < Arguments.Length - 1; j++) { Arguments[j] = Arguments[j + 1]; } Array.Resize<string>(ref Arguments, Arguments.Length - 1); } else { // empty Command = null; } // parse terms if (Command != null) { string cmd = Command.ToLowerInvariant(); switch(cmd) { case "createmeshbuilder": case "[meshbuilder]": { if (cmd == "createmeshbuilder" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "CreateMeshBuilder is not a supported command - did you mean [MeshBuilder]? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "[meshbuilder]" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "[MeshBuilder] is not a supported command - did you mean CreateMeshBuilder? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 0) { Debug.AddMessage(Debug.MessageType.Warning, false, "0 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); Builder = new MeshBuilder(); Normals = new Vector3f[4]; } break; case "addvertex": case "vertex": { if (cmd == "addvertex" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "AddVertex is not a supported command - did you mean Vertex? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "vertex" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "Vertex is not a supported command - did you mean AddVertex? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 6) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 6 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double vx = 0.0, vy = 0.0, vz = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out vx)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument vX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); vx = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out vy)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument vY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); vy = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out vz)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument vZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); vz = 0.0; } double nx = 0.0, ny = 0.0, nz = 0.0; if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[3], out nx)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument nX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); nx = 0.0; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[4], out ny)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument nY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); ny = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[5], out nz)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument nZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); nz = 0.0; } World.Normalize(ref nx, ref ny, ref nz); Array.Resize<World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); while (Builder.Vertices.Length >= Normals.Length) { Array.Resize<Vector3f>(ref Normals, Normals.Length << 1); } Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new Vector3D(vx, vy, vz); Normals[Builder.Vertices.Length - 1] = new Vector3f((float)nx, (float)ny, (float)nz); } break; case "addface": case "addface2": case "face": case "face2": { if (IsB3D) { if (cmd == "addface") { Debug.AddMessage(Debug.MessageType.Warning, false, "AddFace is not a supported command - did you mean Face? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "addface2") { Debug.AddMessage(Debug.MessageType.Warning, false, "AddFace2 is not a supported command - did you mean Face2? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { if (cmd == "face") { Debug.AddMessage(Debug.MessageType.Warning, false, "Face is not a supported command - did you mean AddFace? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "face2") { Debug.AddMessage(Debug.MessageType.Warning, false, "Face2 is not a supported command - did you mean AddFace2? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } if (Arguments.Length < 3) { Debug.AddMessage(Debug.MessageType.Error, false, "At least 3 arguments are required in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { bool valid = true; int[] indices = new int[Arguments.Length]; for (int j = 0; j < Arguments.Length; j++) { if (!Conversions.TryParseIntVb6(Arguments[j], out indices[j])) { Debug.AddMessage(Debug.MessageType.Error, false, "v" + j.ToString(Culture) + " is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); valid = false; break; } else if (indices[j] < 0 | indices[j] >= Builder.Vertices.Length) { Debug.AddMessage(Debug.MessageType.Error, false, "v" + j.ToString(Culture) + " references a non-existing vertex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); valid = false; break; } else if (indices[j] > 65535) { Debug.AddMessage(Debug.MessageType.Error, false, "v" + j.ToString(Culture) + " indexes a vertex above 65535 which is not currently supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); valid = false; break; } } if (valid) { int last = Builder.Faces.Length; Array.Resize<World.MeshFace>(ref Builder.Faces, last + 1); Builder.Faces[last] = new World.MeshFace(); Builder.Faces[last].Vertices = new World.MeshFaceVertex[Arguments.Length]; while (Builder.Vertices.Length > Normals.Length) { Array.Resize<Vector3f>(ref Normals, Normals.Length << 1); } for (int j = 0; j < Arguments.Length; j++) { Builder.Faces[last].Vertices[j].Index = (ushort)indices[j]; Builder.Faces[last].Vertices[j].Normal = Normals[indices[j]]; } if (cmd == "addface2" | cmd == "face2") { Builder.Faces[last].Flags = (byte)World.MeshFace.Face2Mask; } } } } break; case "cube": { if (Arguments.Length > 3) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double x = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out x)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument HalfWidth in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 1.0; } double y = x, z = x; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out y)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument HalfHeight in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 1.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out z)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument HalfDepth in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 1.0; } CreateCube(ref Builder, x, y, z); } break; case "cylinder": { if (Arguments.Length > 4) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int n = 8; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out n)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument n in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); n = 8; } if (n < 2) { Debug.AddMessage(Debug.MessageType.Error, false, "n is expected to be at least 2 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); n = 8; } double r1 = 0.0, r2 = 0.0, h = 1.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out r1)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument UpperRadius in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r1 = 1.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out r2)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument LowerRadius in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r2 = 1.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[3], out h)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Height in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); h = 1.0; } CreateCylinder(ref Builder, n, r1, r2, h); } break; case "translate": case "translateall": { if (Arguments.Length > 3) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double x = 0.0, y = 0.0, z = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out x)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out y)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out z)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 0.0; } ApplyTranslation(Builder, x, y, z); if (cmd == "translateall") { ApplyTranslation(Object, x, y, z); } } break; case "scale": case "scaleall": { if (Arguments.Length > 3) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double x = 1.0, y = 1.0, z = 1.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out x)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 1.0; } else if (x == 0.0) { Debug.AddMessage(Debug.MessageType.Error, false, "X is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 1.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out y)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 1.0; } else if (y == 0.0) { Debug.AddMessage(Debug.MessageType.Error, false, "Y is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 1.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out z)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 1.0; } else if (z == 0.0) { Debug.AddMessage(Debug.MessageType.Error, false, "Z is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 1.0; } ApplyScale(Builder, x, y, z); if (cmd == "scaleall") { ApplyScale(Object, x, y, z); } } break; case "rotate": case "rotateall": { if (Arguments.Length > 4) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double x = 0.0, y = 0.0, z = 0.0, a = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out x)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out y)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out z)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 0.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[3], out a)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Angle in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); a = 0.0; } double t = x * x + y * y + z * z; if (t == 0.0) { x = 1.0; y = 0.0; z = 0.0; t = 1.0; } if (a != 0.0) { t = 1.0 / Math.Sqrt(t); x *= t; y *= t; z *= t; a *= 0.0174532925199433; ApplyRotation(Builder, x, y, z, a); if (cmd == "rotateall") { ApplyRotation(Object, x, y, z, a); } } } break; case "shear": case "shearall": { if (Arguments.Length > 7) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 7 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double dx = 0.0, dy = 0.0, dz = 0.0; double sx = 0.0, sy = 0.0, sz = 0.0; double r = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out dx)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument dX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); dx = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out dy)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument dY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); dy = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out dz)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument dZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); dz = 0.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[3], out sx)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument sX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); sx = 0.0; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[4], out sy)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument sY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); sy = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[5], out sz)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument sZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); sz = 0.0; } if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[6], out r)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Ratio in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = 0.0; } World.Normalize(ref dx, ref dy, ref dz); World.Normalize(ref sx, ref sy, ref sz); ApplyShear(Builder, dx, dy, dz, sx, sy, sz, r); if (cmd == "shearall") { ApplyShear(Object, dx, dy, dz, sx, sy, sz, r); } } break; case "generatenormals": case "[texture]": if (cmd == "generatenormals" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "GenerateNormals is not a supported command - did you mean [Texture]? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "[texture]" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "[Texture] is not a supported command - did you mean GenerateNormals? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } // TODO do something? break; case "setcolor": case "color": { if (cmd == "setcolor" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "SetColor is not a supported command - did you mean Color? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "color" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "Color is not a supported command - did you mean SetColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 4) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int r = 0, g = 0, b = 0, a = 255; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out r)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = 0; } else if (r < 0 | r > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseIntVb6(Arguments[1], out g)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = 0; } else if (g < 0 | g > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseIntVb6(Arguments[2], out b)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = 0; } else if (b < 0 | b > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = b < 0 ? 0 : 255; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseIntVb6(Arguments[3], out a)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Alpha in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); a = 255; } else if (a < 0 | a > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Alpha is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); a = a < 0 ? 0 : 255; } int m = Builder.Materials.Length; Array.Resize<Material>(ref Builder.Materials, m << 1); for (int j = m; j < Builder.Materials.Length; j++) { Builder.Materials[j] = new Material(Builder.Materials[j - m]); Builder.Materials[j].Color = new Color32((byte)r, (byte)g, (byte)b, (byte)a); Builder.Materials[j].BlendMode = Builder.Materials[0].BlendMode; Builder.Materials[j].GlowAttenuationData = Builder.Materials[0].GlowAttenuationData; Builder.Materials[j].DaytimeTexture = Builder.Materials[0].DaytimeTexture; Builder.Materials[j].NighttimeTexture = Builder.Materials[0].NighttimeTexture; Builder.Materials[j].TransparentColor = Builder.Materials[0].TransparentColor; Builder.Materials[j].TransparentColorUsed = Builder.Materials[0].TransparentColorUsed; } for (int j = 0; j < Builder.Faces.Length; j++) { Builder.Faces[j].Material += (ushort)m; } } break; case "setemissivecolor": case "emissivecolor": { if (cmd == "setemissivecolor" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "SetEmissiveColor is not a supported command - did you mean EmissiveColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "emissivecolor" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "EmissiveColor is not a supported command - did you mean SetEmissiveColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 3) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out r)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = 0; } else if (r < 0 | r > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseIntVb6(Arguments[1], out g)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = 0; } else if (g < 0 | g > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseIntVb6(Arguments[2], out b)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = 0; } else if (b < 0 | b > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = b < 0 ? 0 : 255; } int m = Builder.Materials.Length; Array.Resize<Material>(ref Builder.Materials, m << 1); for (int j = m; j < Builder.Materials.Length; j++) { Builder.Materials[j] = new Material(Builder.Materials[j - m]); Builder.Materials[j].EmissiveColor = new Color24((byte)r, (byte)g, (byte)b); Builder.Materials[j].EmissiveColorUsed = true; Builder.Materials[j].BlendMode = Builder.Materials[0].BlendMode; Builder.Materials[j].GlowAttenuationData = Builder.Materials[0].GlowAttenuationData; Builder.Materials[j].DaytimeTexture = Builder.Materials[0].DaytimeTexture; Builder.Materials[j].NighttimeTexture = Builder.Materials[0].NighttimeTexture; Builder.Materials[j].TransparentColor = Builder.Materials[0].TransparentColor; Builder.Materials[j].TransparentColorUsed = Builder.Materials[0].TransparentColorUsed; } for (int j = 0; j < Builder.Faces.Length; j++) { Builder.Faces[j].Material += (ushort)m; } } break; case "setdecaltransparentcolor": case "transparent": { if (cmd == "setdecaltransparentcolor" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "SetDecalTransparentColor is not a supported command - did you mean Transparent? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "transparent" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "Transparent is not a supported command - did you mean SetDecalTransparentColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 3) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out r)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = 0; } else if (r < 0 | r > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseIntVb6(Arguments[1], out g)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = 0; } else if (g < 0 | g > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseIntVb6(Arguments[2], out b)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = 0; } else if (b < 0 | b > 255) { Debug.AddMessage(Debug.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = b < 0 ? 0 : 255; } for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].TransparentColor = new Color24((byte)r, (byte)g, (byte)b); Builder.Materials[j].TransparentColorUsed = true; } } break; case "setblendmode": case "blendmode": { if (cmd == "setblendmode" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "SetBlendMode is not a supported command - did you mean BlendMode? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "blendmode" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "BlendMode is not a supported command - did you mean SetBlendMode? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 3) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } World.MeshMaterialBlendMode blendmode = World.MeshMaterialBlendMode.Normal; if (Arguments.Length >= 1 && Arguments[0].Length > 0) { switch (Arguments[0].ToLowerInvariant()) { case "normal": blendmode = World.MeshMaterialBlendMode.Normal; break; case "additive": blendmode = World.MeshMaterialBlendMode.Additive; break; default: Debug.AddMessage(Debug.MessageType.Error, false, "The given BlendMode is not supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); blendmode = World.MeshMaterialBlendMode.Normal; break; } } double glowhalfdistance = 0.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out glowhalfdistance)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument GlowHalfDistance in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); glowhalfdistance = 0; } World.GlowAttenuationMode glowmode = World.GlowAttenuationMode.DivisionExponent4; if (Arguments.Length >= 3 && Arguments[2].Length > 0) { switch (Arguments[2].ToLowerInvariant()) { case "divideexponent2": glowmode = World.GlowAttenuationMode.DivisionExponent2; break; case "divideexponent4": glowmode = World.GlowAttenuationMode.DivisionExponent4; break; default: Debug.AddMessage(Debug.MessageType.Error, false, "The given GlowAttenuationMode is not supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].BlendMode = blendmode; Builder.Materials[j].GlowAttenuationData = World.GetGlowAttenuationData(glowhalfdistance, glowmode); } } break; case "loadtexture": case "load": { if (cmd == "loadtexture" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "LoadTexture is not a supported command - did you mean Load? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "load" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "Load is not a supported command - did you mean LoadTexture? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 2) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 2 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } string tday = null, tnight = null; if (Arguments.Length >= 1 && Arguments[0].Length != 0) { if (OpenBveApi.Path.ContainsInvalidPathChars(Arguments[0])) { Debug.AddMessage(Debug.MessageType.Error, false, "DaytimeTexture contains illegal characters in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { tday = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]); if (!System.IO.File.Exists(tday)) { Debug.AddMessage(Debug.MessageType.Error, true, "DaytimeTexture " + tday + " could not be found in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); tday = null; } } } if (Arguments.Length >= 2 && Arguments[1].Length != 0) { if (Arguments[0].Length == 0) { Debug.AddMessage(Debug.MessageType.Error, true, "DaytimeTexture is required to be specified in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (OpenBveApi.Path.ContainsInvalidPathChars(Arguments[1])) { Debug.AddMessage(Debug.MessageType.Error, false, "NighttimeTexture contains illegal characters in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { tnight = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[1]); if (!System.IO.File.Exists(tnight)) { Debug.AddMessage(Debug.MessageType.Error, true, "The NighttimeTexture " + tnight + " could not be found in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); tnight = null; } } } } for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].DaytimeTexture = tday; Builder.Materials[j].NighttimeTexture = tnight; } } break; case "settexturecoordinates": case "coordinates": { if (cmd == "settexturecoordinates" & IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "SetTextureCoordinates is not a supported command - did you mean Coordinates? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "coordinates" & !IsB3D) { Debug.AddMessage(Debug.MessageType.Warning, false, "Coordinates is not a supported command - did you mean SetTextureCoordinates? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 3) { Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int j = 0; float x = 0.0f, y = 0.0f; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out j)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument VertexIndex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); j = 0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseFloatVb6(Arguments[1], out x)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 0.0f; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseFloatVb6(Arguments[2], out y)) { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 0.0f; } if (j >= 0 & j < Builder.Vertices.Length) { Builder.Vertices[j].TextureCoordinates = new Vector2f(x, y); } else { Debug.AddMessage(Debug.MessageType.Error, false, "VertexIndex references a non-existing vertex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: if (Command.Length != 0) { Debug.AddMessage(Debug.MessageType.Error, false, "The command " + Command + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; } } } // finalize object ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); World.CreateNormals(ref Object.Mesh); return Object; }
/// <summary>Creates a new background object</summary> /// <param name="Object">The object to use for the background</param> internal BackgroundObject(ObjectManager.StaticObject Object) { this.ObjectBackground = Object; }