internal void Flip() { if ((this.Flags & FaceTypeMask) == FaceTypeQuadStrip) { for (int i = 0; i < this.Vertices.Length; i += 2) { MeshFaceVertex x = this.Vertices[i]; this.Vertices[i] = this.Vertices[i + 1]; this.Vertices[i + 1] = x; } } else { int n = this.Vertices.Length; for (int i = 0; i < (n >> 1); i++) { MeshFaceVertex x = this.Vertices[i]; this.Vertices[i] = this.Vertices[n - i - 1]; this.Vertices[n - i - 1] = x; } } }
/// <summary>Loads a Wavefront object from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in.</param> /// <returns>The object loaded.</returns> internal static StaticObject ReadObject(string FileName, System.Text.Encoding Encoding) { StaticObject Object = new StaticObject(Plugin.currentHost); MeshBuilder Builder = new MeshBuilder(); /* * Temporary arrays */ List <Vector3> tempVertices = new List <Vector3>(); List <Vector3> tempNormals = new List <Vector3>(); List <Vector2> tempCoords = new List <Vector2>(); Material[] TempMaterials = new Material[0]; //Stores the current material int currentMaterial = -1; //Read the contents of the file string[] Lines = File.ReadAllLines(FileName, Encoding); //Preprocess for (int i = 0; i < Lines.Length; i++) { // Strip hash comments int c = Lines[i].IndexOf("#", StringComparison.Ordinal); if (c >= 0) { Lines[i] = Lines[i].Substring(0, c); } // collect arguments List <string> Arguments = new List <string>(Lines[i].Split(new char[] { ' ', '\t' }, StringSplitOptions.None)); for (int j = Arguments.Count - 1; j >= 0; j--) { Arguments[j] = Arguments[j].Trim(new char[] { }); if (Arguments[j] == string.Empty) { Arguments.RemoveAt(j); } } if (Arguments.Count == 0) { continue; } switch (Arguments[0].ToLowerInvariant()) { case "v": //Vertex Vector3 vertex = new Vector3(); if (!double.TryParse(Arguments[1], out vertex.X)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Vertex at Line " + i); } if (!double.TryParse(Arguments[2], out vertex.Y)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Y co-ordinate in Vertex at Line " + i); } if (!double.TryParse(Arguments[3], out vertex.Z)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Z co-ordinate in Vertex at Line " + i); } tempVertices.Add(vertex); break; case "vt": //Vertex texture co-ords Vector2 coords = new Vector2(); if (!double.TryParse(Arguments[1], out coords.X)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-ordinates at Line " + i); } if (!double.TryParse(Arguments[2], out coords.Y)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-Ordinates at Line " + i); } tempCoords.Add(coords); break; case "vn": Vector3 normal = new Vector3(); if (!double.TryParse(Arguments[1], out normal.X)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Vertex Normal at Line " + i); } if (!double.TryParse(Arguments[2], out normal.Y)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Y co-ordinate in Vertex Normal at Line " + i); } if (!double.TryParse(Arguments[3], out normal.Z)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Z co-ordinate in Vertex Normal at Line " + i); } tempNormals.Add(normal); //Vertex normals break; case "vp": //Parameter space verticies, not supported throw new NotSupportedException("Parameter space verticies are not supported by this parser"); case "f": //Creates a new face //Create the temp list to hook out the vertices List <VertexTemplate> vertices = new List <VertexTemplate>(); List <Vector3> normals = new List <Vector3>(); for (int f = 1; f < Arguments.Count; f++) { Vertex newVertex = new Vertex(); string[] faceArguments = Arguments[f].Split(new char[] { '/' }, StringSplitOptions.None); int idx; if (!int.TryParse(faceArguments[0], out idx)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Vertex index in Face " + f + " at Line " + i); continue; } int currentVertex = tempVertices.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentVertex++; currentVertex += idx; } else { currentVertex = idx; } if (currentVertex > tempVertices.Count) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Vertex index " + idx + " was greater than the available number of vertices in Face " + f + " at Line " + i); continue; } newVertex.Coordinates = tempVertices[currentVertex - 1]; if (faceArguments.Length <= 1) { normals.Add(new Vector3()); } else { if (!int.TryParse(faceArguments[1], out idx)) { if (!string.IsNullOrEmpty(faceArguments[1])) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Texture Co-ordinate index in Face " + f + " at Line " + i); } newVertex.TextureCoordinates = new Vector2(); } else { int currentCoord = tempCoords.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentCoord++; currentCoord += idx; } else { currentCoord = idx; } if (currentCoord > tempCoords.Count) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Texture Co-ordinate index " + currentCoord + " was greater than the available number of texture co-ordinates in Face " + f + " at Line " + i); } else { newVertex.TextureCoordinates = tempCoords[currentCoord - 1]; } } } if (faceArguments.Length <= 2) { normals.Add(new Vector3()); } else { if (!int.TryParse(faceArguments[2], out idx)) { if (!string.IsNullOrEmpty(faceArguments[2])) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Vertex Normal index in Face " + f + " at Line " + i); } normals.Add(new Vector3()); } else { int currentNormal = tempNormals.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentNormal++; currentNormal += idx; } else { currentNormal = idx; } if (currentNormal > tempNormals.Count) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Vertex Normal index " + currentNormal + " was greater than the available number of normals in Face " + f + " at Line " + i); normals.Add(new Vector3()); } else { normals.Add(tempNormals[currentNormal - 1]); } } } vertices.Add(newVertex); } MeshFaceVertex[] Vertices = new MeshFaceVertex[vertices.Count]; for (int k = 0; k < vertices.Count; k++) { Builder.Vertices.Add(vertices[k]); Vertices[k].Index = (ushort)(Builder.Vertices.Count - 1); Vertices[k].Normal = normals[k]; } Builder.Faces.Add(currentMaterial == -1 ? new MeshFace(Vertices, 0) : new MeshFace(Vertices, (ushort)currentMaterial)); break; case "g": //Starts a new face group and (normally) applies a new texture ApplyMeshBuilder(ref Object, Builder); Builder = new MeshBuilder(); break; case "s": /* * Changes the smoothing group applied to these vertexes: * 0- Disabled (Overriden by Vertex normals) * Otherwise appears to be a bitmask (32 available groups) * whereby faces within the same groups have their normals averaged * to appear smooth joins * * Not really supported at the minute, probably requires the engine * twiddling to deliberately support specifiying the shading type for a face * */ break; case "mtllib": //Loads the library of materials used by this file string MaterialsPath = OpenBveApi.Path.CombineFile(Path.GetDirectoryName(FileName), Arguments[1]); if (File.Exists(MaterialsPath)) { LoadMaterials(MaterialsPath, ref TempMaterials); } break; case "usemtl": for (int m = 0; m < TempMaterials.Length; m++) { if (TempMaterials[m].Key.ToLowerInvariant() == Arguments[1].ToLowerInvariant()) { bool mf = false; for (int k = 0; k < Builder.Materials.Length; k++) { if (Builder.Materials[k].Key != null && Builder.Materials[k].Key.ToLowerInvariant() == Arguments[1].ToLowerInvariant()) { mf = true; currentMaterial = k; break; } } if (!mf) { Array.Resize(ref Builder.Materials, Builder.Materials.Length + 1); Builder.Materials[Builder.Materials.Length - 1] = TempMaterials[m]; currentMaterial = Builder.Materials.Length - 1; } break; } if (m == TempMaterials.Length) { Plugin.currentHost.AddMessage(MessageType.Error, true, "Material " + Arguments[1] + " was not found."); currentMaterial = -1; } } break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unrecognised command " + Arguments[0]); break; } } ApplyMeshBuilder(ref Object, Builder); Object.Mesh.CreateNormals(); return(Object); }