Beispiel #1
0
        private static ObjectManager.StaticObject LoadBinaryX(byte[] Data, int FloatingPointSize)
        {
            Block block = new BinaryBlock(Data, FloatingPointSize);

            block.FloatingPointSize = FloatingPointSize;
            ObjectManager.StaticObject obj = new ObjectManager.StaticObject();
            MeshBuilder builder            = new MeshBuilder();
            Material    material           = new Material();

            while (block.Position() < block.Length())
            {
                Block subBlock = block.ReadSubBlock();
                ParseSubBlock(subBlock, ref obj, ref builder, ref material);
            }
            builder.Apply(ref obj);
            obj.Mesh.CreateNormals();
            return(obj);
        }
Beispiel #2
0
        private static ObjectManager.StaticObject LoadTextualX(string Text)
        {
            Text = Text.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " ").Trim();
            ObjectManager.StaticObject obj = new ObjectManager.StaticObject();
            MeshBuilder builder            = new MeshBuilder();
            Material    material           = new Material();
            Block       block = new TextualBlock(Text);

            while (block.Position() < block.Length() - 5)
            {
                Block subBlock = block.ReadSubBlock();
                ParseSubBlock(subBlock, ref obj, ref builder, ref material);
            }
            builder.Apply(ref obj);
            obj.Mesh.CreateNormals();
            if (rootMatrix != Matrix4D.NoTransformation)
            {
                for (int i = 0; i < obj.Mesh.Vertices.Length; i++)
                {
                    obj.Mesh.Vertices[i].Coordinates.Transform(rootMatrix);
                }
            }
            return(obj);
        }
Beispiel #3
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">A three-dimemsional vector describing 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(Program.CurrentHost);
            MeshBuilder  Builder = new MeshBuilder(Program.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           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(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, Program.FileSystem.LoksimPackageInstallationDirectory);
                                                if (!File.Exists(tday))
                                                {
                                                    Interface.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
                                                {
                                                    Interface.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, Program.FileSystem.LoksimPackageInstallationDirectory);
                                                if (!File.Exists(transtex))
                                                {
                                                    Interface.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:
                                                    Interface.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
                                            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(MessageType.Error, false, "Invalid argument nX in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    if (!double.TryParse(NormalPoints[1], out ny))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, "Invalid argument nY in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    if (!double.TryParse(NormalPoints[2], out nz))
                                                    {
                                                        Interface.AddMessage(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(MessageType.Error, false, "Invalid argument vX in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    if (!double.TryParse(VertexPoints[1], out vy))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, "Invalid argument yY in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    if (!double.TryParse(VertexPoints[2], out vz))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, "Invalid argument vZ in " + attribute.Name + " in Loksim3D object file " + FileName);
                                                    }
                                                    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
                                            tempVertices[tempVertices.Length - 1] = new Vertex(vx, vy, vz);
                                            tempNormals[tempNormals.Length - 1]   = new Vector3((float)nx, (float)ny, (float)nz);
                                            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(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.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))
                                                    {
                                                        Interface.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))
                                                        {
                                                            Interface.AddMessage(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(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
                    ApplyRotation(Builder, 1, 0, 0, Rotation.Z);
                }


                if (Rotation.X != 0.0)
                {
                    //This is actually the Y-Axis rotation
                    Rotation.X = Rotation.X.ToRadians();
                    //Apply rotation
                    ApplyRotation(Builder, 0, 1, 0, Rotation.X);
                }
                if (Rotation.Y != 0.0)
                {
                    //This is actually the X-Axis rotation
                    Rotation.Y = Rotation.Y.ToRadians();
                    //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;
                    }
                }
            }
            Builder.Apply(ref Object);
            Object.Mesh.CreateNormals();
            return(Object);
        }
Beispiel #4
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));
            }
        }
Beispiel #5
0
        internal static ObjectManager.StaticObject ReadObject(string FileName)
        {
            currentFolder = System.IO.Path.GetDirectoryName(FileName);
            currentFile   = FileName;
            rootMatrix    = Matrix4D.NoTransformation;

#if !DEBUG
            try
            {
#endif
            XFileParser parser = new XFileParser(System.IO.File.ReadAllBytes(FileName));
            Scene scene        = parser.GetImportedData();

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

            // Global
            foreach (var mesh in scene.GlobalMeshes)
            {
                MeshBuilder(ref obj, ref builder, mesh);
            }

            if (scene.RootNode != null)
            {
                // Root Node
                if (scene.RootNode.TrafoMatrix != OpenTK.Matrix4.Zero)
                {
                    rootMatrix = ConvertMatrix(scene.RootNode.TrafoMatrix);
                }

                foreach (var mesh in scene.RootNode.Meshes)
                {
                    MeshBuilder(ref obj, ref builder, mesh);
                }

                // Children Node
                foreach (var node in scene.RootNode.Children)
                {
                    ChildrenNode(ref obj, ref builder, node);
                }
            }

            builder.Apply(ref obj);
            obj.Mesh.CreateNormals();
            if (rootMatrix != Matrix4D.NoTransformation)
            {
                for (int i = 0; i < obj.Mesh.Vertices.Length; i++)
                {
                    obj.Mesh.Vertices[i].Coordinates.Transform(rootMatrix);
                }
            }
            return(obj);

#if !DEBUG
        }

        catch (Exception e)
        {
            Interface.AddMessage(MessageType.Error, false, e.Message + " in " + FileName);
            return(null);
        }
#endif
        }
Beispiel #6
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
        }
Beispiel #7
0
        private static void ParseSubBlock(Block block, ref ObjectManager.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();
                }
                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();
                }
                int nVerts = block.ReadUInt16();
                if (nVerts == 0)
                {
                    //Some null objects contain an empty mesh
                    Interface.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)
                {
                    Interface.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;
            }
        }