Example #1
0
		/// <summary>Checks whether three spatial coordinates are colinear.</summary>
		/// <param name="a">The first spatial coordinate.</param>
		/// <param name="b">The second spatial coordinate.</param>
		/// <param name="c">The third spatial coordinate.</param>
		/// <returns>A boolean indicating whether the three spatial coordinates are colinear.</returns>
		public static bool AreColinear(Vector3f a, Vector3f b, Vector3f c) {
			Vector3f normal = Vector3f.Cross(b - a, c - a);
			return IsNullVector(normal);
		}
		// read object
		/// <summary>Loads a CSV or B3D object from a file.</summary>
		/// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param>
		/// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param>
		/// <param name="LoadMode">The texture load mode.</param>
		/// <param name="ForceTextureRepeatX">Whether to force TextureWrapMode.Repeat for X axis of referenced textures.</param>
		/// <param name="ForceTextureRepeatY">Whether to force TextureWrapMode.Repeat for Y axis of referenced textures.</param>
		/// <returns>The object loaded.</returns>
		internal static ObjectManager.StaticObject ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) {
			System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
			bool IsB3D = string.Equals(System.IO.Path.GetExtension(FileName), ".b3d", StringComparison.OrdinalIgnoreCase);
			// initialize object
			ObjectManager.StaticObject Object = new ObjectManager.StaticObject();
			Object.Mesh.Faces = new World.MeshFace[] { };
			Object.Mesh.Materials = new World.MeshMaterial[] { };
			Object.Mesh.Vertices = new World.Vertex[] { };
			// read lines
			string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding);
			// parse lines
			MeshBuilder Builder = new MeshBuilder();
			Vector3f[] Normals = new Vector3f[4];
			for (int i = 0; i < Lines.Length; i++) {
				{
					// strip away comments
					int j = Lines[i].IndexOf(';');
					if (j >= 0) {
						Lines[i] = Lines[i].Substring(0, j);
					}
				}
				// collect arguments
				string[] Arguments = Lines[i].Split(new char[] { ',' }, StringSplitOptions.None);
				for (int j = 0; j < Arguments.Length; j++) {
					Arguments[j] = Arguments[j].Trim();
				}
				{
					// remove unused arguments at the end of the chain
					int j;
					for (j = Arguments.Length - 1; j >= 0; j--) {
						if (Arguments[j].Length != 0) break;
					}
					Array.Resize<string>(ref Arguments, j + 1);
				}
				// style
				string Command;
				if (IsB3D & Arguments.Length != 0) {
					// b3d
					int space = Arguments[0].IndexOf(' ');
					if (space >= 0) {
						Command = Arguments[0].Substring(0, space).TrimEnd();
						Arguments[0] = Arguments[0].Substring(space + 1).TrimStart();
					} else {
						Command = Arguments[0];
						if (Arguments.Length != 1) {
							Debug.AddMessage(Debug.MessageType.Error, false, "Invalid syntax at line " + (i + 1).ToString(Culture) + " in file " + FileName);
						}
						Arguments = new string[] { };
					}
				} else if (Arguments.Length != 0) {
					// csv
					Command = Arguments[0];
					for (int j = 0; j < Arguments.Length - 1; j++) {
						Arguments[j] = Arguments[j + 1];
					}
					Array.Resize<string>(ref Arguments, Arguments.Length - 1);
				} else {
					// empty
					Command = null;
				}
				// parse terms
				if (Command != null) {
					string cmd = Command.ToLowerInvariant();
					switch(cmd) {
						case "createmeshbuilder":
						case "[meshbuilder]":
							{
								if (cmd == "createmeshbuilder" & IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "CreateMeshBuilder is not a supported command - did you mean [MeshBuilder]? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else if (cmd == "[meshbuilder]" & !IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "[MeshBuilder] is not a supported command - did you mean CreateMeshBuilder? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								if (Arguments.Length > 0) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "0 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY);
								Builder = new MeshBuilder();
								Normals = new Vector3f[4];
							} break;
						case "addvertex":
						case "vertex":
							{
								if (cmd == "addvertex" & IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "AddVertex is not a supported command - did you mean Vertex? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else if (cmd == "vertex" & !IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "Vertex is not a supported command - did you mean AddVertex? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								if (Arguments.Length > 6) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 6 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								double vx = 0.0, vy = 0.0, vz = 0.0;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out vx)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument vX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									vx = 0.0;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out vy)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument vY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									vy = 0.0;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out vz)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument vZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									vz = 0.0;
								}
								double nx = 0.0, ny = 0.0, nz = 0.0;
								if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[3], out nx)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument nX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									nx = 0.0;
								}
								if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[4], out ny)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument nY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									ny = 0.0;
								}
								if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[5], out nz)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument nZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									nz = 0.0;
								}
								World.Normalize(ref nx, ref ny, ref nz);
								Array.Resize<World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1);
								while (Builder.Vertices.Length >= Normals.Length) {
									Array.Resize<Vector3f>(ref Normals, Normals.Length << 1);
								}
								Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new Vector3D(vx, vy, vz);
								Normals[Builder.Vertices.Length - 1] = new Vector3f((float)nx, (float)ny, (float)nz);
							} break;
						case "addface":
						case "addface2":
						case "face":
						case "face2":
							{
								if (IsB3D) {
									if (cmd == "addface") {
										Debug.AddMessage(Debug.MessageType.Warning, false, "AddFace is not a supported command - did you mean Face? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									} else if (cmd == "addface2") {
										Debug.AddMessage(Debug.MessageType.Warning, false, "AddFace2 is not a supported command - did you mean Face2? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									}
								} else {
									if (cmd == "face") {
										Debug.AddMessage(Debug.MessageType.Warning, false, "Face is not a supported command - did you mean AddFace? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									} else if (cmd == "face2") {
										Debug.AddMessage(Debug.MessageType.Warning, false, "Face2 is not a supported command - did you mean AddFace2? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									}
								}
								if (Arguments.Length < 3) {
									Debug.AddMessage(Debug.MessageType.Error, false, "At least 3 arguments are required in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else {
									bool valid = true;
									int[] indices = new int[Arguments.Length];
									for (int j = 0; j < Arguments.Length; j++) {
										if (!Conversions.TryParseIntVb6(Arguments[j], out indices[j])) {
											Debug.AddMessage(Debug.MessageType.Error, false, "v" + j.ToString(Culture) + " is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											valid = false;
											break;
										} else if (indices[j] < 0 | indices[j] >= Builder.Vertices.Length) {
											Debug.AddMessage(Debug.MessageType.Error, false, "v" + j.ToString(Culture) + " references a non-existing vertex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											valid = false;
											break;
										} else if (indices[j] > 65535) {
											Debug.AddMessage(Debug.MessageType.Error, false, "v" + j.ToString(Culture) + " indexes a vertex above 65535 which is not currently supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											valid = false;
											break;
										}
									}
									if (valid) {
										int last = Builder.Faces.Length;
										Array.Resize<World.MeshFace>(ref Builder.Faces, last + 1);
										Builder.Faces[last] = new World.MeshFace();
										Builder.Faces[last].Vertices = new World.MeshFaceVertex[Arguments.Length];
										while (Builder.Vertices.Length > Normals.Length) {
											Array.Resize<Vector3f>(ref Normals, Normals.Length << 1);
										}
										for (int j = 0; j < Arguments.Length; j++) {
											Builder.Faces[last].Vertices[j].Index = (ushort)indices[j];
											Builder.Faces[last].Vertices[j].Normal = Normals[indices[j]];
										}
										if (cmd == "addface2" | cmd == "face2") {
											Builder.Faces[last].Flags = (byte)World.MeshFace.Face2Mask;
										}
									}
								}
							} break;
						case "cube":
							{
								if (Arguments.Length > 3) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								double x = 0.0;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out x)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument HalfWidth in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									x = 1.0;
								}
								double y = x, z = x;
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out y)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument HalfHeight in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									y = 1.0;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out z)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument HalfDepth in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									z = 1.0;
								}
								CreateCube(ref Builder, x, y, z);
							} break;
						case "cylinder":
							{
								if (Arguments.Length > 4) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								int n = 8;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out n)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument n in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									n = 8;
								}
								if (n < 2) {
									Debug.AddMessage(Debug.MessageType.Error, false, "n is expected to be at least 2 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									n = 8;
								}
								double r1 = 0.0, r2 = 0.0, h = 1.0;
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out r1)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument UpperRadius in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r1 = 1.0;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out r2)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument LowerRadius in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r2 = 1.0;
								}
								if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[3], out h)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Height in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									h = 1.0;
								}
								CreateCylinder(ref Builder, n, r1, r2, h);
							} break;
						case "translate":
						case "translateall":
							{
								if (Arguments.Length > 3) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								double x = 0.0, y = 0.0, z = 0.0;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out x)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									x = 0.0;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out y)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									y = 0.0;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out z)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									z = 0.0;
								}
								ApplyTranslation(Builder, x, y, z);
								if (cmd == "translateall") {
									ApplyTranslation(Object, x, y, z);
								}
							} break;
						case "scale":
						case "scaleall":
							{
								if (Arguments.Length > 3) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								double x = 1.0, y = 1.0, z = 1.0;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out x)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									x = 1.0;
								} else if (x == 0.0) {
									Debug.AddMessage(Debug.MessageType.Error, false, "X is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									x = 1.0;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out y)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									y = 1.0;
								} else if (y == 0.0) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Y is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									y = 1.0;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out z)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									z = 1.0;
								} else if (z == 0.0) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Z is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									z = 1.0;
								}
								ApplyScale(Builder, x, y, z);
								if (cmd == "scaleall") {
									ApplyScale(Object, x, y, z);
								}
							} break;
						case "rotate":
						case "rotateall":
							{
								if (Arguments.Length > 4) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								double x = 0.0, y = 0.0, z = 0.0, a = 0.0;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out x)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									x = 0.0;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out y)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									y = 0.0;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out z)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									z = 0.0;
								}
								if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[3], out a)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Angle in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									a = 0.0;
								}
								double t = x * x + y * y + z * z;
								if (t == 0.0) {
									x = 1.0;
									y = 0.0;
									z = 0.0;
									t = 1.0;
								}
								if (a != 0.0) {
									t = 1.0 / Math.Sqrt(t);
									x *= t;
									y *= t;
									z *= t;
									a *= 0.0174532925199433;
									ApplyRotation(Builder, x, y, z, a);
									if (cmd == "rotateall") {
										ApplyRotation(Object, x, y, z, a);
									}
								}
							} break;
						case "shear":
						case "shearall":
							{
								if (Arguments.Length > 7) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 7 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								double dx = 0.0, dy = 0.0, dz = 0.0;
								double sx = 0.0, sy = 0.0, sz = 0.0;
								double r = 0.0;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[0], out dx)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument dX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									dx = 0.0;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out dy)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument dY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									dy = 0.0;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[2], out dz)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument dZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									dz = 0.0;
								}
								if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[3], out sx)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument sX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									sx = 0.0;
								}
								if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[4], out sy)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument sY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									sy = 0.0;
								}
								if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[5], out sz)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument sZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									sz = 0.0;
								}
								if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[6], out r)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Ratio in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r = 0.0;
								}
								World.Normalize(ref dx, ref dy, ref dz);
								World.Normalize(ref sx, ref sy, ref sz);
								ApplyShear(Builder, dx, dy, dz, sx, sy, sz, r);
								if (cmd == "shearall") {
									ApplyShear(Object, dx, dy, dz, sx, sy, sz, r);
								}
							} break;
						case "generatenormals":
						case "[texture]":
							if (cmd == "generatenormals" & IsB3D) {
								Debug.AddMessage(Debug.MessageType.Warning, false, "GenerateNormals is not a supported command - did you mean [Texture]? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
							} else if (cmd == "[texture]" & !IsB3D) {
								Debug.AddMessage(Debug.MessageType.Warning, false, "[Texture] is not a supported command - did you mean GenerateNormals? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
							}
							// TODO do something?
							break;
						case "setcolor":
						case "color":
							{
								if (cmd == "setcolor" & IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "SetColor is not a supported command - did you mean Color? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else if (cmd == "color" & !IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "Color is not a supported command - did you mean SetColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								if (Arguments.Length > 4) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								int r = 0, g = 0, b = 0, a = 255;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out r)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r = 0;
								} else if (r < 0 | r > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r = r < 0 ? 0 : 255;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseIntVb6(Arguments[1], out g)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									g = 0;
								} else if (g < 0 | g > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									g = g < 0 ? 0 : 255;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseIntVb6(Arguments[2], out b)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									b = 0;
								} else if (b < 0 | b > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									b = b < 0 ? 0 : 255;
								}
								if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Conversions.TryParseIntVb6(Arguments[3], out a)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Alpha in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									a = 255;
								} else if (a < 0 | a > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Alpha is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									a = a < 0 ? 0 : 255;
								}
								int m = Builder.Materials.Length;
								Array.Resize<Material>(ref Builder.Materials, m << 1);
								for (int j = m; j < Builder.Materials.Length; j++) {
									Builder.Materials[j] = new Material(Builder.Materials[j - m]);
									Builder.Materials[j].Color = new Color32((byte)r, (byte)g, (byte)b, (byte)a);
									Builder.Materials[j].BlendMode = Builder.Materials[0].BlendMode;
									Builder.Materials[j].GlowAttenuationData = Builder.Materials[0].GlowAttenuationData;
									Builder.Materials[j].DaytimeTexture = Builder.Materials[0].DaytimeTexture;
									Builder.Materials[j].NighttimeTexture = Builder.Materials[0].NighttimeTexture;
									Builder.Materials[j].TransparentColor = Builder.Materials[0].TransparentColor;
									Builder.Materials[j].TransparentColorUsed = Builder.Materials[0].TransparentColorUsed;
								}
								for (int j = 0; j < Builder.Faces.Length; j++) {
									Builder.Faces[j].Material += (ushort)m;
								}
							} break;
						case "setemissivecolor":
						case "emissivecolor":
							{
								if (cmd == "setemissivecolor" & IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "SetEmissiveColor is not a supported command - did you mean EmissiveColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else if (cmd == "emissivecolor" & !IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "EmissiveColor is not a supported command - did you mean SetEmissiveColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								if (Arguments.Length > 3) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								int r = 0, g = 0, b = 0;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out r)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r = 0;
								} else if (r < 0 | r > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r = r < 0 ? 0 : 255;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseIntVb6(Arguments[1], out g)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									g = 0;
								} else if (g < 0 | g > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									g = g < 0 ? 0 : 255;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseIntVb6(Arguments[2], out b)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									b = 0;
								} else if (b < 0 | b > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									b = b < 0 ? 0 : 255;
								}
								int m = Builder.Materials.Length;
								Array.Resize<Material>(ref Builder.Materials, m << 1);
								for (int j = m; j < Builder.Materials.Length; j++) {
									Builder.Materials[j] = new Material(Builder.Materials[j - m]);
									Builder.Materials[j].EmissiveColor = new Color24((byte)r, (byte)g, (byte)b);
									Builder.Materials[j].EmissiveColorUsed = true;
									Builder.Materials[j].BlendMode = Builder.Materials[0].BlendMode;
									Builder.Materials[j].GlowAttenuationData = Builder.Materials[0].GlowAttenuationData;
									Builder.Materials[j].DaytimeTexture = Builder.Materials[0].DaytimeTexture;
									Builder.Materials[j].NighttimeTexture = Builder.Materials[0].NighttimeTexture;
									Builder.Materials[j].TransparentColor = Builder.Materials[0].TransparentColor;
									Builder.Materials[j].TransparentColorUsed = Builder.Materials[0].TransparentColorUsed;
								}
								for (int j = 0; j < Builder.Faces.Length; j++) {
									Builder.Faces[j].Material += (ushort)m;
								}
							} break;
						case "setdecaltransparentcolor":
						case "transparent":
							{
								if (cmd == "setdecaltransparentcolor" & IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "SetDecalTransparentColor is not a supported command - did you mean Transparent? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else if (cmd == "transparent" & !IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "Transparent is not a supported command - did you mean SetDecalTransparentColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								if (Arguments.Length > 3) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								int r = 0, g = 0, b = 0;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out r)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r = 0;
								} else if (r < 0 | r > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									r = r < 0 ? 0 : 255;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseIntVb6(Arguments[1], out g)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									g = 0;
								} else if (g < 0 | g > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									g = g < 0 ? 0 : 255;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseIntVb6(Arguments[2], out b)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									b = 0;
								} else if (b < 0 | b > 255) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									b = b < 0 ? 0 : 255;
								}
								for (int j = 0; j < Builder.Materials.Length; j++) {
									Builder.Materials[j].TransparentColor = new Color24((byte)r, (byte)g, (byte)b);
									Builder.Materials[j].TransparentColorUsed = true;
								}
							} break;
						case "setblendmode":
						case "blendmode":
							{
								if (cmd == "setblendmode" & IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "SetBlendMode is not a supported command - did you mean BlendMode? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else if (cmd == "blendmode" & !IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "BlendMode is not a supported command - did you mean SetBlendMode? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								if (Arguments.Length > 3) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								World.MeshMaterialBlendMode blendmode = World.MeshMaterialBlendMode.Normal;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0) {
									switch (Arguments[0].ToLowerInvariant()) {
										case "normal":
											blendmode = World.MeshMaterialBlendMode.Normal;
											break;
										case "additive":
											blendmode = World.MeshMaterialBlendMode.Additive;
											break;
										default:
											Debug.AddMessage(Debug.MessageType.Error, false, "The given BlendMode is not supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											blendmode = World.MeshMaterialBlendMode.Normal;
											break;
									}
								}
								double glowhalfdistance = 0.0;
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseDoubleVb6(Arguments[1], out glowhalfdistance)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument GlowHalfDistance in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									glowhalfdistance = 0;
								}
								World.GlowAttenuationMode glowmode = World.GlowAttenuationMode.DivisionExponent4;
								if (Arguments.Length >= 3 && Arguments[2].Length > 0) {
									switch (Arguments[2].ToLowerInvariant()) {
											case "divideexponent2": glowmode = World.GlowAttenuationMode.DivisionExponent2; break;
											case "divideexponent4": glowmode = World.GlowAttenuationMode.DivisionExponent4; break;
										default:
											Debug.AddMessage(Debug.MessageType.Error, false, "The given GlowAttenuationMode is not supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											break;
									}
								}
								for (int j = 0; j < Builder.Materials.Length; j++) {
									Builder.Materials[j].BlendMode = blendmode;
									Builder.Materials[j].GlowAttenuationData = World.GetGlowAttenuationData(glowhalfdistance, glowmode);
								}
							} break;
						case "loadtexture":
						case "load":
							{
								if (cmd == "loadtexture" & IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "LoadTexture is not a supported command - did you mean Load? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else if (cmd == "load" & !IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "Load is not a supported command - did you mean LoadTexture? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								if (Arguments.Length > 2) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 2 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								string tday = null, tnight = null;
								if (Arguments.Length >= 1 && Arguments[0].Length != 0) {
									if (OpenBveApi.Path.ContainsInvalidPathChars(Arguments[0])) {
										Debug.AddMessage(Debug.MessageType.Error, false, "DaytimeTexture contains illegal characters in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									} else {
										tday = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]);
										if (!System.IO.File.Exists(tday)) {
											Debug.AddMessage(Debug.MessageType.Error, true, "DaytimeTexture " + tday + " could not be found in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											tday = null;
										}
									}
								}
								if (Arguments.Length >= 2 && Arguments[1].Length != 0) {
									if (Arguments[0].Length == 0) {
										Debug.AddMessage(Debug.MessageType.Error, true, "DaytimeTexture is required to be specified in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									} else {
										if (OpenBveApi.Path.ContainsInvalidPathChars(Arguments[1])) {
											Debug.AddMessage(Debug.MessageType.Error, false, "NighttimeTexture contains illegal characters in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
										} else {
											tnight = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[1]);
											if (!System.IO.File.Exists(tnight)) {
												Debug.AddMessage(Debug.MessageType.Error, true, "The NighttimeTexture " + tnight + " could not be found in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
												tnight = null;
											}
										}
									}
								}
								for (int j = 0; j < Builder.Materials.Length; j++) {
									Builder.Materials[j].DaytimeTexture = tday;
									Builder.Materials[j].NighttimeTexture = tnight;
								}
							} break;
						case "settexturecoordinates":
						case "coordinates":
							{
								if (cmd == "settexturecoordinates" & IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "SetTextureCoordinates is not a supported command - did you mean Coordinates? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								} else if (cmd == "coordinates" & !IsB3D) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "Coordinates is not a supported command - did you mean SetTextureCoordinates? - at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								if (Arguments.Length > 3) {
									Debug.AddMessage(Debug.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
								int j = 0; float x = 0.0f, y = 0.0f;
								if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Conversions.TryParseIntVb6(Arguments[0], out j)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument VertexIndex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									j = 0;
								}
								if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Conversions.TryParseFloatVb6(Arguments[1], out x)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									x = 0.0f;
								}
								if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Conversions.TryParseFloatVb6(Arguments[2], out y)) {
									Debug.AddMessage(Debug.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
									y = 0.0f;
								}
								if (j >= 0 & j < Builder.Vertices.Length) {
									Builder.Vertices[j].TextureCoordinates = new Vector2f(x, y);
								} else {
									Debug.AddMessage(Debug.MessageType.Error, false, "VertexIndex references a non-existing vertex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
								}
							} break;
						default:
							if (Command.Length != 0) {
								Debug.AddMessage(Debug.MessageType.Error, false, "The command " + Command + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
							}
							break;
					}
				}
			}
			// finalize object
			ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY);
			World.CreateNormals(ref Object.Mesh);
			return Object;
		}
Example #3
0
		/// <summary>Translates the vector by a specified offset.</summary>
		/// <param name="offset">The offset.</param>
		public void Translate(Vector3f offset) {
			this.X += offset.X;
			this.Y += offset.Y;
			this.Z += offset.Z;
		}
Example #4
0
		internal static void RotateUpDown(ref Vector3f Vector, double dx, double dy, double cosa, double sina) {
			double x = (double)Vector.X, y = (double)Vector.Y, z = (double)Vector.Z;
			double u = dy * x - dx * z;
			double v = dx * x + dy * z;
			Vector.X = (float)(dy * u + dx * v * cosa - dx * y * sina);
			Vector.Y = (float)(y * cosa + v * sina);
			Vector.Z = (float)(-dx * u + dy * v * cosa - dy * y * sina);
		}
Example #5
0
			internal MeshFaceVertex(int Index, Vector3f Normal) {
				this.Index = (ushort)Index;
				this.Normal = Normal;
			}
Example #6
0
		/// <summary>Gets the square of the euclidean norm of the specified vector.</summary>
		/// <param name="vector">The vector.</param>
		/// <returns>The square of the euclidean norm.</returns>
		public static float NormSquared(Vector3f vector) {
			return vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z;
		}
Example #7
0
		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.Disable(EnableCap.Lighting);
					LightingEnabled = false;
				}
				if (!TexturingEnabled) {
					GL.Enable(EnableCap.Texture2D);
					TexturingEnabled = true;
				}
				if (Alpha == 1.0f) {
					if (BlendEnabled) {
						GL.Disable(EnableCap.Blend);
						BlendEnabled = false;
					}
				} else if (!BlendEnabled) {
					GL.Enable(EnableCap.Blend);
					BlendEnabled = true;
				}
				GL.BindTexture(TextureTarget.Texture2D, Data.Texture.OpenGlTextures[(int)Textures.OpenGlTextureWrapMode.RepeatClamp].Name);
				GL.Color4(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;
				Vector3f[] bottom = new Vector3f[n];
				Vector3f[] top = new Vector3f[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 Vector3f(scale * x, scale * y0, scale * z);
					top[i] = new Vector3f(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.Begin(PrimitiveType.Quads);
					GL.TexCoord2(textureX, 0.005f);
					GL.Vertex3(top[i].X, top[i].Y, top[i].Z);
					GL.TexCoord2(textureX, 0.995f);
					GL.Vertex3(bottom[i].X, bottom[i].Y, bottom[i].Z);
					GL.TexCoord2(textureX + textureIncrement, 0.995f);
					GL.Vertex3(bottom[j].X, bottom[j].Y, bottom[j].Z);
					GL.TexCoord2(textureX + textureIncrement, 0.005f);
					GL.Vertex3(top[j].X, top[j].Y, top[j].Z);
					GL.End();
					// top cap
					GL.Begin(PrimitiveType.Triangles);
					GL.TexCoord2(textureX, 0.005f);
					GL.Vertex3(top[i].X, top[i].Y, top[i].Z);
					GL.TexCoord2(textureX + textureIncrement, 0.005f);
					GL.Vertex3(top[j].X, top[j].Y, top[j].Z);
					GL.TexCoord2(textureX + 0.5 * textureIncrement, 0.1f);
					GL.Vertex3(0.0f, top[i].Y, 0.0f);
					// bottom cap
					GL.TexCoord2(textureX + 0.5 * textureIncrement, 0.9f);
					GL.Vertex3(0.0f, bottom[i].Y, 0.0f);
					GL.TexCoord2(textureX + textureIncrement, 0.995f);
					GL.Vertex3(bottom[j].X, bottom[j].Y, bottom[j].Z);
					GL.TexCoord2(textureX, 0.995f);
					GL.Vertex3(bottom[i].X, bottom[i].Y, bottom[i].Z);
					GL.End();
					// finish
					textureX += textureIncrement;
				}
				GL.Disable(EnableCap.Texture2D);
				TexturingEnabled = false;
				if (!BlendEnabled) {
					GL.Enable(EnableCap.Blend);
					BlendEnabled = true;
				}
			}
		}
Example #8
0
		/// <summary>Gives the cross product of two vectors.</summary>
		/// <param name="a">The first vector.</param>
		/// <param name="b">The second vector.</param>
		/// <returns>The cross product of the two vectors.</returns>
		public static Vector3f Cross(Vector3f a, Vector3f b) {
			return new Vector3f(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
		}
Example #9
0
		/// <summary>Normalizes a vector.</summary>
		/// <param name="vector">The vector.</param>
		/// <returns>The normalized vector.</returns>
		/// <exception cref="System.DivideByZeroException">Raised when the vector is a null vector.</exception>
		public static Vector3f Normalize(Vector3f vector) {
			float norm = vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z;
			if (norm == 0.0f) {
				throw new DivideByZeroException();
			} else {
				float factor = 1.0f / (float)System.Math.Sqrt(norm);
				return new Vector3f(vector.X * factor, vector.Y * factor, vector.Z * factor);
			}
		}
Example #10
0
		/// <summary>Rotates the vector from the default orientation into a specified orientation.</summary>
		/// <param name="orientation">The orientation.</param>
		/// <remarks>The default orientation is X = {1, 0, 0), Y = {0, 1, 0} and Z = {0, 0, 1}.</remarks>
		public void Rotate(Orientation3f orientation) {
			float x = orientation.X.X * this.X + orientation.Y.X * this.Y + orientation.Z.X * this.Z;
			float y = orientation.X.Y * this.X + orientation.Y.Y * this.Y + orientation.Z.Y * this.Z;
			float z = orientation.X.Z * this.X + orientation.Y.Z * this.Y + orientation.Z.Z * this.Z;
			this = new Vector3f(x, y, z);
		}
Example #11
0
		// --- static functions ---

		/// <summary>Gives the dot product of two vectors.</summary>
		/// <param name="a">The first vector.</param>
		/// <param name="b">The second vector.</param>
		/// <returns>The dot product of the two vectors.</returns>
		public static float Dot(Vector3f a, Vector3f b) {
			return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
		}
Example #12
0
		/// <summary>Rotates the vector on the plane perpendicular to a specified direction by a specified angle.</summary>
		/// <param name="direction">The direction perpendicular to the plane on which to rotate.</param>
		/// <param name="cosineOfAngle">The cosine of the angle.</param>
		/// <param name="sineOfAngle">The sine of the angle.</param>
		public void Rotate(Vector3f direction, float cosineOfAngle, float sineOfAngle) {
			float cosineComplement = 1.0f - cosineOfAngle;
			float x = (cosineOfAngle + cosineComplement * direction.X * direction.X) * this.X + (cosineComplement * direction.X * direction.Y - sineOfAngle * direction.Z) * this.Y + (cosineComplement * direction.X * direction.Z + sineOfAngle * direction.Y) * this.Z;
			float y = (cosineOfAngle + cosineComplement * direction.Y * direction.Y) * this.Y + (cosineComplement * direction.X * direction.Y + sineOfAngle * direction.Z) * this.X + (cosineComplement * direction.Y * direction.Z - sineOfAngle * direction.X) * this.Z;
			float z = (cosineOfAngle + cosineComplement * direction.Z * direction.Z) * this.Z + (cosineComplement * direction.X * direction.Z - sineOfAngle * direction.Y) * this.X + (cosineComplement * direction.Y * direction.Z + sineOfAngle * direction.X) * this.Y;
			this = new Vector3f(x, y, z);
		}
Example #13
0
		/// <summary>Scales the vector by a specified factor.</summary>
		/// <param name="factor">The factor.</param>
		public void Scale(Vector3f factor) {
			this.X *= factor.X;
			this.Y *= factor.Y;
			this.Z *= factor.Z;
		}
Example #14
0
		/// <summary>Translates the vector by a specified offset that is measured in a specified orientation.</summary>
		/// <param name="orientation">The orientation.</param>
		/// <param name="offset">The offset measured in the specified orientation.</param>
		public void Translate(Orientation3f orientation, Vector3f offset) {
			this.X += orientation.X.X * offset.X + orientation.Y.X * offset.Y + orientation.Z.X * offset.Z;
			this.Y += orientation.X.Y * offset.X + orientation.Y.Y * offset.Y + orientation.Z.Y * offset.Z;
			this.Z += orientation.X.Z * offset.X + orientation.Y.Z * offset.Y + orientation.Z.Z * offset.Z;
		}
Example #15
0
		/// <summary>Checks whether a vector is a null vector.</summary>
		/// <returns>A boolean indicating whether the vector is a null vector.</returns>
		public static bool IsNullVector(Vector3f vector) {
			return vector.X == 0.0f & vector.Y == 0.0f & vector.Z == 0.0f;
		}
Example #16
0
		/// <summary>Translates a vector by a specified offset.</summary>
		/// <param name="vector">The vector.</param>
		/// <param name="offset">The offset.</param>
		/// <returns>The translated vector.</returns>
		public static Vector3f Translate(Vector3f vector, Vector3f offset) {
			float x = vector.X + offset.X;
			float y = vector.Y + offset.Y;
			float z = vector.Z + offset.Z;
			return new Vector3f(x, y, z);
		}
Example #17
0
		/// <summary>Gets the euclidean norm of the specified vector.</summary>
		/// <param name="vector">The vector.</param>
		/// <returns>The euclidean norm.</returns>
		public static float Norm(Vector3f vector) {
			return (float)System.Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z);
		}
Example #18
0
		/// <summary>Translates a vector by a specified offset that is measured along a specified orientation.</summary>
		/// <param name="vector">The vector.</param>
		/// <param name="orientation">The orientation.</param>
		/// <param name="offset">The offset measured in the specified orientation.</param>
		public static Vector3f Translate(Vector3f vector, Orientation3f orientation, Vector3f offset) {
			float x = vector.X + orientation.X.X * offset.X + orientation.Y.X * offset.Y + orientation.Z.X * offset.Z;
			float y = vector.Y + orientation.X.Y * offset.X + orientation.Y.Y * offset.Y + orientation.Z.Y * offset.Z;
			float z = vector.Z + orientation.X.Z * offset.X + orientation.Y.Z * offset.Y + orientation.Z.Z * offset.Z;
			return new Vector3f(x, y, z);
		}
Example #19
0
		// 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 Vector3f(0.223606797749979f, 0.86602540378444f, -0.447213595499958f);
			OptionLightingResultingAmount = 1.0f;
			OptionClock = false;
			OptionBrakeSystems = false;
		}
