Click here to Skip to main content
15,896,111 members
Articles / Multimedia / DirectX

Believable physics in C#

Rate me:
Please Sign up or sign in to vote.
4.72/5 (18 votes)
29 Dec 20034 min read 101.5K   1.1K   59  
How to create believable physics using C#.
//-----------------------------------------------------------------------------
// File: D3DUtil.cs
//
// Desc: Shortcut functions for using DX objects
//
// Copyright (c) Microsoft Corporation. All rights reserved
//-----------------------------------------------------------------------------
using System;
using System.Windows.Forms;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;




/// <summary>
/// Various helper functions for graphics samples
/// </summary>
public class GraphicsUtility
{
    /// <summary>
    /// Private Constructor 
    /// </summary>
    private GraphicsUtility() 
    { 
    }



    /// <summary>
    /// Initializes a Material structure, setting the diffuse and ambient
    /// colors. It does not set emissive or specular colors.
    /// </summary>
    /// <param name="c">The ambient and diffuse color</param>
    /// <returns>A defined material</returns>
	public static Direct3D.Material InitMaterial(System.Drawing.Color c)
	{
		Material mtrl = new Material();
		mtrl.Ambient = mtrl.Diffuse = c;
		return mtrl;
	}




    /// <summary>
    /// Initializes a light, setting the light position. The
    /// diffuse color is set to white; specular and ambient are left as black.
    /// </summary>
    /// <param name="light">Which light to initialize</param>
    /// <param name="ltType">The type</param>
	public static void InitLight(Light light, LightType ltType, float x, float y, float z)
	{
		light.Type        = ltType;
		light.Diffuse = System.Drawing.Color.White;
		light.Position = new Vector3(x,y,z);
		light.Direction = Vector3.Normalize(light.Position);
		light.Range        = 1000.0f;
	}




    /// <summary>
    /// Helper function to create a texture. It checks the root path first,
    /// then tries the DXSDK media path (as specified in the system registry).
    /// </summary>
	public static Texture CreateTexture(Device device, string textureFilename, Format format)
	{
		// Get the path to the texture
		string path = DXUtil.FindMediaFile(null, textureFilename);

		// Create the texture using D3DX
		return TextureLoader.FromFile(device, path, D3DX.Default, D3DX.Default, D3DX.Default, 0, format, 
			Pool.Managed, Filter.Triangle|Filter.Mirror, 
			Filter.Triangle|Filter.Mirror, 0);
	}




    /// <summary>
    /// Helper function to create a texture. It checks the root path first,
    /// then tries the DXSDK media path (as specified in the system registry).
    /// </summary>
    public static Texture CreateTexture(Device device, string textureFilename)
	{
		return GraphicsUtility.CreateTexture(device, textureFilename, Format.Unknown);
	}





    /// <summary>
    /// Returns a view matrix for rendering to a face of a cubemap.
    /// </summary>
	public static Matrix GetCubeMapViewMatrix(CubeMapFace face)
	{
		Vector3 vEyePt = new Vector3(0.0f, 0.0f, 0.0f);
		Vector3 vLookDir = new Vector3();
		Vector3 vUpDir = new Vector3();

		switch (face)
		{
			case CubeMapFace.PositiveX:
				vLookDir = new Vector3(1.0f, 0.0f, 0.0f);
				vUpDir   = new Vector3(0.0f, 1.0f, 0.0f);
				break;
			case CubeMapFace.NegativeX:
				vLookDir = new Vector3(-1.0f, 0.0f, 0.0f);
				vUpDir   = new Vector3(0.0f, 1.0f, 0.0f);
				break;
			case CubeMapFace.PositiveY:
				vLookDir = new Vector3(0.0f, 1.0f, 0.0f);
				vUpDir   = new Vector3(0.0f, 0.0f,-1.0f);
				break;
			case CubeMapFace.NegativeY:
				vLookDir = new Vector3(0.0f,-1.0f, 0.0f);
				vUpDir   = new Vector3(0.0f, 0.0f, 1.0f);
				break;
			case CubeMapFace.PositiveZ:
				vLookDir = new Vector3(0.0f, 0.0f, 1.0f);
				vUpDir   = new Vector3(0.0f, 1.0f, 0.0f);
				break;
			case CubeMapFace.NegativeZ:
				vLookDir = new Vector3(0.0f, 0.0f,-1.0f);
				vUpDir   = new Vector3(0.0f, 1.0f, 0.0f);
				break;
		}

		// Set the view transform for this cubemap surface
		Matrix matView = Matrix.LookAtLH(vEyePt, vLookDir, vUpDir);
		return matView;
	}




