🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Extracting Mesh (.X) information without a Device

posted in My Epiphany
Published October 07, 2007
Advertisement
Previously, my game engine calculated boundry information from meshes as they were loaded by the Device. While re-working the engine I decided that this wasn't necessary and the information could be extracted directly from the .X file itself. Microsoft provides some bases classes that help with this in the Microsoft.DirectX.Direct3D assembly - the XFile and XFileManager classes.

I created the MeshInfo class which loads a .X file and extracts the vertices and faces of the mesh stored within it. Transformations are done where necessary if frames exist in the .X file.

This is the resource loader function used by MeshInfo.

        ///         /// Creates a resource stream. Checks both physical files and assemblies for the desired resource.        ///         /// The name of the resource.        /// The search location. Checks both physical files and assemblies for the desired resource. If null or empty will search the current directory.        /// The stream.        static public System.IO.Stream CreateResourceStream(string resourceName, string searchPath)        {                        // Default search path to the current directory...            if (string.IsNullOrEmpty(searchPath))                searchPath = System.IO.Directory.GetCurrentDirectory();            if (File.Exists(Path.Combine(searchPath, resourceName)))            {                #region Load from File                // Load from File ...                try                {                    return new FileStream(Path.Combine(searchPath, resourceName), FileMode.Open, FileAccess.Read);                }                catch (Exception exe)                {                    throw new Exception("An error occured while resource from file, \"" + Path.Combine(searchPath, resourceName) + "\".", exe);                }                #endregion            }            else            {                #region Load from Assembly                // Load from assembly...                Assembly asm;                // Browse all assemblies...                foreach (string assemblyPath in Directory.GetFiles(searchPath, "*.dll"))                {                    asm = Assembly.LoadFile(assemblyPath);                    // Browse all resources in assembly...                    foreach (string assResourceName in asm.GetManifestResourceNames())                    {                        // Exists in the assembly?                        if (assResourceName.ToLower().EndsWith(resourceName.ToLower()))                        {                            try                            {                                // Yes.                                return asm.GetManifestResourceStream(assResourceName);                            }                            catch (Exception exe)                            {                                throw new Exception("An error occured while loading resource from assembly, \"" + assemblyPath + "\".", exe);                            }                        }                    }                }                #endregion            }                                    // Resource not found...            throw new FileNotFoundException("The resource \"" + resourceName + "\" could not be found in \"" + searchPath + "\"!");        }


Here is the "face" structure used by the MeshInfo class.

