www.pudn.com > IntroductionTo3DGameEngineDesign.rar > Model.cs


using System; 
using System.Collections; 
using System.Drawing; 
using Microsoft.DirectX; 
using Microsoft.DirectX.Direct3D; 
 
namespace GameEngine 
{ 
	///  
	/// Summary description for Model. 
	///  
	public class Model : Object3D, IDisposable, IDynamic 
	{ 
		#region Attributes 
		private Mesh m_mesh = null; // Our mesh object in sysmem 
		private Material[] m_meshMaterials; // Materials for our m_mesh 
		private Texture[] m_meshTextures; // Textures for our mesh 
		private Vector3  m_vOffset = new Vector3(0.0f, 0.0f, 0.0f); 
		private Attitude m_AttitudeOffset = new Attitude(); 
		private ProgressiveMesh[] m_pMeshes = null;           
		private int m_currentPmesh = 0; 
		private int m_nNumLOD = 1; 
		private float[] m_LODRanges = null; 
		private float m_fMaxLODRange = 1.0f; 
		private GraphicsStream m_adj = null; 
		private Vector3 m_PositiveExtents = new Vector3(-1.0f,-1.0f,-1.0f); 
		private Vector3 m_NegativeExtents = new Vector3(1.0f,1.0f,1.0f); 
		private Vector3[] m_Corners = new Vector3[8]; 
		#endregion 
		public Vector3 Offset { get { return m_vOffset; } } 
 
