Example #1
0
        /// <summary>
        /// Parse a material library declaration in a .OBJ file (a line of the kind "mtllib file.mtl").
        /// </summary>
        private void ParseObjUseMtlDeclaration()
        {
            // Check that the line contains a material name
            if (objParser.IsEndOfLine)
            {
                throw new InvalidObjMtlFileException(string.Format(
                                                         "{0}: Material usage declaration without a corresponding material name.", objParser.GetFilePositionStr()));
            }

            // Load the associated material library
            string materialName = objParser.ReadRestOfLine().Trim();

            if (!materials.ContainsKey(materialName))
            {
                throw new InvalidObjMtlFileException(string.Format(
                                                         "{0}: Can't find the material '{1}'.", objParser.GetFilePositionStr(), materialName));
            }

            currentUsedMaterial = materials[materialName];

            // Create a new mesh since the material has changed
            currentMesh = null;
        }
Example #2
0
        /// <summary>
        /// Parses a object name declaration in a .OBJ file (a line of the kind "o [object name]").
        /// </summary>
        private void ParseObjObjectDeclaration()
        {
            // Check that the line contains an object name
            if (objParser.IsEndOfLine)
            {
                throw new InvalidObjMtlFileException(string.Format(
                                                         "{0}: Object declaration without a corresponding object name.", objParser.GetFilePositionStr()));
            }

            // Create the new object instance
            string newObjectName = objParser.ReadRestOfLine().Trim();

            currentObject = new ObjMtlObject();
            currentMesh   = null;

            // Add the object to the object list
            if (model.Objects.ContainsKey(newObjectName))
            {
                throw new InvalidObjMtlFileException(string.Format(
                                                         "{0}: Duplicate object name '{1}'.", objParser.GetFilePositionStr(), newObjectName));
            }
            model.Objects.Add(newObjectName, currentObject);
        }
Example #3
0
        /// <summary>
        /// Load the model from the .OBJ file
        /// </summary>
        /// <param name="objPath">The path of a file containing the .OBJ file.</param>
        /// <returns>A list of warnings while loading the file.</returns>
        public List <string> LoadObj(string objPath)
        {
            if (objPath == null)
            {
                throw new ArgumentNullException("objPath");
            }

            // Set up the parser status to load a new .OBJ file
            this.warningLog = new List <string>();

            this.objPath   = objPath;
            this.objParser = null;

            this.vertexPositions = new List <Vector3>();
            this.vertexNormals   = new List <Vector3>();
            this.vertexTexCoords = new List <Vector2>();

            this.currentObject       = null;
            this.currentMesh         = null;
            this.currentUsedMaterial = null;

            this.mtlPath   = null;
            this.mtlParser = null;

            this.materials = new Dictionary <string, ObjMtlMaterial>();

            this.currentLoadMaterial = null;

            using (objParser = new ObjMtlParser(objPath))
            {
                while (objParser.ReadNextLine())
                {
                    // Check for empty and comment lines
                    if (!objParser.AdvanceToNextNonWhiteSpace())
                    {
                        continue;
                    }
                    if (objParser.PeekCharacter() == '#')
                    {
                        continue;
                    }

                    // Parse according to the keyword
                    string keyword = objParser.GetNextWord();

                    switch (keyword)
                    {
                    case "o":
                        ParseObjObjectDeclaration();
                        break;

                    case "mtllib":
                        ParseObjMtlLibDeclaration();
                        break;

                    case "usemtl":
                        ParseObjUseMtlDeclaration();
                        break;

                    case "v":
                        ParseObjVertexPositionDeclaration();
                        break;

                    case "vn":
                        ParseObjVertexNormalDeclaration();
                        break;

                    case "vt":
                        ParseObjVertexTexCoordDeclaration();
                        break;

                    case "f":
                        ParseObjFaceDeclaration();
                        break;

                    default:
                        warningLog.Add(string.Format(
                                           "{0}: Unrecognized keyword '{1}'.", objParser.GetFilePositionStr(), keyword));
                        break;
                    }
                }
            }

            return(warningLog);
        }
Example #4
0
        /// <summary>
        /// Parse a face declaration in a .OBJ file (a line of the kind "f 1 2 3").
        /// </summary>
        private void ParseObjFaceDeclaration()
        {
            ObjMtlFace face = new ObjMtlFace();

            for (string word = objParser.GetNextWord(); word != null; word = objParser.GetNextWord())
            {
                // There are four accepted formats for the face declarations:
                // Position, No Normal, No TexCoord: f POS
                // Position, TexCoord, No Normal: f POS/TEXCOORD
                // Position, No TexCoord, Normal: f POS//NORMAL
                // Position, TexCoord, Normal: f POS/TEXCOORD/NORM

                // Also, take note that the vertex indexes start are one-based and not zero-based

                // Split into components
                string[] vertexIndexStrings = word.Split('/');
                if (vertexIndexStrings.Length > 3)
                {
                    throw new InvalidObjMtlFileException(string.Format(
                                                             "{0}: Invalid face declaration format (too many components).", objParser.GetFilePositionStr()));
                }

                // Validate valid format specification
                if (vertexIndexStrings[0] == "")
                {
                    throw new InvalidObjMtlFileException(string.Format(
                                                             "{0}: Invalid face declaration format (vertex position index can't be empty).", objParser.GetFilePositionStr()));
                }

                if (vertexIndexStrings.Length == 2 && vertexIndexStrings[1] == "")
                {
                    throw new InvalidObjMtlFileException(string.Format(
                                                             "{0}: Invalid face declaration format (texture coordinate index can't be empty unless followed by vertex normal index).", objParser.GetFilePositionStr()));
                }

                if (vertexIndexStrings.Length == 3 && vertexIndexStrings[2] == "")
                {
                    throw new InvalidObjMtlFileException(string.Format(
                                                             "{0}: Invalid face declaration format (texture normal index can't be empty).", objParser.GetFilePositionStr()));
                }

                // Parse vertex position index (always the first component, always present)
                int vertexPositionIdx;
                if (!int.TryParse(vertexIndexStrings[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out vertexPositionIdx))
                {
                    throw new InvalidObjMtlFileException(string.Format(
                                                             "{0}: Invalid face declaration format (vertex position index not an integer).", objParser.GetFilePositionStr()));
                }

                if (vertexPositionIdx - 1 < 0 || vertexPositionIdx - 1 >= vertexPositions.Count)
                {
                    throw new InvalidObjMtlFileException(string.Format(
                                                             "{0}: Invalid face declaration format (vertex position index out of bounds).", objParser.GetFilePositionStr()));
                }

                // Parse vertex texture coordinate (always the third component, may not be present)
                int vertexTexCoordIdx = -1;
                if (vertexIndexStrings.Length >= 2 && vertexIndexStrings[1] != "")
                {
                    if (!int.TryParse(vertexIndexStrings[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out vertexTexCoordIdx))
                    {
                        throw new InvalidObjMtlFileException(string.Format(
                                                                 "{0}: Invalid face declaration format (vertex texture coordinate index not an integer).", objParser.GetFilePositionStr()));
                    }

                    if (vertexTexCoordIdx - 1 < 0 || vertexTexCoordIdx - 1 >= vertexTexCoords.Count)
                    {
                        throw new InvalidObjMtlFileException(string.Format(
                                                                 "{0}: Invalid face declaration format (vertex texture coordinate index out of bounds).", objParser.GetFilePositionStr()));
                    }
                }

                // Parse vertex normal index (always the second component, may not be present)
                int vertexNormalIdx = -1;
                if (vertexIndexStrings.Length == 3)
                {
                    if (!int.TryParse(vertexIndexStrings[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out vertexNormalIdx))
                    {
                        throw new InvalidObjMtlFileException(string.Format(
                                                                 "{0}: Invalid face declaration format (vertex normal index not an integer).", objParser.GetFilePositionStr()));
                    }

                    if (vertexNormalIdx - 1 < 0 || vertexNormalIdx - 1 >= vertexNormals.Count)
                    {
                        throw new InvalidObjMtlFileException(string.Format(
                                                                 "{0}: Invalid face declaration format (vertex normal index out of bounds).", objParser.GetFilePositionStr()));
                    }
                }

                ObjMtlVertex vtx = new ObjMtlVertex(
                    vertexPositions[vertexPositionIdx - 1],
                    (vertexNormalIdx != -1) ? (Vector3?)vertexNormals[vertexNormalIdx - 1] : null,
                    (vertexTexCoordIdx != -1) ? (Vector2?)vertexTexCoords[vertexTexCoordIdx - 1] : null);

                // Check that all the faces have the same elements
                if (face.Count != 0)
                {
                    if (face[0].Normal.HasValue != vtx.Normal.HasValue ||
                        face[0].TexCoord.HasValue != vtx.TexCoord.HasValue)
                    {
                        throw new InvalidObjMtlFileException(string.Format(
                                                                 "{0}: Invalid face declaration format (not all elements have the same components).", objParser.GetFilePositionStr()));
                    }
                }

                // Add the vertex to the face
                face.Add(vtx);
            }

            // Validate the number of vertices in the face
            if (face.Count < 3)
            {
                throw new InvalidObjMtlFileException(string.Format(
                                                         "{0}: A face must have at least 3 vertices.", objParser.GetFilePositionStr()));
            }

            // If no object is currently initialized (this happens if the face is outside any object),
            // we add the face to an object named as the empty string.
            if (currentObject == null)
            {
                if (!model.Objects.ContainsKey(""))
                {
                    model.Objects.Add("", new ObjMtlObject());
                }

                currentObject = model.Objects[""];
            }

            // If no mesh is currently initialized (this happens if the face is the first one
            // within the current material), start a new material
            if (currentMesh == null)
            {
                // If no material is used (this happens if the face is before any usemtl declaration),
                // create a new empty material for the face
                if (currentUsedMaterial == null)
                {
                    currentUsedMaterial = new ObjMtlMaterial();
                }

                currentMesh = new ObjMtlMesh(currentUsedMaterial);
                currentObject.Meshes.Add(currentMesh);
            }

            // Add the face to the mesh
            currentMesh.Faces.Add(face);
        }