Example #20
0
		/// <summary>Scales a vector by a specified factor.</summary>
		/// <param name="vector">The vector.</param>
		/// <param name="factor">The factor.</param>
		/// <returns>The scaled vector.</returns>
		public static Vector3f Scale(Vector3f vector, Vector3f factor) {
			float x = vector.X * factor.X;
			float y = vector.Y * factor.Y;
			float z = vector.Z * factor.Z;
			return new Vector3f(x, y, z);
		}
Example #21
0
		internal static void RotatePlane(ref Vector3f Vector, double cosa, double sina) {
			double u = (double)Vector.X * cosa - (double)Vector.Z * sina;
			double v = (double)Vector.X * sina + (double)Vector.Z * cosa;
			Vector.X = (float)u;
			Vector.Z = (float)v;
		}
Example #22
0
		/// <summary>Rotates a vector on the plane perpendicular to a specified direction by a specified angle.</summary>
		/// <param name="vector">The vector.</param>
		/// <param name="direction">The direction perpendicular to the plane on which to rotate.</param>
		/// <param name="cosineOfAngle">The cosine of the angle.</param>
		/// <param name="sineOfAngle">The sine of the angle.</param>
		/// <returns>The rotated vector.</returns>
		public static Vector3f Rotate(Vector3f vector, Vector3f direction, float cosineOfAngle, float sineOfAngle) {
			float cosineComplement = 1.0f - cosineOfAngle;
			float x = (cosineOfAngle + cosineComplement * direction.X * direction.X) * vector.X + (cosineComplement * direction.X * direction.Y - sineOfAngle * direction.Z) * vector.Y + (cosineComplement * direction.X * direction.Z + sineOfAngle * direction.Y) * vector.Z;
			float y = (cosineOfAngle + cosineComplement * direction.Y * direction.Y) * vector.Y + (cosineComplement * direction.X * direction.Y + sineOfAngle * direction.Z) * vector.X + (cosineComplement * direction.Y * direction.Z - sineOfAngle * direction.X) * vector.Z;
			float z = (cosineOfAngle + cosineComplement * direction.Z * direction.Z) * vector.Z + (cosineComplement * direction.X * direction.Z - sineOfAngle * direction.Y) * vector.X + (cosineComplement * direction.Y * direction.Z + sineOfAngle * direction.X) * vector.Y;
			return new Vector3f(x, y, z);
		}
