Click here to Skip to main content
15,881,809 members
Articles / Programming Languages / C#

Space and Matrix Transformations - Building a 3D Engine

Rate me:
Please Sign up or sign in to vote.
4.84/5 (13 votes)
4 Sep 2009CPOL8 min read 94.8K   6.1K   101  
This Article Describes How to Navigate 3D Space
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Tao.OpenGl;
using AGE_Engine3D.Math;
using AGE_Engine3D.RenderObjects;

namespace AGE_Engine3D.RenderObjects
{
    class MeshContainer : IMeshResource
    {
        #region Fields
        public List<BaseMesh> Meshes = null;
        public List<AGE_Vector3Df> PositionList;
        public List<AGE_Vector3Df> NormalList;
        public List<AGE_TextureCoordinate2f> TextureList;
        public List<Material> Materials;
        string Name = "Untitled";



        // Track whether Dispose has been called.
        private bool disposed = false; 
        #endregion

        #region ResourceInterface
        public string ResourceName { get { return this.Name; } }

        #region Implement IDisposable Methods

        private void CleanUp()
        {
            foreach (BaseMesh Mesh in Meshes)
                Mesh.Dispose();
        }

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the
        // runtime from inside the finalizer and you should not reference
        // other objects. Only unmanaged resources can be disposed.
        private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    // Nothing Todo...
                }

                // Call the appropriate methods to clean up
                // unmanaged resources here.
                // If disposing is false,
                // only the following code is executed.
                this.CleanUp();