    /// <summary>
    /// Returns a quaternion for the rotation implied by the window's cursor position
    /// </summary>
	public static Quaternion GetRotationFromCursor(System.Windows.Forms.Form control, float fTrackBallRadius)
	{
		System.Drawing.Point pt = System.Windows.Forms.Cursor.Position;
		System.Drawing.Rectangle rc = control.ClientRectangle;
		pt = control.PointToClient(pt);
		float xpos = (((2.0f * pt.X) / (rc.Right-rc.Left)) - 1);
		float ypos = (((2.0f * pt.Y) / (rc.Bottom-rc.Top)) - 1);
		float sz;

		if (xpos == 0.0f && ypos == 0.0f)
			return new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);

		float d2 = (float)Math.Sqrt(xpos*xpos + ypos*ypos);

		if (d2 < fTrackBallRadius * 0.70710678118654752440) // Inside sphere
			sz = (float)Math.Sqrt(fTrackBallRadius*fTrackBallRadius - d2*d2);
		else                                                 // On hyperbola
			sz = (fTrackBallRadius*fTrackBallRadius) / (2.0f*d2);

		// Get two points on trackball's sphere
		Vector3 p1 = new Vector3(xpos, ypos, sz);
		Vector3 p2 = new Vector3(0.0f, 0.0f, fTrackBallRadius);

		// Get axis of rotation, which is cross product of p1 and p2
		Vector3 axis = Vector3.Cross(p1,p2);

		// Calculate angle for the rotation about that axis
		float t = Vector3.Length(Vector3.Subtract(p2,p1)) / (2.0f*fTrackBallRadius);
		if (t > +1.0f) t = +1.0f;
		if (t < -1.0f) t = -1.0f;
		float fAngle = (float)(2.0f * Math.Asin(t));

