// 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="ForceTextureRepeat">Whether to force TextureWrapMode.Repeat for 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(); World.Vector3Df[] Normals = new World.Vector3Df[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 j = Arguments[0].IndexOf(' '); if (j >= 0) { Command = Arguments[0].Substring(0, j).TrimEnd(); Arguments[0] = Arguments[0].Substring(j + 1).TrimStart(); } else { Command = Arguments[0]; if (Arguments.Length != 1) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 World.Vector3Df[4]; } break; case "addvertex": case "vertex": { if (cmd == "addvertex" & IsB3D) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[0], out vx)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[1], out vy)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[2], out vz)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[3], out nx)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[4], out ny)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[5], out nz)) { Interface.AddMessage(Interface.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<World.Vector3Df>(ref Normals, Normals.Length << 1); } Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new Vector3(vx, vy, vz); Normals[Builder.Vertices.Length - 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); } break; case "addface": case "addface2": case "face": case "face2": { if (IsB3D) { if (cmd == "addface") { Interface.AddMessage(Interface.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") { Interface.AddMessage(Interface.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") { Interface.AddMessage(Interface.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") { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.MessageType.Error, false, "At least 3 arguments are required in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { bool q = true; int[] a = new int[Arguments.Length]; for (int j = 0; j < Arguments.Length; j++) { if (!Interface.TryParseIntVb6(Arguments[j], out a[j])) { Interface.AddMessage(Interface.MessageType.Error, false, "v" + j.ToString(Culture) + " is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); q = false; break; } else if (a[j] < 0 | a[j] >= Builder.Vertices.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "v" + j.ToString(Culture) + " references a non-existing vertex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); q = false; break; } else if (a[j] > 65535) { Interface.AddMessage(Interface.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); q = false; break; } } if (q) { int f = Builder.Faces.Length; Array.Resize<World.MeshFace>(ref Builder.Faces, f + 1); Builder.Faces[f] = new World.MeshFace(); Builder.Faces[f].Vertices = new World.MeshFaceVertex[Arguments.Length]; while (Builder.Vertices.Length > Normals.Length) { Array.Resize<World.Vector3Df>(ref Normals, Normals.Length << 1); } for (int j = 0; j < Arguments.Length; j++) { Builder.Faces[f].Vertices[j].Index = (ushort)a[j]; Builder.Faces[f].Vertices[j].Normal = Normals[a[j]]; } if (cmd == "addface2" | cmd == "face2") { Builder.Faces[f].Flags = (byte)World.MeshFace.Face2Mask; } } } } break; case "cube": { if (Arguments.Length > 3) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[1], out y)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[2], out z)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[0], out n)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument n in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); n = 8; } if (n < 2) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[1], out r1)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[2], out r2)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[3], out h)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[1], out y)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[2], out z)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[1], out y)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[2], out z)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[1], out y)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[2], out z)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[3], out a)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[0], out dx)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[1], out dy)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[2], out dz)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[3], out sx)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[4], out sy)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[5], out sz)) { Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[6], out r)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.MessageType.Warning, false, "[Texture] is not a supported command - did you mean GenerateNormals? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "setcolor": case "color": { if (cmd == "setcolor" & IsB3D) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[3], out a)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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: Interface.AddMessage(Interface.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 && !Interface.TryParseDoubleVb6(Arguments[1], out glowhalfdistance)) { Interface.AddMessage(Interface.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: Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.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)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.MessageType.Error, true, "DaytimeTexture is required to be specified in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (Interface.ContainsInvalidPathChars(Arguments[1])) { Interface.AddMessage(Interface.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)) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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 && !Interface.TryParseIntVb6(Arguments[0], out j)) { Interface.AddMessage(Interface.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 && !Interface.TryParseFloatVb6(Arguments[1], out x)) { Interface.AddMessage(Interface.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 && !Interface.TryParseFloatVb6(Arguments[2], out y)) { Interface.AddMessage(Interface.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 World.Vector2Df(x, y); } else { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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; }
// create cylinder private static void CreateCylinder(ref MeshBuilder Builder, int n, double r1, double r2, double h) { // parameters bool uppercap = r1 > 0.0; bool lowercap = r2 > 0.0; int m = (uppercap ? 1 : 0) + (lowercap ? 1 : 0); r1 = Math.Abs(r1); r2 = Math.Abs(r2); double ns = h >= 0.0 ? 1.0 : -1.0; // initialization int v = Builder.Vertices.Length; Array.Resize<World.Vertex>(ref Builder.Vertices, v + 2 * n); World.Vector3Df[] Normals = new World.Vector3Df[2 * n]; double d = 2.0 * Math.PI / (double)n; double g = 0.5 * h; double t = 0.0; double a = h != 0.0 ? Math.Atan((r2 - r1) / h) : 0.0; double cosa = Math.Cos(a); double sina = Math.Sin(a); // vertices and normals for (int i = 0; i < n; i++) { double dx = Math.Cos(t); double dz = Math.Sin(t); double lx = dx * r2; double lz = dz * r2; double ux = dx * r1; double uz = dz * r1; Builder.Vertices[v + 2 * i + 0].Coordinates = new Vector3(ux, g, uz); Builder.Vertices[v + 2 * i + 1].Coordinates = new Vector3(lx, -g, lz); double nx = dx * ns, ny = 0.0, nz = dz * ns; double sx, sy, sz; World.Cross(nx, ny, nz, 0.0, 1.0, 0.0, out sx, out sy, out sz); World.Rotate(ref nx, ref ny, ref nz, sx, sy, sz, cosa, sina); Normals[2 * i + 0] = new World.Vector3Df((float)nx, (float)ny, (float)nz); Normals[2 * i + 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); t += d; } // faces int f = Builder.Faces.Length; Array.Resize<World.MeshFace>(ref Builder.Faces, f + n + m); for (int i = 0; i < n; i++) { Builder.Faces[f + i].Flags = 0; int i0 = (2 * i + 2) % (2 * n); int i1 = (2 * i + 3) % (2 * n); int i2 = 2 * i + 1; int i3 = 2 * i; Builder.Faces[f + i].Vertices = new World.MeshFaceVertex[] { new World.MeshFaceVertex(v + i0, Normals[i0]), new World.MeshFaceVertex(v + i1, Normals[i1]), new World.MeshFaceVertex(v + i2, Normals[i2]), new World.MeshFaceVertex(v + i3, Normals[i3]) }; } for (int i = 0; i < m; i++) { Builder.Faces[f + n + i].Vertices = new World.MeshFaceVertex[n]; for (int j = 0; j < n; j++) { if (i == 0 & lowercap) { // lower cap Builder.Faces[f + n + i].Vertices[j] = new World.MeshFaceVertex(v + 2 * j + 1); } else { // upper cap Builder.Faces[f + n + i].Vertices[j] = new World.MeshFaceVertex(v + 2 * (n - j - 1)); } } } }
// reset internal static void Reset() { LoadTexturesImmediately = LoadTextureImmediatelyMode.NotYet; Objects = new Object[256]; ObjectCount = 0; StaticOpaque = new ObjectGroup[] { }; StaticOpaqueForceUpdate = true; DynamicOpaque = new ObjectList(); DynamicAlpha = new ObjectList(); OverlayOpaque = new ObjectList(); OverlayAlpha = new ObjectList(); OptionLighting = true; OptionAmbientColor = new Color24(160, 160, 160); OptionDiffuseColor = new Color24(160, 160, 160); OptionLightPosition = new World.Vector3Df(0.223606797749979f, 0.86602540378444f, -0.447213595499958f); OptionLightingResultingAmount = 1.0f; OptionClock = false; OptionBrakeSystems = false; }
// 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; }
private static void RenderBackground(World.Background Data, double dx, double dy, double dz, float Alpha, float scale) { if (Data.Texture != null && Textures.LoadTexture(Data.Texture, Textures.OpenGlTextureWrapMode.RepeatClamp)) { if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } if (!TexturingEnabled) { Gl.glEnable(Gl.GL_TEXTURE_2D); TexturingEnabled = true; } if (Alpha == 1.0f) { if (BlendEnabled) { Gl.glDisable(Gl.GL_BLEND); BlendEnabled = false; } } else if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; } Gl.glBindTexture(Gl.GL_TEXTURE_2D, Data.Texture.OpenGlTextures[(int)Textures.OpenGlTextureWrapMode.RepeatClamp].Name); Gl.glColor4f(1.0f, 1.0f, 1.0f, Alpha); float y0, y1; if (Data.KeepAspectRatio) { int tw = Data.Texture.Width; int th = Data.Texture.Height; double hh = Math.PI * World.BackgroundImageDistance * (double)th / ((double)tw * (double)Data.Repetition); y0 = (float)(-0.5 * hh); y1 = (float)(1.5 * hh); } else { y0 = (float)(-0.125 * World.BackgroundImageDistance); y1 = (float)(0.375 * World.BackgroundImageDistance); } const int n = 32; World.Vector3Df[] bottom = new World.Vector3Df[n]; World.Vector3Df[] top = new World.Vector3Df[n]; double angleValue = 2.61799387799149 - 3.14159265358979 / (double)n; double angleIncrement = 6.28318530717958 / (double)n; /* * To ensure that the whole background cylinder is rendered inside the viewing frustum, * the background is rendered before the scene with z-buffer writes disabled. Then, * the actual distance from the camera is irrelevant as long as it is inside the frustum. * */ for (int i = 0; i < n; i++) { float x = (float)(World.BackgroundImageDistance * Math.Cos(angleValue)); float z = (float)(World.BackgroundImageDistance * Math.Sin(angleValue)); bottom[i] = new World.Vector3Df(scale * x, scale * y0, scale * z); top[i] = new World.Vector3Df(scale * x, scale * y1, scale * z); angleValue += angleIncrement; } float textureStart = 0.5f * (float)Data.Repetition / (float)n; float textureIncrement = -(float)Data.Repetition / (float)n; double textureX = textureStart; for (int i = 0; i < n; i++) { int j = (i + 1) % n; // side wall Gl.glBegin(Gl.GL_QUADS); Gl.glTexCoord2d(textureX, 0.005f); Gl.glVertex3f(top[i].X, top[i].Y, top[i].Z); Gl.glTexCoord2d(textureX, 0.995f); Gl.glVertex3f(bottom[i].X, bottom[i].Y, bottom[i].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.995f); Gl.glVertex3f(bottom[j].X, bottom[j].Y, bottom[j].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.005f); Gl.glVertex3f(top[j].X, top[j].Y, top[j].Z); Gl.glEnd(); // top cap Gl.glBegin(Gl.GL_TRIANGLES); Gl.glTexCoord2d(textureX, 0.005f); Gl.glVertex3f(top[i].X, top[i].Y, top[i].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.005f); Gl.glVertex3f(top[j].X, top[j].Y, top[j].Z); Gl.glTexCoord2d(textureX + 0.5 * textureIncrement, 0.1f); Gl.glVertex3f(0.0f, top[i].Y, 0.0f); // bottom cap Gl.glTexCoord2d(textureX + 0.5 * textureIncrement, 0.9f); Gl.glVertex3f(0.0f, bottom[i].Y, 0.0f); Gl.glTexCoord2d(textureX + textureIncrement, 0.995f); Gl.glVertex3f(bottom[j].X, bottom[j].Y, bottom[j].Z); Gl.glTexCoord2d(textureX, 0.995f); Gl.glVertex3f(bottom[i].X, bottom[i].Y, bottom[i].Z); Gl.glEnd(); // finish textureX += textureIncrement; } Gl.glDisable(Gl.GL_TEXTURE_2D); TexturingEnabled = false; if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; } } }
// reset internal static void Reset() { LoadTexturesImmediately = LoadTextureImmediatelyMode.NotYet; ObjectList = new Object[256]; ObjectListCount = 0; OpaqueList = new ObjectFace[256]; OpaqueListCount = 0; TransparentColorList = new ObjectFace[256]; TransparentColorListDistance = new double[256]; TransparentColorListCount = 0; AlphaList = new ObjectFace[256]; AlphaListDistance = new double[256]; AlphaListCount = 0; OverlayList = new ObjectFace[256]; OverlayListDistance = new double[256]; OverlayListCount = 0; OptionLighting = true; OptionAmbientColor = new World.ColorRGB(160, 160, 160); OptionDiffuseColor = new World.ColorRGB(160, 160, 160); OptionLightPosition = new World.Vector3Df(0.215920077052065f, 0.875724044222352f, -0.431840154104129f); OptionLightingResultingAmount = 1.0f; GL.Disable(EnableCap.Fog); FogEnabled = false; }
// 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; }
private static void RenderBackground(World.Background Data, double dx, double dy, double dz, float Alpha) { if (Data.Texture >= 0) { int OpenGlTextureIndex = TextureManager.UseTexture(Data.Texture, TextureManager.UseMode.LoadImmediately); if (OpenGlTextureIndex > 0) { if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } if (!TexturingEnabled) { Gl.glEnable(Gl.GL_TEXTURE_2D); TexturingEnabled = true; } if (Alpha == 1.0f) { if (BlendEnabled) { Gl.glDisable(Gl.GL_BLEND); BlendEnabled = false; } } else if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; } Gl.glBindTexture(Gl.GL_TEXTURE_2D, OpenGlTextureIndex); Gl.glColor4f(1.0f, 1.0f, 1.0f, Alpha); float y0, y1; if (Data.KeepAspectRatio) { int tw = TextureManager.Textures[Data.Texture].Width; int th = TextureManager.Textures[Data.Texture].Height; double hh = Math.PI * World.BackgroundImageDistance * (double)th / ((double)tw * (double)Data.Repetition); y0 = (float)(-0.5 * hh); y1 = (float)(1.5 * hh); } else { y0 = (float)(-0.125 * World.BackgroundImageDistance); y1 = (float)(0.375 * World.BackgroundImageDistance); } const int n = 32; World.Vector3Df[] bottom = new World.Vector3Df[n]; World.Vector3Df[] top = new World.Vector3Df[n]; double angleValue = 2.61799387799149 - 3.14159265358979 / (double)n; double angleIncrement = 6.28318530717958 / (double)n; for (int i = 0; i < n; i++) { float x = (float)(World.BackgroundImageDistance * Math.Cos(angleValue)); float z = (float)(World.BackgroundImageDistance * Math.Sin(angleValue)); bottom[i] = new World.Vector3Df(x, y0, z); top[i] = new World.Vector3Df(x, y1, z); angleValue += angleIncrement; } float textureStart = 0.5f * (float)Data.Repetition / (float)n; float textureIncrement = -(float)Data.Repetition / (float)n; double textureX = textureStart; for (int i = 0; i < n; i++) { int j = (i + 1) % n; // side wall Gl.glBegin(Gl.GL_QUADS); Gl.glTexCoord2d(textureX, 0.005f); Gl.glVertex3f(top[i].X, top[i].Y, top[i].Z); Gl.glTexCoord2d(textureX, 0.995f); Gl.glVertex3f(bottom[i].X, bottom[i].Y, bottom[i].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.995f); Gl.glVertex3f(bottom[j].X, bottom[j].Y, bottom[j].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.005f); Gl.glVertex3f(top[j].X, top[j].Y, top[j].Z); Gl.glEnd(); // top cap Gl.glBegin(Gl.GL_TRIANGLES); Gl.glTexCoord2d(textureX, 0.005f); Gl.glVertex3f(top[i].X, top[i].Y, top[i].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.005f); Gl.glVertex3f(top[j].X, top[j].Y, top[j].Z); Gl.glTexCoord2d(textureX + 0.5 * textureIncrement, 0.1f); Gl.glVertex3f(0.0f, top[i].Y, 0.0f); // bottom cap Gl.glTexCoord2d(textureX + 0.5 * textureIncrement, 0.9f); Gl.glVertex3f(0.0f, bottom[i].Y, 0.0f); Gl.glTexCoord2d(textureX + textureIncrement, 0.995f); Gl.glVertex3f(bottom[j].X, bottom[j].Y, bottom[j].Z); Gl.glTexCoord2d(textureX, 0.995f); Gl.glVertex3f(bottom[i].X, bottom[i].Y, bottom[i].Z); Gl.glEnd(); // finish textureX += textureIncrement; } Gl.glDisable(Gl.GL_TEXTURE_2D); TexturingEnabled = false; if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; } } } }
// 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); }