// try parse hex color internal static bool TryParseHexColor(string Expression, out World.ColorRGB Color) { if (Expression.StartsWith("#")) { string a = Expression.Substring(1).TrimStart(); int x; if (int.TryParse(a, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out x)) { int r = (x >> 16) & 0xFF; int g = (x >> 8) & 0xFF; int b = x & 0xFF; if (r >= 0 & r <= 255 & g >= 0 & g <= 255 & b >= 0 & b <= 255) { Color = new World.ColorRGB((byte)r, (byte)g, (byte)b); return(true); } else { Color = new World.ColorRGB(0, 0, 255); return(false); } } else { Color = new World.ColorRGB(0, 0, 255); return(false); } } else { Color = new World.ColorRGB(0, 0, 255); return(false); } }
internal Fog(float Start, float End, World.ColorRGB Color, double TrackPosition) { this.Start = Start; this.End = End; this.Color = Color; this.TrackPosition = TrackPosition; }
internal Material(Material Prototype) { this.Color = Prototype.Color; this.EmissiveColor = Prototype.EmissiveColor; this.EmissiveColorUsed = Prototype.EmissiveColorUsed; this.TransparentColor = Prototype.TransparentColor; this.TransparentColorUsed = Prototype.TransparentColorUsed; this.DaytimeTexture = Prototype.DaytimeTexture; this.NighttimeTexture = Prototype.NighttimeTexture; this.BlendMode = Prototype.BlendMode; this.GlowAttenuationData = Prototype.GlowAttenuationData; }
internal Material() { this.Color = new World.ColorRGBA(255, 255, 255, 255); this.EmissiveColor = new World.ColorRGB(0, 0, 0); this.EmissiveColorUsed = false; this.TransparentColor = new World.ColorRGB(0, 0, 0); this.TransparentColorUsed = false; this.DaytimeTexture = null; this.NighttimeTexture = null; this.BlendMode = World.MeshMaterialBlendMode.Normal; this.GlowAttenuationData = 0; }
internal Material() { this.Color = new World.ColorRGBA(255, 255, 255, 255); this.EmissiveColor = new World.ColorRGB(0, 0, 0); this.EmissiveColorUsed = false; this.TransparentColor = new World.ColorRGB(0, 0, 0); this.TransparentColorUsed = false; this.DaytimeTexture = null; this.NighttimeTexture = null; this.BlendMode = World.MeshMaterialBlendMode.Normal; this.GlowAttenuationData = 0; }
internal Material(Material Prototype) { this.Color = Prototype.Color; this.EmissiveColor = Prototype.EmissiveColor; this.EmissiveColorUsed = Prototype.EmissiveColorUsed; this.TransparentColor = Prototype.TransparentColor; this.TransparentColorUsed = Prototype.TransparentColorUsed; this.DaytimeTexture = Prototype.DaytimeTexture; this.NighttimeTexture = Prototype.NighttimeTexture; this.BlendMode = Prototype.BlendMode; this.GlowAttenuationData = Prototype.GlowAttenuationData; }
internal static int RegisterTexture(string FileName, World.ColorRGB TransparentColor, byte TransparentColorUsed, TextureLoadMode LoadMode, TextureWrapMode WrapModeX, TextureWrapMode WrapModeY, bool DontAllowUnload, int ClipLeft, int ClipTop, int ClipWidth, int ClipHeight) { int i = FindTexture(FileName, TransparentColor, TransparentColorUsed, LoadMode, WrapModeX, WrapModeY, ClipLeft, ClipTop, ClipWidth, ClipHeight); if (i >= 0) { return(i); } else { i = GetFreeTexture(); Textures[i] = new Texture(); Textures[i].Queried = false; Textures[i].Loaded = false; Textures[i].FileName = FileName; Textures[i].TransparentColor = TransparentColor; Textures[i].TransparentColorUsed = TransparentColorUsed; Textures[i].LoadMode = LoadMode; Textures[i].WrapModeX = WrapModeX; Textures[i].WrapModeY = WrapModeY; Textures[i].ClipLeft = ClipLeft; Textures[i].ClipTop = ClipTop; Textures[i].ClipWidth = ClipWidth; Textures[i].ClipHeight = ClipHeight; Textures[i].DontAllowUnload = DontAllowUnload; Textures[i].LoadImmediately = false; Textures[i].OpenGlTextureIndex = 0; bool alpha = false; switch (System.IO.Path.GetExtension(Textures[i].FileName).ToLowerInvariant()) { case ".gif": case ".png": alpha = true; Textures[i].LoadImmediately = true; break; } if (alpha) { Textures[i].Transparency = TextureTransparencyMode.Alpha; } else if (TransparentColorUsed != 0) { Textures[i].Transparency = TextureTransparencyMode.TransparentColor; } else { Textures[i].Transparency = TextureTransparencyMode.None; } Textures[i].IsRGBA = Textures[i].Transparency != TextureTransparencyMode.None | LoadMode != TextureLoadMode.Normal; return(i); } }
internal Material() { this.Color = new World.ColorRGBA(255, 255, 255, 255); this.EmissiveColor = new World.ColorRGB(0, 0, 0); this.EmissiveColorUsed = false; this.TransparentColor = new World.ColorRGB(0, 0, 0); this.TransparentColorUsed = false; this.DaytimeTexture = null; this.NighttimeTexture = null; this.BlendMode = World.MeshMaterialBlendMode.Normal; this.GlowAttenuationData = 0; this.TextColor = System.Drawing.Color.Black; this.BackgroundColor = System.Drawing.Color.White; this.TextPadding = new Vector2(0, 0); this.Font = "Arial"; }
internal static int RegisterTexture(Bitmap Bitmap, World.ColorRGB TransparentColor) { int i = GetFreeTexture(); int[] a = new int[1]; GL.GenTextures(1, a); Textures[i] = new Texture(); Textures[i].Queried = false; Textures[i].OpenGlTextureIndex = a[0]; Textures[i].Transparency = TextureTransparencyMode.TransparentColor; Textures[i].TransparentColor = TransparentColor; Textures[i].TransparentColorUsed = 1; Textures[i].FileName = null; Textures[i].Loaded = true; Textures[i].DontAllowUnload = true; LoadTextureRGBAforData(Bitmap, Textures[i].TransparentColor, Textures[i].TransparentColorUsed, i); LoadTextureRGBAforOpenGl(i); return(i); }
internal static int RegisterTexture(Bitmap Bitmap, World.ColorRGB TransparentColor) { int i = GetFreeTexture(); int[] a = new int[1]; GL.GenTextures(1, a); Textures[i] = new Texture { Queried = false, OpenGlTextureIndex = a[0], Transparency = TextureTransparencyMode.TransparentColor, TransparentColor = TransparentColor, TransparentColorUsed = 1, FileName = null, Loaded = true, Width = Bitmap.Width, Height = Bitmap.Height, DontAllowUnload = true }; LoadTextureRGBAforData(Bitmap, Textures[i].TransparentColor, Textures[i].TransparentColorUsed, i); LoadTextureRGBAforOpenGl(i); return(i); }
// find texture private static int FindTexture(string FileName, World.ColorRGB TransparentColor, byte TransparentColorUsed, TextureLoadMode LoadMode, TextureWrapMode WrapModeX, TextureWrapMode WrapModeY, int ClipLeft, int ClipTop, int ClipWidth, int ClipHeight) { for (int i = 1; i < Textures.Length; i++) { if (Textures[i] != null && Textures[i].FileName != null) { if (string.Compare(Textures[i].FileName, FileName, StringComparison.OrdinalIgnoreCase) == 0) { if (Textures[i].LoadMode == LoadMode & Textures[i].WrapModeX == WrapModeX & Textures[i].WrapModeY == WrapModeY) { if (Textures[i].ClipLeft == ClipLeft & Textures[i].ClipTop == ClipTop & Textures[i].ClipWidth == ClipWidth & Textures[i].ClipHeight == ClipHeight) { if (TransparentColorUsed == 0) { if (Textures[i].TransparentColorUsed == 0) { return(i); } } else { if (Textures[i].TransparentColorUsed != 0) { if (Textures[i].TransparentColor.R == TransparentColor.R & Textures[i].TransparentColor.G == TransparentColor.G & Textures[i].TransparentColor.B == TransparentColor.B) { return(i); } } } } } } } } return(-1); }
internal static int RegisterTexture(string FileName, World.ColorRGB TransparentColor, byte TransparentColorUsed, TextureWrapMode WrapModeX, TextureWrapMode WrapModeY, bool DontAllowUnload) { return(RegisterTexture(FileName, TransparentColor, TransparentColorUsed, TextureLoadMode.Normal, WrapModeX, WrapModeY, DontAllowUnload, 0, 0, 0, 0)); }
// try parse hex color internal static bool TryParseHexColor(string Expression, out World.ColorRGB Color) { if (Expression.StartsWith("#")) { string a = Expression.Substring(1).TrimStart(); int x; if (int.TryParse(a, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out x)) { int r = (x >> 16) & 0xFF; int g = (x >> 8) & 0xFF; int b = x & 0xFF; if (r >= 0 & r <= 255 & g >= 0 & g <= 255 & b >= 0 & b <= 255) { Color = new World.ColorRGB((byte)r, (byte)g, (byte)b); return true; } else { Color = new World.ColorRGB(0, 0, 255); return false; } } else { Color = new World.ColorRGB(0, 0, 255); return false; } } else { Color = new World.ColorRGB(0, 0, 255); return false; } }
// reset internal static void Reset() { LoadTexturesImmediately = LoadTextureImmediatelyMode.NotYet; ObjectList = new Object[256]; ObjectListCount = 0; OpaqueList = new ObjectFace[256]; OpaqueListCount = 0; TransparentColorList = new ObjectFace[256]; TransparentColorListDistance = new double[256]; TransparentColorListCount = 0; AlphaList = new ObjectFace[256]; AlphaListDistance = new double[256]; AlphaListCount = 0; OverlayList = new ObjectFace[256]; OverlayListDistance = new double[256]; OverlayListCount = 0; OptionLighting = true; OptionAmbientColor = new World.ColorRGB(160, 160, 160); OptionDiffuseColor = new World.ColorRGB(160, 160, 160); OptionLightPosition = new World.Vector3Df(0.215920077052065f, 0.875724044222352f, -0.431840154104129f); OptionLightingResultingAmount = 1.0f; GL.Disable(EnableCap.Fog); FogEnabled = false; }
// load texture rgba private static void LoadTextureRGBAforData(Bitmap Bitmap, World.ColorRGB TransparentColor, byte TransparentColorUsed, int TextureIndex) { try { // load bytes int Width, Height, Stride; byte[] Data; { if (Textures[TextureIndex].ClipWidth == 0) { Textures[TextureIndex].ClipWidth = Bitmap.Width; } if (Textures[TextureIndex].ClipHeight == 0) { Textures[TextureIndex].ClipHeight = Bitmap.Height; } Width = Textures[TextureIndex].ClipWidth; Height = Textures[TextureIndex].ClipHeight; Bitmap c = new Bitmap(Width, Height, GDIPixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(c); Rectangle dst = new Rectangle(0, 0, Width, Height); Rectangle src = new Rectangle(Textures[TextureIndex].ClipLeft, Textures[TextureIndex].ClipTop, Textures[TextureIndex].ClipWidth, Textures[TextureIndex].ClipHeight); g.DrawImage(Bitmap, dst, src, GraphicsUnit.Pixel); g.Dispose(); BitmapData d = c.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, c.PixelFormat); Stride = d.Stride; Data = new byte[Stride * Height]; System.Runtime.InteropServices.Marshal.Copy(d.Scan0, Data, 0, Stride * Height); c.UnlockBits(d); c.Dispose(); } // load mode if (Textures[TextureIndex].LoadMode == TextureLoadMode.Bve4SignalGlow) { // bve 4 signal glow int p = 0, pn = Stride - 4 * Width; byte tr, tg, tb; if (TransparentColorUsed != 0) { tr = TransparentColor.R; tg = TransparentColor.G; tb = TransparentColor.B; } else { tr = 0; tg = 0; tb = 0; } // invert lightness byte[] Temp = new byte[Stride * Height]; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { if (Data[p] == tb & Data[p + 1] == tg & Data[p + 2] == tr) { Temp[p] = 0; Temp[p + 1] = 0; Temp[p + 2] = 0; } else if (Data[p] != 255 | Data[p + 1] != 255 | Data[p + 2] != 255) { int b = Data[p], g = Data[p + 1], r = Data[p + 2]; InvertLightness(ref r, ref g, ref b); int l = r >= g & r >= b ? r : g >= b ? g : b; Temp[p] = (byte)(l * b / 255); Temp[p + 1] = (byte)(l * g / 255); Temp[p + 2] = (byte)(l * r / 255); } else { Temp[p] = Data[p]; Temp[p + 1] = Data[p + 1]; Temp[p + 2] = Data[p + 2]; } p += 4; } p += pn; } p = 0; // blur the image and multiply by lightness int s = 4; int n = Stride - (2 * s + 1 << 2); for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { int q = p - s * (Stride + 4); int r = 0, g = 0, b = 0, c = 0; for (int yr = y - s; yr <= y + s; yr++) { if (yr >= 0 & yr < Height) { for (int xr = x - s; xr <= x + s; xr++) { if (xr >= 0 & xr < Width) { b += (int)Temp[q]; g += (int)Temp[q + 1]; r += (int)Temp[q + 2]; c++; } q += 4; } q += n; } else { q += Stride; } } if (c == 0) { Data[p] = 0; Data[p + 1] = 0; Data[p + 2] = 0; Data[p + 3] = 255; } else { r /= c; g /= c; b /= c; int l = r >= g & r >= b ? r : g >= b ? g : b; Data[p] = (byte)(l * b / 255); Data[p + 1] = (byte)(l * g / 255); Data[p + 2] = (byte)(l * r / 255); Data[p + 3] = 255; } p += 4; } p += pn; } Textures[TextureIndex].Transparency = TextureTransparencyMode.None; Textures[TextureIndex].DontAllowUnload = true; } else if (TransparentColorUsed != 0) { // transparent color int p = 0, pn = Stride - 4 * Width; byte tr = TransparentColor.R; byte tg = TransparentColor.G; byte tb = TransparentColor.B; bool used = false; // check if alpha is actually used int y; for (y = 0; y < Height; y++) { int x; for (x = 0; x < Width; x++) { if (Data[p + 3] != 255) { break; } p += 4; } if (x < Width) { break; } p += pn; } if (y == Height) { Textures[TextureIndex].Transparency = TextureTransparencyMode.TransparentColor; } // duplicate color data from adjacent pixels p = 0; pn = Stride - 4 * Width; for (y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { if (Data[p] == tb & Data[p + 1] == tg & Data[p + 2] == tr) { used = true; if (x == 0) { int q = p; int v; for (v = y; v < Height; v++) { int u; for (u = v == y ? x + 1 : 0; u < Width; u++) { if (Data[q] != tb | Data[q + 1] != tg | Data[q + 2] != tr) { Data[p] = Data[q]; Data[p + 1] = Data[q + 1]; Data[p + 2] = Data[q + 2]; Data[p + 3] = 0; break; } q += 4; } if (u < Width) { break; } else { q += pn; } } if (v == Height) { if (y == 0) { Data[p] = 128; Data[p + 1] = 128; Data[p + 2] = 128; Data[p + 3] = 0; } else { Data[p] = Data[p - Stride]; Data[p + 1] = Data[p - Stride + 1]; Data[p + 2] = Data[p - Stride + 2]; Data[p + 3] = 0; } } } else { Data[p] = Data[p - 4]; Data[p + 1] = Data[p - 3]; Data[p + 2] = Data[p - 2]; Data[p + 3] = 0; } } p += 4; } p += pn; } // transparent color is not actually used if (!used & Textures[TextureIndex].Transparency == TextureTransparencyMode.TransparentColor) { Textures[TextureIndex].Transparency = TextureTransparencyMode.None; } } else if (Textures[TextureIndex].Transparency == TextureTransparencyMode.Alpha) { // check if alpha is actually used int p = 0, pn = Stride - 4 * Width; int y; for (y = 0; y < Height; y++) { int x; for (x = 0; x < Width; x++) { if (Data[p + 3] != 255) { break; } p += 4; } if (x < Width) { break; } p += pn; } if (y == Height) { Textures[TextureIndex].Transparency = TextureTransparencyMode.None; } } // non-power of two int TargetWidth = Interface.RoundToPowerOfTwo(Width); int TargetHeight = Interface.RoundToPowerOfTwo(Height); if (TargetWidth != Width | TargetHeight != Height) { Bitmap b = new Bitmap(Width, Height, GDIPixelFormat.Format32bppArgb); BitmapData d = b.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, b.PixelFormat); System.Runtime.InteropServices.Marshal.Copy(Data, 0, d.Scan0, d.Stride * d.Height); b.UnlockBits(d); Bitmap c = new Bitmap(TargetWidth, TargetHeight, GDIPixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(c); g.DrawImage(b, 0, 0, TargetWidth, TargetHeight); g.Dispose(); b.Dispose(); d = c.LockBits(new Rectangle(0, 0, TargetWidth, TargetHeight), ImageLockMode.ReadOnly, c.PixelFormat); Stride = d.Stride; Data = new byte[Stride * TargetHeight]; System.Runtime.InteropServices.Marshal.Copy(d.Scan0, Data, 0, Stride * TargetHeight); c.UnlockBits(d); c.Dispose(); } Textures[TextureIndex].Width = TargetWidth; Textures[TextureIndex].Height = TargetHeight; Textures[TextureIndex].Data = Data; } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Internal error in TextureManager.cs::LoadTextureRGBAForData: " + ex.Message); throw; } }
internal Fog(float Start, float End, World.ColorRGB Color, double TrackPosition) { this.Start = Start; this.End = End; this.Color = Color; this.TrackPosition = TrackPosition; }
// read object /// <summary>Loads a Loksim3D object from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <param name="ForceTextureRepeatX">Whether to force TextureWrapMode.Repeat for the X-axis</param> /// /// <param name="ForceTextureRepeatY">Whether to force TextureWrapMode.Repeat for the Y-axis</param> /// <returns>The object loaded.</returns> internal static ObjectManager.StaticObject ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY, double RotationX, double RotationY, double RotationZ) { XmlDocument currentXML = new XmlDocument(); //May need to be changed to use de-DE System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; //Initialise the object ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Object.Mesh.Faces = new World.MeshFace[] { }; Object.Mesh.Materials = new World.MeshMaterial[] { }; Object.Mesh.Vertices = new World.Vertex[] { }; MeshBuilder Builder = new MeshBuilder(); World.Vector3Df[] Normals = new World.Vector3Df[4]; bool PropertiesFound = false; World.Vertex[] tempVertices = new World.Vertex[0]; World.Vector3Df[] tempNormals = new World.Vector3Df[0]; World.ColorRGB transparentColor = new World.ColorRGB(); string tday = null; string tnight = null; bool TransparencyUsed = false; bool Face2 = false; int TextureWidth = 0; int TextureHeight = 0; if (File.Exists(FileName)) { currentXML.Load(FileName); } else { return(null); } //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/OBJECT"); //Check this file actually contains Loksim3D object nodes if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.HasChildNodes) { foreach (XmlNode node in outerNode.ChildNodes) { //I think there should only be one properties node?? //Need better format documentation if (node.Name == "Props" && PropertiesFound == false) { if (node.Attributes != null) { //Our node has child nodes, therefore this properties node should be valid //Needs better validation PropertiesFound = true; foreach (XmlAttribute attribute in node.Attributes) { switch (attribute.Name) { //Sets the texture //Loksim3D objects only support daytime textures case "Texture": tday = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), attribute.Value); if (File.Exists(tday)) { try { using (Bitmap TextureInformation = new Bitmap(tday)) { TextureWidth = TextureInformation.Width; TextureHeight = TextureInformation.Height; Color color = TextureInformation.GetPixel(1, 1); transparentColor = new World.ColorRGB((byte)color.R, (byte)color.G, (byte)color.B); } } catch { Interface.AddMessage(Interface.MessageType.Error, true, "An error occured loading daytime texture " + tday + " in file " + FileName); tday = null; } } else { Interface.AddMessage(Interface.MessageType.Error, true, "DaytimeTexture " + tday + " could not be found in file " + FileName); } break; //Defines whether the texture uses transparency //May be omitted case "Transparent": if (attribute.Value == "TRUE") { TransparencyUsed = true; } else { TransparencyUsed = false; } break; //Sets the transparency type case "TransparentTyp": switch (attribute.Value) { case "0": //Transparency is disabled TransparencyUsed = false; break; case "1": //Transparency is solid black TransparencyUsed = true; transparentColor = new World.ColorRGB(0, 0, 0); break; case "2": //Transparency is the color at Pixel 1,1 TransparencyUsed = true; break; case "3": //This is used when transparency is used with an alpha bitmap //Not currently supported TransparencyUsed = false; break; } break; //Sets whether the rears of the faces are to be drawn case "Drawrueckseiten": Face2 = true; break; /* * MISSING PROPERTIES: * AutoRotate - Rotate with tracks?? LS3D presumably uses a 3D world system. * Beleuchtet- Translates as illuminated. Presume something to do with lighting? - What emissive color? * FileAuthor * FileInfo * FilePicture */ } } } } //The point command is eqivilant to a vertex else if (node.Name == "Point" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { //Vertex double vx = 0.0, vy = 0.0, vz = 0.0; //Normals double nx = 0.0, ny = 0.0, nz = 0.0; foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { //Sets the vertex normals case "Normal": string[] NormalPoints = attribute.Value.Split(';'); double.TryParse(NormalPoints[0], out nx); double.TryParse(NormalPoints[1], out ny); double.TryParse(NormalPoints[2], out nz); break; //Sets the vertex 3D co-ordinates case "Vekt": string[] VertexPoints = attribute.Value.Split(';'); double.TryParse(VertexPoints[0], out vx); double.TryParse(VertexPoints[1], out vy); double.TryParse(VertexPoints[2], out vz); break; } } World.Normalize(ref nx, ref ny, ref nz); { //Resize temp arrays Array.Resize <World.Vertex>(ref tempVertices, tempVertices.Length + 1); Array.Resize <World.Vector3Df>(ref tempNormals, tempNormals.Length + 1); //Add vertex and normals to temp array tempVertices[tempVertices.Length - 1].Coordinates = new World.Vector3D(vx, vy, vz); tempNormals[tempNormals.Length - 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); } Array.Resize <World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); while (Builder.Vertices.Length >= Normals.Length) { Array.Resize <World.Vector3Df>(ref Normals, Normals.Length << 1); } Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new World.Vector3D(vx, vy, vz); Normals[Builder.Vertices.Length - 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); } } } //The Flaeche command creates a face else if (node.Name == "Flaeche" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { //Defines the verticies in this face //**NOTE**: A vertex may appear in multiple faces with different texture co-ordinates if (childNode.Attributes["Points"] != null) { string[] Verticies = childNode.Attributes["Points"].Value.Split(';'); int f = Builder.Faces.Length; //Add 1 to the length of the face array Array.Resize <World.MeshFace>(ref Builder.Faces, f + 1); Builder.Faces[f] = new World.MeshFace(); //Create the vertex array for the face Builder.Faces[f].Vertices = new World.MeshFaceVertex[Verticies.Length]; while (Builder.Vertices.Length > Normals.Length) { Array.Resize <World.Vector3Df>(ref Normals, Normals.Length << 1); } //Run through the vertices list and grab from the temp array for (int j = 0; j < Verticies.Length; j++) { //This is the position of the vertex in the temp array int currentVertex; int.TryParse(Verticies[j], out currentVertex); //Add one to the actual vertex array Array.Resize <World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); //Set coordinates Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = tempVertices[currentVertex].Coordinates; //Set the vertex index Builder.Faces[f].Vertices[j].Index = (ushort)(Builder.Vertices.Length - 1); //Set the normals Builder.Faces[f].Vertices[j].Normal = tempNormals[currentVertex]; //Now deal with the texture //Texture mapping points are in pixels X,Y and are relative to the face in question rather than the vertex if (childNode.Attributes["Texture"] != null) { string[] TextureCoords = childNode.Attributes["Texture"].Value.Split(';'); World.Vector2Df currentCoords; float OpenBVEWidth; float OpenBVEHeight; string[] splitCoords = TextureCoords[j].Split(','); float.TryParse(splitCoords[0], out OpenBVEWidth); float.TryParse(splitCoords[1], out OpenBVEHeight); if (TextureWidth != 0 && TextureHeight != 0) { currentCoords.X = (OpenBVEWidth / TextureWidth); currentCoords.Y = (OpenBVEHeight / TextureHeight); } else { currentCoords.X = 0; currentCoords.Y = 0; } Builder.Vertices[Builder.Vertices.Length - 1].TextureCoordinates = currentCoords; } if (Face2) { Builder.Faces[f].Flags = (byte)World.MeshFace.Face2Mask; } } } } } } } } } } //Apply rotation /* * NOTES: * No rotation order is specified * The rotation string in a .l3dgrp file is ordered Y, X, Z ??? Can't find a good reason for this ??? * Rotations must still be performed in X,Y,Z order to produce correct results */ if (RotationX != 0.0) { //This is actually the Y-Axis rotation //Convert to radians RotationX *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 0, 1, 0, RotationX); } if (RotationY != 0.0) { //This is actually the X-Axis rotation //Convert to radians RotationY *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 1, 0, 0, RotationY); } if (RotationZ != 0.0) { //Convert to radians RotationZ *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 0, 0, 1, RotationZ); } //These files appear to only have one texture defined //Therefore import later- May have to change if (File.Exists(tday)) { for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].DaytimeTexture = tday; Builder.Materials[j].NighttimeTexture = tnight; } } if (TransparencyUsed == true) { for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].TransparentColor = transparentColor; Builder.Materials[j].TransparentColorUsed = true; } } } ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); World.CreateNormals(ref Object.Mesh); return(Object); }
// read object /// <summary>Loads a Loksim3D object from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <param name="ForceTextureRepeatX">Whether to force TextureWrapMode.Repeat for the X-axis</param> /// /// <param name="ForceTextureRepeatY">Whether to force TextureWrapMode.Repeat for the Y-axis</param> /// <returns>The object loaded.</returns> internal static ObjectManager.StaticObject ReadObject(string FileName, System.Text.Encoding Encoding,ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY, double RotationX, double RotationY, double RotationZ) { XmlDocument currentXML = new XmlDocument(); //May need to be changed to use de-DE System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; //Initialise the object ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Object.Mesh.Faces = new World.MeshFace[] { }; Object.Mesh.Materials = new World.MeshMaterial[] { }; Object.Mesh.Vertices = new World.Vertex[] { }; MeshBuilder Builder = new MeshBuilder(); World.Vector3Df[] Normals = new World.Vector3Df[4]; bool PropertiesFound = false; World.Vertex[] tempVertices = new World.Vertex[0]; World.Vector3Df[] tempNormals = new World.Vector3Df[0]; World.ColorRGB transparentColor = new World.ColorRGB(); string tday = null; string tnight = null; bool TransparencyUsed = false; bool Face2 = false; int TextureWidth = 0; int TextureHeight = 0; if (File.Exists(FileName)) { currentXML.Load(FileName); } else { return null; } //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/OBJECT"); //Check this file actually contains Loksim3D object nodes if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.HasChildNodes) { foreach (XmlNode node in outerNode.ChildNodes) { //I think there should only be one properties node?? //Need better format documentation if (node.Name == "Props" && PropertiesFound == false) { if (node.Attributes != null) { //Our node has child nodes, therefore this properties node should be valid //Needs better validation PropertiesFound = true; foreach (XmlAttribute attribute in node.Attributes) { switch (attribute.Name) { //Sets the texture //Loksim3D objects only support daytime textures case "Texture": tday = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), attribute.Value); if (File.Exists(tday)) { try { using (Bitmap TextureInformation = new Bitmap(tday)) { TextureWidth = TextureInformation.Width; TextureHeight = TextureInformation.Height; Color color = TextureInformation.GetPixel(1, 1); transparentColor = new World.ColorRGB((byte) color.R, (byte) color.G, (byte) color.B); } } catch { Interface.AddMessage(Interface.MessageType.Error, true, "An error occured loading daytime texture " + tday + " in file " + FileName); tday = null; } } else { Interface.AddMessage(Interface.MessageType.Error, true, "DaytimeTexture " + tday + " could not be found in file " + FileName); } break; //Defines whether the texture uses transparency //May be omitted case "Transparent": if (attribute.Value == "TRUE") { TransparencyUsed = true; } else { TransparencyUsed = false; } break; //Sets the transparency type case "TransparentTyp": switch (attribute.Value) { case "0": //Transparency is disabled TransparencyUsed = false; break; case "1": //Transparency is solid black TransparencyUsed = true; transparentColor = new World.ColorRGB(0,0,0); break; case "2": //Transparency is the color at Pixel 1,1 TransparencyUsed = true; break; case "3": //This is used when transparency is used with an alpha bitmap //Not currently supported TransparencyUsed = false; break; } break; //Sets whether the rears of the faces are to be drawn case "Drawrueckseiten": Face2 = true; break; /* * MISSING PROPERTIES: * AutoRotate - Rotate with tracks?? LS3D presumably uses a 3D world system. * Beleuchtet- Translates as illuminated. Presume something to do with lighting? - What emissive color? * FileAuthor * FileInfo * FilePicture */ } } } } //The point command is eqivilant to a vertex else if (node.Name == "Point" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { //Vertex double vx = 0.0, vy = 0.0, vz = 0.0; //Normals double nx = 0.0, ny = 0.0, nz = 0.0; foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { //Sets the vertex normals case "Normal": string[] NormalPoints = attribute.Value.Split(';'); double.TryParse(NormalPoints[0], out nx); double.TryParse(NormalPoints[1], out ny); double.TryParse(NormalPoints[2], out nz); break; //Sets the vertex 3D co-ordinates case "Vekt": string[] VertexPoints = attribute.Value.Split(';'); double.TryParse(VertexPoints[0], out vx); double.TryParse(VertexPoints[1], out vy); double.TryParse(VertexPoints[2], out vz); break; } } World.Normalize(ref nx, ref ny, ref nz); { //Resize temp arrays Array.Resize<World.Vertex>(ref tempVertices, tempVertices.Length + 1); Array.Resize<World.Vector3Df>(ref tempNormals, tempNormals.Length + 1); //Add vertex and normals to temp array tempVertices[tempVertices.Length - 1].Coordinates = new World.Vector3D(vx, vy, vz); tempNormals[tempNormals.Length - 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); } Array.Resize<World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); while (Builder.Vertices.Length >= Normals.Length) { Array.Resize<World.Vector3Df>(ref Normals, Normals.Length << 1); } Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new World.Vector3D(vx, vy, vz); Normals[Builder.Vertices.Length - 1] = new World.Vector3Df((float) nx, (float) ny, (float) nz); } } } //The Flaeche command creates a face else if (node.Name == "Flaeche" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { //Defines the verticies in this face //**NOTE**: A vertex may appear in multiple faces with different texture co-ordinates if (childNode.Attributes["Points"] != null) { string[] Verticies = childNode.Attributes["Points"].Value.Split(';'); int f = Builder.Faces.Length; //Add 1 to the length of the face array Array.Resize<World.MeshFace>(ref Builder.Faces, f + 1); Builder.Faces[f] = new World.MeshFace(); //Create the vertex array for the face Builder.Faces[f].Vertices = new World.MeshFaceVertex[Verticies.Length]; while (Builder.Vertices.Length > Normals.Length) { Array.Resize<World.Vector3Df>(ref Normals, Normals.Length << 1); } //Run through the vertices list and grab from the temp array for (int j = 0; j < Verticies.Length; j++) { //This is the position of the vertex in the temp array int currentVertex; int.TryParse(Verticies[j], out currentVertex); //Add one to the actual vertex array Array.Resize<World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1); //Set coordinates Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = tempVertices[currentVertex].Coordinates; //Set the vertex index Builder.Faces[f].Vertices[j].Index = (ushort)(Builder.Vertices.Length - 1); //Set the normals Builder.Faces[f].Vertices[j].Normal = tempNormals[currentVertex]; //Now deal with the texture //Texture mapping points are in pixels X,Y and are relative to the face in question rather than the vertex if (childNode.Attributes["Texture"] != null) { string[] TextureCoords = childNode.Attributes["Texture"].Value.Split(';'); World.Vector2Df currentCoords; float OpenBVEWidth; float OpenBVEHeight; string[] splitCoords = TextureCoords[j].Split(','); float.TryParse(splitCoords[0], out OpenBVEWidth); float.TryParse(splitCoords[1], out OpenBVEHeight); if (TextureWidth != 0 && TextureHeight != 0) { currentCoords.X = (OpenBVEWidth / TextureWidth); currentCoords.Y = (OpenBVEHeight / TextureHeight); } else { currentCoords.X = 0; currentCoords.Y = 0; } Builder.Vertices[Builder.Vertices.Length - 1].TextureCoordinates = currentCoords; } if (Face2) { Builder.Faces[f].Flags = (byte) World.MeshFace.Face2Mask; } } } } } } } } } } //Apply rotation /* * NOTES: * No rotation order is specified * The rotation string in a .l3dgrp file is ordered Y, X, Z ??? Can't find a good reason for this ??? * Rotations must still be performed in X,Y,Z order to produce correct results */ if (RotationX != 0.0) { //This is actually the Y-Axis rotation //Convert to radians RotationX *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 0, 1, 0, RotationX); } if (RotationY != 0.0) { //This is actually the X-Axis rotation //Convert to radians RotationY *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 1, 0, 0, RotationY); } if (RotationZ != 0.0) { //Convert to radians RotationZ *= 0.0174532925199433; //Apply rotation ApplyRotation(Builder, 0, 0, 1, RotationZ); } //These files appear to only have one texture defined //Therefore import later- May have to change if (File.Exists(tday)) { for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].DaytimeTexture = tday; Builder.Materials[j].NighttimeTexture = tnight; } } if (TransparencyUsed == true) { for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].TransparentColor = transparentColor; Builder.Materials[j].TransparentColorUsed = true; } } } ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); World.CreateNormals(ref Object.Mesh); return Object; }
internal static int RegisterTexture(string FileName, World.ColorRGB TransparentColor, byte TransparentColorUsed, TextureLoadMode LoadMode, TextureWrapMode WrapModeX, TextureWrapMode WrapModeY, bool DontAllowUnload, int ClipLeft, int ClipTop, int ClipWidth, int ClipHeight) { if (FileName == null) { //Need to find out why the object parser sometimes decides to pass a null filename, but this works around it return(-1); } int i = FindTexture(FileName, TransparentColor, TransparentColorUsed, LoadMode, WrapModeX, WrapModeY, ClipLeft, ClipTop, ClipWidth, ClipHeight); if (i >= 0) { return(i); } else { i = GetFreeTexture(); Textures[i] = new Texture { Queried = false, Loaded = false, FileName = FileName, TransparentColor = TransparentColor, TransparentColorUsed = TransparentColorUsed, LoadMode = LoadMode, WrapModeX = WrapModeX, WrapModeY = WrapModeY, ClipLeft = ClipLeft, ClipTop = ClipTop, ClipWidth = ClipWidth, ClipHeight = ClipHeight, DontAllowUnload = DontAllowUnload, LoadImmediately = false, OpenGlTextureIndex = 0 }; bool alpha = false; switch (System.IO.Path.GetExtension(Textures[i].FileName).ToLowerInvariant()) { case ".gif": case ".png": alpha = true; Textures[i].LoadImmediately = true; break; } if (alpha) { Textures[i].Transparency = TextureTransparencyMode.Alpha; } else if (TransparentColorUsed != 0) { Textures[i].Transparency = TextureTransparencyMode.TransparentColor; } else { Textures[i].Transparency = TextureTransparencyMode.None; } Textures[i].IsRGBA = Textures[i].Transparency != TextureTransparencyMode.None | LoadMode != TextureLoadMode.Normal; //Check that our image is a valid power of two int tw, th; GetImageDimensions(FileName, out tw, out th); int w = Interface.RoundToPowerOfTwo(tw); int h = Interface.RoundToPowerOfTwo(th); if (w != tw) { Interface.AddMessage(Interface.MessageType.Information, false, "The Texture: " + FileName + " has a width of " + tw + " . This is not a valid power of two."); } if (h != th) { Interface.AddMessage(Interface.MessageType.Information, false, "The Texture: " + FileName + " has a height of " + th + " . This is not a valid power of two."); } return(i); } }