using Microsoft.DirectX;using System;namespace GameEngine.Math.Structures{	/// 	/// A Ray.	/// 	public struct Ray	{		#region variables		/// 		/// Ray direction.		/// 		public Vector3 origin;		/// 		/// Ray origin.		/// 		public Vector3 direction;		#endregion        #region properties        ///         /// Retreives an empty ray.        ///         static public Ray Empty        {            get            {                Ray result;                result = new Ray(Vector3.Empty,Vector3.Empty);                return result;            }        }        #endregion		#region constructors		/// 		/// Constructor.		/// 		/// Ray direction.		/// Ray origin.		public Ray (Vector3 direction, Vector3 origin)		{			this.direction = direction;			this.origin = origin;		}		#endregion				#region operators			public override string ToString()		{			return "Origin : " + this.origin + ", " + "Direction : " + this.direction;		}		public static bool operator == (Ray x, Ray y) 		{						if (x.direction == y.direction)				if (x.origin == y.origin)					return true;								return false;		}			public static bool operator != (Ray x, Ray y)  		{			return (!(x == y));		}				public override bool Equals(object obj)		{			return base.Equals(obj);		}		public override int GetHashCode()		{			return base.GetHashCode ();		}		#endregion	}		/// 	/// A Triangle	/// 	public struct Triangle	{		#region variables		/// 		/// 3 vertices that make up the triangle.		/// 		public Vector3 [] positions;		/// 		/// Normal of the triangle.		/// 		public Vector3 normal;		#endregion			#region properties			/// 		/// Retreives an empty triangle.		/// 		static public Triangle Empty		{			get			{				Triangle result;								result = new Triangle();				result.normal = Vector3.Empty;				result.positions = null;				return result;			}		}		#endregion		#region constructors		/// 		/// Constructor.		/// 		/// Vertex 1 of the Triangle.		/// Vertex 2 of the Triangle.		/// Vertex 3 of the Triangle.		/// Normal of the Triangle.		public Triangle (Vector3 pos1, Vector3 pos2, Vector3 pos3, Vector3 normal)		{			this.positions = new Vector3[3];			this.positions[0] = pos1;			this.positions[1] = pos2;			this.positions[2] = pos3;			this.normal = normal;		}		#endregion		#region methods		/// 		/// Returns the Plane that this Triangle creates.		/// 		/// 		/// The Plane that the Triangle creates.		/// 				public Plane GetPlane()		{									return Plane.FromPointNormal(this.positions[0],this.normal);		}				/// 		/// Returns a Transformed Triangle.		/// 		/// The Transformation Matrix to transform the Triangle by.		/// 		/// The Triangle transformed by the specified Transformation Matrix.		/// 		/// 		/// Source : 		/// http://www.gignews.com/realtime020100.htm				/// 		public Triangle GetTransformedTriangle ( Matrix transformation )		{			Triangle triangle;			Vector3 triangleEdge1, triangleEdge2;			triangle.positions = new Vector3 [3];			// Transform the Positions of the Triangle.			for (int i = 0; i < this.positions.Length; i++)			{				triangle.positions = Vector3.TransformCoordinate(this.positions,transformation);					}												// Transform the Normal of the Triangle.			// The Normal of the Triangle can be derived from the Cross Product of its Edges...			triangleEdge1 = triangle.positions[0] - triangle.positions[1];			triangleEdge2 = triangle.positions[0] - triangle.positions[2];			triangle.normal = Vector3.Cross(triangleEdge1,triangleEdge2);			triangle.normal.Normalize();			return triangle;		}        ///         /// Returns the barycenter (center of gravity) of the triangle.        ///         /// The barycenter of the triangle.        public Vector3 GetBarycenter()        {                        float midX, midY, midZ;            midX = (this.positions[0].X + this.positions[1].X + this.positions[2].X) / 3.0f;            midY = (this.positions[0].Y + this.positions[1].Y + this.positions[2].Y) / 3.0f;            midZ = (this.positions[0].Z + this.positions[1].Z + this.positions[2].Z) / 3.0f;            return new Vector3(midX, midY, midZ);        }        ///         /// Computes a normal for the triangle.        ///                 /// The normal of the triangle.        public Vector3 ComputeNormal ()        {            Vector3 triangleEdge1, triangleEdge2, result;            triangleEdge1 = this.positions[0] - this.positions[1];            triangleEdge2 = this.positions[2] - this.positions[1];            result = Vector3.Normalize(Vector3.Cross(triangleEdge2, triangleEdge1));            return result;        }		#endregion		#region operators	        ///         /// 06/06/2007  SV  Now call IsEqual() when comparing positions. Rounds to Minimum Precision.        /// 		public static bool operator == (Triangle x, Triangle y) 		{						if (Math.Library.IsEqual(x.normal,y.normal))			{				if ((x.positions == null) && (y.positions == null))					return true;				if (x.positions.Length != x.positions.Length)					return false;				for (int index=0 ; index < x.positions.Length; index ++)                    if (!Math.Library.IsEqual(x.positions[index],y.positions[index]))						return false;				return true;			}								return false;		}			public static bool operator != (Triangle x, Triangle y)  		{			return (!(x == y));		}				public override bool Equals(object obj)		{			return base.Equals(obj);		}		public override int GetHashCode()		{			return base.GetHashCode ();		}		public override string ToString()		{			if (this.positions == null) 				return "";			else				return "\nv0: \n" + this.positions[0] + "\nv1: \n" + this.positions[1] + "\nv2: \n" + this.positions[2] + "\nnormal: \n" + this.normal;					}		#endregion	}	/// 	/// A Sphere.	/// 	public struct Sphere	{		#region variables		/// 		/// The Center of the Sphere.		/// 		public Vector3 center;		/// 		/// The Radius of the Sphere.		/// 		public float radius;		#endregion		#region properties		/// 		/// Retreives an empty sphere.		/// 		static public Sphere Empty		{			get			{				Sphere result;								result = new Sphere();				result.center = Vector3.Empty;				result.radius = 0;				return result;			}		}		#endregion		#region constructors		/// 		/// Constructor.		/// 		/// The center of the sphere.		/// The radius of the sphere.		public Sphere (Vector3 center, float radius)		{			this.center = center;			this.radius = radius;		}        ///         /// Constructor.        ///         /// The points the sphere should encapsulate.        public Sphere(Vector3[] points)        {            center = Vector3.Empty;            radius = 0.0f;            if (points != null ? points.Length > 0 : false)            {                // Find the center of the points.                center = Library.GetCenter(points);                // Now find the furthest point from the center. The distance of this point from the center                // will be the radius.                for (int index = 0; index < points.Length; index++)                {                    radius = System.Math.Max(System.Math.Max(System.Math.Max(                        radius,                        System.Math.Abs(center.X - points[index].X)),                        System.Math.Abs(center.Y - points[index].Y)),                        System.Math.Abs(center.Z - points[index].Z));                }            }                    }        #endregion        #region methods        ///         /// Merge two spheres.        ///         /// The first sphere.        /// The second sphere.        /// The encapsulating sphere.        static public Sphere Merge(Sphere sphereA, Sphere sphereB)		{			Vector3 center;			float radius;			float distCenterToSphereA;			float distCenterToSphereB;			center = new Vector3(	(sphereA.center.X + sphereB.center.X) / 2, 									(sphereA.center.Y + sphereB.center.Y) / 2,									(sphereA.center.Z + sphereB.center.Z) / 2);							distCenterToSphereA = Math.Library.GetDistance(center,sphereA.center);			distCenterToSphereB = Math.Library.GetDistance(center,sphereB.center);				radius = System.Math.Max(distCenterToSphereA + sphereA.radius,distCenterToSphereB + sphereB.radius);			return new Sphere(center,radius);		}        		#endregion		#region operators			public static bool operator == (Sphere x, Sphere y) 		{			if (x.center == y.center)				if (x.radius == y.radius)					return true;			return false;		}			public static bool operator != (Sphere x, Sphere y)  		{			return (!(x == y));		}				public override bool Equals(object obj)		{			return base.Equals(obj);		}		public override int GetHashCode()		{			return base.GetHashCode ();		}		public override string ToString()		{			return "center = " + this.center.ToString() + ", radius = " + this.radius;		}		#endregion	}	/// 	/// A Cylinder.	/// 	public struct Cylinder	{		#region variables		/// 		/// The radius of the cylinder.		/// 		public float radius;        ///         /// The center of endcap "p" of the cylinder.        ///         public Vector3 p;        ///         /// The center of endcap "q" of the cylinder.        ///         public Vector3 q;		#endregion		#region properties		/// 		/// Returns an Empty Cylinder.		/// 		public Cylinder Empty		{			get			{				return new Cylinder(0,Vector3.Empty,Vector3.Empty);			}		}		#endregion		#region constructors		/// 		/// Constructor.		/// 		/// The radius of the cylinder.		/// The center of endcap "p" of the cylinder.        /// The center of endcap "q" of the cylinder.				public Cylinder( float radius, Vector3 p, Vector3 q )		{			this.radius = radius;            this.p = p;            this.q = q;		}		#endregion		#region operators		public static bool operator == (Cylinder x, Cylinder y) 		{			if (x.radius == y.radius)				if (x.p == y.p)					if (x.q == y.q)											    return true;			return false;		}			public static bool operator != (Cylinder x, Cylinder y)  		{			return (!(x == y));		}				public override bool Equals(object obj)		{			return base.Equals(obj);		}		public override int GetHashCode()		{			return base.GetHashCode ();		}		public override string ToString()		{			return "radius = " + this.radius + ", p = " + this.p + ", q = " + this.q;		}		#endregion	}    ///     /// A Capsule.    ///     public struct Capsule    {        #region variables        ///         /// The cylinder representing the capsule.        ///         private Cylinder cylinder;        #endregion        #region properties        ///         /// The radius of the capsule.        ///         public float Radius        {            get            {                return this.cylinder.radius;            }            set            {                this.cylinder.radius = value;            }        }        ///         /// The center the capsule's end sphere "p".        ///         public Vector3 P        {            get            {                return this.cylinder.p;            }            set            {                this.cylinder.p = value;            }        }        ///         /// The center the capsule's end sphere "q".        ///         public Vector3 Q        {            get            {                return this.cylinder.q;            }            set            {                this.cylinder.q = value;            }        }        ///         /// Returns an empty capsule.        ///         public Capsule Empty        {            get            {                return new Capsule(0, Vector3.Empty, Vector3.Empty);            }        }        #endregion        #region constructors        ///         /// Constructor.        ///         /// The radius of the capsule.        /// The center the capsule's end sphere "p".        /// The center the capsule's end sphere "q".		        public Capsule(float radius, Vector3 p, Vector3 q)        {            this.cylinder = new Cylinder(radius, p, q);        }        #endregion        #region operators        public static bool operator ==(Capsule x, Capsule y)        {            if (x.cylinder.radius == y.cylinder.radius)                if (x.cylinder.p == y.cylinder.p)                    if (x.cylinder.q == y.cylinder.q)                        return true;            return false;        }        public static bool operator !=(Capsule x, Capsule y)        {            return (!(x == y));        }        public override bool Equals(object obj)        {            return base.Equals(obj);        }        public override int GetHashCode()        {            return base.GetHashCode();        }        public override string ToString()        {            return "radius = " + this.cylinder.radius + ", p = " + this.cylinder.p + ", q = " + this.cylinder.q;        }        #endregion    }	/// 	/// An Axis-Aligned Box.	/// 	public struct AxisAlignedBox	{		#region variables		/// 		/// The Lower-Left Corner of the Axis-Aligned Box.		/// 		public Vector3 min;		/// 		/// The Upper-Right Corner of the Axis-Aligned Box. 		/// 		public Vector3 max;		#endregion		#region properties		/// 		/// The X-Dimension of the Axis-Aligned Box.		/// 		public float DimX		{			get			{				return System.Math.Abs(this.max.X-this.min.X);				}		}		/// 		/// The Y-Dimension of the Axis-Aligned Box.		/// 		public float DimY		{			get			{				return System.Math.Abs(this.max.Y-this.min.Y);												}		}		/// 		/// The Z-Dimension of the Axis-Aligned Box.		/// 		public float DimZ		{			get			{				return System.Math.Abs(this.max.Z-this.min.Z);			}		}		/// 		/// The center of the axis-aligned box.		///         ///         /// 06/04/2007  SV  No longer adding min and max before dividing by 2. If min and max have components that are maximum and minimum will result in NaN.        /// 06/04/2007  SV  Now handling situations where both max and min are +/- infinity. Was resulting in a NaN component for the resulting center.        /// 		public Vector3 Center		{			get			{                Vector3 center;                if (float.IsPositiveInfinity(this.max.X) && float.IsNegativeInfinity(this.min.X))                    center.X = 0;                else                    center.X = (this.max.X / 2.0f) + (this.min.X / 2.0F);                if (float.IsPositiveInfinity(this.max.Y) && float.IsNegativeInfinity(this.min.Y))                    center.Y = 0;                else                    center.Y = (this.max.Y / 2.0f) + (this.min.Y / 2.0F);                if (float.IsPositiveInfinity(this.max.Z) && float.IsNegativeInfinity(this.min.Z))                    center.Z = this.max.Z = 0;                else                    center.Z = (this.max.Z / 2.0f) + (this.min.Z / 2.0F);                return center;			}		}		/// 		/// The volume of the axis-aligned box.		/// 		public float Volume		{			get			{				return this.DimX * this.DimY * this.DimZ;			}		}		/// 		/// Retreives an empty Axis-Aligned Box.		/// 		static public AxisAlignedBox Empty		{			get			{				AxisAlignedBox result;								result = new AxisAlignedBox();				result.max = Vector3.Empty;				result.min = Vector3.Empty;				return result;			}		}				#endregion		#region constructors		/// 		/// Constructor.		/// 		/// The lower-left corner of the box.		/// The upper-right Corner of the box.		public AxisAlignedBox (Vector3 min, Vector3 max)		{			this.min = min;			this.max = max;		}        ///         /// Constructor.        ///         /// The center of the box.        /// The x-dimension of the box.        /// The y-dimension of the box.        /// The z-dimension of the box.        public AxisAlignedBox(Vector3 center, float dimX, float dimY, float dimZ)        {            if (float.IsNaN(dimX) || float.IsNaN(dimY) || float.IsNaN(dimZ))                throw new Exception("Can not create an invalid axis aligned box. The x-dimension, y-dimension, and z-dimensions of the box can not be NaN!");            this.min = new Vector3(center.X - (dimX / 2.0f), center.Y - (dimY / 2.0f), center.Z - (dimZ / 2.0f));            this.max = new Vector3(center.X + (dimX / 2.0f), center.Y + (dimY / 2.0f), center.Z + (dimZ / 2.0f));        }        ///         /// Constructor.        ///         /// The points the box should encapsulate.        public AxisAlignedBox(Vector3[] points)        {            this.min = Vector3.Empty;            this.max = Vector3.Empty;                        if (points != null ? points.Length > 0 : false)            {                // Iterate through all of the points and find the maximum and minimum constraints for                // X, Y, and Z. These are the max and min of the AABB.                for (int index = 0; index < points.Length; index++)                {                    // Maximum.                    this.max.X = System.Math.Max(points[index].X, this.max.X);                    this.max.Y = System.Math.Max(points[index].Y, this.max.Y);                    this.max.Z = System.Math.Max(points[index].Z, this.max.Z);                    // Minimum.                    this.min.X = System.Math.Min(points[index].X, this.min.X);                    this.min.Y = System.Math.Min(points[index].Y, this.min.Y);                    this.min.Z = System.Math.Min(points[index].Z, this.min.Z);                }            }                    }		#endregion		#region methods        ///         /// Returns a transformed axis aligned box.        ///         /// The transformation matrix to transform the box by.        ///         /// The box transformed by the specified transformation matrix.        ///         public AxisAlignedBox GetTransformedBox(Matrix transformation)        {            Vector3 transCenter;            float transDimX, transDimY, transDimZ;                        // Transform the center of the box.            transCenter = Vector3.TransformCoordinate(this.Center, transformation);            // Transfom the dimensions of the box.            transDimX = this.DimX * transformation.M11;            transDimY = this.DimY * transformation.M22;            transDimZ = this.DimZ * transformation.M33;            return new AxisAlignedBox(                new Vector3(transCenter.X - (transDimX / 2.0f), transCenter.Y - (transDimY / 2.0f), transCenter.Z - (transDimZ / 2.0f)),                new Vector3(transCenter.X + (transDimX / 2.0f), transCenter.Y + (transDimY / 2.0f), transCenter.Z + (transDimZ / 2.0f)));        }		/// 		/// Merge two axis-aligned boxes.		/// 		/// The first box.		/// The second box.		/// The encapsulating box.		static public AxisAlignedBox Merge (AxisAlignedBox boxA, AxisAlignedBox boxB)		{			Vector3 max;			Vector3 min;			min = Vector3.Empty;			max = Vector3.Empty;			max = Vector3.Maximize(boxA.max,boxB.max);			min = Vector3.Minimize(boxA.min,boxB.min);			return new AxisAlignedBox(min,max);					}		#endregion		#region operators	        ///         /// 06/06/2007  SV  Now call IsEqual() when comparing min/max of boxes. Rounds to Minimum Precision.        /// 		public static bool operator == (AxisAlignedBox x, AxisAlignedBox y) 		{            if (Math.Library.IsEqual(x.min,y.min))                if (Math.Library.IsEqual(x.max,y.max))										return true;			return false;		}			public static bool operator != (AxisAlignedBox x, AxisAlignedBox y)  		{			return (!(x == y));		}				public override bool Equals(object obj)		{			return base.Equals(obj);		}		public override int GetHashCode()		{			return base.GetHashCode ();		}		#endregion	}}


Here is the MeshInfo class.

using GameEngine.Math.Structures;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;using System;using System.Collections;using System.Collections.Generic;using System.Configuration;using System.IO;using System.Text;namespace GameEngine.Resources{    ///     /// Retreieves mesh info from a .X file. Does not load the mesh into graphics memory.    ///     public class MeshInfo    {        #region support classes        ///         /// Mesh list.         ///         private class MeshList : List        { }        ///         /// Frame list.        ///         private class FrameList : List        { }        ///         /// Represents a frame.         ///         private class Frame        {            #region variables            ///             ///  Frame name.            ///             internal string Name;            ///             /// Parent frame.            ///             internal Frame Parent;            ///             /// Child frame(s).            ///             internal FrameList Children;            ///             /// Mesh(s).             ///             internal MeshList Meshes;            ///             /// Frame transformation.            ///             internal Matrix Transformation;            #endregion            #region properties            ///             /// Total vertices.            ///             internal int TotalVertices            {                get                {                    int result;                    result = 0;                    // Add vertices in the frame's meshes.                    foreach (Mesh mesh in this.Meshes)                        result += (mesh.Faces != null) ? mesh.Faces.Length * 3 : 0;                    // Add vertices from the frame's children.                    foreach (Frame child in this.Children)                        result += child.TotalVertices;                    return result;                }            }            ///             /// Total faces.            ///             internal int TotalFaces            {                get                {                    int result;                    result = 0;                    // Add triangles in the frame's meshes.                    foreach (Mesh mesh in this.Meshes)                        result += (mesh.Faces != null) ? mesh.Faces.Length : 0;                    // Add triangles from the frame's children.                    foreach (Frame child in this.Children)                        result += child.TotalFaces;                    return result;                }            }            #endregion            #region constructors            ///             /// Constructor.            ///             internal Frame()            {                this.Name = "";                this.Parent = null;                this.Children = new FrameList();                this.Meshes = new MeshList();                this.Transformation = Matrix.Identity;            }            #endregion            #region methods            ///             /// Gets the faces for the frame.            ///             /// The transformation to apply to the faces.            /// The index to write to in the faces array.            /// The faces array.            private void GetTransformedFaces(Matrix transformation, int index, ref Triangle[] faces)            {                Matrix combinedTransform;                combinedTransform = this.Transformation * transformation;                // Add triangles in the frame's meshes.                foreach (Mesh mesh in this.Meshes)                    foreach (Triangle tri in mesh.GetTransformedFaces(combinedTransform))                        faces[index++] = tri;                // Add triangles from the frame's children.                foreach (Frame child in this.Children)                    child.GetTransformedFaces(combinedTransform, index, ref faces);            }            ///             /// Gets the vertices for the frame.            ///             /// The transformation to apply to the vertices.            /// The index to write to in the vertice array.            /// The vertice array.            private void GetTransformedVertices(Matrix transformation, int index, ref Vector3[] vertices)            {                Matrix combinedTransform;                combinedTransform = this.Transformation * transformation;                // Add vertices in the frame's meshes.                foreach (Mesh mesh in this.Meshes)                    foreach (Vector3 vert in mesh.GetTransformedVertices(combinedTransform))                        vertices[index++] = vert;                // Add vertices from the frame's children.                foreach (Frame child in this.Children)                    child.GetTransformedVertices(combinedTransform, index, ref vertices);            }            ///             /// Gets the faces for the frame.            ///             /// The transformation to apply to the faces.            /// The frame's faces.            internal Triangle[] GetTransformedFaces(Matrix transformation)            {                Triangle[] result;                result = new Triangle[this.TotalFaces];                this.GetTransformedFaces(transformation, 0, ref result);                return result;            }            ///             /// Get the vertices for the frame.            ///             /// The frame.            /// The transformation to apply to the vertices.            /// The frame's vertices.            internal Vector3[] GetTransformedVertices(Matrix transformation)            {                Vector3[] result;                result = new Vector3[this.TotalVertices];                this.GetTransformedVertices(transformation, 0, ref result);                return result;            }            #endregion        }        /// &l


0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement