Exemple #1
0
        //Parses an XML dynamic lighting definition
        public static bool ReadLightingXML(string fileName)
        {
            //Prep
            Renderer.LightDefinitions = new Renderer.LightDefinition[0];
            //The current XML file to load
            XmlDocument currentXML = new XmlDocument();

            //Load the object's XML file
            try
            {
                currentXML.Load(fileName);
            }
            catch
            {
                return(false);
            }

            bool defined = false;

            //Check for null
            if (currentXML.DocumentElement != null)
            {
                XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Brightness");
                //Check this file actually contains OpenBVE light definition nodes
                if (DocumentNodes != null)
                {
                    foreach (XmlNode n in DocumentNodes)
                    {
                        Renderer.LightDefinition currentLight = new Renderer.LightDefinition();
                        if (n.HasChildNodes)
                        {
                            bool   tf = false, al = false, dl = false, ld = false, cb = false;
                            string ts = null;
                            foreach (XmlNode c in n.ChildNodes)
                            {
                                string[] Arguments = c.InnerText.Split(',');
                                switch (c.Name.ToLowerInvariant())
                                {
                                case "cablighting":
                                    double b;
                                    if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out b))
                                    {
                                        cb = true;
                                    }
                                    if (b > 255 || b < 0)
                                    {
                                        Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " is not a valid brightness value in file " + fileName);
                                        currentLight.CabBrightness = 255;
                                        break;
                                    }
                                    currentLight.CabBrightness = b;
                                    break;

                                case "time":
                                    double t;
                                    if (Interface.TryParseTime(Arguments[0].Trim(), out t))
                                    {
                                        currentLight.Time = (int)t;
                                        tf = true;
                                        //Keep back for error report later
                                        ts = Arguments[0];
                                    }
                                    else
                                    {
                                        Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not parse to a valid time in file " + fileName);
                                    }
                                    break;

                                case "ambientlight":
                                    if (Arguments.Length == 3)
                                    {
                                        double R, G, B;
                                        if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out R) && NumberFormats.TryParseDoubleVb6(Arguments[1].Trim(), out G) && NumberFormats.TryParseDoubleVb6(Arguments[2].Trim(), out B))
                                        {
                                            currentLight.AmbientColor = new Color24((byte)R, (byte)G, (byte)B);
                                            al = true;
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not parse to a valid color in file " + fileName);
                                        }
                                    }
                                    else
                                    {
                                        if (Arguments.Length == 1)
                                        {
                                            if (Color24.TryParseHexColor(Arguments[0], out currentLight.DiffuseColor))
                                            {
                                                al = true;
                                                break;
                                            }
                                        }
                                        Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not contain three arguments in file " + fileName);
                                    }
                                    break;

                                case "directionallight":
                                    if (Arguments.Length == 3)
                                    {
                                        double R, G, B;
                                        if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out R) && NumberFormats.TryParseDoubleVb6(Arguments[1].Trim(), out G) && NumberFormats.TryParseDoubleVb6(Arguments[2].Trim(), out B))
                                        {
                                            currentLight.DiffuseColor = new Color24((byte)R, (byte)G, (byte)B);
                                            dl = true;
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not parse to a valid color in file " + fileName);
                                        }
                                    }
                                    else
                                    {
                                        if (Arguments.Length == 1)
                                        {
                                            if (Color24.TryParseHexColor(Arguments[0], out currentLight.DiffuseColor))
                                            {
                                                dl = true;
                                                break;
                                            }
                                        }
                                        Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not contain three arguments in file " + fileName);
                                    }
                                    break;

                                case "cartesianlightdirection":
                                case "lightdirection":
                                    if (Arguments.Length == 3)
                                    {
                                        double X, Y, Z;
                                        if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out X) && NumberFormats.TryParseDoubleVb6(Arguments[1].Trim(), out Y) && NumberFormats.TryParseDoubleVb6(Arguments[2].Trim(), out Z))
                                        {
                                            currentLight.LightPosition = new Vector3(X, Y, Z);
                                            ld = true;
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not parse to a valid direction in file " + fileName);
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not contain three arguments in file " + fileName);
                                    }
                                    break;

                                case "sphericallightdirection":
                                    if (Arguments.Length == 2)
                                    {
                                        double theta, phi;
                                        if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out theta) && NumberFormats.TryParseDoubleVb6(Arguments[1].Trim(), out phi))
                                        {
                                            currentLight.LightPosition = new Vector3(Math.Cos(theta) * Math.Sin(phi), -Math.Sin(theta), Math.Cos(theta) * Math.Cos(phi));
                                            ld = true;
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not parse to a valid direction in file " + fileName);
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not contain two arguments in file " + fileName);
                                    }
                                    break;
                                }
                            }
                            //We want to be able to add a completely default light element,  but not one that's not been defined in the XML properly
                            if (tf || al || ld || dl || cb)
                            {
                                //HACK: No way to break out of the first loop and continue with the second, so we've got to use a variable
                                bool Break = false;
                                int  l     = Renderer.LightDefinitions.Length;
                                for (int i = 0; i > l; i++)
                                {
                                    if (Renderer.LightDefinitions[i].Time == currentLight.Time)
                                    {
                                        Break = true;
                                        if (ts == null)
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Multiple undefined times were encountered in file " + fileName);
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Duplicate time found: " + ts + " in file " + fileName);
                                        }
                                        break;
                                    }
                                }
                                if (Break)
                                {
                                    continue;
                                }
                                //We've got there, so now figure out where to add the new light into our list of light definitions
                                int t = 0;
                                if (l == 1)
                                {
                                    t = currentLight.Time > Renderer.LightDefinitions[0].Time ? 1 : 0;
                                }
                                else if (l > 1)
                                {
                                    for (int i = 1; i < l; i++)
                                    {
                                        t = i + 1;
                                        if (currentLight.Time > Renderer.LightDefinitions[i - 1].Time && currentLight.Time < Renderer.LightDefinitions[i].Time)
                                        {
                                            break;
                                        }
                                    }
                                }
                                //Resize array
                                defined = true;
                                Array.Resize(ref Renderer.LightDefinitions, l + 1);
                                if (t == l)
                                {
                                    //Straight insert at the end of the array
                                    Renderer.LightDefinitions[l] = currentLight;
                                }
                                else
                                {
                                    for (int u = t; u < l; u++)
                                    {
                                        //Otherwise, shift all elements to compensate
                                        Renderer.LightDefinitions[u + 1] = Renderer.LightDefinitions[u];
                                    }
                                    Renderer.LightDefinitions[t] = currentLight;
                                }
                            }
                        }
                    }
                }
            }
            //We couldn't find any valid XML, so return false
            return(defined);
        }
Exemple #2
0
        /// <summary>Loads a Loksim3D object from a file.</summary>
        /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param>
        /// <param name="LoadMode">The texture load mode.</param>
        /// <param name="Rotation">The rotation to be applied.</param>
        /// <returns>The object loaded.</returns>
        internal static ObjectManager.StaticObject ReadObject(string FileName, ObjectManager.ObjectLoadMode LoadMode, Vector3 Rotation)
        {
            string      BaseDir    = System.IO.Path.GetDirectoryName(FileName);
            XmlDocument currentXML = new XmlDocument();

            //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();

            Vector3[] Normals         = new Vector3[4];
            bool      PropertiesFound = false;

            World.Vertex[] tempVertices       = new World.Vertex[0];
            Vector3[]      tempNormals        = new Vector3[0];
            Color24        transparentColor   = new Color24();
            string         tday               = null;
            string         transtex           = null;
            string         tnight             = null;
            bool           TransparencyUsed   = false;
            bool           TransparentTypSet  = false;
            bool           FirstPxTransparent = false;
            Color24        FirstPxColor       = new Color24();
            bool           Face2              = false;
            int            TextureWidth       = 0;
            int            TextureHeight      = 0;

            if (File.Exists(FileName))
            {
                try
                {
                    currentXML.Load(FileName);
                }
                catch
                {
                    return(null);
                }
            }
            else
            {
                Interface.AddMessage(Interface.MessageType.Error, false, "Loksim3D object " + FileName + " does not exist.");
                return(null);
            }
            //Check for null
            if (currentXML.DocumentElement != null)
            {
                XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/OBJECT");
                //Check this file actually contains Loksim3D object nodes
                if (DocumentNodes != null)
                {
                    foreach (XmlNode outerNode in DocumentNodes)
                    {
                        if (outerNode.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.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory);
                                                if (!File.Exists(tday))
                                                {
                                                    Interface.AddMessage(Interface.MessageType.Warning, true, "Ls3d Texture file " + attribute.Value + " not found.");
                                                    break;
                                                }
                                                try
                                                {
                                                    using (Bitmap TextureInformation = new Bitmap(tday))
                                                    {
                                                        TextureWidth  = TextureInformation.Width;
                                                        TextureHeight = TextureInformation.Height;
                                                        Color color = TextureInformation.GetPixel(0, 0);
                                                        FirstPxColor = new Color24(color.R, color.G, color.B);
                                                    }
                                                }
                                                catch
                                                {
                                                    Interface.AddMessage(Interface.MessageType.Error, true,
                                                                         "An error occured loading daytime texture " + tday +
                                                                         " in file " + FileName);
                                                    tday = null;
                                                }
                                                break;

                                            //Defines whether the texture uses transparency
                                            //May be omitted
                                            case "Transparent":
                                                if (TransparentTypSet)
                                                {
                                                    //Appears to be ignored with TransparentTyp set
                                                    continue;
                                                }
                                                if (attribute.Value == "TRUE")
                                                {
                                                    TransparencyUsed = true;
                                                    transparentColor = new Color24(0, 0, 0);
                                                }
                                                break;

                                            case "TransTexture":
                                                if (string.IsNullOrEmpty(attribute.Value))
                                                {
                                                    //Empty....
                                                    continue;
                                                }
                                                transtex = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory);
                                                if (!File.Exists(transtex))
                                                {
                                                    Interface.AddMessage(Interface.MessageType.Error, true, "AlphaTexture " + transtex + " could not be found in file " + FileName);
                                                    transtex = null;
                                                }
                                                break;

                                            //Sets the transparency type
                                            case "TransparentTyp":
                                                TransparentTypSet = true;
                                                switch (attribute.Value)
                                                {
                                                case "0":
                                                    //Transparency is disabled
                                                    TransparencyUsed = false;
                                                    break;

                                                case "1":
                                                    //Transparency is solid black
                                                    TransparencyUsed   = true;
                                                    transparentColor   = new Color24(0, 0, 0);
                                                    FirstPxTransparent = false;
                                                    break;

                                                case "2":
                                                    //Transparency is the color at Pixel 1,1
                                                    TransparencyUsed   = true;
                                                    FirstPxTransparent = true;
                                                    break;

                                                case "3":
                                                case "4":
                                                    //This is used when transparency is used with an alpha bitmap
                                                    TransparencyUsed   = false;
                                                    FirstPxTransparent = false;
                                                    break;

                                                default:
                                                    Interface.AddMessage(Interface.MessageType.Error, false, "Unrecognised transparency type " + attribute.Value + " detected in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    break;
                                                }
                                                break;

                                            //Sets whether the rears of the faces are to be drawn
                                            case "Drawrueckseiten":
                                                if (attribute.Value == "TRUE" || string.IsNullOrEmpty(attribute.Value))
                                                {
                                                    Face2 = true;
                                                }
                                                else
                                                {
                                                    Face2 = false;
                                                }
                                                break;

                                                /*
                                                 * MISSING PROPERTIES:
                                                 * AutoRotate - Rotate with tracks?? LS3D presumably uses a 3D world system.
                                                 * Beleuchtet- Translates as illuminated. Presume something to do with lighting? - What emissive color?
                                                 * FileAuthor
                                                 * FileInfo
                                                 * FilePicture
                                                 */
                                            }
                                        }
                                    }
                                }
                                //The point command is eqivilant to a vertex
                                else if (node.Name == "Point" && node.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(';');
                                                    if (!double.TryParse(NormalPoints[0], out nx))
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nX in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    if (!double.TryParse(NormalPoints[1], out ny))
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nY in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    if (!double.TryParse(NormalPoints[2], out nz))
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nZ in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    break;

                                                //Sets the vertex 3D co-ordinates
                                                case "Vekt":
                                                    string[] VertexPoints = attribute.Value.Split(';');
                                                    if (!double.TryParse(VertexPoints[0], out vx))
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument vX in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    if (!double.TryParse(VertexPoints[1], out vy))
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument yY in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    if (!double.TryParse(VertexPoints[2], out vz))
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument vZ in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    break;
                                                }
                                            }
                                            World.Normalize(ref nx, ref ny, ref nz);
                                            //Resize temp arrays
                                            Array.Resize <World.Vertex>(ref tempVertices, tempVertices.Length + 1);
                                            Array.Resize <Vector3>(ref tempNormals, tempNormals.Length + 1);
                                            //Add vertex and normals to temp array
                                            tempVertices[tempVertices.Length - 1].Coordinates = new Vector3(vx, vy, vz);
                                            tempNormals[tempNormals.Length - 1] = new Vector3((float)nx, (float)ny, (float)nz);
                                            Array.Resize <World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1);
                                            while (Builder.Vertices.Length >= Normals.Length)
                                            {
                                                Array.Resize <Vector3>(ref Normals, Normals.Length << 1);
                                            }
                                            Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new Vector3(vx, vy, vz);
                                            Normals[Builder.Vertices.Length - 1] = new Vector3((float)nx, (float)ny, (float)nz);
                                        }
                                    }
                                }
                                //The Flaeche command creates a face
                                else if (node.Name == "Flaeche" && node.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 <Vector3>(ref Normals,
                                                                           Normals.Length << 1);
                                                }
                                                //Run through the vertices list and grab from the temp array

                                                int smallestX = TextureWidth;
                                                int smallestY = TextureHeight;
                                                for (int j = 0; j < Verticies.Length; j++)
                                                {
                                                    //This is the position of the vertex in the temp array
                                                    int currentVertex;
                                                    if (!int.TryParse(Verticies[j], out currentVertex))
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, false, Verticies[j] + " does not parse to a valid Vertex in " + node.Name + " in Loksim3D object file " + FileName);
                                                        continue;
                                                    }
                                                    //Add one to the actual vertex array
                                                    Array.Resize <World.Vertex>(ref Builder.Vertices, Builder.Vertices.Length + 1);
                                                    //Set coordinates
                                                    Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = tempVertices[currentVertex].Coordinates;
                                                    //Set the vertex index
                                                    Builder.Faces[f].Vertices[j].Index = (ushort)(Builder.Vertices.Length - 1);
                                                    //Set the normals
                                                    Builder.Faces[f].Vertices[j].Normal = tempNormals[currentVertex];
                                                    //Now deal with the texture
                                                    //Texture mapping points are in pixels X,Y and are relative to the face in question rather than the vertex
                                                    if (childNode.Attributes["Texture"] != null)
                                                    {
                                                        string[] TextureCoords = childNode.Attributes["Texture"].Value.Split(';');
                                                        Vector2  currentCoords;
                                                        float    OpenBVEWidth;
                                                        float    OpenBVEHeight;
                                                        string[] splitCoords = TextureCoords[j].Split(',');
                                                        if (!float.TryParse(splitCoords[0], out OpenBVEWidth))
                                                        {
                                                            Interface.AddMessage(Interface.MessageType.Error, false, "Invalid texture width specified in " + node.Name + " in Loksim3D object file " + FileName);
                                                            continue;
                                                        }
                                                        if (!float.TryParse(splitCoords[1], out OpenBVEHeight))
                                                        {
                                                            Interface.AddMessage(Interface.MessageType.Error, false, "Invalid texture height specified in " + node.Name + " in Loksim3D object file " + FileName);
                                                            continue;
                                                        }
                                                        if (OpenBVEWidth <= smallestX && OpenBVEHeight <= smallestY)
                                                        {
                                                            //Clamp texture width and height
                                                            smallestX = (int)OpenBVEWidth;
                                                            smallestY = (int)OpenBVEHeight;
                                                        }
                                                        if (TextureWidth != 0 && TextureHeight != 0)
                                                        {
                                                            //Calculate openBVE co-ords
                                                            currentCoords.X = (OpenBVEWidth / TextureWidth);
                                                            currentCoords.Y = (OpenBVEHeight / TextureHeight);
                                                        }
                                                        else
                                                        {
                                                            //Invalid, so just return zero
                                                            currentCoords.X = 0;
                                                            currentCoords.Y = 0;
                                                        }
                                                        Builder.Vertices[Builder.Vertices.Length - 1].TextureCoordinates = currentCoords;
                                                    }
                                                }
                                                if (Face2)
                                                {
                                                    //Add face2 flag if required
                                                    Builder.Faces[f].Flags = (byte)World.MeshFace.Face2Mask;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                //Apply rotation

                /*
                 * NOTES:
                 * No rotation order is specified
                 * The rotation string in a .l3dgrp file is ordered Y, Z, X    ??? Can't find a good reason for this ???
                 * Rotations must still be performed in X,Y,Z order to produce correct results
                 */

                if (Rotation.Z != 0.0)
                {
                    //Convert to radians
                    Rotation.Z *= 0.0174532925199433;
                    //Apply rotation
                    ApplyRotation(Builder, 1, 0, 0, Rotation.Z);
                }


                if (Rotation.X != 0.0)
                {
                    //This is actually the Y-Axis rotation
                    //Convert to radians
                    Rotation.X *= 0.0174532925199433;
                    //Apply rotation
                    ApplyRotation(Builder, 0, 1, 0, Rotation.X);
                }
                if (Rotation.Y != 0.0)
                {
                    //This is actually the X-Axis rotation
                    //Convert to radians
                    Rotation.Y *= 0.0174532925199433;
                    //Apply rotation
                    ApplyRotation(Builder, 0, 0, 1, Rotation.Y);
                }


                //These files appear to only have one texture defined
                //Therefore import later- May have to change
                if (File.Exists(tday))
                {
                    for (int j = 0; j < Builder.Materials.Length; j++)
                    {
                        Builder.Materials[j].DaytimeTexture      = tday;
                        Builder.Materials[j].TransparencyTexture = transtex;
                        Builder.Materials[j].NighttimeTexture    = tnight;
                    }
                }
                if (TransparencyUsed == true)
                {
                    for (int j = 0; j < Builder.Materials.Length; j++)
                    {
                        Builder.Materials[j].TransparentColor     = FirstPxTransparent ? FirstPxColor : transparentColor;
                        Builder.Materials[j].TransparentColorUsed = true;
                    }
                }
            }
            ApplyMeshBuilder(ref Object, Builder, LoadMode, false, false);
            World.CreateNormals(ref Object.Mesh);
            return(Object);
        }
Exemple #3
0
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Color24 rawColor = (Color24)value;

            return(System.Windows.Media.Color.FromRgb(rawColor.R, rawColor.G, rawColor.B));
        }
            // --- functions ---
            /// <summary>Gets the texture from this origin.</summary>
            /// <param name="texture">Receives the texture.</param>
            /// <returns>Whether the texture could be obtained successfully.</returns>
            internal override bool GetTexture(out OpenBveApi.Textures.Texture texture)
            {
                Bitmap    bitmap = this.Bitmap;
                Rectangle rect   = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

                /*
                 * If the bitmap format is not already 32-bit BGRA,
                 * then convert it to 32-bit BGRA.
                 * */
                Color24[] p = null;
                if (bitmap.PixelFormat != PixelFormat.Format32bppArgb && bitmap.PixelFormat != PixelFormat.Format24bppRgb)
                {
                    /* Only store the color palette data for
                     * textures using a restricted palette
                     * With a large number of textures loaded at
                     * once, this can save a decent chunk of memory
                     * */
                    p = new Color24[bitmap.Palette.Entries.Length];
                    for (int i = 0; i < bitmap.Palette.Entries.Length; i++)
                    {
                        p[i] = bitmap.Palette.Entries[i];
                    }
                }
                if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
                {
                    Bitmap   compatibleBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb);
                    Graphics graphics         = Graphics.FromImage(compatibleBitmap);
                    graphics.DrawImage(bitmap, rect, rect, GraphicsUnit.Pixel);
                    graphics.Dispose();
                    bitmap = compatibleBitmap;
                }

                /*
                 * Extract the raw bitmap data.
                 * */
                BitmapData data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);

                if (data.Stride == 4 * data.Width)
                {
                    /*
                     * Copy the data from the bitmap
                     * to the array in BGRA format.
                     * */
                    byte[] raw = new byte[data.Stride * data.Height];
                    System.Runtime.InteropServices.Marshal.Copy(data.Scan0, raw, 0, data.Stride * data.Height);
                    bitmap.UnlockBits(data);
                    int width  = bitmap.Width;
                    int height = bitmap.Height;

                    /*
                     * Change the byte order from BGRA to RGBA.
                     * */
                    for (int i = 0; i < raw.Length; i += 4)
                    {
                        byte temp = raw[i];
                        raw[i]     = raw[i + 2];
                        raw[i + 2] = temp;
                    }
                    texture = new OpenBveApi.Textures.Texture(width, height, 32, raw, p);
                    texture = texture.ApplyParameters(this.Parameters);
                    return(true);
                }

                /*
                 * The stride is invalid. This indicates that the
                 * CLI either does not implement the conversion to
                 * 32-bit BGRA correctly, or that the CLI has
                 * applied additional padding that we do not
                 * support.
                 * */
                bitmap.UnlockBits(data);
                texture = null;
                return(false);
            }
Exemple #5
0
 public void SetLightDiffuse(Color24 LightDiffuse)
 {
     GL.Uniform3(UniformLayout.LightDiffuse, LightDiffuse.R / 255.0f, LightDiffuse.G / 255.0f, LightDiffuse.B / 255.0f);
 }
Exemple #6
0
 public void SetMaterialEmission(Color24 MaterialEmission)
 {
     GL.Uniform3(UniformLayout.MaterialEmission, MaterialEmission.R / 255.0f, MaterialEmission.G / 255.0f, MaterialEmission.B / 255.0f);
 }
Exemple #7
0
 // load texture rgba
 private static void LoadTextureRGBAforData(Bitmap Bitmap, Color24 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, System.Drawing.Imaging.PixelFormat.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, System.Drawing.Imaging.PixelFormat.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, System.Drawing.Imaging.PixelFormat.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;
     }
 }
Exemple #8
0
        private byte[] GetRawBitmapData(Bitmap bitmap, out int width, out int height, out Color24[] p)
        {
            p = null;
            if (EnabledHacks.ReduceTransparencyColorDepth && (bitmap.PixelFormat != PixelFormat.Format32bppArgb && bitmap.PixelFormat != PixelFormat.Format24bppRgb))
            {
                /*
                 * Our source bitmap is *not* a 256 color bitmap but has been made for BVE2 / BVE4.
                 * These process transparency in 256 colors (even if the file is 24bpp / 32bpp), thus:
                 * Let's open the bitmap, and attempt to construct a reduced color pallette
                 * If our bitmap contains more than 256 unique colors, we break out of the loop
                 * and assume that this file is an incorrect match
                 *
                 * WARNING NOTE:
                 * Unfortunately, we can't just pull out the color pallette from the bitmap, as there
                 * is no native way to remove unused entries. We therefore have to itinerate through
                 * each pixel.....
                 * This is *slow* so use with caution!
                 *
                 */

                BitmapData        inputData       = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
                HashSet <Color24> reducedPallette = new HashSet <Color24>();
                unsafe
                {
                    byte *bmpPtr = (byte *)inputData.Scan0.ToPointer();
                    int   ic, oc, r;
                    if (bitmap.PixelFormat == PixelFormat.Format24bppRgb)
                    {
                        for (r = 0; r < inputData.Height; r++)
                        {
                            for (ic = oc = 0; oc < inputData.Width; ic += 3, oc++)
                            {
                                byte    blue  = bmpPtr[r * inputData.Stride + ic];
                                byte    green = bmpPtr[r * inputData.Stride + ic + 1];
                                byte    red   = bmpPtr[r * inputData.Stride + ic + 2];
                                Color24 c     = new Color24(red, green, blue);
                                if (!reducedPallette.Contains(c))
                                {
                                    reducedPallette.Add(c);
                                }
                                if (reducedPallette.Count > 256)
                                {
                                    //as breaking out of nested loops is a pita
                                    goto EndLoop;
                                }
                            }
                        }
                    }
                    else
                    {
                        for (r = 0; r < inputData.Height; r++)
                        {
                            for (ic = oc = 0; oc < inputData.Width; ic += 4, oc++)
                            {
                                byte    blue  = bmpPtr[r * inputData.Stride + ic];
                                byte    green = bmpPtr[r * inputData.Stride + ic + 1];
                                byte    red   = bmpPtr[r * inputData.Stride + ic + 2];
                                Color24 c     = new Color24(red, green, blue);
                                if (!reducedPallette.Contains(c))
                                {
                                    reducedPallette.Add(c);
                                }
                                if (reducedPallette.Count > 256)
                                {
                                    //as breaking out of nested loops is a pita
                                    goto EndLoop;
                                }
                            }
                        }
                    }
                }

                p = reducedPallette.ToArray();
EndLoop:
                bitmap.UnlockBits(inputData);
            }


            if (bitmap.PixelFormat != PixelFormat.Format32bppArgb && bitmap.PixelFormat != PixelFormat.Format24bppRgb && p == null)
            {
                /* Otherwise, only store the color palette data for
                 * textures using a restricted palette
                 * With a large number of textures loaded at
                 * once, this can save a decent chunk of memory
                 */
                p = new Color24[bitmap.Palette.Entries.Length];
                for (int i = 0; i < bitmap.Palette.Entries.Length; i++)
                {
                    p[i] = bitmap.Palette.Entries[i];
                }
            }
            Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

            /*
             * If the bitmap format is not already 32-bit BGRA,
             * then convert it to 32-bit BGRA.
             */
            if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
            {
                Bitmap   compatibleBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb);
                Graphics graphics         = Graphics.FromImage(compatibleBitmap);
                graphics.DrawImage(bitmap, rect, rect, GraphicsUnit.Pixel);
                graphics.Dispose();
                bitmap.Dispose();
                bitmap = compatibleBitmap;
            }

            /*
             * Extract the raw bitmap data.
             */
            BitmapData data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);

            if (data.Stride == 4 * data.Width)
            {
                /*
                 * Copy the data from the bitmap
                 * to the array in BGRA format.
                 */
                byte[] raw = new byte[data.Stride * data.Height];
                System.Runtime.InteropServices.Marshal.Copy(data.Scan0, raw, 0, data.Stride * data.Height);
                bitmap.UnlockBits(data);
                width  = bitmap.Width;
                height = bitmap.Height;

                /*
                 * Change the byte order from BGRA to RGBA.
                 */
                for (int i = 0; i < raw.Length; i += 4)
                {
                    byte temp = raw[i];
                    raw[i]     = raw[i + 2];
                    raw[i + 2] = temp;
                }

                return(raw);
            }

            /*
             * The stride is invalid. This indicates that the
             * CLI either does not implement the conversion to
             * 32-bit BGRA correctly, or that the CLI has
             * applied additional padding that we do not
             * support.
             */
            bitmap.UnlockBits(data);
            bitmap.Dispose();
            CurrentHost.ReportProblem(ProblemType.InvalidOperation, "Invalid stride encountered.");
            width  = 0;
            height = 0;
            return(null);
        }
Exemple #9
0
        private static void  MeshBuilder(ref StaticObject obj, ref MeshBuilder builder, AssimpNET.X.Mesh mesh)
        {
            if (builder.Vertices.Count != 0)
            {
                builder.Apply(ref obj);
                builder = new MeshBuilder(Plugin.currentHost);
            }

            int nVerts = mesh.Positions.Count;

            if (nVerts == 0)
            {
                //Some null objects contain an empty mesh
                Plugin.currentHost.AddMessage(MessageType.Warning, false, "nVertices should be greater than zero in Mesh " + mesh.Name);
            }
            for (int i = 0; i < nVerts; i++)
            {
                builder.Vertices.Add(new Vertex(mesh.Positions[i]));
            }

            int nFaces = mesh.PosFaces.Count;

            for (int i = 0; i < nFaces; i++)
            {
                int fVerts = mesh.PosFaces[i].Indices.Count;
                if (nFaces == 0)
                {
                    throw new Exception("fVerts must be greater than zero");
                }
                MeshFace f = new MeshFace();
                f.Vertices = new MeshFaceVertex[fVerts];
                for (int j = 0; j < fVerts; j++)
                {
                    f.Vertices[j].Index = (ushort)mesh.PosFaces[i].Indices[j];
                }
                builder.Faces.Add(f);
            }

            int nMaterials   = mesh.Materials.Count;
            int nFaceIndices = mesh.FaceMaterials.Count;

            for (int i = 0; i < nFaceIndices; i++)
            {
                int      fMaterial = (int)mesh.FaceMaterials[i];
                MeshFace f         = builder.Faces[i];
                f.Material       = (ushort)(fMaterial + 1);
                builder.Faces[i] = f;
            }
            for (int i = 0; i < nMaterials; i++)
            {
                int m = builder.Materials.Length;
                Array.Resize(ref builder.Materials, m + 1);
                builder.Materials[m]       = new OpenBveApi.Objects.Material();
                builder.Materials[m].Color = new Color32((byte)(255 * mesh.Materials[i].Diffuse.R), (byte)(255 * mesh.Materials[i].Diffuse.G), (byte)(255 * mesh.Materials[i].Diffuse.B), (byte)(255 * mesh.Materials[i].Diffuse.A));
                double  mPower    = mesh.Materials[i].SpecularExponent;             //TODO: Unsure what this does...
                Color24 mSpecular = new Color24((byte)mesh.Materials[i].Specular.R, (byte)mesh.Materials[i].Specular.G, (byte)mesh.Materials[i].Specular.B);
                builder.Materials[m].EmissiveColor = new Color24((byte)(255 * mesh.Materials[i].Emissive.R), (byte)(255 * mesh.Materials[i].Emissive.G), (byte)(255 * mesh.Materials[i].Emissive.B));
                builder.Materials[m].Flags        |= MaterialFlags.Emissive;          //TODO: Check exact behaviour
                if (Plugin.EnabledHacks.BlackTransparency)
                {
                    builder.Materials[m].TransparentColor = Color24.Black;                     //TODO: Check, also can we optimise which faces have the transparent color set?
                    builder.Materials[m].Flags           |= MaterialFlags.TransparentColor;
                }

                if (mesh.Materials[i].Textures.Count > 0)
                {
                    string texturePath = mesh.Materials[i].Textures[0].Name;

                    // If the specified file name is an absolute path, make it the file name only.
                    // Some object files specify absolute paths.
                    // And BVE4/5 doesn't allow textures to be placed in a different directory than the object file.
                    if (Plugin.EnabledHacks.BveTsHacks && OpenBveApi.Path.IsAbsolutePath(texturePath))
                    {
                        texturePath = texturePath.Split('/', '\\').Last();
                    }

                    try
                    {
                        builder.Materials[m].DaytimeTexture = OpenBveApi.Path.CombineFile(currentFolder, texturePath);
                    }
                    catch (Exception e)
                    {
                        Plugin.currentHost.AddMessage(MessageType.Error, false, $"Texture file path {texturePath} in file {currentFile} has the problem: {e.Message}");
                        builder.Materials[m].DaytimeTexture = null;
                    }

                    if (builder.Materials[m].DaytimeTexture != null && !System.IO.File.Exists(builder.Materials[m].DaytimeTexture))
                    {
                        Plugin.currentHost.AddMessage(MessageType.Error, true, "Texure " + builder.Materials[m].DaytimeTexture + " was not found in file " + currentFile);
                        builder.Materials[m].DaytimeTexture = null;
                    }
                }
            }

            if (mesh.TexCoords.Length > 0 && mesh.TexCoords[0] != null)
            {
                int nCoords = mesh.TexCoords[0].Count;
                for (int i = 0; i < nCoords; i++)
                {
                    builder.Vertices[i].TextureCoordinates = new Vector2(mesh.TexCoords[0][i].X, mesh.TexCoords[0][i].Y);
                }
            }

            int nNormals = mesh.Normals.Count;

            Vector3[] normals = new Vector3[nNormals];
            for (int i = 0; i < nNormals; i++)
            {
                normals[i] = new Vector3(mesh.Normals[i].X, mesh.Normals[i].Y, mesh.Normals[i].Z);
                normals[i].Normalize();
            }
            int nFaceNormals = mesh.NormFaces.Count;

            if (nFaceNormals > builder.Faces.Count)
            {
                throw new Exception("nFaceNormals must match the number of faces in the mesh");
            }
            for (int i = 0; i < nFaceNormals; i++)
            {
                int nVertexNormals = mesh.NormFaces[i].Indices.Count;
                if (nVertexNormals > builder.Faces[i].Vertices.Length)
                {
                    throw new Exception("nVertexNormals must match the number of verticies in the face");
                }
                for (int j = 0; j < nVertexNormals; j++)
                {
                    builder.Faces[i].Vertices[j].Normal = normals[(int)mesh.NormFaces[i].Indices[j]];
                }
            }

            int nVertexColors = (int)mesh.NumColorSets;

            for (int i = 0; i < nVertexColors; i++)
            {
                builder.Vertices[i] = new ColoredVertex((Vertex)builder.Vertices[i], new Color128(mesh.Colors[0][i].R, mesh.Colors[0][i].G, mesh.Colors[0][i].B, mesh.Colors[0][i].A));
            }
        }
Exemple #10
0
 internal static int RegisterTexture(string FileName, Color24 TransparentColor, byte TransparentColorUsed, TextureWrapMode WrapModeX, TextureWrapMode WrapModeY, bool DontAllowUnload)
 {
     return(RegisterTexture(FileName, TransparentColor, TransparentColorUsed, TextureLoadMode.Normal, WrapModeX, WrapModeY, DontAllowUnload, 0, 0, 0, 0));
 }
Exemple #11
0
        // --- apply transparent color ---

        /// <summary>Applies a transparent color onto a texture.</summary>
        /// <param name="texture">The original texture.</param>
        /// <param name="color">The transparent color, or a null reference.</param>
        /// <returns>The texture with the transparent color applied.</returns>
        /// <exception cref="System.NotSupportedException">Raised when the number of bits per pixel in the texture is not supported.</exception>
        internal static Texture ApplyTransparentColor(Texture texture, Color24?color)
        {
            if (color == null)
            {
                return(texture);
            }
            if (texture.Palette != null && texture.CompatibleTransparencyMode == true)
            {
                switch (texture.Palette.Length)
                {
                case 0:
                    //ignore if no reduced pallette
                    break;

                default:
                    Color24 c = (Color24)color;
                    color = GetClosestColor(texture.Palette, c);
                    break;
                }
            }
            if (texture.BitsPerPixel == 32)
            {
                int    width  = texture.Width;
                int    height = texture.Height;
                byte[] source = texture.Bytes;
                byte[] target = new byte[4 * width * height];
                byte   r      = color.Value.R;
                byte   g      = color.Value.G;
                byte   b      = color.Value.B;
                if (source[0] == r && source[1] == g && source[2] == b)
                {
                    target[0] = 128;
                    target[1] = 128;
                    target[2] = 128;
                    target[3] = 0;
                }
                else
                {
                    target[0] = source[0];
                    target[1] = source[1];
                    target[2] = source[2];
                    target[3] = source[3];
                }
                for (int i = 4; i < source.Length; i += 4)
                {
                    if (source[i] == r && source[i + 1] == g && source[i + 2] == b)
                    {
                        target[i + 0] = target[i - 4];
                        target[i + 1] = target[i - 3];
                        target[i + 2] = target[i - 2];
                        target[i + 3] = 0;
                    }
                    else
                    {
                        target[i + 0] = source[i + 0];
                        target[i + 1] = source[i + 1];
                        target[i + 2] = source[i + 2];
                        target[i + 3] = source[i + 3];
                    }
                }
                return(new Texture(width, height, 32, target, texture.Palette));
            }
            throw new NotSupportedException();
        }
Exemple #12
0
 private static int GetDiff(Color24 c1, Color24 c2)
 {
     return((int)System.Math.Sqrt((c1.R - c2.R) * (c1.R - c2.R)
                                  + (c1.G - c2.G) * (c1.G - c2.G)
                                  + (c1.B - c2.B) * (c1.B - c2.B)));
 }