Example #23
0
			internal MeshFaceVertex(int Index) {
				this.Index = (ushort)Index;
				this.Normal = new Vector3f(0.0f, 0.0f, 0.0f);
			}
Example #24
0
		/// <summary>Rotates a vector from the default orientation into a specified orientation.</summary>
		/// <param name="vector">The vector.</param>
		/// <param name="orientation">The orientation.</param>
		/// <returns>The rotated vector.</returns>
		/// <remarks>The default orientation is X = {1, 0, 0), Y = {0, 1, 0} and Z = {0, 0, 1}.</remarks>
		public static Vector3f Rotate(Vector3f vector, Orientation3f orientation) {
			float x = orientation.X.X * vector.X + orientation.Y.X * vector.Y + orientation.Z.X * vector.Z;
			float y = orientation.X.Y * vector.X + orientation.Y.Y * vector.Y + orientation.Z.Y * vector.Z;
			float z = orientation.X.Z * vector.X + orientation.Y.Z * vector.Y + orientation.Z.Z * vector.Z;
			return new Vector3f(x, y, z);
		}
Example #25
0
		// --- constructors ---

		/// <summary>Creates a new orientation in three-dimensional space.</summary>
		/// <param name="x">The vector pointing right.</param>
		/// <param name="y">The vector pointing up.</param>
		/// <param name="z">The vector pointing forward.</param>
		public Orientation3f(Vector3f x, Vector3f y, Vector3f z) {
			this.X = x;
			this.Y = y;
			this.Z = z;
		}