		public Model(string name, string meshFile, Vector3 offset, Attitude adjust) : base(name) 
		{ 
			Mesh pTempMesh = null; 
			WeldEpsilons Epsilons = new WeldEpsilons(); 
 
			Vector3 objectCenter;        // Center of bounding sphere of object 
			m_vOffset = offset; 
			m_AttitudeOffset = adjust; 
			m_vPosition.X = 100.0f; 
			m_vPosition.Z = 100.0f; 
			ExtendedMaterial[] materials = null; 
 
			try  
			{ 
				// Load the m_mesh from the specified file 
				m_mesh = Mesh.FromFile(meshFile, MeshFlags.SystemMemory, CGameEngine.Device3D,  out m_adj, out materials); 
				// Lock the vertex buffer to generate a simple bounding sphere 
				VertexBuffer vb = m_mesh.VertexBuffer; 
				GraphicsStream vertexData = vb.Lock(0, 0, LockFlags.NoSystemLock); 
				m_fRadius = Geometry.ComputeBoundingSphere(vertexData, m_mesh.NumberVertices, m_mesh.VertexFormat, out objectCenter); 
				Geometry.ComputeBoundingBox(vertexData,m_mesh.NumberVertices, m_mesh.VertexFormat, out m_NegativeExtents, out m_PositiveExtents ); 
				vb.Unlock(); 
				vb.Dispose(); 
 
				m_vOffset.Y = -m_NegativeExtents.Y; 
 
				m_Corners[0].X = m_NegativeExtents.X; 
				m_Corners[0].Y = m_NegativeExtents.Y + m_vOffset.Y; 
				m_Corners[0].Z = m_NegativeExtents.Z; 
				 
				m_Corners[1].X = m_PositiveExtents.X; 
				m_Corners[1].Y = m_NegativeExtents.Y + m_vOffset.Y; 
				m_Corners[1].Z = m_NegativeExtents.Z; 
				 
				m_Corners[2].X = m_NegativeExtents.X; 
				m_Corners[2].Y = m_PositiveExtents.Y + m_vOffset.Y; 
				m_Corners[2].Z = m_NegativeExtents.Z; 
				 
				m_Corners[3].X = m_PositiveExtents.X; 
				m_Corners[3].Y = m_PositiveExtents.Y + m_vOffset.Y; 
				m_Corners[3].Z = m_NegativeExtents.Z; 
				 
				m_Corners[4].X = m_NegativeExtents.X; 
				m_Corners[4].Y = m_NegativeExtents.Y + m_vOffset.Y; 
				m_Corners[4].Z = m_PositiveExtents.Z; 
				 
				m_Corners[5].X = m_PositiveExtents.X; 
				m_Corners[5].Y = m_NegativeExtents.Y + m_vOffset.Y; 
				m_Corners[5].Z = m_PositiveExtents.Z; 
				 
				m_Corners[6].X = m_PositiveExtents.X; 
				m_Corners[6].Y = m_PositiveExtents.Y + m_vOffset.Y; 
				m_Corners[6].Z = m_PositiveExtents.Z; 
				 
				m_Corners[7].X = m_PositiveExtents.X; 
				m_Corners[7].Y = m_PositiveExtents.Y + m_vOffset.Y; 
				m_Corners[7].Z = m_PositiveExtents.Z; 
				 
//					Console.AddLine("Max extents " + m_PositiveExtents); 
//					Console.AddLine("Min extents " + m_NegativeExtents); 
 
 
				// perform simple cleansing operations on m_mesh 
				pTempMesh = Mesh.Clean(m_mesh, m_adj, m_adj); 
				m_mesh.Dispose(); 
 
				m_mesh = pTempMesh; 
				//  Perform a weld to try and remove excess vertices like the model bigship1.x in the DX9.0 SDK (current model is fixed) 
				//    Weld the m_mesh using all epsilons of 0.0f.  A small epsilon like 1e-6 works well too 
				m_mesh.WeldVertices( 0, Epsilons, m_adj, m_adj); 
				// verify validity of m_mesh for simplification 
				m_mesh.Validate(m_adj); 
 
				CreateLod(); 
 
			} 
			catch (DirectXException d3de) 
			{ 
				Console.AddLine("Unable to load mesh " + meshFile); 
				Console.AddLine(d3de.ErrorString); 
			} 
			catch ( Exception e ) 
			{ 
				Console.AddLine("Unable to load mesh " + meshFile); 
				Console.AddLine(e.Message); 
			} 
			 
			if (m_meshTextures == null && materials != null) 
			{ 
				// We need to extract the material properties and texture names  
				m_meshTextures  = new Texture[materials.Length]; 
				m_meshMaterials = new Material[materials.Length]; 
				for( int i=0; i 1 ) 
				{ 
					m_pMeshes[m_pMeshes.Length - 1 - iPMesh].TrimByVertices(cVerticesMin + cVerticesPerMesh * iPMesh, cVerticesMin + cVerticesPerMesh * (iPMesh+1)); 
				} 
 
				m_pMeshes[m_pMeshes.Length - 1 - iPMesh].OptimizeBaseLevelOfDetail(MeshFlags.OptimizeVertexCache); 
			} 
			m_currentPmesh = 0; 
			m_pMeshes[m_currentPmesh].NumberVertices = cVerticesMax; 
			pPMesh.Dispose(); 
		} 
		public override bool InRect( Rectangle rect ) 
		{ 
			// check to see if the bounding circle around the model  
			// intersects this rectangle 
			int center_x = (rect.Left + rect.Right)/2; 
			int center_z = (rect.Top + rect.Bottom)/2; 
 
			int delta_x = center_x - (int)m_vPosition.X; 
			int delta_z = center_z - (int)m_vPosition.Z; 
			int distance_squared = delta_x * delta_x + delta_z * delta_z; 
			int combined_radius = (int)(m_fRadius * m_fRadius)+(rect.Width*rect.Width); 
			bool bInside = distance_squared < combined_radius; 
			return bInside; 
		} 
 
		Vector3 GetCorner( int index ) 
		{ 
			Vector3 WorldCorner = Vector3.TransformCoordinate(m_Corners[index],m_Matrix); 
			return WorldCorner; 
		} 
 
		public override bool Collide( Object3D Other )  
		{  
			bool bCollide = false; 
 
			if ( Visible ) 
			{ 
				Plane[] planeCollide;    // planes of the collide box 
				Vector3[] WorldCorners = new Vector3[8]; 
 
				// perform bounding sphere collision test 
				float delta_north = Other.North - North; 
				float delta_east = Other.East - East; 
				float distance_squared = delta_north * delta_north + delta_east * delta_east; 
				float combined_radius = (Radius * Radius)+(Other.Radius * Other.Radius); 
				bCollide = distance_squared < combined_radius; 
 
				// if the bounding spheres are in contact perform a more precise collision test 
				if ( bCollide ) 
				{ 
					planeCollide = new Plane[6]; 
 
					for( int i = 0; i < 8; i++ ) 
						WorldCorners[i] = Vector3.TransformCoordinate(m_Corners[i],m_Matrix); 
 
					planeCollide[0] = Plane.FromPoints(WorldCorners[7],WorldCorners[3],WorldCorners[5]); // Right 
					planeCollide[1] = Plane.FromPoints(WorldCorners[2],WorldCorners[6],WorldCorners[4]); // Left 
					planeCollide[2] = Plane.FromPoints(WorldCorners[6],WorldCorners[7],WorldCorners[5]); // Far 
					planeCollide[3] = Plane.FromPoints(WorldCorners[0],WorldCorners[1],WorldCorners[2]); // Near 
					planeCollide[4] = Plane.FromPoints(WorldCorners[2],WorldCorners[3],WorldCorners[6]); // Top 
					planeCollide[5] = Plane.FromPoints(WorldCorners[1],WorldCorners[0],WorldCorners[4]); // Bottom 
 
					if ( Other.GetType() == typeof(Model) ) 
					{ 
						for( int i = 0; i < 8; i++ ) 
						{ 
							float distance; 
							Vector3 testPoint = ((Model)Other).GetCorner(i); 
 
							for( int iPlane = 0; iPlane < 6; iPlane++ ) 
							{ 
								distance = planeCollide[iPlane].Dot( testPoint ); 
								if ( distance > 0.0f ) bCollide = true; 
							} 
						} 
					} 
					else 
					{ 
						float distance; 
						Vector3 testPoint = Other.Position; 
						testPoint.Y += 0.1f; 
 
						for( int iPlane = 0; iPlane < 6; iPlane++ )   
						{ 
							distance = planeCollide[iPlane].Dot( testPoint ); 
							if ( distance > 0.0f )  
							{ 
								bCollide = true; 
							} 
						} 
						for( int i = 0; i < 8; i++ ) 
						{ 
							testPoint = Other.Position; 
 
							float angle = ((float)Math.PI / 4) * i; 
 
							testPoint.X += (float)Math.Cos(angle) * Other.Radius; 
							testPoint.Y += 0.2f; 
							testPoint.Z += (float)Math.Sin(angle) * Other.Radius; 
 
							for( int iPlane = 0; iPlane < 6; iPlane++ )  
							{ 
								distance = planeCollide[iPlane].Dot( testPoint ); 
								if ( distance > 0.0f )  
								{ 
									bCollide = true; 
								} 
							} 
						} 
					} 
				} 
			} 
 
			return bCollide;  
		} 
 
		public override void Render( Camera cam ) 
		{ 
			if ( Visible ) 
			{ 
				Matrix world_matrix; 
 
				// Meshes are divided into subsets, one for each material.  
				// Render them in a loop 
				CGameEngine.Device3D.RenderState.CullMode = Microsoft.DirectX.Direct3D.Cull.CounterClockwise; 
 
				if ( m_Parent != null ) 
				{ 
					world_matrix = Matrix.Multiply(m_Matrix, m_Parent.WorldMatrix); 
				} 
				else 
				{ 
					world_matrix = m_Matrix; 
				} 
 
				CGameEngine.Device3D.Transform.World = world_matrix; 
 
				for( int i=0; i 0 ) 
				{ 
					Object3D obj; 
					for ( int i=0; i= m_nNumLOD ) index = m_nNumLOD-1; 
					m_currentPmesh = index; 
 
					if ( m_bHasMoved && m_Quads.Count > 0 ) 
					{ 
						Quad q = (Quad)m_Quads[0]; 
						q.Update( this ); 
					} 
				} 
				catch (DirectXException d3de) 
				{ 
					Console.AddLine("Unable to update a Model " + Name); 
					Console.AddLine(d3de.ErrorString); 
				} 
				catch ( Exception e ) 
				{ 
					Console.AddLine("Unable to update a Model " + Name); 
					Console.AddLine(e.Message); 
				} 
 
				if ( m_Children.Count > 0 ) 
				{ 
					Object3D obj; 
					for ( int i=0; i 0 ) 
			{ 
				Object3D obj; 
				for ( int i=0; i