		// Convert axis to quaternion
		return Quaternion.RotationAxis(axis, fAngle);
	}




    /// <summary>
    /// Returns a quaternion for the rotation implied by the window's cursor position
    /// </summary>
	public static Quaternion GetRotationFromCursor(System.Windows.Forms.Form control)
	{
		return GetRotationFromCursor(control, 1.0f);
	}




    /// <summary>
    /// Axis to axis quaternion double angle (no normalization)
    /// Takes two points on unit sphere an angle THETA apart, returns
    /// quaternion that represents a rotation around cross product by 2*THETA.
    /// </summary>
	public static Quaternion D3DXQuaternionUnitAxisToUnitAxis2(Vector3 fromVector, Vector3 toVector)
	{
		Vector3 axis = Vector3.Cross(fromVector, toVector);    // proportional to sin(theta)
		return new Quaternion(axis.X, axis.Y, axis.Z, Vector3.Dot(fromVector, toVector));
	}




    /// <summary>
    /// Axis to axis quaternion 
    /// Takes two points on unit sphere an angle THETA apart, returns
    /// quaternion that represents a rotation around cross product by theta.
    /// </summary>
	public static Quaternion D3DXQuaternionAxisToAxis(Vector3 fromVector, Vector3 toVector)
	{
		Vector3 vA = Vector3.Normalize(fromVector), vB = Vector3.Normalize(toVector);
		Vector3 vHalf = Vector3.Add(vA,vB);
		vHalf = Vector3.Normalize(vHalf);
		return GraphicsUtility.D3DXQuaternionUnitAxisToUnitAxis2(vA, vHalf);
	}



    
    /// <summary>
    /// Gets the number of ColorChanelBits from a format
    /// </summary>
	static public int GetColorChannelBits(Format format)
	{
		switch (format)
		{
			case Format.R8G8B8:
				return 8;
			case Format.A8R8G8B8:
				return 8;
			case Format.X8R8G8B8:
				return 8;
			case Format.R5G6B5:
				return 5;
			case Format.X1R5G5B5:
				return 5;
			case Format.A1R5G5B5:
				return 5;
			case Format.A4R4G4B4:
				return 4;
			case Format.R3G3B2:
				return 2;
			case Format.A8R3G3B2:
				return 2;
			case Format.X4R4G4B4:
				return 4;
			case Format.A2B10G10R10:
				return 10;
			case Format.A2R10G10B10:
				return 10;
			default:
				return 0;
		}
	}




    /// <summary>
    /// Gets the number of alpha channel bits 
    /// </summary>
	static public int GetAlphaChannelBits(Format format)
	{
		switch (format)
		{
			case Format.R8G8B8:
				return 0;
			case Format.A8R8G8B8:
				return 8;
			case Format.X8R8G8B8:
				return 0;
			case Format.R5G6B5:
				return 0;
			case Format.X1R5G5B5:
				return 0;
			case Format.A1R5G5B5:
				return 1;
			case Format.A4R4G4B4:
				return 4;
			case Format.R3G3B2:
				return 0;
			case Format.A8R3G3B2:
				return 8;
			case Format.X4R4G4B4:
				return 0;
			case Format.A2B10G10R10:
				return 2;
			case Format.A2R10G10B10:
				return 2;
			default:
				return 0;
		}
	}



    
    /// <summary>
    /// Gets the number of depth bits
    /// </summary>
	static public int GetDepthBits(DepthFormat format)
	{
		switch (format)
		{
			case DepthFormat.D16:
				return 16;
			case DepthFormat.D15S1:
				return 15;
			case DepthFormat.D24X8:
				return 24;
			case DepthFormat.D24S8:
				return 24;
			case DepthFormat.D24X4S4:
				return 24;
			case DepthFormat.D32:
				return 32;
			default:
				return 0;
		}
	}




    /// <summary>
    /// Gets the number of stencil bits
    /// </summary>
	static public int GetStencilBits(DepthFormat format)
	{
		switch (format)
		{
			case DepthFormat.D16:
				return 0;
			case DepthFormat.D15S1:
				return 1;
			case DepthFormat.D24X8:
				return 0;
			case DepthFormat.D24S8:
				return 8;
			case DepthFormat.D24X4S4:
				return 4;
			case DepthFormat.D32:
				return 0;
			default:
				return 0;
		}
	}




    /// <summary>
    /// Assembles and creates a file-based vertex shader
    /// </summary>
	public static VertexShader CreateVertexShader(Device device, string filename)
	{
		GraphicsStream code = null;
		string path = null;
	
		// Get the path to the vertex shader file
		path = DXUtil.FindMediaFile(null, filename);

		// Assemble the vertex shader file
		code = ShaderLoader.FromFile(path, null, 0);

		// Create the vertex shader
		return new VertexShader(device, code);
	}

}




/// <summary>
/// An arc ball class
/// </summary>
public class GraphicsArcBall
{
	private int internalWidth;   // ArcBall's window width
	private int internalHeight;  // ArcBall's window height
	private float internalradius;  // ArcBall's radius in screen coords
	private float internalradiusTranslation; // ArcBall's radius for translating the target

	private Quaternion internaldownQuat;               // Quaternion before button down
	private Quaternion internalnowQuat;                // Composite quaternion for current drag
	private Matrix internalrotationMatrix;         // Matrix for arcball's orientation
	private Matrix internalrotationDelta;    // Matrix for arcball's orientation
	private Matrix internaltranslationMatrix;      // Matrix for arcball's position
	private Matrix internaltranslationDelta; // Matrix for arcball's position
	private bool internaldragging;               // Whether user is dragging arcball
	private bool internaluseRightHanded;        // Whether to use RH coordinate system
	private int saveMouseX = 0;      // Saved mouse position
	private int saveMouseY = 0;
	private Vector3 internalvectorDown;         // Button down vector
	System.Windows.Forms.Control parent; // parent