Exemple #13
0
        private RawMapEntity LoadMapEntityFromStream(string chunkFourCC, EndianBinaryReader reader, MapEntityDataDescriptor template)
        {
            RawMapEntity obj = new RawMapEntity();

            obj.Fields = new PropertyCollection();
            obj.FourCC = chunkFourCC;

            // We're going to examine the Template's properties and load based on the current template type.
            for (int i = 0; i < template.Fields.Count; i++)
            {
                var          templateProperty = template.Fields[i];
                string       propertyName     = templateProperty.FieldName;
                PropertyType type             = templateProperty.FieldType;
                object       value            = null;

                switch (type)
                {
                case PropertyType.FixedLengthString:
                    value = reader.ReadString(templateProperty.Length).Trim(new[] { '\0' });
                    break;

                case PropertyType.String:
                    value = reader.ReadStringUntil('\0');
                    break;

                case PropertyType.Byte:
                    value = reader.ReadByte();
                    break;

                case PropertyType.Short:
                    value = reader.ReadInt16();
                    break;

                case PropertyType.Int32BitField:
                case PropertyType.Int32:
                    value = reader.ReadInt32();
                    break;

                case PropertyType.Float:
                    value = reader.ReadSingle();
                    break;

                case PropertyType.Vector3:
                    value = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                    break;

                case PropertyType.Vector2:
                    value = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                    break;

                case PropertyType.Enum:
                    byte enumIndexBytes = reader.ReadByte();     // ToDo: Resolve to actual Enum later.
                    value = enumIndexBytes;
                    break;

                case PropertyType.ObjectReference:
                    // When we first resolve them, we're going to keep the value as the reference byte,
                    // and then when they are post-processed they'll be turned into a proper type.
                    value = (int)reader.ReadByte();
                    break;

                case PropertyType.ObjectReferenceShort:
                    // When we first resolve them, we're going to keep the value as the reference byte,
                    // and then when they are post-processed they'll be turned into a proper type.
                    value = (int)reader.ReadUInt16();
                    break;

                case PropertyType.ObjectReferenceArray:
                    // When we first resolve them, we're going to keep the value as the reference byte,
                    // and then when they are post-processed they'll be turned into a proper type.
                    var refList = new BindingList <object>();
                    for (int refArray = 0; refArray < templateProperty.Length; refArray++)
                    {
                        refList.Add((int)reader.ReadByte());
                    }
                    value = refList;
                    break;

                case PropertyType.XYRotation:
                {
                    Vector3 eulerAngles = new Vector3();
                    for (int f = 0; f < 2; f++)
                    {
                        eulerAngles[f] = (reader.ReadInt16() * (180 / 32786f));
                    }

                    Quaternion xAxis = Quaternion.FromAxisAngle(new Vector3(1, 0, 0), eulerAngles.X * MathE.Deg2Rad);
                    Quaternion yAxis = Quaternion.FromAxisAngle(new Vector3(0, 1, 0), eulerAngles.Y * MathE.Deg2Rad);

                    // Swizzling to the ZYX order seems to be the right one.
                    Quaternion finalRot = yAxis * xAxis;
                    value = finalRot;
                }
                break;

                case PropertyType.XYZRotation:
                {
                    Vector3 eulerAngles = new Vector3();
                    for (int f = 0; f < 3; f++)
                    {
                        eulerAngles[f] = (reader.ReadInt16() * (180 / 32786f));
                    }

                    Quaternion xAxis = Quaternion.FromAxisAngle(new Vector3(1, 0, 0), eulerAngles.X * MathE.Deg2Rad);
                    Quaternion yAxis = Quaternion.FromAxisAngle(new Vector3(0, 1, 0), eulerAngles.Y * MathE.Deg2Rad);
                    Quaternion zAxis = Quaternion.FromAxisAngle(new Vector3(0, 0, 1), eulerAngles.Z * MathE.Deg2Rad);

                    // Swizzling to the ZYX order seems to be the right one.
                    Quaternion finalRot = zAxis * yAxis * xAxis;
                    value = finalRot;
                }
                break;

                case PropertyType.YRotation:
                {
                    float yRotation = reader.ReadInt16() * (180 / 32786f);

                    Quaternion yAxis = Quaternion.FromAxisAngle(new Vector3(0, 1, 0), yRotation * MathE.Deg2Rad);
                    value = yAxis;
                }
                break;

                case PropertyType.Color32:
                    value = new Color32(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
                    break;

                case PropertyType.Color24:
                    value = new Color24(reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
                    break;

                case PropertyType.Vector3Byte:
                    type  = PropertyType.Vector3Byte;
                    value = new Vector3(reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
                    break;

                case PropertyType.Bits:
                    value = (byte)reader.ReadBits(templateProperty.Length);
                    break;
                }

                // This... this could get dicy. If the template we just read was a "Name" then we now have the technical name (and value)
                // of the object. We can then search for the MapObjectDataDescriptor that matches the technical name, and then edit the
                // remaining fields. However, this gets somewhat dicey, because we're modifying the length of the Fields array for templates
                // while iterating through it. However, the Name field always comes before any of the fields we'd want to modify, we're going to
                // do an in-place replacement of the fields (since Fields.Count will increase) and then we get free loading of the complex templates
                // without later post-processing them.
                if (templateProperty.FieldName == "Name")
                {
                    // See if our template list has a complex version of this file, otherwise grab the default.
                    MapObjectDataDescriptor complexDescriptor = m_editorCore.Templates.MapObjectDataDescriptors.Find(x => x.FourCC == chunkFourCC && x.TechnicalName == templateProperty.FieldName);
                    if (complexDescriptor == null)
                    {
                        complexDescriptor = m_editorCore.Templates.DefaultMapObjectDataDescriptor;
                    }

                    // Determine which field we need to remove, and then insert in the other fields (in order) where it used to be.
                    foreach (var fieldToReplace in complexDescriptor.DataOverrides)
                    {
                        for (int k = 0; k < template.Fields.Count; k++)
                        {
                            if (template.Fields[k].FieldName == fieldToReplace.ParameterName)
                            {
                                // Remove the old field.
                                template.Fields.RemoveAt(k);

                                // Now insert the new fields starting at the location of the one we just replaced.
                                template.Fields.InsertRange(k, fieldToReplace.Values);
                                break;
                            }
                        }
                    }
                }

                Property instanceProp = new Property(templateProperty.FieldName, type, value);
                obj.Fields.Properties.Add(instanceProp);
            }

            return(obj);
        }
Exemple #14
0
        // render scene
        internal void RenderScene(double TimeElapsed)
        {
            // initialize
            ResetOpenGlState();

            if (OptionWireFrame)
            {
                if (Program.CurrentRoute.CurrentFog.Start < Program.CurrentRoute.CurrentFog.End)
                {
                    const float fogDistance = 600.0f;
                    float       n           = (fogDistance - Program.CurrentRoute.CurrentFog.Start) / (Program.CurrentRoute.CurrentFog.End - Program.CurrentRoute.CurrentFog.Start);
                    float       cr          = n * inv255 * Program.CurrentRoute.CurrentFog.Color.R;
                    float       cg          = n * inv255 * Program.CurrentRoute.CurrentFog.Color.G;
                    float       cb          = n * inv255 * Program.CurrentRoute.CurrentFog.Color.B;
                    GL.ClearColor(cr, cg, cb, 1.0f);
                }
                else
                {
                    GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
                }
            }

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            UpdateViewport(ViewportChangeMode.ChangeToScenery);

            // set up camera
            CurrentViewMatrix = Matrix4D.LookAt(Vector3.Zero, new Vector3(Camera.AbsoluteDirection.X, Camera.AbsoluteDirection.Y, -Camera.AbsoluteDirection.Z), new Vector3(Camera.AbsoluteUp.X, Camera.AbsoluteUp.Y, -Camera.AbsoluteUp.Z));
            GL.Light(LightName.Light0, LightParameter.Position, new[] { (float)Lighting.OptionLightPosition.X, (float)Lighting.OptionLightPosition.Y, (float)-Lighting.OptionLightPosition.Z, 0.0f });

            // fog
            double fd = Program.CurrentRoute.NextFog.TrackPosition - Program.CurrentRoute.PreviousFog.TrackPosition;

            if (fd != 0.0)
            {
                float fr  = (float)((World.CameraTrackFollower.TrackPosition - Program.CurrentRoute.PreviousFog.TrackPosition) / fd);
                float frc = 1.0f - fr;
                Program.CurrentRoute.CurrentFog.Start   = Program.CurrentRoute.PreviousFog.Start * frc + Program.CurrentRoute.NextFog.Start * fr;
                Program.CurrentRoute.CurrentFog.End     = Program.CurrentRoute.PreviousFog.End * frc + Program.CurrentRoute.NextFog.End * fr;
                Program.CurrentRoute.CurrentFog.Color.R = (byte)(Program.CurrentRoute.PreviousFog.Color.R * frc + Program.CurrentRoute.NextFog.Color.R * fr);
                Program.CurrentRoute.CurrentFog.Color.G = (byte)(Program.CurrentRoute.PreviousFog.Color.G * frc + Program.CurrentRoute.NextFog.Color.G * fr);
                Program.CurrentRoute.CurrentFog.Color.B = (byte)(Program.CurrentRoute.PreviousFog.Color.B * frc + Program.CurrentRoute.NextFog.Color.B * fr);
            }
            else
            {
                Program.CurrentRoute.CurrentFog = Program.CurrentRoute.PreviousFog;
            }

            // render background
            GL.Disable(EnableCap.DepthTest);
            Program.CurrentRoute.UpdateBackground(TimeElapsed, Game.CurrentInterface != Game.InterfaceType.Normal);

            events.Render(Camera.AbsolutePosition);

            // fog
            float aa = Program.CurrentRoute.CurrentFog.Start;
            float bb = Program.CurrentRoute.CurrentFog.End;

            if (aa < bb & aa < Program.CurrentRoute.CurrentBackground.BackgroundImageDistance)
            {
                OptionFog = true;
                Fog.Start = aa;
                Fog.End   = bb;
                Fog.Color = Program.CurrentRoute.CurrentFog.Color;
                SetFogForImmediateMode();
            }
            else
            {
                OptionFog = false;
            }

            // world layer
            // opaque face
            ResetOpenGlState();

            foreach (FaceState face in VisibleObjects.OpaqueFaces)
            {
                if (Interface.CurrentOptions.IsUseNewRenderer)
                {
                    DefaultShader.Activate();
                    ResetShader(DefaultShader);
                    RenderFace(DefaultShader, face);
                }
                else
                {
                    RenderFaceImmediateMode(face);
                }
            }

            // alpha face
            ResetOpenGlState();
            VisibleObjects.SortPolygonsInAlphaFaces();

            if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance)
            {
                SetBlendFunc();
                SetAlphaFunc(AlphaFunction.Greater, 0.0f);
                GL.DepthMask(false);

                foreach (FaceState face in VisibleObjects.AlphaFaces)
                {
                    if (Interface.CurrentOptions.IsUseNewRenderer)
                    {
                        DefaultShader.Activate();
                        ResetShader(DefaultShader);
                        RenderFace(DefaultShader, face);
                    }
                    else
                    {
                        RenderFaceImmediateMode(face);
                    }
                }
            }
            else
            {
                UnsetBlendFunc();
                SetAlphaFunc(AlphaFunction.Equal, 1.0f);
                GL.DepthMask(true);

                foreach (FaceState face in VisibleObjects.AlphaFaces)
                {
                    if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Normal && face.Object.Prototype.Mesh.Materials[face.Face.Material].GlowAttenuationData == 0)
                    {
                        if (face.Object.Prototype.Mesh.Materials[face.Face.Material].Color.A == 255)
                        {
                            if (Interface.CurrentOptions.IsUseNewRenderer)
                            {
                                DefaultShader.Activate();
                                ResetShader(DefaultShader);
                                RenderFace(DefaultShader, face);
                            }
                            else
                            {
                                RenderFaceImmediateMode(face);
                            }
                        }
                    }
                }

                SetBlendFunc();
                SetAlphaFunc(AlphaFunction.Less, 1.0f);
                GL.DepthMask(false);
                bool additive = false;

                foreach (FaceState face in VisibleObjects.AlphaFaces)
                {
                    if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Additive)
                    {
                        if (!additive)
                        {
                            UnsetAlphaFunc();
                            additive = true;
                        }

                        if (Interface.CurrentOptions.IsUseNewRenderer)
                        {
                            DefaultShader.Activate();
                            ResetShader(DefaultShader);
                            RenderFace(DefaultShader, face);
                        }
                        else
                        {
                            RenderFaceImmediateMode(face);
                        }
                    }
                    else
                    {
                        if (additive)
                        {
                            SetAlphaFunc();
                            additive = false;
                        }

                        if (Interface.CurrentOptions.IsUseNewRenderer)
                        {
                            DefaultShader.Activate();
                            ResetShader(DefaultShader);
                            RenderFace(DefaultShader, face);
                        }
                        else
                        {
                            RenderFaceImmediateMode(face);
                        }
                    }
                }
            }

            // motion blur
            ResetOpenGlState();
            SetAlphaFunc(AlphaFunction.Greater, 0.0f);
            GL.Disable(EnableCap.DepthTest);
            GL.DepthMask(false);
            OptionLighting = false;

            if (Interface.CurrentOptions.MotionBlur != MotionBlurMode.None)
            {
                MotionBlur.RenderFullscreen(Interface.CurrentOptions.MotionBlur, FrameRate, Math.Abs(Camera.CurrentSpeed));
            }

            // overlay layer
            OptionFog = false;
            UpdateViewport(ViewportChangeMode.ChangeToCab);
            CurrentViewMatrix = Matrix4D.LookAt(Vector3.Zero, new Vector3(Camera.AbsoluteDirection.X, Camera.AbsoluteDirection.Y, -Camera.AbsoluteDirection.Z), new Vector3(Camera.AbsoluteUp.X, Camera.AbsoluteUp.Y, -Camera.AbsoluteUp.Z));

            if (Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable || Camera.CurrentRestriction == CameraRestrictionMode.Restricted3D)
            {
                ResetOpenGlState();                 // TODO: inserted
                GL.Clear(ClearBufferMask.DepthBufferBit);
                OptionLighting = true;
                Color24 prevOptionAmbientColor = Lighting.OptionAmbientColor;
                Color24 prevOptionDiffuseColor = Lighting.OptionDiffuseColor;
                Lighting.OptionAmbientColor = new Color24(178, 178, 178);
                Lighting.OptionDiffuseColor = new Color24(178, 178, 178);
                GL.Light(LightName.Light0, LightParameter.Ambient, new[] { inv255 * 178, inv255 * 178, inv255 * 178, 1.0f });
                GL.Light(LightName.Light0, LightParameter.Diffuse, new[] { inv255 * 178, inv255 * 178, inv255 * 178, 1.0f });

                // overlay opaque face
                foreach (FaceState face in VisibleObjects.OverlayOpaqueFaces)
                {
                    if (Interface.CurrentOptions.IsUseNewRenderer)
                    {
                        DefaultShader.Activate();
                        ResetShader(DefaultShader);
                        RenderFace(DefaultShader, face);
                    }
                    else
                    {
                        RenderFaceImmediateMode(face);
                    }
                }

                // overlay alpha face
                ResetOpenGlState();
                VisibleObjects.SortPolygonsInOverlayAlphaFaces();

                if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance)
                {
                    SetBlendFunc();
                    SetAlphaFunc(AlphaFunction.Greater, 0.0f);
                    GL.DepthMask(false);

                    foreach (FaceState face in VisibleObjects.OverlayAlphaFaces)
                    {
                        if (Interface.CurrentOptions.IsUseNewRenderer)
                        {
                            DefaultShader.Activate();
                            ResetShader(DefaultShader);
                            RenderFace(DefaultShader, face);
                        }
                        else
                        {
                            RenderFaceImmediateMode(face);
                        }
                    }
                }
                else
                {
                    UnsetBlendFunc();
                    SetAlphaFunc(AlphaFunction.Equal, 1.0f);
                    GL.DepthMask(true);

                    foreach (FaceState face in VisibleObjects.OverlayAlphaFaces)
                    {
                        if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Normal && face.Object.Prototype.Mesh.Materials[face.Face.Material].GlowAttenuationData == 0)
                        {
                            if (face.Object.Prototype.Mesh.Materials[face.Face.Material].Color.A == 255)
                            {
                                if (Interface.CurrentOptions.IsUseNewRenderer)
                                {
                                    DefaultShader.Activate();
                                    ResetShader(DefaultShader);
                                    RenderFace(DefaultShader, face);
                                }
                                else
                                {
                                    RenderFaceImmediateMode(face);
                                }
                            }
                        }
                    }

                    SetBlendFunc();
                    SetAlphaFunc(AlphaFunction.Less, 1.0f);
                    GL.DepthMask(false);
                    bool additive = false;

                    foreach (FaceState face in VisibleObjects.OverlayAlphaFaces)
                    {
                        if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Additive)
                        {
                            if (!additive)
                            {
                                UnsetAlphaFunc();
                                additive = true;
                            }

                            if (Interface.CurrentOptions.IsUseNewRenderer)
                            {
                                DefaultShader.Activate();
                                ResetShader(DefaultShader);
                                RenderFace(DefaultShader, face);
                            }
                            else
                            {
                                RenderFaceImmediateMode(face);
                            }
                        }
                        else
                        {
                            if (additive)
                            {
                                SetAlphaFunc();
                                additive = false;
                            }

                            if (Interface.CurrentOptions.IsUseNewRenderer)
                            {
                                DefaultShader.Activate();
                                ResetShader(DefaultShader);
                                RenderFace(DefaultShader, face);
                            }
                            else
                            {
                                RenderFaceImmediateMode(face);
                            }
                        }
                    }
                }

                Lighting.OptionAmbientColor = prevOptionAmbientColor;
                Lighting.OptionDiffuseColor = prevOptionDiffuseColor;
                Lighting.Initialize();
            }
            else
            {
                /*
                 * Render 2D Cab
                 * This is actually an animated object generated on the fly and held in memory
                 */
                ResetOpenGlState();
                OptionLighting = false;
                SetBlendFunc();
                UnsetAlphaFunc();
                GL.Disable(EnableCap.DepthTest);
                GL.DepthMask(false);
                VisibleObjects.SortPolygonsInOverlayAlphaFaces();

                foreach (FaceState face in VisibleObjects.OverlayAlphaFaces)
                {
                    if (Interface.CurrentOptions.IsUseNewRenderer)
                    {
                        DefaultShader.Activate();
                        ResetShader(DefaultShader);
                        RenderFace(DefaultShader, face);
                    }
                    else
                    {
                        RenderFaceImmediateMode(face);
                    }
                }
            }

            // render touch
            OptionLighting = false;
            Touch.RenderScene();

            // render overlays
            ResetOpenGlState();
            UnsetAlphaFunc();
            GL.Disable(EnableCap.DepthTest);
            overlays.Render(TimeElapsed);
            OptionLighting = true;
        }