                // Note disposing has been done.
                disposed = true;

            }
        }

        #endregion 
        #endregion

        #region Methods
        
        #region Constructors
        public MeshContainer()
        {
            this.Meshes = new List<BaseMesh>();
            this.PositionList = new List<AGE_Vector3Df>();
            this.NormalList = new List<AGE_Vector3Df>();
            this.TextureList = new List<AGE_TextureCoordinate2f>();
            this.Materials = new List<Material>();
        }
        
        #endregion

        public void Initialize()
        {
            foreach (BaseMesh Mesh in this.Meshes)
            {
                Mesh.Generate_List_ID();
            }
        }

        public void RenderOpenGL()
        {
            Gl.glEnable(Gl.GL_COLOR_MATERIAL);
            Gl.glColorMaterial(Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT_AND_DIFFUSE);
            foreach (BaseMesh Mesh in Meshes)
            {
                foreach (string Name in Mesh.MaterialNames)
                {
                    foreach (Material Mat in this.Materials)
                    {
                        if (Mat.Name == Name)
                        {
                            Mat.LoadMaterial();
                            Mesh.RenderOpenGL();
                        }
                    }
                }
               
            }
        }
        
        
        #endregion

        #region StaticMethods

        public static MeshContainer LoadObjFile(string FilePath)
        {
            MeshContainer result = null;
            List<string> ObjFileCode = new List<string>();
            #region Get the Obj File Data
            try
            {
                using (StreamReader ObjFileStream = File.OpenText(FilePath))
                {
                    #region ReadData
                    string LineInput = "";

                    while ((LineInput = ObjFileStream.ReadLine()) != null)
                    {
                        ObjFileCode.Add(LineInput);
                    }


                    #endregion
                    ObjFileStream.Close();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);

            } 
            #endregion
            #region Parse object File
            try
            {
                if (ObjFileCode.Count > 0)
                {
                    result = new MeshContainer();
                    result.Name = Path.GetFileNameWithoutExtension(FilePath);
                    BaseMesh CurrGroup = null;
                    char[] Deliminator = { ' ' };
                    foreach (string CurrLine in ObjFileCode)
                    {
                        //If the Line length is zero then skip it
                        if (CurrLine.Length != 0)
                        {
                            //If the String Starts with '#' then it is a Comment.
                            if (CurrLine[0] != '#')
                            {

                                string[] arg = CurrLine.Split(Deliminator, StringSplitOptions.RemoveEmptyEntries);
                                if (arg.Length > 0)
                                {
                                    #region Parse each Line of the Obj File
                                    switch (arg[0].ToUpper())
                                    {
                                        #region v Geometric vertices
                                        case "V":
                                            if (arg.Length < 4)
                                                throw new FileLoadException();
                                            float Vx = float.Parse(arg[1]);
                                            float Vy = float.Parse(arg[2]);
                                            float Vz = float.Parse(arg[3]);
                                            result.PositionList.Add(new AGE_Vector3Df(Vx, Vy, Vz));
                                            break;
                                        #endregion

                                        #region vt Texture vertices
                                        case "VT":
                                            if (arg.Length < 3)
                                                throw new FileLoadException();
                                            float Vu = float.Parse(arg[1]);
                                            float Vv = float.Parse(arg[2]);
                                            result.TextureList.Add(new AGE_TextureCoordinate2f(Vu, Vv));
                                            break;
                                        #endregion

                                        #region vn Vertex normals
                                        case "VN":
                                            if (arg.Length < 4)
                                                throw new FileLoadException();
                                            float Nx = float.Parse(arg[1]);
                                            float Ny = float.Parse(arg[2]);
                                            float Nz = float.Parse(arg[3]);
                                            AGE_Vector3Df Norm = new AGE_Vector3Df(Nx, Ny, Nz);
                                            result.NormalList.Add(Norm.UnitVector());
                                            break;
                                        #endregion

                                        #region Currently Unused

                                        #region vp Parameter space vertices

                                        case "VP":
                                            break;

                                        #endregion


                                        case "DEG":
                                            break;

                                        case "BMAT":
                                            break;

                                        case "STEP":
                                            break;

                                        case "CSTYPE":
                                            break;

                                        #endregion

                                        #region Group name
                                        //case "G":
                                        //    if (CurrGroup != null)
                                        //        result.Meshes.Add(CurrGroup);

                                        //    CurrGroup = new BaseMesh(ref result.PositionList,
                                        //                                 ref result.NormalList,
                                        //                                 ref result.TextureList);
                                        //    CurrGroup.Name = arg[1];

                                        //    break;
                                        #endregion

                                        #region Object name
                                        case "O":
                                            if (CurrGroup != null)
                                                result.Meshes.Add(CurrGroup);

                                            CurrGroup = new BaseMesh(ref result.PositionList,
                                                                         ref result.NormalList,
                                                                         ref result.TextureList);
                                            CurrGroup.Name = arg[1];

                                            break;
                                        #endregion

                                        #region Smoothing Group
                                        case "S":
                                            //Ignore for now
                                            break;
                                        #endregion

                                        #region Faces
                                        case "F":
                                            FaceIndices NewFace;
                                            if (FaceIndices.ParseObjLine(CurrLine, out NewFace))
                                            {
                                                CurrGroup.Faces.Add(NewFace);
                                            }
                                            break;

                                        #endregion

                                        #region Assign Material Name
                                        case "USEMTL":
                                            if (CurrGroup != null)
                                                CurrGroup.MaterialNames.Add(arg[1]);
                                            break;
                                        #endregion

                                        #region Load Materials from a Material File
                                        case "MTLLIB":
                                            List<Material> TempMatList = ParseMaterialLibrary(arg[1], Path.GetDirectoryName(FilePath));
                                            if (TempMatList != null)
                                            {
                                                if (TempMatList.Count > 0)
                                                {
                                                    bool AddMaterial = false;
                                                    foreach (Material tmpMat in TempMatList)
                                                    {
                                                        AddMaterial = true;
                                                        for (int i = 0; i < result.Materials.Count; i++)
                                                        {
                                                            if (result.Materials[i].Name == tmpMat.Name)
                                                            {
                                                                AddMaterial = false;
                                                                break;
                                                            }
                                                        }
                                                        if (AddMaterial)
                                                            result.Materials.Add(tmpMat);
                                                    }
                                                }
                                            }
                                            break;
                                        #endregion

                                        default:
                                            break;
                                    }
                                    #endregion
                                }
                            }
                        }
                    }
                    if (CurrGroup != null)
                        result.Meshes.Add(CurrGroup);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                result = null;
            } 
            #endregion
            
            return result;
        }

        static List<Material> ParseMaterialLibrary(string FileName, string FilePath)
        {
            List<Material> result = null;
            List<string> MaterialFileCode = new List<string>();
            
            #region Get the Material File Data
            try
            {
                using (StreamReader MaterialFileStream = File.OpenText(FilePath+"\\"+FileName))
                {
                    #region ReadData
                    string LineInput = "";

                    while ((LineInput = MaterialFileStream.ReadLine()) != null)
                    {
                        MaterialFileCode.Add(LineInput);
                    }


                    #endregion
                    MaterialFileStream.Close();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);

            }
            #endregion
            #region Parsing Material file
            try
            {
                if (MaterialFileCode.Count > 0)
                {
                    result = new List<Material>();
                    Material CurrentMat = null;
                    char[] Deliminator = { ' ' };
                    foreach (string CurrLine in MaterialFileCode)
                    {
                        //If the Line length is zero then skip it
                        if (CurrLine.Length != 0)
                        {
                            //If the String Starts with '#' then it is a Comment.
                            if (CurrLine[0] != '#')
                            {
                                string[] arg = CurrLine.Split(Deliminator, StringSplitOptions.RemoveEmptyEntries);
                                if (arg.Length > 0)
                                {
                                    #region Parse each Line of the Material File
                                    switch (arg[0].ToUpper())
                                    {
                                        #region New Materials
                                        case "NEWMTL":
                                            if (CurrentMat != null)
                                                result.Add(CurrentMat);
                                            CurrentMat = new Material();
                                            CurrentMat.Name = arg[1];
                                            break;

                                        #endregion

                                        #region Shininess
                                        case "NS":
                                            if (CurrentMat != null)
                                                CurrentMat.Shininess = float.Parse(arg[1]);
                                            break;
                                        #endregion

                                        #region Ambient color
                                        case "KA":
                                            if (CurrentMat != null)
                                            {
                                                CurrentMat.AmbientColor.Red = float.Parse(arg[1]);
                                                CurrentMat.AmbientColor.Green = float.Parse(arg[2]);
                                                CurrentMat.AmbientColor.Blue = float.Parse(arg[3]);
                                            }
                                            break;
                                        #endregion

                                        #region Diffuse color
                                        case "KD":
                                            if (CurrentMat != null)
                                            {
                                                CurrentMat.DiffuseColor.Red = float.Parse(arg[1]);
                                                CurrentMat.DiffuseColor.Green = float.Parse(arg[2]);
                                                CurrentMat.DiffuseColor.Blue = float.Parse(arg[3]);
                                            }
                                            break;
                                        #endregion

                                        #region Specular color
                                        case "KS":
                                            if (CurrentMat != null)
                                            {
                                                CurrentMat.SpecularColor.Red = float.Parse(arg[1]);
                                                CurrentMat.SpecularColor.Green = float.Parse(arg[2]);
                                                CurrentMat.SpecularColor.Blue = float.Parse(arg[3]);
                                            }
                                            break;
                                        #endregion

                                        #region Texture
                                        case "MAP_KA":
                                            if (CurrentMat != null)
                                            {
                                                CurrentMat.TextureName = arg[1];
                                            }
                                            break;
                                        #endregion
                                        default:
                                            break;
                                    }
                                    #endregion
                                }
                            }
                        }
                    }
                    if (CurrentMat != null)
                        result.Add(CurrentMat);
                }
            }
            catch (Exception e)
            {

            } 
            #endregion
            
            

            return result;
        }

        #endregion
    }


}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer Lea+Elliott, Inc.
United States United States
I am a licensed Electrical Engineer at Lea+Elliott, Inc. We specialize in the planning, procurement and implementation of transportation systems, with special emphasis on automated and emerging technologies.

Comments and Discussions