    /// <summary>
    /// Constructor
    /// </summary>
	public GraphicsArcBall(System.Windows.Forms.Control p)
	{
		internaldownQuat = Quaternion.Identity;
		internalnowQuat = Quaternion.Identity;
		internalrotationMatrix = Matrix.Identity;
		internalrotationDelta = Matrix.Identity;
		internaltranslationMatrix = Matrix.Identity;
		internaltranslationDelta  = Matrix.Identity;
		internaldragging = false;
		internalradiusTranslation = 1.0f;
		internaluseRightHanded = false;

		parent = p;
		// Hook the events 
		p.MouseDown += new MouseEventHandler(this.OnContainerMouseDown);
		p.MouseUp += new MouseEventHandler(this.OnContainerMouseUp);
		p.MouseMove += new MouseEventHandler(this.OnContainerMouseMove);
	}




    /// <summary>
    /// Set the window dimensions
    /// </summary>
	public void SetWindow(int width, int height, float radius)
	{
		// Set ArcBall info
		internalWidth  = width;
		internalHeight = height;
		internalradius = radius;
	}




    /// <summary>
    /// Screen coords to a vector
    /// </summary>
	private Vector3 ScreenToVector(int xpos, int ypos)
	{
		// Scale to screen
		float x   = -(xpos - internalWidth/2)  / (internalradius*internalWidth/2);
		float y   =  (ypos - internalHeight/2) / (internalradius*internalHeight/2);

		if (internaluseRightHanded)
		{
			x = -x;
			y = -y;
		}

		float z   = 0.0f;
		float mag = x*x + y*y;

		if (mag > 1.0f)
		{
			float scale = 1.0f/(float)Math.Sqrt(mag);
			x *= scale;
			y *= scale;
		}
		else
			z = (float)Math.Sqrt(1.0f - mag);

		// Return vector
		return new Vector3(x, y, z);
	}




    /// <summary>
    /// Fired when the containers mouse button is down
    /// </summary>
	private void OnContainerMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
	{
		// Store off the position of the cursor when the button is pressed
		saveMouseX = e.X;
		saveMouseY = e.Y;

		if (e.Button == System.Windows.Forms.MouseButtons.Left)
		{
			// Start drag mode
			internaldragging = true;
			internalvectorDown = ScreenToVector(e.X, e.Y);
			internaldownQuat = internalnowQuat;
		}
	}




    /// <summary>
    /// Fired when the containers mouse button has been released
    /// </summary>
	private void OnContainerMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
	{
		if (e.Button == System.Windows.Forms.MouseButtons.Left)
		{
			// End drag mode
			internaldragging = false;
		}
	}




    /// <summary>
    /// Fired when the containers mouse is moving
    /// </summary>
	private void OnContainerMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
	{
		if (e.Button == System.Windows.Forms.MouseButtons.Left)
		{
			if (internaldragging)
			{
				// recompute nowQuat
				Vector3 vCur = ScreenToVector(e.X, e.Y);
				Quaternion qAxisToAxis = GraphicsUtility.D3DXQuaternionAxisToAxis(internalvectorDown, vCur);
				internalnowQuat = internaldownQuat;
				internalnowQuat = Quaternion.Multiply(internalnowQuat,qAxisToAxis);
				internalrotationDelta = Matrix.RotationQuaternion(qAxisToAxis);
			}
			else
				internalrotationDelta = Matrix.Identity;

			internalrotationMatrix = Matrix.RotationQuaternion(internalnowQuat);
			internaldragging = true;
		}

		if ((e.Button == System.Windows.Forms.MouseButtons.Right) || (e.Button == System.Windows.Forms.MouseButtons.Middle))
		{
			// Normalize based on size of window and bounding sphere radius
			float fDeltaX = (saveMouseX-e.X) * internalradiusTranslation / internalWidth;
			float fDeltaY = (saveMouseY-e.Y) * internalradiusTranslation / internalHeight;

			if (e.Button == System.Windows.Forms.MouseButtons.Right)
			{
				internaltranslationDelta = Matrix.Translation(-2 * fDeltaX, 2 * fDeltaY, 0.0f);
				internaltranslationMatrix = Matrix.Multiply(internaltranslationMatrix, internaltranslationDelta);
			}
			if (e.Button == System.Windows.Forms.MouseButtons.Middle)
			{
				internaltranslationDelta = Matrix.Translation(0.0f, 0.0f, 5 * fDeltaY);
				internaltranslationMatrix = Matrix.Multiply(internaltranslationMatrix, internaltranslationDelta);
			}

			// Store mouse coordinate
			saveMouseX = e.X;
			saveMouseY = e.Y;
		}
	}