Exemple #15
0
        // render scene
        internal void RenderScene(double TimeElapsed, double RealTimeElapsed)
        {
            ReleaseResources();
            // initialize
            ResetOpenGlState();

            if (OptionWireFrame)
            {
                if (Program.CurrentRoute.CurrentFog.Start < Program.CurrentRoute.CurrentFog.End)
                {
                    const float fogDistance = 600.0f;
                    float       n           = (fogDistance - Program.CurrentRoute.CurrentFog.Start) / (Program.CurrentRoute.CurrentFog.End - Program.CurrentRoute.CurrentFog.Start);
                    float       cr          = n * inv255 * Program.CurrentRoute.CurrentFog.Color.R;
                    float       cg          = n * inv255 * Program.CurrentRoute.CurrentFog.Color.G;
                    float       cb          = n * inv255 * Program.CurrentRoute.CurrentFog.Color.B;
                    GL.ClearColor(cr, cg, cb, 1.0f);
                }
                else
                {
                    GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
                }
            }

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            UpdateViewport(ViewportChangeMode.ChangeToScenery);

            // set up camera
            CurrentViewMatrix        = Matrix4D.LookAt(Vector3.Zero, new Vector3(Camera.AbsoluteDirection.X, Camera.AbsoluteDirection.Y, -Camera.AbsoluteDirection.Z), new Vector3(Camera.AbsoluteUp.X, Camera.AbsoluteUp.Y, -Camera.AbsoluteUp.Z));
            TransformedLightPosition = new Vector3(Lighting.OptionLightPosition.X, Lighting.OptionLightPosition.Y, -Lighting.OptionLightPosition.Z);
            TransformedLightPosition.Transform(CurrentViewMatrix);
            if (!AvailableNewRenderer)
            {
                GL.Light(LightName.Light0, LightParameter.Position, new[] { (float)TransformedLightPosition.X, (float)TransformedLightPosition.Y, (float)TransformedLightPosition.Z, 0.0f });
            }

            Lighting.OptionLightingResultingAmount = (Lighting.OptionAmbientColor.R + Lighting.OptionAmbientColor.G + Lighting.OptionAmbientColor.B) / 480.0f;

            if (Lighting.OptionLightingResultingAmount > 1.0f)
            {
                Lighting.OptionLightingResultingAmount = 1.0f;
            }
            // fog
            double fd = Program.CurrentRoute.NextFog.TrackPosition - Program.CurrentRoute.PreviousFog.TrackPosition;

            if (fd != 0.0)
            {
                float fr  = (float)((CameraTrackFollower.TrackPosition - Program.CurrentRoute.PreviousFog.TrackPosition) / fd);
                float frc = 1.0f - fr;
                Program.CurrentRoute.CurrentFog.Start   = Program.CurrentRoute.PreviousFog.Start * frc + Program.CurrentRoute.NextFog.Start * fr;
                Program.CurrentRoute.CurrentFog.End     = Program.CurrentRoute.PreviousFog.End * frc + Program.CurrentRoute.NextFog.End * fr;
                Program.CurrentRoute.CurrentFog.Color.R = (byte)(Program.CurrentRoute.PreviousFog.Color.R * frc + Program.CurrentRoute.NextFog.Color.R * fr);
                Program.CurrentRoute.CurrentFog.Color.G = (byte)(Program.CurrentRoute.PreviousFog.Color.G * frc + Program.CurrentRoute.NextFog.Color.G * fr);
                Program.CurrentRoute.CurrentFog.Color.B = (byte)(Program.CurrentRoute.PreviousFog.Color.B * frc + Program.CurrentRoute.NextFog.Color.B * fr);
                if (!Program.CurrentRoute.CurrentFog.IsLinear)
                {
                    Program.CurrentRoute.CurrentFog.Density = (byte)(Program.CurrentRoute.PreviousFog.Density * frc + Program.CurrentRoute.NextFog.Density * fr);
                }
            }
            else
            {
                Program.CurrentRoute.CurrentFog = Program.CurrentRoute.PreviousFog;
            }

            // render background
            GL.Disable(EnableCap.DepthTest);
            Program.CurrentRoute.UpdateBackground(TimeElapsed, Program.Renderer.CurrentInterface != InterfaceType.Normal);

            events.Render(Camera.AbsolutePosition);

            // fog
            float aa = Program.CurrentRoute.CurrentFog.Start;
            float bb = Program.CurrentRoute.CurrentFog.End;

            if (aa < bb & aa < Program.CurrentRoute.CurrentBackground.BackgroundImageDistance)
            {
                OptionFog    = true;
                Fog.Start    = aa;
                Fog.End      = bb;
                Fog.Color    = Program.CurrentRoute.CurrentFog.Color;
                Fog.Density  = Program.CurrentRoute.CurrentFog.Density;
                Fog.IsLinear = Program.CurrentRoute.CurrentFog.IsLinear;
                Fog.SetForImmediateMode();
            }
            else
            {
                OptionFog = false;
            }

            // world layer
            // opaque face
            if (AvailableNewRenderer)
            {
                //Setup the shader for rendering the scene
                DefaultShader.Activate();
                if (OptionLighting)
                {
                    DefaultShader.SetIsLight(true);
                    DefaultShader.SetLightPosition(TransformedLightPosition);
                    DefaultShader.SetLightAmbient(Lighting.OptionAmbientColor);
                    DefaultShader.SetLightDiffuse(Lighting.OptionDiffuseColor);
                    DefaultShader.SetLightSpecular(Lighting.OptionSpecularColor);
                    DefaultShader.SetLightModel(Lighting.LightModel);
                }
                if (OptionFog)
                {
                    DefaultShader.SetIsFog(true);
                    DefaultShader.SetFog(Fog);
                }
                DefaultShader.SetTexture(0);
                DefaultShader.SetCurrentProjectionMatrix(CurrentProjectionMatrix);
            }
            ResetOpenGlState();
            foreach (FaceState face in VisibleObjects.OpaqueFaces)
            {
                face.Draw();
            }

            // alpha face
            ResetOpenGlState();
            VisibleObjects.SortPolygonsInAlphaFaces();
            if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance)
            {
                SetBlendFunc();
                SetAlphaFunc(AlphaFunction.Greater, 0.0f);
                GL.DepthMask(false);

                foreach (FaceState face in VisibleObjects.AlphaFaces)
                {
                    face.Draw();
                }
            }
            else
            {
                UnsetBlendFunc();
                SetAlphaFunc(AlphaFunction.Equal, 1.0f);
                GL.DepthMask(true);

                foreach (FaceState face in VisibleObjects.AlphaFaces)
                {
                    if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Normal && face.Object.Prototype.Mesh.Materials[face.Face.Material].GlowAttenuationData == 0)
                    {
                        if (face.Object.Prototype.Mesh.Materials[face.Face.Material].Color.A == 255)
                        {
                            face.Draw();
                        }
                    }
                }

                SetBlendFunc();
                SetAlphaFunc(AlphaFunction.Less, 1.0f);
                GL.DepthMask(false);
                bool additive = false;

                foreach (FaceState face in VisibleObjects.AlphaFaces)
                {
                    if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Additive)
                    {
                        if (!additive)
                        {
                            UnsetAlphaFunc();
                            additive = true;
                        }

                        face.Draw();
                    }
                    else
                    {
                        if (additive)
                        {
                            SetAlphaFunc();
                            additive = false;
                        }
                        face.Draw();
                    }
                }
            }

            // motion blur
            ResetOpenGlState();
            SetAlphaFunc(AlphaFunction.Greater, 0.0f);
            GL.Disable(EnableCap.DepthTest);
            GL.DepthMask(false);
            OptionLighting = false;

            if (Interface.CurrentOptions.MotionBlur != MotionBlurMode.None)
            {
                DefaultShader.Deactivate();
                MotionBlur.RenderFullscreen(Interface.CurrentOptions.MotionBlur, FrameRate, Math.Abs(Camera.CurrentSpeed));
            }
            // overlay layer
            OptionFog = false;
            UpdateViewport(ViewportChangeMode.ChangeToCab);
            if (AvailableNewRenderer)
            {
                /*
                 * We must reset the shader between overlay and world layers for correct lighting results.
                 * Additionally, the viewport change updates the projection matrix
                 */
                DefaultShader.Activate();
                ResetShader(DefaultShader);
                DefaultShader.SetCurrentProjectionMatrix(CurrentProjectionMatrix);
            }
            CurrentViewMatrix = Matrix4D.LookAt(Vector3.Zero, new Vector3(Camera.AbsoluteDirection.X, Camera.AbsoluteDirection.Y, -Camera.AbsoluteDirection.Z), new Vector3(Camera.AbsoluteUp.X, Camera.AbsoluteUp.Y, -Camera.AbsoluteUp.Z));
            if (Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable || Camera.CurrentRestriction == CameraRestrictionMode.Restricted3D)
            {
                ResetOpenGlState();                 // TODO: inserted
                GL.Clear(ClearBufferMask.DepthBufferBit);
                OptionLighting = true;
                Color24 prevOptionAmbientColor = Lighting.OptionAmbientColor;
                Color24 prevOptionDiffuseColor = Lighting.OptionDiffuseColor;
                Lighting.OptionAmbientColor = Color24.LightGrey;
                Lighting.OptionDiffuseColor = Color24.LightGrey;
                if (AvailableNewRenderer)
                {
                    DefaultShader.SetIsLight(true);
                    TransformedLightPosition = new Vector3(Lighting.OptionLightPosition.X, Lighting.OptionLightPosition.Y, -Lighting.OptionLightPosition.Z);
                    DefaultShader.SetLightPosition(TransformedLightPosition);
                    DefaultShader.SetLightAmbient(Lighting.OptionAmbientColor);
                    DefaultShader.SetLightDiffuse(Lighting.OptionDiffuseColor);
                    DefaultShader.SetLightSpecular(Lighting.OptionSpecularColor);
                    DefaultShader.SetLightModel(Lighting.LightModel);
                }
                else
                {
                    GL.Light(LightName.Light0, LightParameter.Ambient, new[] { inv255 * 178, inv255 * 178, inv255 * 178, 1.0f });
                    GL.Light(LightName.Light0, LightParameter.Diffuse, new[] { inv255 * 178, inv255 * 178, inv255 * 178, 1.0f });
                }


                // overlay opaque face
                foreach (FaceState face in VisibleObjects.OverlayOpaqueFaces)
                {
                    face.Draw();
                }

                // overlay alpha face
                ResetOpenGlState();
                VisibleObjects.SortPolygonsInOverlayAlphaFaces();

                if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance)
                {
                    SetBlendFunc();
                    SetAlphaFunc(AlphaFunction.Greater, 0.0f);
                    GL.DepthMask(false);

                    foreach (FaceState face in VisibleObjects.OverlayAlphaFaces)
                    {
                        face.Draw();
                    }
                }
                else
                {
                    UnsetBlendFunc();
                    SetAlphaFunc(AlphaFunction.Equal, 1.0f);
                    GL.DepthMask(true);

                    foreach (FaceState face in VisibleObjects.OverlayAlphaFaces)
                    {
                        if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Normal && face.Object.Prototype.Mesh.Materials[face.Face.Material].GlowAttenuationData == 0)
                        {
                            if (face.Object.Prototype.Mesh.Materials[face.Face.Material].Color.A == 255)
                            {
                                face.Draw();
                            }
                        }
                    }

                    SetBlendFunc();
                    SetAlphaFunc(AlphaFunction.Less, 1.0f);
                    GL.DepthMask(false);
                    bool additive = false;

                    foreach (FaceState face in VisibleObjects.OverlayAlphaFaces)
                    {
                        if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Additive)
                        {
                            if (!additive)
                            {
                                UnsetAlphaFunc();
                                additive = true;
                            }

                            face.Draw();
                        }
                        else
                        {
                            if (additive)
                            {
                                SetAlphaFunc();
                                additive = false;
                            }

                            face.Draw();
                        }
                    }
                }

                Lighting.OptionAmbientColor = prevOptionAmbientColor;
                Lighting.OptionDiffuseColor = prevOptionDiffuseColor;
                Lighting.Initialize();
            }
            else
            {
                /*
                 * Render 2D Cab
                 * This is actually an animated object generated on the fly and held in memory
                 */
                ResetOpenGlState();
                OptionLighting = false;
                if (AvailableNewRenderer)
                {
                    DefaultShader.SetIsLight(false);
                }

                SetBlendFunc();
                UnsetAlphaFunc();
                GL.Disable(EnableCap.DepthTest);
                GL.DepthMask(false);
                VisibleObjects.SortPolygonsInOverlayAlphaFaces();
                foreach (FaceState face in VisibleObjects.OverlayAlphaFaces)
                {
                    face.Draw();
                }
            }
            if (AvailableNewRenderer)
            {
                /*
                 * Must remember to de-activate at the end of the render sequence if in GL3 mode.
                 * The overlays currently use immediate mode and do not work correctly with the shader active
                 */
                DefaultShader.Deactivate();
            }
            // render touch
            OptionLighting = false;
            Touch.RenderScene();

            // render overlays
            ResetOpenGlState();
            UnsetAlphaFunc();
            SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);             //FIXME: Remove when text switches between two renderer types
            GL.Disable(EnableCap.DepthTest);
            overlays.Render(RealTimeElapsed);
            OptionLighting = true;
        }
Exemple #16
0
 public void SetFogColor(Color24 FogColor)
 {
     GL.Uniform3(UniformLayout.FogColor, FogColor.R / 255.0f, FogColor.G / 255.0f, FogColor.B / 255.0f);
 }
Exemple #17
0
        internal static ObjectManager.StaticObject ReadObject(string FileName)
        {
            currentFolder = System.IO.Path.GetDirectoryName(FileName);
            currentFile   = FileName;

#if !DEBUG
            try
            {
#endif
            ObjFileParser parser = new ObjFileParser(System.IO.File.ReadAllLines(currentFile), null, System.IO.Path.GetFileNameWithoutExtension(currentFile), currentFile);
            Model model          = parser.GetModel();

            ObjectManager.StaticObject obj = new ObjectManager.StaticObject();
            MeshBuilder builder            = new MeshBuilder();

            List <Vertex> allVertices = new List <Vertex>();
            foreach (var vertex in model.Vertices)
            {
                allVertices.Add(new Vertex(vertex.X, vertex.Y, vertex.Z));
            }

            List <Vector2> allTexCoords = new List <Vector2>();
            foreach (var texCoord in model.TextureCoord)
            {
                allTexCoords.Add(new Vector2(texCoord.X, texCoord.Y));
            }

            List <Vector3> allNormals = new List <Vector3>();
            foreach (var normal in model.Normals)
            {
                allNormals.Add(new Vector3(normal.X, normal.Y, normal.Z));
            }

            foreach (AssimpNET.Obj.Mesh mesh in model.Meshes)
            {
                foreach (Face face in mesh.Faces)
                {
                    int nVerts = face.Vertices.Count;
                    if (nVerts == 0)
                    {
                        throw new Exception("nVertices must be greater than zero");
                    }
                    int v = builder.Vertices.Length;
                    Array.Resize(ref builder.Vertices, v + nVerts);
                    for (int i = 0; i < nVerts; i++)
                    {
                        builder.Vertices[v + i] = allVertices[(int)face.Vertices[i]];
                    }

                    int f = builder.Faces.Length;
                    Array.Resize(ref builder.Faces, f + 1);
                    builder.Faces[f]          = new MeshFace();
                    builder.Faces[f].Vertices = new MeshFaceVertex[nVerts];
                    for (int i = 0; i < nVerts; i++)
                    {
                        builder.Faces[f].Vertices[i].Index = (ushort)i;
                    }
                    builder.Faces[f].Material = 1;

                    int m = builder.Materials.Length;
                    Array.Resize(ref builder.Materials, m + 1);
                    builder.Materials[m] = new Material();
                    uint materialIndex = mesh.MaterialIndex;
                    if (materialIndex != AssimpNET.Obj.Mesh.NoMaterial)
                    {
                        AssimpNET.Obj.Material material = model.MaterialMap[model.MaterialLib[(int)materialIndex]];
                        builder.Materials[m].Color = new Color32((byte)(255 * material.Diffuse.R), (byte)(255 * material.Diffuse.G), (byte)(255 * material.Diffuse.B), (byte)(255 * material.Diffuse.A));
                        Color24 mSpecular = new Color24((byte)material.Specular.R, (byte)material.Specular.G, (byte)material.Specular.B);
                        builder.Materials[m].EmissiveColor        = new Color24((byte)(255 * material.Emissive.R), (byte)(255 * material.Emissive.G), (byte)(255 * material.Emissive.B));
                        builder.Materials[m].EmissiveColorUsed    = true;                              //TODO: Check exact behaviour
                        builder.Materials[m].TransparentColor     = new Color24((byte)(255 * material.Transparent.R), (byte)(255 * material.Transparent.G), (byte)(255 * material.Transparent.B));
                        builder.Materials[m].TransparentColorUsed = true;

                        if (material.Texture != null)
                        {
                            builder.Materials[m].DaytimeTexture = OpenBveApi.Path.CombineFile(currentFolder, material.Texture);
                            if (!System.IO.File.Exists(builder.Materials[m].DaytimeTexture))
                            {
                                Interface.AddMessage(MessageType.Error, true, "Texure " + builder.Materials[m].DaytimeTexture + " was not found in file " + currentFile);
                                builder.Materials[m].DaytimeTexture = null;
                            }
                        }
                    }

                    int nCoords = face.TexturCoords.Count;
                    for (int i = 0; i < nCoords; i++)
                    {
                        builder.Vertices[i].TextureCoordinates = allTexCoords[(int)face.TexturCoords[i]];
                    }

                    int       nNormals = face.Normals.Count;
                    Vector3[] normals  = new Vector3[nNormals];
                    for (int i = 0; i < nNormals; i++)
                    {
                        normals[i] = allNormals[(int)face.Normals[i]];
                        normals[i].Normalize();
                    }
                    for (int i = 0; i < nNormals; i++)
                    {
                        builder.Faces[0].Vertices[i].Normal = normals[i];
                    }

                    builder.Apply(ref obj);
                    builder = new MeshBuilder();
                }
            }
            obj.Mesh.CreateNormals();
            return(obj);

#if !DEBUG
        }

        catch (Exception e)
        {
            Interface.AddMessage(MessageType.Error, false, e.Message + " in " + FileName);
            return(null);
        }
#endif
        }
Exemple #18
0
 public void SetLightAmbient(Color24 LightAmbient)
 {
     GL.Uniform3(UniformLayout.LightAmbient, LightAmbient.R / 255.0f, LightAmbient.G / 255.0f, LightAmbient.B / 255.0f);
 }
Exemple #19
0
        private static void  MeshBuilder(ref ObjectManager.StaticObject obj, ref MeshBuilder builder, AssimpNET.X.Mesh mesh)
        {
            if (builder.Vertices.Length != 0)
            {
                builder.Apply(ref obj);
                builder = new MeshBuilder();
            }

            int nVerts = mesh.Positions.Count;

            if (nVerts == 0)
            {
                //Some null objects contain an empty mesh
                Interface.AddMessage(MessageType.Warning, false, "nVertices should be greater than zero in Mesh " + mesh.Name);
            }
            int v = builder.Vertices.Length;

            Array.Resize(ref builder.Vertices, v + nVerts);
            for (int i = 0; i < nVerts; i++)
            {
                builder.Vertices[v + i] = new Vertex(mesh.Positions[i].X, mesh.Positions[i].Y, mesh.Positions[i].Z);
            }

            int nFaces = mesh.PosFaces.Count;
            int f      = builder.Faces.Length;

            Array.Resize(ref builder.Faces, f + nFaces);
            for (int i = 0; i < nFaces; i++)
            {
                int fVerts = mesh.PosFaces[i].Indices.Count;
                if (nFaces == 0)
                {
                    throw new Exception("fVerts must be greater than zero");
                }
                builder.Faces[f + i]          = new MeshFace();
                builder.Faces[f + i].Vertices = new MeshFaceVertex[fVerts];
                for (int j = 0; j < fVerts; j++)
                {
                    builder.Faces[f + i].Vertices[j].Index = (ushort)mesh.PosFaces[i].Indices[j];
                }
            }

            int nMaterials   = mesh.Materials.Count;
            int nFaceIndices = mesh.FaceMaterials.Count;

            for (int i = 0; i < nFaceIndices; i++)
            {
                int fMaterial = (int)mesh.FaceMaterials[i];
                builder.Faces[i].Material = (ushort)(fMaterial + 1);
            }
            for (int i = 0; i < nMaterials; i++)
            {
                int m = builder.Materials.Length;
                Array.Resize(ref builder.Materials, m + 1);
                builder.Materials[m]       = new Material();
                builder.Materials[m].Color = new Color32((byte)(255 * mesh.Materials[i].Diffuse.R), (byte)(255 * mesh.Materials[i].Diffuse.G), (byte)(255 * mesh.Materials[i].Diffuse.B), (byte)(255 * mesh.Materials[i].Diffuse.A));
                double  mPower    = mesh.Materials[i].SpecularExponent;             //TODO: Unsure what this does...
                Color24 mSpecular = new Color24((byte)mesh.Materials[i].Specular.R, (byte)mesh.Materials[i].Specular.G, (byte)mesh.Materials[i].Specular.B);
                builder.Materials[m].EmissiveColor        = new Color24((byte)(255 * mesh.Materials[i].Emissive.R), (byte)(255 * mesh.Materials[i].Emissive.G), (byte)(255 * mesh.Materials[i].Emissive.B));
                builder.Materials[m].EmissiveColorUsed    = true;              //TODO: Check exact behaviour
                builder.Materials[m].TransparentColor     = Color24.Black;     //TODO: Check, also can we optimise which faces have the transparent color set?
                builder.Materials[m].TransparentColorUsed = true;

                if (mesh.Materials[i].Textures.Count > 0)
                {
                    builder.Materials[m].DaytimeTexture = OpenBveApi.Path.CombineFile(currentFolder, mesh.Materials[i].Textures[0].Name);
                    if (!System.IO.File.Exists(builder.Materials[m].DaytimeTexture))
                    {
                        Interface.AddMessage(MessageType.Error, true, "Texure " + builder.Materials[m].DaytimeTexture + " was not found in file " + currentFile);
                        builder.Materials[m].DaytimeTexture = null;
                    }
                }
            }

            if (mesh.TexCoords.Length > 0 && mesh.TexCoords[0] != null)
            {
                int nCoords = mesh.TexCoords[0].Count;
                for (int i = 0; i < nCoords; i++)
                {
                    builder.Vertices[i].TextureCoordinates = new Vector2(mesh.TexCoords[0][i].X, mesh.TexCoords[0][i].Y);
                }
            }

            int nNormals = mesh.Normals.Count;

            Vector3[] normals = new Vector3[nNormals];
            for (int i = 0; i < nNormals; i++)
            {
                normals[i] = new Vector3(mesh.Normals[i].X, mesh.Normals[i].Y, mesh.Normals[i].Z);
                normals[i].Normalize();
            }
            int nFaceNormals = mesh.NormFaces.Count;

            if (nFaceNormals > builder.Faces.Length)
            {
                throw new Exception("nFaceNormals must match the number of faces in the mesh");
            }
            for (int i = 0; i < nFaceNormals; i++)
            {
                int nVertexNormals = mesh.NormFaces[i].Indices.Count;
                if (nVertexNormals > builder.Faces[i].Vertices.Length)
                {
                    throw new Exception("nVertexNormals must match the number of verticies in the face");
                }
                for (int j = 0; j < nVertexNormals; j++)
                {
                    builder.Faces[i].Vertices[j].Normal = normals[(int)mesh.NormFaces[i].Indices[j]];
                }
            }

            int nVertexColors = (int)mesh.NumColorSets;

            for (int i = 0; i < nVertexColors; i++)
            {
                builder.Vertices[i] = new ColoredVertex((Vertex)builder.Vertices[i], new Color128(mesh.Colors[0][i].R, mesh.Colors[0][i].G, mesh.Colors[0][i].B, mesh.Colors[0][i].A));
            }
        }
Exemple #20
0
 public void SetLightSpecular(Color24 LightSpecular)
 {
     GL.Uniform3(UniformLayout.LightSpecular, LightSpecular.R / 255.0f, LightSpecular.G / 255.0f, LightSpecular.B / 255.0f);
 }
Exemple #21
0
        public void Execute(int index)
        {
            var r = (byte)(GrayInput[index] * byte.MaxValue);

            RgbOutput[index] = new Color24(r, r, r);
        }
Exemple #22
0
        /// <summary>Loads a texture from the specified file.</summary>
        /// <param name="file">The file that holds the texture.</param>
        /// <param name="texture">Receives the texture.</param>
        /// <returns>Whether loading the texture was successful.</returns>
        private bool Parse(string file, out Texture texture)
        {
            /*
             * Read the bitmap. This will be a bitmap of just
             * any format, not necessarily the one that allows
             * us to extract the bitmap data easily.
             */
            Bitmap bitmap = new Bitmap(file);

            Color24[] p = null;
            if (file.ToLowerInvariant().EndsWith(".bmp") && EnabledHacks.ReduceTransparencyColorDepth && (bitmap.PixelFormat == PixelFormat.Format32bppArgb || bitmap.PixelFormat == PixelFormat.Format24bppRgb))
            {
                /*
                 * Reduce our bitmap to 256 colors
                 * WARNING: This is *slow* for lots of textures, so should
                 * be used sparingly, if at all!
                 */
                bitmap = bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), PixelFormat.Format8bppIndexed);
            }


            if (bitmap.PixelFormat != PixelFormat.Format32bppArgb && bitmap.PixelFormat != PixelFormat.Format24bppRgb)
            {
                /* Otherwise, only store the color palette data for
                 * textures using a restricted palette
                 * With a large number of textures loaded at
                 * once, this can save a decent chunk of memory
                 */
                p = new Color24[bitmap.Palette.Entries.Length];
                for (int i = 0; i < bitmap.Palette.Entries.Length; i++)
                {
                    p[i] = bitmap.Palette.Entries[i];
                }
            }
            Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

            /*
             * If the bitmap format is not already 32-bit BGRA,
             * then convert it to 32-bit BGRA.
             */
            if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
            {
                Bitmap   compatibleBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb);
                Graphics graphics         = Graphics.FromImage(compatibleBitmap);
                graphics.DrawImage(bitmap, rect, rect, GraphicsUnit.Pixel);
                graphics.Dispose();
                bitmap.Dispose();
                bitmap = compatibleBitmap;
            }

            /*
             * Extract the raw bitmap data.
             */
            BitmapData data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);

            if (data.Stride == 4 * data.Width)
            {
                /*
                 * Copy the data from the bitmap
                 * to the array in BGRA format.
                 */
                byte[] raw = new byte[data.Stride * data.Height];
                System.Runtime.InteropServices.Marshal.Copy(data.Scan0, raw, 0, data.Stride * data.Height);
                bitmap.UnlockBits(data);
                int width  = bitmap.Width;
                int height = bitmap.Height;

                /*
                 * Change the byte order from BGRA to RGBA.
                 */
                for (int i = 0; i < raw.Length; i += 4)
                {
                    byte temp = raw[i];
                    raw[i]     = raw[i + 2];
                    raw[i + 2] = temp;
                }
                texture = new Texture(width, height, 32, raw, p);
                bitmap.Dispose();
                return(true);
            }

            /*
             * The stride is invalid. This indicates that the
             * CLI either does not implement the conversion to
             * 32-bit BGRA correctly, or that the CLI has
             * applied additional padding that we do not
             * support.
             */
            bitmap.UnlockBits(data);
            bitmap.Dispose();
            CurrentHost.ReportProblem(ProblemType.InvalidOperation, "Invalid stride encountered.");
            texture = null;
            return(false);
        }
Exemple #23
0
 public void SetLightAmbient(Color24 LightAmbient)
 {
     GL.ProgramUniform3(handle, UniformLayout.LightAmbient, LightAmbient.R / 255.0f, LightAmbient.G / 255.0f, LightAmbient.B / 255.0f);
 }
Exemple #24
0
 internal TimetableElement()
 {
     Width            = 0.0;
     Height           = 0.0;
     TransparentColor = Color24.Blue;
 }
Exemple #25
0
 public void SetLightDiffuse(Color24 LightDiffuse)
 {
     GL.ProgramUniform3(handle, UniformLayout.LightDiffuse, LightDiffuse.R / 255.0f, LightDiffuse.G / 255.0f, LightDiffuse.B / 255.0f);
 }