Example #26
0
		/// <summary>Creates a unit vector perpendicular to the plane described by three spatial coordinates, suitable for being a surface normal.</summary>
		/// <param name="a">The first spatial coordinate.</param>
		/// <param name="b">The second spatial coordinate.</param>
		/// <param name="c">The third spatial coordinate.</param>
		/// <param name="normal">On success, receives the vector perpendicular to the described plane. On failure, receives Vector3f.Up.</param>
		/// <returns>The success of the operation. This operation fails if the specified three vectors are colinear.</returns>
		public static bool CreateNormal(Vector3f a, Vector3f b, Vector3f c, out Vector3f normal) {
			normal = Vector3f.Cross(b - a, c - a);
			float norm = normal.X * normal.X + normal.Y * normal.Y + normal.Z * normal.Z;
			if (norm != 0.0f) {
				normal *= 1.0f / (float)System.Math.Sqrt(norm);
				return true;
			} else {
				normal = Vector3f.Up;
				return false;
			}
		}
		// 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);
			Vector3f[] Normals = new Vector3f[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 Vector3D(ux, g, uz);
				Builder.Vertices[v + 2 * i + 1].Coordinates = new Vector3D(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 Vector3f((float)nx, (float)ny, (float)nz);
				Normals[2 * i + 1] = new Vector3f((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));
					}
				}
			}
		}
Example #28
0
		// 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) {
					Debug.AddMessage(Debug.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) {
								Debug.AddMessage(Debug.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)) {
								Debug.AddMessage(Debug.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[])) {
								Debug.AddMessage(Debug.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)) {
								Debug.AddMessage(Debug.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[])) {
								Debug.AddMessage(Debug.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) {
								Debug.AddMessage(Debug.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) {
								Debug.AddMessage(Debug.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) {
								Debug.AddMessage(Debug.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) {
								Debug.AddMessage(Debug.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") {
									Debug.AddMessage(Debug.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) {
									Debug.AddMessage(Debug.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)) {
									Debug.AddMessage(Debug.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)) {
									Debug.AddMessage(Debug.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)) {
									Debug.AddMessage(Debug.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 Vector3D(x, y, z);
							}
							// collect faces
							int[][] Faces = new int[nFaces][];
							Vector3f[][] FaceNormals = new Vector3f[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") {
									Debug.AddMessage(Debug.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) {
									Debug.AddMessage(Debug.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)) {
									Debug.AddMessage(Debug.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[])) {
									Debug.AddMessage(Debug.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) {
									Debug.AddMessage(Debug.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) {
									Debug.AddMessage(Debug.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 Vector3f[nFaceVertexIndices];
								for (int k = 0; k < nFaceVertexIndices; k++) {
									if (faceVertexIndices[k] < 0 | faceVertexIndices[k] >= nVertices) {
										Debug.AddMessage(Debug.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 Vector3f(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) {
									Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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)) {
												Debug.AddMessage(Debug.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)) {
												Debug.AddMessage(Debug.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[])) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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) {
													Debug.AddMessage(Debug.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 Color32(255, 255, 255, 255);
												Materials[mn + k].specularColor = new Color24(0, 0, 0);
												Materials[mn + k].emissiveColor = new Color24(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) {
													Debug.AddMessage(Debug.MessageType.Error, false, "Unexpected inlined argument encountered in MeshMaterialList in Mesh in x object file " + FileName);
													return false;
												} else if (h.Name != "Material") {
													Debug.AddMessage(Debug.MessageType.Error, false, "Material template expected in MeshMaterialList in Mesh in x object file " + FileName);
													return false;
												} else {
													// material
													if (h.Data.Length < 4) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.MessageType.Error, false, "faceColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName);
														return false;
													} else if (!(h.Data[1] is double)) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.MessageType.Error, false, "specularColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName);
														return false;
													} else if (!(h.Data[3] is Structure)) {
														Debug.AddMessage(Debug.MessageType.Error, false, "emissiveColor is expected to be a Color32 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 != "Color32") {
														Debug.AddMessage(Debug.MessageType.Error, false, "faceColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName);
														return false;
													} else if (faceColor.Data.Length != 4) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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 Color32((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 != "Color24") {
														Debug.AddMessage(Debug.MessageType.Error, false, "specularColor is expected to be a Color24 in Material in MeshMaterialList in Mesh in x object file " + FileName);
														return false;
													} else if (specularColor.Data.Length != 3) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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 Color24((byte)Math.Round(255.0 * red), (byte)Math.Round(255.0 * green), (byte)Math.Round(255.0 * blue));
													// collect emissive color
													if (emissiveColor.Name != "Color24") {
														Debug.AddMessage(Debug.MessageType.Error, false, "emissiveColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName);
														return false;
													} else if (emissiveColor.Data.Length != 3) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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)) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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 Color24((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) {
															Debug.AddMessage(Debug.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) {
																		Debug.AddMessage(Debug.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)) {
																		Debug.AddMessage(Debug.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 (OpenBveApi.Path.ContainsInvalidPathChars(filename)) {
																		Debug.AddMessage(Debug.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 {
																			Debug.AddMessage(Debug.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
																Debug.AddMessage(Debug.MessageType.Warning, false, "Unsupported template " + e.Name + " encountered in MeshMaterialList in Mesh in x object file " + FileName);
																break;
														}
													}
													// finish
													MaterialIndex++;
												}
											} if (MaterialIndex != mn + nMaterials) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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)) {
												Debug.AddMessage(Debug.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[])) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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") {
													Debug.AddMessage(Debug.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) {
													Debug.AddMessage(Debug.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)) {
													Debug.AddMessage(Debug.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)) {
													Debug.AddMessage(Debug.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 Vector2f((float)u, (float)v);
											}
										} break;
									case "MeshNormals":
										{
											// meshnormals
											if (g.Data.Length != 4) {
												Debug.AddMessage(Debug.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)) {
												Debug.AddMessage(Debug.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[])) {
												Debug.AddMessage(Debug.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)) {
												Debug.AddMessage(Debug.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[])) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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) {
												Debug.AddMessage(Debug.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
											Vector3f[] Normals = new Vector3f[nNormals];
											for (int k = 0; k < nNormals; k++) {
												if (normals[k].Name != "Vector") {
													Debug.AddMessage(Debug.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) {
													Debug.AddMessage(Debug.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)) {
													Debug.AddMessage(Debug.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)) {
													Debug.AddMessage(Debug.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)) {
													Debug.AddMessage(Debug.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 Vector3f((float)x, (float)y, (float)z);
											}
											// collect faces
											for (int k = 0; k < nFaceNormals; k++) {
												if (faceNormals[k].Name != "MeshFace") {
													Debug.AddMessage(Debug.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) {
													Debug.AddMessage(Debug.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)) {
													Debug.AddMessage(Debug.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[])) {
													Debug.AddMessage(Debug.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) {
													Debug.AddMessage(Debug.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) {
													Debug.AddMessage(Debug.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) {
														Debug.AddMessage(Debug.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
										Debug.AddMessage(Debug.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 Color32(255, 255, 255, 255);
								Materials[0].emissiveColor = new Color24(0, 0, 0);
								Materials[0].specularColor = new Color24(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) {
									Textures.RegisterTexture(Materials[j].TextureFilename, new OpenBveApi.Textures.TextureParameters(null, Color24.Black), out Object.Mesh.Materials[mm + j].DaytimeTexture);
									transparent = true;
								} else {
									Object.Mesh.Materials[mm + j].DaytimeTexture = null;
									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 Color24(0, 0, 0);
								Object.Mesh.Materials[mm + j].EmissiveColor = Materials[j].emissiveColor;
								Object.Mesh.Materials[mm + j].NighttimeTexture = null;
								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
						Debug.AddMessage(Debug.MessageType.Warning, false, "Unsupported template " + f.Name + " encountered in x object file " + FileName);
						break;
				}
			}
			// return
			World.CreateNormals(ref Object.Mesh);
			return true;
		}