	#region Various properties of the class
	public float Radius
	{
		set
		{ internalradiusTranslation = value; }
	}
	public bool RightHanded
	{
		get { return internaluseRightHanded; }
		set { internaluseRightHanded = value; }
	}
	public Matrix RotationMatrix
	{
		get { return internalrotationMatrix; }
	}
	public Matrix RotationDeltaMatrix
	{
		get { return internalrotationDelta; }
	}
	public Matrix TranslationMatrix
	{
		get { return internaltranslationMatrix; }
	}
	public Matrix TranslationDeltaMatrix
	{
		get { return internaltranslationDelta; }
	}
	public bool IsBeingDragged
	{
		get { return internaldragging; }
	}
	#endregion
}




/// <summary>
/// Handles our meshes
/// </summary>
public class GraphicsMesh : IDisposable
{
	private string fileName = null;
	private Mesh systemMemoryMesh = null; // SysMem mesh, lives through resize
	private Mesh localMemoryMesh = null; // Local mesh, rebuilt on resize
	private Direct3D.Material[] materials = null;
	private Texture[] textures = null;
	private bool isUsingMeshMaterials = true;
	private VertexBuffer systemMemoryVertexBuffer = null;
	private VertexBuffer localMemoryVertexBuffer = null;
	private IndexBuffer systemMemoryIndexBuffer = null;
	private IndexBuffer localMemoryIndexBuffer = null;




    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="filename">The initial filename</param>
	public GraphicsMesh(string filename)
	{
		fileName = filename;
	}
    public GraphicsMesh() : this ("CD3DFile_Mesh") {}




    /// <summary>
    /// The system memory mesh
    /// </summary>
	public Mesh SystemMesh
	{
		get { return systemMemoryMesh; }
	}




    /// <summary>
    /// The local memory mesh
    /// </summary>
	public Mesh LocalMesh
	{
		get { return localMemoryMesh; }
	}




    /// <summary>
    /// Should we use the mesh materials
    /// </summary>
	public bool IsUsingMaterials
	{
		set { isUsingMeshMaterials = value; }
	}




    /// <summary>
    /// Creates a new mesh
    /// </summary>
    /// <param name="device">The device used to create the mesh</param>
    /// <param name="filename">the file to load</param>
	public void Create(Device device, string filename)
	{
		string strPath = null;
		GraphicsStream adjacencyBuffer;
		ExtendedMaterial[] Mat;

		if (device != null)
		{
			device.DeviceLost += new System.EventHandler(this.InvalidateDeviceObjects);
			device.Disposing += new System.EventHandler(this.InvalidateDeviceObjects);
			device.DeviceReset += new System.EventHandler(this.RestoreDeviceObjects);
		}

		// Find the path for the file, and convert it to ANSI (for the D3DX API)
		strPath = DXUtil.FindMediaFile(null, filename);

		// Load the mesh
		systemMemoryMesh =  Mesh.FromFile(strPath, MeshFlags.SystemMemory, device, out adjacencyBuffer, out Mat);

		// Optimize the mesh for performance
		systemMemoryMesh.OptimizeInPlace(MeshFlags.OptimizeCompact | MeshFlags.OptimizeAttrSort | MeshFlags.OptimizeVertexCache, adjacencyBuffer);

		textures = new Texture[Mat.Length];
		materials = new Direct3D.Material[Mat.Length];

		for (int i=0; i<Mat.Length; i++)
		{
			materials[i] = Mat[i].Material3D;
			// Set the ambient color for the material (D3DX does not do this)
			materials[i].Ambient = materials[i].Diffuse;
     
			if (Mat[i].TextureFilename != null)
			{
				// Create the texture
				string texturefilename = DXUtil.FindMediaFile(null, Mat[i].TextureFilename);
				textures[i] = TextureLoader.FromFile(device, texturefilename);
			}
		}

		adjacencyBuffer.Close();
		adjacencyBuffer = null;
	}




    /// <summary>
    /// Set the flexible vertex format
    /// </summary>
	public void SetVertexFormat(Device device, VertexFormats format)
	{
		Mesh pTempSysMemMesh = null;
		Mesh pTempLocalMesh  = null;

		if (systemMemoryMesh != null)
		{
			pTempSysMemMesh = systemMemoryMesh.Clone(MeshFlags.SystemMemory, format, device);
		}
		if (localMemoryMesh != null)
		{
			try
			{
				pTempLocalMesh = localMemoryMesh.Clone(0, format, device);
			}
			catch (Exception e)
			{
				pTempSysMemMesh.Dispose();
				pTempSysMemMesh = null;
				throw e;
			}
		}

		if (systemMemoryMesh != null)
			systemMemoryMesh.Dispose();
		systemMemoryMesh = null;

		if (localMemoryMesh != null)
			localMemoryMesh.Dispose();
		localMemoryMesh = null;

		// Clean up any vertex/index buffers
		DisposeLocalBuffers(true, true);

		if (pTempSysMemMesh != null) systemMemoryMesh = pTempSysMemMesh;
		if (pTempLocalMesh != null) localMemoryMesh  = pTempLocalMesh;

		// Compute normals in case the meshes have them
		if (systemMemoryMesh != null)
			systemMemoryMesh.ComputeNormals();
		if (localMemoryMesh != null)
			localMemoryMesh.ComputeNormals();
	}




    /// <summary>
    /// Restore the device objects after the device was reset
    /// </summary>
	public void RestoreDeviceObjects(object sender, EventArgs e)
	{
		if (null == systemMemoryMesh)
			throw new ArgumentException();

		Device device = (Device)sender;
		// Make a local memory version of the mesh. Note: because we are passing in
		// no flags, the default behavior is to clone into local memory.
		localMemoryMesh = systemMemoryMesh.Clone(0, systemMemoryMesh.VertexFormat, device);
		// Clean up any vertex/index buffers
		DisposeLocalBuffers(false, true);

	}




    /// <summary>
    /// Invalidate our local mesh
    /// </summary>
	public void InvalidateDeviceObjects(object sender, EventArgs e)
	{
		if (localMemoryMesh != null)
			localMemoryMesh.Dispose();
		localMemoryMesh = null;
		// Clean up any vertex/index buffers
		DisposeLocalBuffers(false, true);
	}




    /// <summary>
    /// Get the vertex buffer assigned to the system mesh
    /// </summary>
	public VertexBuffer SystemVertexBuffer
	{
		get
		{
			if (systemMemoryVertexBuffer != null)
				return systemMemoryVertexBuffer;

			if (systemMemoryMesh == null)
				return null;
			
			systemMemoryVertexBuffer = systemMemoryMesh.VertexBuffer;
			return systemMemoryVertexBuffer;
		}
	}




    /// <summary>
    /// Get the vertex buffer assigned to the Local mesh
    /// </summary>
	public VertexBuffer LocalVertexBuffer
	{
		get
		{
			if (localMemoryVertexBuffer != null)
				return localMemoryVertexBuffer;

			if (localMemoryMesh == null)
				return null;
			
			localMemoryVertexBuffer = localMemoryMesh.VertexBuffer;
			return localMemoryVertexBuffer;
		}
	}