Exemple #26
0
        private static void ParseSubBlock(Block block, ref StaticObject obj, ref MeshBuilder builder, ref Material material)
        {
            Block subBlock;

            switch (block.Token)
            {
            default:
                return;

            case TemplateID.Template:
                string GUID = block.ReadString();

                /*
                 * Valid Microsoft templates are listed here:
                 * https://docs.microsoft.com/en-us/windows/desktop/direct3d9/dx9-graphics-reference-x-file-format-templates
                 * However, an application may define it's own template (or by the looks of things override another)
                 * by declaring this at the head of the file, and using a unique GUID
                 *
                 * Mesquoia does this by defining a copy of the Boolean template using a WORD as opposed to a DWORD
                 * No practical effect in this case, however be wary of this....
                 */
                return;

            case TemplateID.Header:
                int majorVersion = block.ReadUInt16();
                int minorVersion = block.ReadUInt16();
                int flags        = block.ReadUInt16();
                switch (flags)
                {
                /* According to http://paulbourke.net/dataformats/directx/#xfilefrm_Template_Header
                 * it is possible for a file to contain a mix of both binary and textual blocks.
                 *
                 * The Header block controls the format of the file from this point onwards.
                 * majorVersion and minorVersion relate to the legacy Direct3D retained mode API
                 * and can probably be ignored. (Assume that features are cumulative and backwards compatible)
                 * flags sets whether the blocks from this point onwards are binary or textual.
                 *
                 * TODO: Need a mixed mode file sample if we want this to work.
                 * Probably exceedingly uncommon, so low priority
                 */

                case 0:
                    if (block is TextualBlock)
                    {
                        throw new Exception("Mixed-mode text and binary objects are not supported by this parser.");
                    }
                    break;

                default:
                    if (block is BinaryBlock)
                    {
                        throw new Exception("Mixed-mode text and binary objects are not supported by this parser.");
                    }
                    break;
                }
                return;

            case TemplateID.Frame:
                currentLevel++;
                if (builder.Vertices.Length != 0)
                {
                    builder.Apply(ref obj);
                    builder = new MeshBuilder(Plugin.currentHost);
                }
                while (block.Position() < block.Length() - 5)
                {
                    /*
                     * TODO: Whilst https://docs.microsoft.com/en-us/windows/desktop/direct3d9/frame suggests the Frame template should only contain
                     * Mesh, FrameTransformMatrix or Frame templates by default, 3DS Max stuffs all manner of things into here
                     *
                     * It would be nice to get 3DS max stuff detected specifically, especially as we don't support most of this
                     */
                    //TemplateID[] validTokens = { TemplateID.Mesh , TemplateID.FrameTransformMatrix, TemplateID.Frame };
                    subBlock = block.ReadSubBlock();
                    ParseSubBlock(subBlock, ref obj, ref builder, ref material);
                }
                currentLevel--;
                break;

            case TemplateID.FrameTransformMatrix:
                double[] matrixValues = new double[16];
                for (int i = 0; i < 16; i++)
                {
                    matrixValues[i] = block.ReadSingle();
                }

                if (currentLevel > 1)
                {
                    builder.TransformMatrix = new Matrix4D(matrixValues);
                }
                else
                {
                    rootMatrix = new Matrix4D(matrixValues);
                }
                break;

            case TemplateID.Mesh:
                if (builder.Vertices.Length != 0)
                {
                    builder.Apply(ref obj);
                    builder = new MeshBuilder(Plugin.currentHost);
                }
                int nVerts = block.ReadUInt16();
                if (nVerts == 0)
                {
                    //Some null objects contain an empty mesh
                    Plugin.currentHost.AddMessage(MessageType.Warning, false, "nVertices should be greater than zero in Mesh " + block.Label);
                }
                int v = builder.Vertices.Length;
                Array.Resize(ref builder.Vertices, v + nVerts);
                for (int i = 0; i < nVerts; i++)
                {
                    builder.Vertices[v + i] = new Vertex(new Vector3(block.ReadSingle(), block.ReadSingle(), block.ReadSingle()));
                }
                int nFaces = block.ReadUInt16();
                if (nFaces == 0)
                {
                    try
                    {
                        /*
                         * A mesh has been defined with no faces.
                         * If we are not at the end of the block,
                         * attempt to read the next sub-block
                         *
                         * If this fails, the face count is probably incorrect
                         *
                         * NOTE: In this case, the face statement will be an empty string / whitespace
                         * hence the block.ReadString() call
                         */
                        block.ReadString();
                        if (block.Position() < block.Length() - 5)
                        {
                            subBlock = block.ReadSubBlock();
                            ParseSubBlock(subBlock, ref obj, ref builder, ref material);
                        }
                        goto NoFaces;
                    }
                    catch
                    {
                        throw new Exception("nFaces was declared as zero, but unrecognised data remains in the block");
                    }
                }
                int f = builder.Faces.Length;
                Array.Resize(ref builder.Faces, f + nFaces);
                for (int i = 0; i < nFaces; i++)
                {
                    int fVerts = block.ReadUInt16();
                    if (nFaces == 0)
                    {
                        throw new Exception("fVerts must be greater than zero");
                    }
                    builder.Faces[f + i]          = new MeshFace();
                    builder.Faces[f + i].Vertices = new MeshFaceVertex[fVerts];
                    for (int j = 0; j < fVerts; j++)
                    {
                        builder.Faces[f + i].Vertices[j].Index = block.ReadUInt16();
                    }
                }
NoFaces:
                while (block.Position() < block.Length() - 5)
                {
                    subBlock = block.ReadSubBlock();
                    ParseSubBlock(subBlock, ref obj, ref builder, ref material);
                }
                break;

            case TemplateID.MeshMaterialList:
                int nMaterials   = block.ReadUInt16();
                int nFaceIndices = block.ReadUInt16();
                if (nFaceIndices == 1 && builder.Faces.Length > 1)
                {
                    //Single material for all faces
                    int globalMaterial = block.ReadUInt16();
                    for (int i = 0; i < builder.Faces.Length; i++)
                    {
                        builder.Faces[i].Material = (ushort)(globalMaterial + 1);
                    }
                }
                else if (nFaceIndices == builder.Faces.Length)
                {
                    for (int i = 0; i < nFaceIndices; i++)
                    {
                        int fMaterial = block.ReadUInt16();
                        builder.Faces[i].Material = (ushort)(fMaterial + 1);
                    }
                }
                else
                {
                    throw new Exception("nFaceIndices must match the number of faces in the mesh");
                }
                for (int i = 0; i < nMaterials; i++)
                {
                    subBlock = block.ReadSubBlock(TemplateID.Material);
                    ParseSubBlock(subBlock, ref obj, ref builder, ref material);
                }
                break;

            case TemplateID.Material:
                int m = builder.Materials.Length;
                Array.Resize(ref builder.Materials, m + 1);
                builder.Materials[m]       = new Material();
                builder.Materials[m].Color = new Color32((byte)(255 * block.ReadSingle()), (byte)(255 * block.ReadSingle()), (byte)(255 * block.ReadSingle()), (byte)(255 * block.ReadSingle()));
                double  mPower    = block.ReadSingle();                     //TODO: Unsure what this does...
                Color24 mSpecular = new Color24((byte)block.ReadSingle(), (byte)block.ReadSingle(), (byte)block.ReadSingle());
                builder.Materials[m].EmissiveColor        = new Color24((byte)(255 * block.ReadSingle()), (byte)(255 * block.ReadSingle()), (byte)(255 * block.ReadSingle()));
                builder.Materials[m].EmissiveColorUsed    = true;                      //TODO: Check exact behaviour
                builder.Materials[m].TransparentColor     = Color24.Black;             //TODO: Check, also can we optimise which faces have the transparent color set?
                builder.Materials[m].TransparentColorUsed = true;
                if (block.Position() < block.Length() - 5)
                {
                    subBlock = block.ReadSubBlock(TemplateID.TextureFilename);
                    ParseSubBlock(subBlock, ref obj, ref builder, ref builder.Materials[m]);
                }
                break;

            case TemplateID.TextureFilename:
                try
                {
                    material.DaytimeTexture = OpenBveApi.Path.CombineFile(currentFolder, block.ReadString());
                }
                catch
                {
                    //Empty / malformed texture argument
                    material.DaytimeTexture = null;
                }
                if (!System.IO.File.Exists(material.DaytimeTexture) && material.DaytimeTexture != null)
                {
                    Plugin.currentHost.AddMessage(MessageType.Error, true, "Texure " + material.DaytimeTexture + " was not found in file " + currentFile);
                    material.DaytimeTexture = null;
                }
                break;

            case TemplateID.MeshTextureCoords:
                int nCoords = block.ReadUInt16();
                for (int i = 0; i < nCoords; i++)
                {
                    builder.Vertices[i].TextureCoordinates = new Vector2(block.ReadSingle(), block.ReadSingle());
                }
                break;

            case TemplateID.MeshNormals:
                int       nNormals = block.ReadUInt16();
                Vector3[] normals  = new Vector3[nNormals];
                for (int i = 0; i < nNormals; i++)
                {
                    normals[i] = new Vector3(block.ReadSingle(), block.ReadSingle(), block.ReadSingle());
                    normals[i].Normalize();
                }
                int nFaceNormals = block.ReadUInt16();
                if (nFaceNormals != builder.Faces.Length)
                {
                    throw new Exception("nFaceNormals must match the number of faces in the mesh");
                }
                for (int i = 0; i < nFaceNormals; i++)
                {
                    int nVertexNormals = block.ReadUInt16();
                    if (nVertexNormals != builder.Faces[i].Vertices.Length)
                    {
                        throw new Exception("nVertexNormals must match the number of verticies in the face");
                    }
                    for (int j = 0; j < nVertexNormals; j++)
                    {
                        builder.Faces[i].Vertices[j].Normal = normals[block.ReadUInt16()];
                    }
                }
                break;

            case TemplateID.MeshVertexColors:
                int nVertexColors = block.ReadUInt16();
                for (int i = 0; i < nVertexColors; i++)
                {
                    builder.Vertices[i] = new ColoredVertex((Vertex)builder.Vertices[i], new Color128(block.ReadSingle(), block.ReadSingle(), block.ReadSingle()));
                }
                break;
            }
        }
Exemple #27
0
 public void SetLightSpecular(Color24 LightSpecular)
 {
     GL.ProgramUniform3(handle, UniformLayout.LightSpecular, LightSpecular.R / 255.0f, LightSpecular.G / 255.0f, LightSpecular.B / 255.0f);
 }
Exemple #28
0
        /// <summary>Updates the lighting model on a per frame basis</summary>
        public void UpdateLighting(double Time)
        {
            //Check that we have more than one light definition & that the array is not null
            if (DynamicLighting == false || LightDefinitions == null || LightDefinitions.Length < 2)
            {
                return;
            }

            //Convert to absolute time of day
            //Use a while loop as it's possible to run through two days
            while (Time > 86400)
            {
                Time -= 86400;
            }

            //Run through the array
            int j = 0;

            for (int i = j; i < LightDefinitions.Length; i++)
            {
                if (Time < LightDefinitions[i].Time)
                {
                    break;
                }

                j = i;
            }

            //We now know that our light definition is between the values defined in j and j + 1 (Or 0 if this is the end of the array)
            int k;

            if (j == 0)
            {
                //Our NEW light is to be the first entry in the array
                //This means the OLD light is the last entry, but j and k do not need reversing
                k = 0;
                j = LightDefinitions.Length - 1;
            }
            else if (j == LightDefinitions.Length - 1)
            {
                //We are wrapping around to the end of the array
                //Reverse j and k, as we have not yet passed the time for the first array entry
                j = 0;
                k = LightDefinitions.Length - 1;
            }
            else
            {
                //Somewhere in the middle, so the NEW light is simply one greater
                k = j + 1;
            }

            int t1 = LightDefinitions[j].Time, t2 = LightDefinitions[k].Time;

            double cb1 = LightDefinitions[j].CabBrightness, cb2 = LightDefinitions[k].Time;

            //Calculate, inverting if necessary

            //Ensure we're not about to divide by zero
            if (t2 == 0)
            {
                t2 = 1;
            }

            if (t1 == 0)
            {
                t1 = 1;
            }

            //Calculate the percentage
            double mu;

            if (k == LightDefinitions.Length - 1)
            {
                //Wrapping around
                mu = (86400 - Time + t1) / (86400 - t2 + t1);
            }
            else
            {
                mu = (Time - t1) / (t2 - t1);
            }

            //Calculate the final colors and positions
            OptionDiffuseColor  = Color24.CosineInterpolate(LightDefinitions[j].DiffuseColor, LightDefinitions[k].DiffuseColor, mu);
            OptionAmbientColor  = Color24.CosineInterpolate(LightDefinitions[j].AmbientColor, LightDefinitions[k].AmbientColor, mu);
            OptionLightPosition = Vector3.CosineInterpolate(LightDefinitions[j].LightPosition, LightDefinitions[k].LightPosition, mu);

            //Interpolate the cab brightness value
            double mu2 = (1 - Math.Cos(mu * Math.PI)) / 2;

            DynamicCabBrightness = (cb1 * (1 - mu2) + cb2 * mu2);

            //Reinitialize the lighting model with the new information
            Initialize();

            //NOTE: This does not refresh the display lists
            //If we sit in place with extreme time acceleration (1000x) lighting for faces may appear a little inconsistant
        }
Exemple #29
0
 public void SetMaterialEmission(Color24 MaterialEmission)
 {
     GL.ProgramUniform3(handle, UniformLayout.MaterialEmission, MaterialEmission.R / 255.0f, MaterialEmission.G / 255.0f, MaterialEmission.B / 255.0f);
 }
Exemple #30
0
        private RawMapEntity LoadMapEntityFromStream(string chunkFourCC, EndianBinaryReader reader, MapEntityDataDescriptor template)
        {
            RawMapEntity obj = new RawMapEntity();
            obj.Fields = new PropertyCollection();
            obj.FourCC = chunkFourCC;

            // We're going to examine the Template's properties and load based on the current template type.
            for (int i = 0; i < template.Fields.Count; i++)
            {
                var templateProperty = template.Fields[i];
                string propertyName = templateProperty.FieldName;
                PropertyType type = templateProperty.FieldType;
                object value = null;

                switch (type)
                {
                    case PropertyType.FixedLengthString:
                        value = reader.ReadString(templateProperty.Length).Trim(new[] { '\0' });
                        break;

                    case PropertyType.String:
                        value = reader.ReadStringUntil('\0');
                        break;

                    case PropertyType.Byte:
                        value = reader.ReadByte();
                        break;

                    case PropertyType.Short:
                        value = reader.ReadInt16();
                        break;

                    case PropertyType.Int32BitField:
                    case PropertyType.Int32:
                        value = reader.ReadInt32();
                        break;

                    case PropertyType.Float:
                        value = reader.ReadSingle();
                        break;

                    case PropertyType.Vector3:
                        value = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                        break;

                    case PropertyType.Vector2:
                        value = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                        break;

                    case PropertyType.Enum:
                        byte enumIndexBytes = reader.ReadByte(); // ToDo: Resolve to actual Enum later.
                        value = enumIndexBytes;
                        break;

                    case PropertyType.ObjectReference:
                        // When we first resolve them, we're going to keep the value as the reference byte,
                        // and then when they are post-processed they'll be turned into a proper type.
                        value = (int)reader.ReadByte();
                        break;

                    case PropertyType.ObjectReferenceShort:
                        // When we first resolve them, we're going to keep the value as the reference byte,
                        // and then when they are post-processed they'll be turned into a proper type.
                        value = (int)reader.ReadUInt16();
                        break;

                    case PropertyType.ObjectReferenceArray:
                        // When we first resolve them, we're going to keep the value as the reference byte,
                        // and then when they are post-processed they'll be turned into a proper type.
                        var refList = new BindingList<object>();
                        for (int refArray = 0; refArray < templateProperty.Length; refArray++)
                        {
                            refList.Add((int)reader.ReadByte());
                        }
                        value = refList;
                        break;

                    case PropertyType.XYRotation:
                        {
                            Vector3 eulerAngles = new Vector3();
                            for (int f = 0; f < 2; f++)
                                eulerAngles[f] = (reader.ReadInt16() * (180 / 32786f));

                            Quaternion xAxis = Quaternion.FromAxisAngle(new Vector3(1, 0, 0), eulerAngles.X * MathE.Deg2Rad);
                            Quaternion yAxis = Quaternion.FromAxisAngle(new Vector3(0, 1, 0), eulerAngles.Y * MathE.Deg2Rad);

                            // Swizzling to the ZYX order seems to be the right one.
                            Quaternion finalRot = yAxis * xAxis;
                            value = finalRot;
                        }
                        break;

                    case PropertyType.XYZRotation:
                        {
                            Vector3 eulerAngles = new Vector3();
                            for (int f = 0; f < 3; f++)
                                eulerAngles[f] = (reader.ReadInt16() * (180 / 32786f));

                            Quaternion xAxis = Quaternion.FromAxisAngle(new Vector3(1, 0, 0), eulerAngles.X * MathE.Deg2Rad);
                            Quaternion yAxis = Quaternion.FromAxisAngle(new Vector3(0, 1, 0), eulerAngles.Y * MathE.Deg2Rad);
                            Quaternion zAxis = Quaternion.FromAxisAngle(new Vector3(0, 0, 1), eulerAngles.Z * MathE.Deg2Rad);

                            // Swizzling to the ZYX order seems to be the right one.
                            Quaternion finalRot = zAxis * yAxis * xAxis;
                            value = finalRot;
                        }
                        break;
                    case PropertyType.YRotation:
                        {
                            float yRotation = reader.ReadInt16() * (180 / 32786f);

                            Quaternion yAxis = Quaternion.FromAxisAngle(new Vector3(0, 1, 0), yRotation * MathE.Deg2Rad);
                            value = yAxis;
                        }
                        break;

                    case PropertyType.Color32:
                        value = new Color32(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
                        break;

                    case PropertyType.Color24:
                        value = new Color24(reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
                        break;

                    case PropertyType.Vector3Byte:
                        type = PropertyType.Vector3Byte;
                        value = new Vector3(reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
                        break;

                    case PropertyType.Bits:
                        value = (byte)reader.ReadBits(templateProperty.Length);
                        break;
                }

                // This... this could get dicy. If the template we just read was a "Name" then we now have the technical name (and value)
                // of the object. We can then search for the MapObjectDataDescriptor that matches the technical name, and then edit the
                // remaining fields. However, this gets somewhat dicey, because we're modifying the length of the Fields array for templates
                // while iterating through it. However, the Name field always comes before any of the fields we'd want to modify, we're going to
                // do an in-place replacement of the fields (since Fields.Count will increase) and then we get free loading of the complex templates
                // without later post-processing them.
                if (templateProperty.FieldName == "Name")
                {
                    // See if our template list has a complex version of this file, otherwise grab the default.
                    MapObjectDataDescriptor complexDescriptor = m_editorCore.Templates.MapObjectDataDescriptors.Find(x => x.FourCC == chunkFourCC && x.TechnicalName == templateProperty.FieldName);
                    if (complexDescriptor == null)
                        complexDescriptor = m_editorCore.Templates.DefaultMapObjectDataDescriptor;

                    // Determine which field we need to remove, and then insert in the other fields (in order) where it used to be.
                    foreach (var fieldToReplace in complexDescriptor.DataOverrides)
                    {
                        for (int k = 0; k < template.Fields.Count; k++)
                        {
                            if (template.Fields[k].FieldName == fieldToReplace.ParameterName)
                            {
                                // Remove the old field.
                                template.Fields.RemoveAt(k);

                                // Now insert the new fields starting at the location of the one we just replaced.
                                template.Fields.InsertRange(k, fieldToReplace.Values);
                                break;
                            }
                        }
                    }
                }

                Property instanceProp = new Property(templateProperty.FieldName, type, value);
                obj.Fields.Properties.Add(instanceProp);
            }

            return obj;
        }
Exemple #31
0
        /// <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="Rotation">The rotation to be applied</param>
        /// <returns>The object loaded.</returns>
        internal static StaticObject ReadObject(string FileName, Vector3 Rotation)
        {
            string      BaseDir    = System.IO.Path.GetDirectoryName(FileName);
            XmlDocument currentXML = new XmlDocument();
            //Initialise the object
            StaticObject Object  = new StaticObject(Plugin.currentHost);
            MeshBuilder  Builder = new MeshBuilder(Plugin.currentHost);

            Vector3[] Normals         = new Vector3[4];
            bool      PropertiesFound = false;

            VertexTemplate[] tempVertices       = new VertexTemplate[0];
            Vector3[]        tempNormals        = new Vector3[0];
            Color24          transparentColor   = new Color24();
            string           tday               = null;
            string           tnight             = null;
            string           transtex           = null;
            bool             TransparencyUsed   = false;
            bool             TransparentTypSet  = false;
            bool             FirstPxTransparent = false;
            Color24          FirstPxColor       = new Color24();
            bool             Face2              = false;
            int TextureWidth  = 0;
            int TextureHeight = 0;

            if (File.Exists(FileName))
            {
                try
                {
                    currentXML.Load(FileName);
                }
                catch
                {
                    return(null);
                }
            }
            else
            {
                Plugin.currentHost.AddMessage(MessageType.Error, false, "Loksim3D object " + FileName + " does not exist.");
                return(null);
            }
            //Check for null
            if (currentXML.DocumentElement != null)
            {
                XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/OBJECT");
                //Check this file actually contains Loksim3D object nodes
                if (DocumentNodes != null)
                {
                    foreach (XmlNode outerNode in DocumentNodes)
                    {
                        if (outerNode.ChildNodes.OfType <XmlElement>().Any())
                        {
                            foreach (XmlNode node in outerNode.ChildNodes)
                            {
                                //I think there should only be one properties node??
                                //Need better format documentation
                                if (node.Name == "Props" && PropertiesFound == false)
                                {
                                    if (node.Attributes != null)
                                    {
                                        //Our node has child nodes, therefore this properties node should be valid
                                        //Needs better validation
                                        PropertiesFound = true;
                                        foreach (XmlAttribute attribute in node.Attributes)
                                        {
                                            switch (attribute.Name)
                                            {
                                            //Sets the texture
                                            //Loksim3D objects only support daytime textures
                                            case "Texture":
                                                tday = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Plugin.LoksimPackageFolder);
                                                if (!File.Exists(tday))
                                                {
                                                    Plugin.currentHost.AddMessage(MessageType.Warning, true, "Ls3d Texture file " + attribute.Value + " not found.");
                                                    break;
                                                }
                                                try
                                                {
                                                    using (Bitmap TextureInformation = new Bitmap(tday))
                                                    {
                                                        TextureWidth  = TextureInformation.Width;
                                                        TextureHeight = TextureInformation.Height;
                                                        Color color = TextureInformation.GetPixel(0, 0);
                                                        FirstPxColor = new Color24(color.R, color.G, color.B);
                                                    }
                                                }
                                                catch
                                                {
                                                    Plugin.currentHost.AddMessage(MessageType.Error, true,
                                                                                  "An error occured loading daytime texture " + tday +
                                                                                  " in file " + FileName);
                                                    tday = null;
                                                }
                                                break;

                                            //Defines whether the texture uses transparency
                                            //May be omitted
                                            case "Transparent":
                                                if (TransparentTypSet)
                                                {
                                                    //Appears to be ignored with TransparentTyp set
                                                    continue;
                                                }
                                                if (attribute.Value == "TRUE")
                                                {
                                                    TransparencyUsed = true;
                                                    transparentColor = Color24.Black;
                                                }
                                                break;

                                            case "TransTexture":
                                                if (string.IsNullOrEmpty(attribute.Value))
                                                {
                                                    //Empty....
                                                    continue;
                                                }
                                                transtex = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Plugin.LoksimPackageFolder);
                                                if (!File.Exists(transtex))
                                                {
                                                    Plugin.currentHost.AddMessage(MessageType.Error, true, "AlphaTexture " + transtex + " could not be found in file " + FileName);
                                                    transtex = null;
                                                }
                                                break;

                                            //Sets the transparency type
                                            case "TransparentTyp":
                                                TransparentTypSet = true;
                                                switch (attribute.Value)
                                                {
                                                case "0":
                                                    //Transparency is disabled
                                                    TransparencyUsed = false;
                                                    break;

                                                case "1":
                                                    //Transparency is solid black
                                                    TransparencyUsed   = true;
                                                    transparentColor   = Color24.Black;
                                                    FirstPxTransparent = false;
                                                    break;

                                                case "2":
                                                    //Transparency is the color at Pixel 1,1
                                                    TransparencyUsed   = true;
                                                    FirstPxTransparent = true;
                                                    break;

                                                case "3":
                                                case "4":
                                                    //This is used when transparency is used with an alpha bitmap
                                                    TransparencyUsed   = false;
                                                    FirstPxTransparent = false;
                                                    break;

                                                case "5":
                                                    //Use the alpha channel from the image, so we don't need to do anything fancy
                                                    //TODO: (Low priority) Check what happens in Loksim itself when an image uses the Alpha channel, but doesn't actually specify type 5
                                                    break;

                                                default:
                                                    Plugin.currentHost.AddMessage(MessageType.Error, false, "Unrecognised transparency type " + attribute.Value + " detected in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    break;
                                                }
                                                break;

                                            //Sets whether the rears of the faces are to be drawn
                                            case "Drawrueckseiten":
                                                if (attribute.Value == "TRUE" || string.IsNullOrEmpty(attribute.Value))
                                                {
                                                    Face2 = true;
                                                }
                                                else
                                                {
                                                    Face2 = false;
                                                }
                                                break;

                                                /*
                                                 * MISSING PROPERTIES:
                                                 * AutoRotate - Rotate with tracks?? LS3D presumably uses a 3D world system.
                                                 * Beleuchtet- Translates as illuminated. Presume something to do with lighting? - What emissive color?
                                                 * FileAuthor
                                                 * FileInfo
                                                 * FilePicture
                                                 */
                                            }
                                        }
                                    }
                                }
                                //The point command is eqivilant to a vertex
                                else if (node.Name == "Point" && node.ChildNodes.OfType <XmlElement>().Any())
                                {
                                    foreach (XmlNode childNode in node.ChildNodes)
                                    {
                                        if (childNode.Name == "Props" && childNode.Attributes != null)
                                        {
                                            //Vertex
                                            Vector3 v  = new Vector3();
                                            bool    vF = false;
                                            //Normals
                                            Vector3 n = new Vector3();
                                            foreach (XmlAttribute attribute in childNode.Attributes)
                                            {
                                                switch (attribute.Name)
                                                {
                                                //Sets the vertex normals
                                                case "Normal":
                                                    if (!Vector3.TryParse(attribute.Value, ';', out n))
                                                    {
                                                        Plugin.currentHost.AddMessage(MessageType.Warning, true, "Invalid vertex normal vector " + attribute.Value + " supplied in Ls3d object file.");
                                                    }
                                                    break;

                                                //Sets the vertex 3D co-ordinates
                                                case "Vekt":
                                                    if (!Vector3.TryParse(attribute.Value, ';', out v))
                                                    {
                                                        Plugin.currentHost.AddMessage(MessageType.Warning, true, "Invalid vertex coordinate vector " + attribute.Value + " supplied in Ls3d object file.");
                                                    }
                                                    vF = true;
                                                    break;
                                                }
                                            }
                                            //Resize temp arrays
                                            Array.Resize <VertexTemplate>(ref tempVertices, tempVertices.Length + 1);
                                            Array.Resize <Vector3>(ref tempNormals, tempNormals.Length + 1);
                                            //Add vertex and normals to temp array
                                            if (vF == false)
                                            {
                                                Plugin.currentHost.AddMessage(MessageType.Warning, true, "Vertex with no co-ordinates supplied encountered in Ls3d object file.");
                                                continue;
                                            }
                                            tempVertices[tempVertices.Length - 1] = new Vertex((Vector3)v);
                                            tempNormals[tempNormals.Length - 1]   = new Vector3(n);
                                            tempNormals[tempNormals.Length - 1].Normalize();
                                            Array.Resize <VertexTemplate>(ref Builder.Vertices, Builder.Vertices.Length + 1);
                                            while (Builder.Vertices.Length >= Normals.Length)
                                            {
                                                Array.Resize <Vector3>(ref Normals, Normals.Length << 1);
                                            }
                                            Builder.Vertices[Builder.Vertices.Length - 1] = new Vertex(new Vector3((Vector3)v));
                                            Normals[Builder.Vertices.Length - 1]          = new Vector3(n);
                                        }
                                    }
                                }
                                //The Flaeche command creates a face
                                else if (node.Name == "Flaeche" && node.ChildNodes.OfType <XmlElement>().Any())
                                {
                                    foreach (XmlNode childNode in node.ChildNodes)
                                    {
                                        if (childNode.Name == "Props" && childNode.Attributes != null)
                                        {
                                            //Defines the verticies in this face
                                            //**NOTE**: A vertex may appear in multiple faces with different texture co-ordinates
                                            if (childNode.Attributes["Points"] != null)
                                            {
                                                string[] Verticies = childNode.Attributes["Points"].Value.Split(';');
                                                int      f         = Builder.Faces.Length;
                                                //Add 1 to the length of the face array
                                                Array.Resize <MeshFace>(ref Builder.Faces, f + 1);
                                                Builder.Faces[f] = new MeshFace();
                                                //Create the vertex array for the face
                                                Builder.Faces[f].Vertices = new MeshFaceVertex[Verticies.Length];
                                                while (Builder.Vertices.Length > Normals.Length)
                                                {
                                                    Array.Resize <Vector3>(ref Normals,
                                                                           Normals.Length << 1);
                                                }
                                                //Run through the vertices list and grab from the temp array

                                                int smallestX = TextureWidth;
                                                int smallestY = TextureHeight;
                                                for (int j = 0; j < Verticies.Length; j++)
                                                {
                                                    //This is the position of the vertex in the temp array
                                                    int currentVertex;
                                                    if (!int.TryParse(Verticies[j], out currentVertex))
                                                    {
                                                        Plugin.currentHost.AddMessage(MessageType.Error, false, Verticies[j] + " does not parse to a valid Vertex in " + node.Name + " in Loksim3D object file " + FileName);
                                                        continue;
                                                    }
                                                    //Add one to the actual vertex array
                                                    Array.Resize <VertexTemplate>(ref Builder.Vertices, Builder.Vertices.Length + 1);
                                                    //Set coordinates
                                                    Builder.Vertices[Builder.Vertices.Length - 1] = new Vertex(tempVertices[currentVertex].Coordinates);
                                                    //Set the vertex index
                                                    Builder.Faces[f].Vertices[j].Index = (ushort)(Builder.Vertices.Length - 1);
                                                    //Set the normals
                                                    Builder.Faces[f].Vertices[j].Normal = tempNormals[currentVertex];
                                                    //Now deal with the texture
                                                    //Texture mapping points are in pixels X,Y and are relative to the face in question rather than the vertex
                                                    if (childNode.Attributes["Texture"] != null)
                                                    {
                                                        string[] TextureCoords = childNode.Attributes["Texture"].Value.Split(';');
                                                        Vector2  currentCoords;
                                                        float    OpenBVEWidth;
                                                        float    OpenBVEHeight;
                                                        string[] splitCoords = TextureCoords[j].Split(',');
                                                        if (!float.TryParse(splitCoords[0], out OpenBVEWidth))
                                                        {
                                                            Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid texture width specified in " + node.Name + " in Loksim3D object file " + FileName);
                                                            continue;
                                                        }
                                                        if (!float.TryParse(splitCoords[1], out OpenBVEHeight))
                                                        {
                                                            Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid texture height specified in " + node.Name + " in Loksim3D object file " + FileName);
                                                            continue;
                                                        }
                                                        if (OpenBVEWidth <= smallestX && OpenBVEHeight <= smallestY)
                                                        {
                                                            //Clamp texture width and height
                                                            smallestX = (int)OpenBVEWidth;
                                                            smallestY = (int)OpenBVEHeight;
                                                        }
                                                        if (TextureWidth != 0 && TextureHeight != 0)
                                                        {
                                                            //Calculate openBVE co-ords
                                                            currentCoords.X = (OpenBVEWidth / TextureWidth);
                                                            currentCoords.Y = (OpenBVEHeight / TextureHeight);
                                                        }
                                                        else
                                                        {
                                                            //Invalid, so just return zero
                                                            currentCoords.X = 0;
                                                            currentCoords.Y = 0;
                                                        }
                                                        Builder.Vertices[Builder.Vertices.Length - 1].TextureCoordinates = currentCoords;
                                                    }
                                                }
                                                if (Face2)
                                                {
                                                    //Add face2 flag if required
                                                    Builder.Faces[f].Flags = (byte)MeshFace.Face2Mask;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                //Apply rotation

                /*
                 * NOTES:
                 * No rotation order is specified
                 * The rotation string in a .l3dgrp file is ordered Y, Z, X    ??? Can't find a good reason for this ???
                 * Rotations must still be performed in X,Y,Z order to produce correct results
                 */

                if (Rotation.Z != 0.0)
                {
                    Rotation.Z = Rotation.Z.ToRadians();
                    //Apply rotation
                    Builder.ApplyRotation(Vector3.Right, Rotation.Z);
                }


                if (Rotation.X != 0.0)
                {
                    //This is actually the Y-Axis rotation
                    Rotation.X = Rotation.X.ToRadians();
                    //Apply rotation
                    Builder.ApplyRotation(Vector3.Down, Rotation.X);
                }
                if (Rotation.Y != 0.0)
                {
                    //This is actually the X-Axis rotation
                    Rotation.Y = Rotation.Y.ToRadians();
                    //Apply rotation
                    Builder.ApplyRotation(Vector3.Forward, Rotation.Y);
                }


                //These files appear to only have one texture defined
                //Therefore import later- May have to change
                if (File.Exists(tday))
                {
                    for (int j = 0; j < Builder.Materials.Length; j++)
                    {
                        Builder.Materials[j].DaytimeTexture      = tday;
                        Builder.Materials[j].NighttimeTexture    = tnight;
                        Builder.Materials[j].TransparencyTexture = transtex;
                    }
                }
                if (TransparencyUsed == true)
                {
                    for (int j = 0; j < Builder.Materials.Length; j++)
                    {
                        Builder.Materials[j].TransparentColor     = FirstPxTransparent ? FirstPxColor : transparentColor;
                        Builder.Materials[j].TransparentColorUsed = true;
                    }
                }
            }
            Builder.Apply(ref Object);
            Object.Mesh.CreateNormals();
            return(Object);
        }