    /// <summary>
    /// Get the Index buffer assigned to the system mesh
    /// </summary>
	public IndexBuffer SystemIndexBuffer
	{
		get
		{
			if (systemMemoryIndexBuffer != null)
				return systemMemoryIndexBuffer;

			if (systemMemoryMesh == null)
				return null;

			systemMemoryIndexBuffer = systemMemoryMesh.IndexBuffer;
			return systemMemoryIndexBuffer;
		}
	}




    /// <summary>
    /// Get the Index buffer assigned to the Local mesh
    /// </summary>
	public IndexBuffer LocalIndexBuffer
	{
		get
		{
			if (localMemoryIndexBuffer != null)
				return localMemoryIndexBuffer;

			if (localMemoryMesh == null)
				return null;

			localMemoryIndexBuffer = localMemoryMesh.IndexBuffer;
			return localMemoryIndexBuffer;
		}
	}




    /// <summary>
    /// Clean up any resources
    /// </summary>
	public void Dispose()
	{
		if (textures != null)
		{
			for (int i = 0; i<textures.Length; i++)
			{
				if (textures[i] != null)
					textures[i].Dispose();
				textures[i] = null;
			}
			textures = null;
		}

		// Clean up any vertex/index buffers
		DisposeLocalBuffers(true, true);

		// Clean up any memory
		if (systemMemoryMesh != null)
			systemMemoryMesh.Dispose();
		systemMemoryMesh = null;

		// In case the finalizer hasn't been called yet.
		GC.SuppressFinalize(this);
	}




    /// <summary>
    /// Actually draw the mesh
    /// </summary>
    /// <param name="device">The device used to draw</param>
    /// <param name="canDrawOpaque">Can draw the opaque parts of the mesh</param>
    /// <param name="canDrawAlpha">Can draw the alpha parts of the mesh</param>
	public void Render(Device device, bool canDrawOpaque, bool canDrawAlpha)
	{
		if (null == localMemoryMesh)
			throw new ArgumentException();

		RenderStates rs = device.RenderState;
		// Frist, draw the subsets without alpha
		if (canDrawOpaque)
		{
			for (int i=0; i<materials.Length; i++)
			{
				if (isUsingMeshMaterials)
				{
					if (canDrawAlpha)
					{
						if (materials[i].Diffuse.A < 0xff)
							continue;
					}
					device.Material = materials[i];
					device.SetTexture(0, textures[i]);
				}
				localMemoryMesh.DrawSubset(i);
			}
		}

		// Then, draw the subsets with alpha
		if (canDrawAlpha && isUsingMeshMaterials)
		{
			// Enable alpha blending
			rs.AlphaBlendEnable = true;
			rs.SourceBlend = Blend.SourceAlpha;
			rs.DestinationBlend = Blend.InvSourceAlpha;
			for (int i=0; i<materials.Length; i++)
			{
				if (materials[i].Diffuse.A == 0xff)
					continue;

				// Set the material and texture
				device.Material = materials[i];
				device.SetTexture(0, textures[i]);
				localMemoryMesh.DrawSubset(i);
			}
			// Restore state
			rs.AlphaBlendEnable = false;
		}
	}




    /// <summary>
    /// Draw the mesh with opaque and alpha 
    /// </summary>
    public void Render(Device device)
	{
		Render(device, true, true);
	}




    /// <summary>
    /// Cleans up the local vertex buffers/index buffers
    /// </summary>
    /// <param name="systemBuffers"></param>
    /// <param name="localBuffers"></param>
	private void DisposeLocalBuffers(bool systemBuffers, bool localBuffers)
	{
		if (systemBuffers)
		{
			if (systemMemoryIndexBuffer != null)
				systemMemoryIndexBuffer.Dispose();
			systemMemoryIndexBuffer = null;

			if (systemMemoryVertexBuffer != null)
				systemMemoryVertexBuffer.Dispose();
			systemMemoryVertexBuffer = null;
		}
		if (localBuffers)
		{
			if (localMemoryIndexBuffer != null)
				localMemoryIndexBuffer.Dispose();
			localMemoryIndexBuffer = null;

			if (localMemoryVertexBuffer != null)
				localMemoryVertexBuffer.Dispose();
			localMemoryVertexBuffer = null;
		}
	}
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions