using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Text;
using System.Threading;
using System.IO;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using D3D = Microsoft.DirectX.Direct3D;
using D3DFont = Microsoft.DirectX.Direct3D.Font;

using MhsModelCSharp;
using Mhs = MhsModelCSharp;

namespace MhsApplication
{
	/// <summary>
	/// PlantPatchRenderer is an extension of Forms.Panel that contains directx rendering
	/// code. PlantPatchRenderer renders a representation of a single plant patch. 
	/// Additionally the position of the stylus is rendered in "real time" at a fixed framerate.
	/// <para>The render is 1/100th scale. That means that 100 centimeters (1 meter) in the real
	/// world is 1 render unit in the render world. The render box is 1 x 1 x 3 </para>
	/// </summary>
	public class PlantPatchRenderer : 
		System.Windows.Forms.UserControl,	// extends Panel to be a forms component
		ISupportInitialize					// implements ISupportInitialize to do things before being drawn
	{

		#region /*-- Core Direct3d variables -----------------------*/
		private Color baseClearColor = Color.White;

		public bool RenderPaused
		{ 
			get { return renderPaused;}
			set { renderPaused = value;}
		}
		private bool renderPaused = true;

		// Direct3D core stuff
		private D3D.Device d3dDevice = null;
		private PresentParameters presentParams = new PresentParameters();

		/// <summary>
		/// The location in space of the cameras
		/// </summary>
		private Vector3[] cameraEyePosition = new Vector3[6] {	 new Vector3(3.3f,3.3f,.99f),
																 new Vector3(3.3f,3.3f,1.99f),
																 new Vector3(3.3f,3.3f,2.99f),
																 new Vector3(4.5f, 2.25f, 3.02f),
																 new Vector3(2.25f, 4.5f, 3.02f),
																 new Vector3(3.7f, 3.7f, 1.5f) };

			/// <summary>
			/// The direction the cameras are pointed. The point they are aiming at.
			/// </summary>
			//private Vector3 cameraLookAtPoint = new Vector3(0.0f, 0.0f, 1.6f);
		private Vector3[] cameraLookAtPoint = new Vector3[6] {	new Vector3(0.0f,0.0f,0.5f),
																new Vector3(0.0f,0.0f,1.5f),
																new Vector3(0.0f,0.0f,2.5f),
																 new Vector3(0.0f, 0.23f, 1.4f),
																 new Vector3(0.23f, 0.0f, 1.4f),
																 new Vector3(0.0f, 0.0f, 1.6f) };

		private const int defaultCameraIndex = 4;
		/// <summary>
		/// Select camera angle to render with
		/// </summary>
		public int CameraPositionIndex
		{
			get { return cameraIndex; }
			set
			{
				if( value < 0 || value > defaultCameraIndex )
				{
					cameraIndex = defaultCameraIndex;
				}
				else
				{
					cameraIndex = value;
				}
			}
		}
		private int cameraIndex = defaultCameraIndex;

		/// <summary>
		/// Given a camera position and direction, which way is up?
		/// </summary>
		private Vector3 cameraUp = new Vector3(0.0f, 0.0f, 1.0f);
				
		/// <summary>
		/// The place in the scene where the light is
		/// </summary>
		private Vector3 lightPosition = new Vector3(.5f,.5f,4f);

		/// <summary>
		/// The direction where the light points
		/// </summary>
		private Vector3 lightDirection = new Vector3(0f,0f,-1.0f);
		#endregion

		#region /*-- Stylus geometry variables, continuous and depressed ----------------*/

		/// <summary>
		/// Accessor to set the stylus' current position
		/// </summary>
		public Vector3 StylusContinuousPosition
		{
			set 
			{
				stylusContinuousPosition.X=value.X;
				stylusContinuousPosition.Y=value.Y;
				stylusContinuousPosition.Z=value.Z;
			}
		}
		private Vector3 stylusContinuousPosition = new Vector3(0f,0f,150f);

		/// <summary>
		/// Accessor to set the stylus' last clicked position
		/// </summary>
		public Vector3 StylusDepressedPosition
		{
			set 
			{
				stylusDepressedPosition.X=value.X;
				stylusDepressedPosition.Y=value.Y;
				stylusDepressedPosition.Z=value.Z;
			}
		}
		private Vector3 stylusDepressedPosition = new Vector3();

		/// <summary>
		/// Should the current position of the stylus be rendered?
		/// </summary>
		public bool DrawStylusPosition
		{ 
			get { return drawStylusContinuousLastPosition;}
			set { drawStylusContinuousLastPosition = value;}
		}
		private bool drawStylusContinuousLastPosition = true;

		/// <summary>
		/// A mesh (sphere) to represent the current position of the stylus
		/// </summary>
		private Mesh stylusMesh = null;

		/// <summary>
		/// Cheesy way to do a shadow. Just use a flat cylinder of the same
		/// radius as the stylus dot. Render it black at altitude zero.
		/// </summary>
		private Mesh stylusShadowMesh = null;

		private const float dotRadius = .02f;

		private Material stylusContinuousMaterial = new Material();

		/// <summary>
		/// Shadow material. Black.
		/// </summary>
		private Material stylusShadowMaterial = new Material();
		private Material stylusDepressedMaterial = new Material();
		private Color stylusContinuousColor = Color.Blue;
		private Color stylusDepressedColor  = Color.Pink;

		/// <summary>
		/// Should the last position where the stylus button was click be rendered?
		/// </summary>
		private const bool drawStylusDepressedLastPosition = false;

		#endregion

		#region /*-- Cyg variables ------------------------*/

		private Color cygColor = Color.Green;
		private Color cygSelectedColor  = Color.Green;

		/// <summary>
		/// Draw only one CYG at a time or show all 9
		/// </summary>
		public bool DrawOnlySelectedCygs
		{ 
			get { return drawOnlySelectedCygs;}
			set { drawOnlySelectedCygs = value;}
		}
		private bool drawOnlySelectedCygs = false;

		/// <summary>
		/// Blink the selected cyg and it's shadow & dot
		/// </summary>
		public bool BlinkSelectedCyg
		{ 
			get { return blinkSelectedCyg;}
			set { blinkSelectedCyg = value;}
		}
		private bool blinkSelectedCyg = false;

		/// <summary>
		/// Should Cyg's be rendered?
		/// </summary>
		public bool DrawCygs
		{ 
			get { return drawCygs;}
			set { drawCygs = value;}
		}
		private bool drawCygs = true;

		/// <summary>
		/// A mesh (cylinder shaped like a cone) to represent a CYG. Reused and 
		/// transformed for each CYG.
		/// </summary>
		private Mesh cygMesh = null;
		private Material cygMaterial = new Material();
		private Material cygSelectedMaterial = new Material();
		private Texture cygTexture = null;
		private Texture cygSelectedTexture = null;

		#endregion

		#region /*-- earth and sky variables -------------------------*/

		private Color groundColor  = Color.PaleGreen;
		private Color skyColor  = Color.Blue;

		/// <summary>
		/// Draw the ground?
		/// </summary>
		public bool DrawGround
		{ 
			get { return drawGround;}
			set { drawGround = value;}
		}
		private bool drawGround = true;

		public bool DrawSky
		{ 
			get { return drawSky;}
			set { drawSky = value;}
		}
		private bool drawSky = true;

		/// <summary>
		/// A vertex buffer to draw the ground & sky surface
		/// </summary>
		private Mesh groundMesh = null;
		private Material groundMaterial = new Material();
		private Material skyMaterial = new Material();

		#endregion

		#region /*-- Coordinate axis variables -----------*/		
		/// <summary>
		/// The material to use to render the stylus' continuous mesh
		/// </summary>
		private Material axisMaterial = new Material();

		/// <summary>
		/// Should the coordinate axis be rendered?
		/// </summary>
		public bool DrawAxis
		{ 
			get { return drawAxis;}
			set { drawAxis = value;}
		}
		private  bool drawAxis = true;

		/// <summary>
		/// A vertex buffer to hold the points that render 
		/// the lines that represent the coordinate axis.
		/// </summary>
		private VertexBuffer drawAxisVertexBuffer = null;

		/// <summary>
		/// Should the coordinate axis be labeled. Kind of rough around the edges and primarily
		/// for debugging
		/// </summary>
		public bool DrawAxisLabels
		{ 
			get { return drawAxisLabels;}
			set { drawAxisLabels = value;}
		}
		private bool drawAxisLabels = false;

		/// <summary>
		/// axis label text
		/// </summary>
		private const string axisLabelRX = "+rX";
		private const string axisLabelRY = "+rY";
		private const string axisLabelRZ = "+rZ";

		/// <summary>
		/// x axis font mesh. For creating a representation of the x axis text.
		/// </summary>
		private Mesh axisFontMeshRX = null;
		private Mesh axisFontMeshRY = null;
		private Mesh axisFontMeshRZ = null;
		private System.Drawing.Font axisFont =  new System.Drawing.Font(FontFamily.GenericMonospace,12.0f);
		private const float axisFontLumpiness = .001F;
		private const float axisFontThickness  = .000001F;
		#endregion

		#region /*-- Altimeter variables -----------*/		
		/// <summary>
		/// Should altimiter helper lines be drawn
		/// </summary>
		public bool DrawAltimeter
		{ 
			get { return drawAltimeter;}
			set { drawAltimeter = value;}
		}
		private bool drawAltimeter = true;


		#endregion

		#region /*-- Bounding cylinder variables -----------*/		

		private Color cylinderColor = Color.White;

		/// <summary>
		///  Should the bounding cylinder of the plantpatch model be rendered?
		/// </summary>
		public bool DrawBoundingCylinder
		{ 
			get { return drawCylinderOutline;}
			set { drawCylinderOutline = value;}
		}
		private bool drawCylinderOutline = true;


		// TODO: Switch this to a cylinder mesh and render in wire mode
		private Mesh boundingCylinderMesh = null;
		private Material boundingCylinderMaterial = new Material();
		private const float boundingCylinderRadius  = .5f;
		private Texture boundingCylinderTexture = null;
		//private VertexBuffer boundingCylinderVertexBuffer = null;
		//private Vector3 boundingCylinderCenter = new Vector3(.5f,.5f,0f);
		//private const int boundingCylinderNumPoints = 100;

		#endregion

		#region /*-- 2D Text sprites ---------------*/
		/// <summary>
		/// Should the stylus coordinates be rendered as 2d text? Mainly for debugging
		/// </summary>
		public bool DrawStylusContinuousSprite
		{
			get { return drawStylusContinuousPositionSprite; }
			set { drawStylusContinuousPositionSprite = value; }
		}
		private bool drawStylusContinuousPositionSprite = false;

		/// <summary>
		/// Should the last click position stylus coordinates 
		/// be rendered as 2d text? Mainly for debugging
		/// </summary>
		public bool DrawStylusDepressedSprite
		{
			get { return drawStylusDepressedPositionSprite; }
			set { drawStylusDepressedPositionSprite = value; }
		}
		private bool drawStylusDepressedPositionSprite = false;

		/// <summary>
		/// The System font for the 3d coordinate text sprites in 2d
		/// <seealso cref="postitionD3dFont2d"/>
		/// </summary>
		private System.Drawing.Font positionFont2d = new System.Drawing.Font(FontFamily.GenericMonospace,24.0f);

		/// <summary>
		/// The Direct3d font to render the 3d coordinate text sprites in 2d. Direct3d fonts are different
		/// than system fonts, though d3d fonts rely on system fonts.
		/// <seealso cref="positionFont2d"/>
		/// </summary>
		private D3D.Font postitionD3dFont2d = null;

		#endregion

		#region /*-- Model variables --------------------*/
		/// <summary>
		/// The selected cyg
		/// </summary>
		public int CygIndex
		{
			get { return cygIdx; }
			set { cygIdx = value; }
		}
		private int cygIdx = -1;

		/// <summary>
		/// Accessor to set the plant patch to be rendered
		/// </summary>
		public PlantPatch PlantPatch
		{
			set { plantPatch = value; }
		}
		private PlantPatch plantPatch = new PlantPatch();

		#endregion

		#region /*-- Polhemus base station model, gray cube ---------------*/
		
		private Color polhemusBaseStationColor  = Color.LightGray;
		private float polhemusBaseStationSize = .10f;
		private VertexBuffer drawPolhemusBaseStationVertexBuffer = null;
		/// <summary>
		/// Draw the ground?
		/// </summary>
		public bool DrawPolhemusBaseStation
		{ 
			get { return drawPolhemusBaseStation;}
			set { drawPolhemusBaseStation = value;}
		}
		private bool drawPolhemusBaseStation = true;

		/// <summary>
		/// A vertex buffer to draw the ground & sky surface
		/// </summary>
		private Mesh polhemusBaseStationMesh = null;

		private Material polhemusBaseStationMaterial = new Material();
		#endregion

		private System.ComponentModel.IContainer components;
		private int renderFrameCount = 0;
		private const int renderBlinkRate = 20;
		private const float drawingScale = (1f/100f); // 100 model cm  == 1 world unit
		private System.Windows.Forms.ToolTip toolTip;

		/// <summary>
		/// Constructor. VistualStudio stock. We use the ISupportInitialize
		/// interface to do the D3d setup.
		/// <seealso cref="BeginInit"/>
		/// </summary>
		public PlantPatchRenderer()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			toolTip.SetToolTip(this,"Double click to cycle through camera views.\nPress the 1-3 keys to view different plant depth levels.\nUse the 0 (zero) key to view the entire plant patch cylinder.");
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if( components != null )
					components.Dispose();
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			this.toolTip = new System.Windows.Forms.ToolTip(this.components);
			// 
			// toolTip
			// 
			this.toolTip.AutoPopDelay = 15000;
			this.toolTip.InitialDelay = 500;
			this.toolTip.ReshowDelay = 100;
			// 
			// PlantPatchRenderer
			// 
			this.Name = "PlantPatchRenderer";
			this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.PlantPatchRenderer_KeyPress);
			this.Click += new System.EventHandler(this.PlantPatchRenderer_Click);

		}
		#endregion

		/// <summary>
		/// Part of the ISupportInitialize interface. Not used, but must be implemented.
		/// </summary>
		public void BeginInit(){}

		/// <summary>
		/// part of the ISupportInitialize interface. Called after
		/// the object is done initializing
		/// Used to avoid setting up internals when in design mode
		/// because the DesignMode property isn't set yet in the constructor.
		/// So we wait until we get here to do D3d 
		/// initialization.
		/// </summary>
		public void EndInit()
		{
			// if we are designing in visual studio
			// don't setup direct3d
			if( ! DesignMode )
			{
				// direct3d setup
				try
				{
					InitializeDirect3D();
				}
				catch( Direct3DXException d3e)
				{
					MessageBox.Show( d3e.Message );
				}
			
			}	
		}


		/// <summary>
		/// Handle key press events. Number keys correspond to camera positions.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void PlantPatchRenderer_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
		{
			char k = e.KeyChar;			
			switch(k)
			{
				case (char)Keys.D1:
					CameraPositionIndex = 0;
					break;
				case (char)Keys.D2:
					CameraPositionIndex = 1;
					break;
				case (char)Keys.D3:
					CameraPositionIndex = 2;
					break;
				case (char)Keys.D4:
				case (char)Keys.D0:
					CameraPositionIndex = 3;
					break;
				default:
					break;
			}
		
		}

		/// <summary>
		/// When the user double clicks the 3d scene, cycle
		/// through available camera positions
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void PlantPatchRenderer_Click(object sender, System.EventArgs e)
		{
			cameraIndex = ((cameraIndex + 1) % cameraEyePosition.Length);
		}


		#region Direct3D managing code

		/// <summary>
		/// Initialize the direct3d device with presentation parameters. Probes the 
		/// device capabilities a little to try to use some hardware features.
		/// Relatively basic.
		/// </summary>
		/// <returns></returns>
		private bool InitializeDirect3D() 
		{
			try 
			{
				// setup presentation parameters
				presentParams.DeviceWindow = this; // me
				presentParams.Windowed = true;  // not full screen
				presentParams.PresentationInterval = D3D.PresentInterval.Immediate; // don't sync vertical
				presentParams.SwapEffect = SwapEffect.Discard; // how to manage the back buffer
				presentParams.PresentFlag = PresentFlag.DiscardDepthStencil;
				presentParams.EnableAutoDepthStencil = true; // direct3d managed depth stencil buffers
				presentParams.AutoDepthStencilFormat = DepthFormat.D16; // 16 bit z buffer
				presentParams.BackBufferHeight=this.Height;
				presentParams.BackBufferFormat=D3D.Format.Unknown;

				// determine hardware capabilities
				Caps caps = Manager.GetDeviceCaps( Manager.Adapters.Default.Adapter, DeviceType.Hardware );

				CreateFlags flags;
				if( caps.DeviceCaps.SupportsHardwareTransformAndLight )
					flags = CreateFlags.HardwareVertexProcessing;
				else
					flags = CreateFlags.SoftwareVertexProcessing;

				// initialize the device
				this.d3dDevice = new D3D.Device(	
					0,
					DeviceType.Hardware,
					this.Handle,
					flags,
					presentParams);

				// ignore resizes
				d3dDevice.DeviceResizing += new CancelEventHandler(d3dDevice_CancelResize);

				// use on device lost event to cleanup
				d3dDevice.DeviceLost += new EventHandler(d3dDevice_DeviceLost);

				// setup OnDeviceReset handler and call it for the first time to setup vertexbuffers, meshes...
				d3dDevice.DeviceReset += new System.EventHandler(d3dDevice_DeviceReset);
				d3dDevice_DeviceReset(d3dDevice, null);

				renderPaused = false;
				return true;
			} 
			catch(DirectXException d3e) 
			{
				MessageBox.Show("Error initializing the Direct3D device. You should exit the application.\n" + d3e.Message);
				return false;
			}
		}

		
		/// <summary>
		/// Initialize the vertex buffers used in rendering, axiis, 
		/// cylinder etc.
		/// </summary>
		void SetupVertexBuffers()
		{
			drawAxisVertexBuffer = new VertexBuffer( typeof(CustomVertex.PositionColored),
				18, 
				this.d3dDevice,
				Usage.Dynamic | Usage.WriteOnly,
				CustomVertex.PositionColored.Format,
				Pool.Default );

			CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[18];

			int i = 0;
			// x
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 0.0f );
			verts[i++].Position = new Vector3( 1.0f, 0.0f, 0.0f );
			// y
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 0.0f );
			verts[i++].Position = new Vector3( 0.0f, 1.0f, 0.0f );
			// z
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 0.0f );
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 1.0f );

			// x @ z=1
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 1.0f );
			verts[i++].Position = new Vector3( 1.0f, 0.0f, 1.0f );
			// y@z=1
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 1.0f );
			verts[i++].Position = new Vector3( 0.0f, 1.0f, 1.0f );

			// x @ z=2
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 2.0f );
			verts[i++].Position = new Vector3( 1.0f, 0.0f, 2.0f );
			// y@z=2
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 2.0f );
			verts[i++].Position = new Vector3( 0.0f, 1.0f, 2.0f );

			// x @ z=3
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 3.0f );
			verts[i++].Position = new Vector3( 1.0f, 0.0f, 3.0f );
			// y@z=3
			verts[i++].Position = new Vector3( 0.0f, 0.0f, 3.0f );
			verts[i++].Position = new Vector3( 0.0f, 1.0f, 3.0f );

			for( int j = 0; j < verts.Length; j++)
				verts[j].Color = Color.Black.ToArgb();

			drawAxisVertexBuffer.SetData(verts, 0, LockFlags.None);


			// base station frame
			drawPolhemusBaseStationVertexBuffer = new VertexBuffer( typeof(CustomVertex.PositionColored),
				24, 
				this.d3dDevice,
				Usage.Dynamic | Usage.WriteOnly,
				CustomVertex.PositionColored.Format,
				Pool.Default );
			CustomVertex.PositionColored[] pbsVerts = new CustomVertex.PositionColored[24];
			i = 0;
			// bottom
			pbsVerts[i++].Position = new Vector3( 0.0f, 0.0f, 0.0f );
			pbsVerts[i++].Position = new Vector3( 1.0f, 0.0f, 0.0f );

			pbsVerts[i++].Position = new Vector3( 0.0f, 0.0f, 0.0f );
			pbsVerts[i++].Position = new Vector3( 0.0f, 1.0f, 0.0f );

			pbsVerts[i++].Position = new Vector3( 0.0f, 1.0f, 0.0f );
			pbsVerts[i++].Position = new Vector3( 1.0f, 1.0f, 0.0f );

			pbsVerts[i++].Position = new Vector3( 1.0f, 0.0f, 0.0f );
			pbsVerts[i++].Position = new Vector3( 1.0f, 1.0f, 0.0f );

			// legs
			pbsVerts[i++].Position = new Vector3( 0.0f, 0.0f, 0.0f );
			pbsVerts[i++].Position = new Vector3( 0.0f, 0.0f, 1.0f );

			pbsVerts[i++].Position = new Vector3( 0.0f, 1.0f, 0.0f );
			pbsVerts[i++].Position = new Vector3( 0.0f, 1.0f, 1.0f );

			pbsVerts[i++].Position = new Vector3( 1.0f, 0.0f, 0.0f );
			pbsVerts[i++].Position = new Vector3( 1.0f, 0.0f, 1.0f );

			pbsVerts[i++].Position = new Vector3( 1.0f, 1.0f, 0.0f );
			pbsVerts[i++].Position = new Vector3( 1.0f, 1.0f, 1.0f );

			// top
			pbsVerts[i++].Position = new Vector3( 0.0f, 0.0f, 1.0f );
			pbsVerts[i++].Position = new Vector3( 1.0f, 0.0f, 1.0f );

			pbsVerts[i++].Position = new Vector3( 0.0f, 0.0f, 1.0f );
			pbsVerts[i++].Position = new Vector3( 0.0f, 1.0f, 1.0f );

			pbsVerts[i++].Position = new Vector3( 0.0f, 1.0f, 1.0f );
			pbsVerts[i++].Position = new Vector3( 1.0f, 1.0f, 1.0f );

			pbsVerts[i++].Position = new Vector3( 1.0f, 0.0f, 1.0f );
			pbsVerts[i++].Position = new Vector3( 1.0f, 1.0f, 1.0f );

			for( int j = 0; j < pbsVerts.Length; j++)
				pbsVerts[j].Color = Color.Black.ToArgb();

			drawPolhemusBaseStationVertexBuffer.SetData(pbsVerts, 0, LockFlags.None);

		
		}

		/// <summary>
		/// Setup meshes that will render 3d objects, fonts, stylus position,
		/// cyg cone etc.
		/// </summary>
		private void SetupMeshes()
		{

			// the pen dot
			Material m = new Material();
			stylusMesh = Mesh.Sphere(d3dDevice,dotRadius,15,15);
			stylusContinuousMaterial.Ambient = Color.Black;
			stylusContinuousMaterial.Diffuse = stylusContinuousColor;
			stylusDepressedMaterial.Ambient = Color.Black;
			stylusDepressedMaterial.Diffuse = stylusDepressedColor;
			stylusShadowMesh = Mesh.Cylinder(d3dDevice,dotRadius,dotRadius,.00000000001f,15,1);
			stylusShadowMaterial.Ambient=Color.Black;
			stylusShadowMaterial.Diffuse=Color.Black;

			
			// the axis labels
			GlyphMetricsFloat[] glyphMetrics = new GlyphMetricsFloat[axisLabelRX.Length]; 
			this.axisFontMeshRX = Mesh.TextFromFont(
				this.d3dDevice,		 // The device
				this.axisFont,       // The font we want to render with
				axisLabelRX,         // The text we want
				axisFontLumpiness,   // How "lumpy"? 
				axisFontThickness,   // How thick? 
				out glyphMetrics     // Information about the meshes
				); 
			glyphMetrics = new GlyphMetricsFloat[axisLabelRY.Length]; 
			this.axisFontMeshRY = Mesh.TextFromFont(
				this.d3dDevice,		 // The device, of course
				this.axisFont,       // The font we want to render with
				axisLabelRY,         // The text we want
				axisFontLumpiness,   // How "lumpy"? 
				axisFontThickness,   // How thick? 
				out glyphMetrics     // Information about the meshes
				); 
			glyphMetrics = new GlyphMetricsFloat[axisLabelRZ.Length]; 
			this.axisFontMeshRZ = Mesh.TextFromFont(
				this.d3dDevice,		 // The device, of course
				this.axisFont,       // The font we want to render with
				axisLabelRZ,         // The text we want
				axisFontLumpiness,   // How "lumpy"? 
				axisFontThickness,   // How thick? 
				out glyphMetrics     // Information about the meshes
				);

			// the axis
			axisMaterial.Ambient = Color.LightGray;
			axisMaterial.Diffuse = Color.LightGray;

			// the cyg mesh. will be used many times
			// radius .5 (diam 1), length one
			cygMesh = Mesh.Cylinder(d3dDevice,.5f,.3f,1.0f,30,30);
			// TODO jerk
			//cygTexture = TextureLoader.FromFile(d3dDevice, "C:\\Documents and Settings\\axjww\\Desktop\\CS470 Software project\\mhs\\icons\\green_a80.tga");
			cygMaterial.Ambient = Color.Black;
			cygMaterial.Diffuse = cygColor;
			// TODO jerk
			//cygSelectedTexture = TextureLoader.FromFile(d3dDevice, "C:\\Documents and Settings\\axjww\\Desktop\\CS470 Software project\\mhs\\icons\\green_a50.tga");
			cygSelectedMaterial.Ambient = Color.Black;
			cygSelectedMaterial.Diffuse = cygSelectedColor;

			// ground/sky
			groundMesh = Mesh.Polygon(d3dDevice,1f,4);
			groundMaterial.Ambient=groundColor;
			groundMaterial.Diffuse=groundColor;

			// ground/sky
			polhemusBaseStationMesh = Mesh.Box(d3dDevice,polhemusBaseStationSize,polhemusBaseStationSize,polhemusBaseStationSize);
			polhemusBaseStationMaterial.Ambient=Color.Gray;
			polhemusBaseStationMaterial.Diffuse=Color.Gray;

			// sky uses ground mesh at different color
			skyMaterial.Ambient=skyColor;
			skyMaterial.Diffuse=skyColor;

			// the bounding cylinder mesh
			//Mesh tmpMsh = Mesh.Cylinder(d3dDevice,boundingCylinderRadius,boundingCylinderRadius,.00001f,30,1);
			System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
			System.IO.Stream stream = assembly.GetManifestResourceStream("MhsApplication.black_a10.tga");
			boundingCylinderTexture = D3D.TextureLoader.FromStream(d3dDevice, stream);
			stream.Close();

			boundingCylinderMesh = Mesh.Cylinder(d3dDevice,boundingCylinderRadius,boundingCylinderRadius,.00001f,30,1);
			boundingCylinderMaterial.Ambient = cylinderColor;
			boundingCylinderMaterial.Diffuse = cylinderColor;

		}

		/// <summary>
		/// Sets up the view and projection matrixes. The world matrix
		/// is not set here because it is constantly adjusted during Render()
		/// to orient an position 3d objects.
		/// </summary>
		private void SetupMatrices() 
		{
			//Set the view matrix to be a left handed coordinate system.
			// TODO jerk, magic number
			this.d3dDevice.Transform.View = Matrix.LookAtLH(cameraEyePosition[defaultCameraIndex], cameraLookAtPoint[defaultCameraIndex], cameraUp);

			//build the projection matrix. This is a pretty standard projection matrix.
			//The thing you'll usually vary is the z buffer.
			this.d3dDevice.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI/4.0f, .35f, 0.1f, ModelParameters.PLANTPATCH_DIAMETER_CENTIMETERS * 10f );
			//this.d3dDevice.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI/4.0f, .4f, 0.1f, 40.0f );
			//this.d3dDevice.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI/4.0f, this.Height/this.Width, 1.0f, 40.0f );
			//this.d3dDevice.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI/4.0f, (float)this.Width/(float)this.Height, .1f, 40.0f );
		}


		/// <summary>
		/// Returns a transformation Matrix that can orient 
		/// 3d text to face the camera.
		/// </summary>
		/// <returns>a transformation matrix to orient 3d text</returns>
		private Matrix OrientText()
		{
			Matrix moveText = Matrix.Identity;
			Matrix wrld = Matrix.Identity;

			Matrix scale = Matrix.Identity;
			Matrix rotX = Matrix.Identity;
			Matrix rotZ = Matrix.Identity;

			scale.Scale(.2f,.2f,.2f);
			rotX.RotateX( Geometry.DegreeToRadian( 90 ) );
			rotZ.RotateZ( Geometry.DegreeToRadian( -30 ) );

			wrld = scale * rotX * rotZ;

			return wrld;
		}


		/// <summary>
		/// Render items in the scene. Meshes and vertext buffers should already be built
		/// by OnResetDevice.
		/// <para>The model coordinate system is "normal" cartestian z+ up,x+ right, y+ toward the viewer</para>
		/// </summary>
		public void Render() 
		{
			try
			{
				// If this fails we'll throw an Device exception
				// and jump down below to the catch blocks
				d3dDevice.TestCooperativeLevel();

				this.d3dDevice.Transform.View = Matrix.LookAtLH(cameraEyePosition[cameraIndex], cameraLookAtPoint[cameraIndex], cameraUp);

				// star
				this.d3dDevice.Clear( ClearFlags.Target | ClearFlags.ZBuffer , baseClearColor, 1.0f, 0);
				this.d3dDevice.BeginScene();

				if( drawSky)		
				{ RenderSky(); }

				if( drawGround)		
				{ RenderGround(); }

				if( drawAxis )		
				{ RenderAxis();}

				if( drawPolhemusBaseStation )
					RenderPolhemusBaseStation();

				if( drawAltimeter)		
				{ RenderStylusContinuousAltimeter();}
			
				// draw a mesh representing the position where the stylus was last clicked?
				if( drawStylusDepressedLastPosition )
				{ RenderStylusDepressedLastPosition();}

				if( drawStylusContinuousLastPosition )
				{ RenderStylusContinuousMesh();}

				if( drawStylusContinuousPositionSprite ||
					drawStylusDepressedPositionSprite )
				{ RenderStylusTextSprites();}

				// draw the plant patch's CYG's?
				if( drawCygs )
				{ RenderCygs(); }

				// draw the bounding cylinder for the plantpatch?
				if( drawCylinderOutline)
				{ RenderBoundingCylinder(); }


				// commit rendered scene
				this.d3dDevice.EndScene();

				// write the back buffer to screen
				// If this fails we'll throw an Device exception
				this.d3dDevice.Present();

				renderFrameCount++;
			}
			catch(D3D.DeviceLostException)
			{
				int coopLevel;
				d3dDevice.CheckCooperativeLevel( out coopLevel );
				while( coopLevel == (int)D3D.ResultCode.DeviceLost )
				{
					Thread.Sleep(250);
					d3dDevice.CheckCooperativeLevel( out coopLevel );
				}
			}
			catch(D3D.DeviceNotResetException)
			{
				d3dDevice.Reset(presentParams);
			}			
		}



		private void RenderAxis()
		{
			d3dDevice.Material = axisMaterial;
			// axis lines
			d3dDevice.Transform.World = Matrix.Identity;
			d3dDevice.SetStreamSource(0, drawAxisVertexBuffer, 0);
			d3dDevice.DrawPrimitives(PrimitiveType.LineList,0,9);

			Matrix scale = Matrix.Identity;
			scale.Scale(1f,1f,ModelParameters.NUM_DEPTH_LEVELS_PER_PLANTPATCH);
			d3dDevice.Transform.World = Matrix.Identity * scale;
			d3dDevice.SetStreamSource(0, drawAxisVertexBuffer, 0);
			d3dDevice.DrawPrimitives(PrimitiveType.LineList,4,1);


			if( drawAxisLabels )
			{
				// axis labels
				Matrix moveText = Matrix.Identity;
				Matrix scaleText = Matrix.Identity;

				this.d3dDevice.Transform.World = OrientText();
				moveText = Matrix.Identity; 
				moveText.Translate(1.0f,0.0f,-0.2f);
				this.d3dDevice.Transform.World *= moveText;
				this.axisFontMeshRX.DrawSubset(0);

				this.d3dDevice.Transform.World = OrientText();
				moveText = Matrix.Identity; 
				moveText.Translate(-0.5f,1.0f,-0.3f);
				this.d3dDevice.Transform.World *= moveText;
				this.axisFontMeshRY.DrawSubset(0);

				this.d3dDevice.Transform.World = OrientText();
				moveText = Matrix.Identity; 
				moveText.Translate(0.0f,0.0f,3.2f);
				this.d3dDevice.Transform.World *= moveText;
				this.axisFontMeshRZ.DrawSubset(0);
			}
		}

		private void RenderAxisAltimeter(Vector3 vector, Color color)
		{
			Matrix translate = Matrix.Identity;
			Matrix scale = Matrix.Identity;
			Matrix rot = Matrix.Identity;
			Matrix translate2 = Matrix.Identity;

			Material m = new Material();
			m.Ambient = color;
			m.Diffuse= color;

			d3dDevice.Material = m;

			// x axis walker
			scale = Matrix.Identity;
			scale.Scale(1f,1f,ModelParameters.NUM_DEPTH_LEVELS_PER_PLANTPATCH);

			translate = Matrix.Identity;
			translate.Translate(vector.X * drawingScale,0f,0f);

			this.d3dDevice.Transform.World = Matrix.Identity * scale * translate;

			d3dDevice.SetStreamSource(0, drawAxisVertexBuffer, 0);
			d3dDevice.DrawPrimitives(PrimitiveType.LineList,4,1); // z axis

			// y axis walker
			scale = Matrix.Identity;
			scale.Scale(1f,1f,ModelParameters.NUM_DEPTH_LEVELS_PER_PLANTPATCH);

			translate = Matrix.Identity;
			translate.Translate(0f,vector.Y * drawingScale,0f);

			this.d3dDevice.Transform.World = Matrix.Identity * scale * translate;

			d3dDevice.SetStreamSource(0, drawAxisVertexBuffer, 0);
			d3dDevice.DrawPrimitives(PrimitiveType.LineList,4,1); // z axis

			// z axis walker
			translate = Matrix.Identity;
			//translate.Translate(0f,-.5f,0f);

			scale = Matrix.Identity;
			scale.Scale(.1f,.1f,.1f); // base

			rot = Matrix.Identity;
			//rot.RotateZ(Geometry.DegreeToRadian( 45.0f ));

			translate2 = Matrix.Identity;
			translate2.Translate(0f,0f,vector.Z * drawingScale);

			this.d3dDevice.Transform.World = Matrix.Identity * translate * scale * rot * translate2;

			d3dDevice.SetStreamSource(0, drawAxisVertexBuffer, 0);
			d3dDevice.DrawPrimitives(PrimitiveType.LineList,0,2); // x& y axis

		}

		// use the coordinate axs vertex buffers
		private void RenderStylusContinuousAltimeter()
		{
			RenderAxisAltimeter(stylusContinuousPosition,Color.Black);
		}

		/// <summary>
		/// Orients and position the CYG mesh to match an instance of a CYG
		/// </summary>
		/// <remarks>
		/// CYGs are measured in stages, base point, tip point, base diameter.
		/// We want to represent those stages graphically.
		/// When a CYG has a base point, but it's tip is the zero vector and
		/// its base diameter is zero then we only want to render a point.
		/// When the CYG has a base point and tip point but the base diameter
		/// is zero then we want to render a line.
		/// Finally when all CYG components have been set then we draw
		/// a cone that reflects those values.
		/// </remarks>
		/// <param name="cyg">The CYG to render.</param>
		private void RenderCyg(CurrentYearGrowth cyg)
		{
			// if all three elements are set then render a cone
			if( cyg.BasePointIsSet &&
				cyg.TipPointIsSet  &&
				cyg.BaseDiameterIsSet )
			{
				// scale diameter and length
				// base mesh has diam=1 and length=1
				Matrix scale = Matrix.Identity;
				scale.Scale(2*cyg.BaseDiameter*drawingScale,2*cyg.BaseDiameter*drawingScale,cyg.Length*drawingScale); // base
			
				// translate up 1/2 length so base sits on xy plane
				Matrix translate = Matrix.Identity;
				translate.Translate(0,0,cyg.Length*drawingScale/2.0f);

				// Orient cone to match cyg's base to tip line
				// Aligning two vectors
				// A := current vector (z axis)
				// B := target vector (cyg's vector)
				// axis of rotation = ( A x B ) / | A x B |
				// theta = atan2( | A x B |, A dot B )
				Vector3 zAxisCone = new Vector3(0f,0f,1f);
				Vector3 axisRot = Vector3.Cross(zAxisCone,cyg.Vector);
				float axisRotLength = axisRot.Length();
				axisRot.Normalize();
				float theta = (float) Math.Atan2((double)axisRotLength,(double)Vector3.Dot(zAxisCone,cyg.Vector));
				Matrix rotate = Matrix.Identity;
				rotate.RotateAxis(axisRot,theta);

				// translate the cone base to the CYG's base coordinate
				Matrix translate2 = Matrix.Identity;
				translate2.Translate(cyg.BasePoint*drawingScale);

				this.d3dDevice.Transform.World = Matrix.Identity * scale * translate * rotate * translate2;

				cygMesh.DrawSubset(0);
			}
			else if ( cyg.BasePointIsSet && cyg.TipPointIsSet )
			{
				// scale length
				Matrix scale = Matrix.Identity;
				scale.Scale(1f,1f,cyg.Length * drawingScale); // base
			
				// Orient line to match cyg's base to tip line
				// Aligning two vectors
				// A := current vector (z axis)
				// B := target vector (cyg's vector)
				// axis of rotation = ( A x B ) / | A x B |
				// theta = atan2( | A x B |, A dot B )
				Vector3 zAxisCone = new Vector3(0f,0f,1f);
				Vector3 axisRot = Vector3.Cross(zAxisCone,cyg.Vector);
				float axisRotLength = axisRot.Length();
				axisRot.Normalize();
				float theta = (float) Math.Atan2((double)axisRotLength,(double)Vector3.Dot(zAxisCone,cyg.Vector));
				Matrix rotate = Matrix.Identity;
				rotate.RotateAxis(axisRot,theta);

				// translate the cone to the CYG's base coordinate
				Matrix translate2 = Matrix.Identity;
				translate2.Translate(cyg.BasePoint*drawingScale);

				this.d3dDevice.Transform.World = Matrix.Identity * scale * rotate * translate2;

				d3dDevice.SetStreamSource(0, drawAxisVertexBuffer, 0);
				d3dDevice.DrawPrimitives(PrimitiveType.LineList,4,1);

			}
			else if ( cyg.BasePointIsSet )
			{
				d3dDevice.Transform.World  = Matrix.Translation(cyg.BasePoint*drawingScale);
				stylusMesh.DrawSubset(0);
			}
			else
			{
				// this is a virgin CYG w/ all zeros 
				// that shouldn't be rendered
			}

		}

		private void RenderCygShadow(CurrentYearGrowth cyg)
		{
			// scale diameter and length
			// base mesh has diam=1 and length=1
			Matrix scale = Matrix.Identity;
			scale.Scale(cyg.BaseDiameter,cyg.BaseDiameter,cyg.Length); // base
			
			// translate up 1/2 length so base sits on xy plane
			Matrix translate = Matrix.Identity;
			translate.Translate(0,0,cyg.Length/2.0f);

			// Orient cone to match cyg's base to tip line
			// Aligning two vectors
			// A := current vector (z axis)
			// B := target vector (cyg's vector)
			// axis of rotation = ( A x B ) / | A x B |
			// theta = atan2( | A x B |, A dot B )
			Vector3 zAxisCone = new Vector3(0f,0f,1f);
			Vector3 axisRot = Vector3.Cross(zAxisCone,cyg.Vector);
			float axisRotLength = axisRot.Length();
			axisRot.Normalize();
			float theta = (float) Math.Atan2((double)axisRotLength,(double)Vector3.Dot(zAxisCone,cyg.Vector));
			Matrix rotate = Matrix.Identity;
			rotate.RotateAxis(axisRot,theta);

			// translate the cone to the CYG's base coordinate
			Matrix translate2 = Matrix.Identity;
			translate2.Translate(cyg.BasePoint);

			Matrix m = new Matrix();
			m.M11 = 1f;
			m.M22 = 1f;
			m.M33 = .01f;

			this.d3dDevice.Transform.World = Matrix.Identity * scale * translate * rotate * translate2 * m;

			cygMesh.DrawSubset(0);
		}

		private void RenderGround()
		{
			//m.VertexBuffer = groundVertexBuffer;
			d3dDevice.Transform.World = Matrix.Identity;

			Matrix rot = Matrix.Identity;
			rot.RotateZ(Geometry.DegreeToRadian(45.0f));

			d3dDevice.Transform.World *= rot;

			rot = Matrix.Identity;
			rot.Translate(.5f,.5f,-.001f);

			d3dDevice.Transform.World *= rot;

			d3dDevice.Material=groundMaterial;
			groundMesh.DrawSubset(0);
		}

		private void RenderPolhemusBaseStation()
		{
			d3dDevice.Transform.World = Matrix.Identity;

			Matrix trans = Matrix.Identity;
			trans.Translate(polhemusBaseStationSize/2f,polhemusBaseStationSize/2f,Mhs.ModelParameters.PLANTPATCH_HEIGHT_CENTIMETERS * drawingScale / 2f);

			d3dDevice.Transform.World *= trans;

			d3dDevice.Material=polhemusBaseStationMaterial;
			d3dDevice.RenderState.ZBufferFunction = Compare.Less;
			polhemusBaseStationMesh.DrawSubset(0);


			// draw a wire box to wrap the polhemus
			// wire box is 1x1x1. scale down to size
			Material m = new Material();
			m.Ambient = Color.Black;
			m.Diffuse = Color.Black;
			d3dDevice.Material=m;

			d3dDevice.Transform.World = Matrix.Identity;


			// translate to center like the mesh, the cube is 1x1x1
			trans = Matrix.Identity;
			trans.Translate(-.5f,-.5f,-.5f);

			d3dDevice.Transform.World *= trans;

			// scale to polhemus mesh size
			trans = Matrix.Identity;
			trans.Scale(polhemusBaseStationSize,polhemusBaseStationSize,polhemusBaseStationSize);

			d3dDevice.Transform.World *= trans;

			// do the same translation we did to the meth
			trans = Matrix.Identity;
			trans.Translate(polhemusBaseStationSize/2f,polhemusBaseStationSize/2f, Mhs.ModelParameters.PLANTPATCH_HEIGHT_CENTIMETERS * drawingScale / 2f);

			d3dDevice.Transform.World *= trans;

			d3dDevice.RenderState.ZBufferFunction = Compare.LessEqual;
			d3dDevice.SetStreamSource(0, drawPolhemusBaseStationVertexBuffer, 0);
			d3dDevice.DrawPrimitives(PrimitiveType.LineList,0,12);
 

		}

		private void RenderSky()
		{

			// first the yz sky
			d3dDevice.Transform.World = Matrix.Identity;
			Matrix rot = Matrix.Identity;

			// rotate to match axis
			rot = Matrix.Identity;
			rot.RotateZ(Geometry.DegreeToRadian(45f));
			d3dDevice.Transform.World *= rot;

			// translate to -x +y plane
			rot = Matrix.Identity;
			rot.Translate(-.5f,.5f,0f);
			d3dDevice.Transform.World *= rot;

			// flip onto yz plane
			rot = Matrix.Identity;
			rot.RotateY(Geometry.DegreeToRadian(90.0f));
			d3dDevice.Transform.World *= rot;

			rot = Matrix.Identity;
			rot.Scale(1f,1f,ModelParameters.NUM_DEPTH_LEVELS_PER_PLANTPATCH);
			d3dDevice.Transform.World *= rot;

			// then just a little fudge to show the axis clearly
			rot = Matrix.Identity;
			rot.Translate(-.01f,-.01f,0f);
			d3dDevice.Transform.World *= rot;

			d3dDevice.Material=skyMaterial;
			groundMesh.DrawSubset(0);

			// ----------------------

			// then the xz sky
			d3dDevice.Transform.World = Matrix.Identity;

			// rotate to match axis
			rot = Matrix.Identity;
			rot.RotateZ(Geometry.DegreeToRadian(45f));
			d3dDevice.Transform.World *= rot;

			// translate to +x -y plane
			rot = Matrix.Identity;
			rot.Translate(.5f,-.5f,0f);
			d3dDevice.Transform.World *= rot;

			// flip onto yz plane
			rot = Matrix.Identity;
			rot.RotateX(Geometry.DegreeToRadian(-90.0f));
			d3dDevice.Transform.World *= rot;

			// scale to hieght
			rot = Matrix.Identity;
			rot.Scale(1f,1f,ModelParameters.NUM_DEPTH_LEVELS_PER_PLANTPATCH);
			d3dDevice.Transform.World *= rot;

			// then just a little fudge to show the axis clearly
			rot = Matrix.Identity;
			rot.Translate(-.01f,-.01f,0f);
			d3dDevice.Transform.World *= rot;

			d3dDevice.Material=skyMaterial;
			groundMesh.DrawSubset(0);
		
		}

		private void RenderStylusTextSprites()
		{
			StringBuilder sb = new StringBuilder();
			if( drawStylusContinuousPositionSprite )
			{
				sb.Append("Realtime ");
				sb.Append(this.stylusContinuousPosition.X.ToString("000000.00"));
				sb.Append(", ");
				sb.Append(this.stylusContinuousPosition.Y.ToString("000000.00"));
				sb.Append(", ");
				sb.Append(this.stylusContinuousPosition.Z.ToString("000000.00"));
				sb.Append("\n");
			}
			if( drawStylusDepressedPositionSprite )
			{
				sb.Append("Stylusbtn ");
				sb.Append(this.stylusDepressedPosition.X.ToString("000000.00"));
				sb.Append(", ");
				sb.Append(this.stylusDepressedPosition.Y.ToString("000000.00"));
				sb.Append(", ");
				sb.Append(this.stylusDepressedPosition.Z.ToString("000000.00"));
			}
			this.postitionD3dFont2d.DrawText(null,sb.ToString(),10,10,Color.Black);
		}			

		private void RenderStylusDepressedLastPosition()
		{	
			Matrix trans = Matrix.Identity;
			d3dDevice.Transform.World = Matrix.Identity;

			// translate sphere center to origin
			trans = Matrix.Identity;
			trans.Translate(dotRadius/2f,dotRadius/2f,dotRadius/2f);
			d3dDevice.Transform.World*=trans;

			// translate to proportional location
			trans = Matrix.Identity;		
			trans.Translate(stylusDepressedPosition * drawingScale);
			d3dDevice.Transform.World*=trans;

			d3dDevice.Material = stylusDepressedMaterial;
			stylusMesh.DrawSubset(0);
		}

		private void RenderCygReferencePoint(CurrentYearGrowth cyg)
		{	
			d3dDevice.Transform.World = Matrix.Identity;
			Matrix trans = Matrix.Identity;

			// translate sphere center to origin
			trans = Matrix.Identity;
			trans.Translate(dotRadius/2f,dotRadius/2f,dotRadius/2f);
			d3dDevice.Transform.World*=trans;

			trans = Matrix.Identity;		
			trans.Translate(cyg.ReferencePoint * drawingScale);
			d3dDevice.Transform.World*=trans;
			stylusMesh.DrawSubset(0);

			// shadow
			d3dDevice.Transform.World=Matrix.Identity;

			// center on z axis  in xy plane
			trans = Matrix.Identity;		
			trans.Translate(dotRadius/2f,dotRadius/2f,0);
			d3dDevice.Transform.World*=trans;

			// hard to see make it bigger
			trans = Matrix.Identity;		
			trans.Scale(1.2f,1.2f,0);
			d3dDevice.Transform.World*=trans;

			// translate to dots x & y in xy plane (z=0)
			trans = Matrix.Identity;		
			trans.Translate(cyg.ReferencePoint.X * drawingScale,cyg.ReferencePoint.Y * drawingScale,.0001f);
			d3dDevice.Transform.World*=trans;

			// draw her.
			d3dDevice.Material=stylusShadowMaterial;
			stylusShadowMesh.DrawSubset(0);
		}

		private void RenderBoundingCylinder()
		{
			d3dDevice.Transform.World = Matrix.Identity;
			d3dDevice.Material = boundingCylinderMaterial;
			d3dDevice.SetTexture(0,boundingCylinderTexture);						
						
			Matrix trans = Matrix.Identity;

			for( int i = 0 ; i < ModelParameters.NUM_DEPTH_LEVELS_PER_PLANTPATCH;i++ )
			{
				trans = Matrix.Identity;
				trans.Translate(.5f,.5f,(float)i);
				d3dDevice.Transform.World = Matrix.Identity * trans ;
				boundingCylinderMesh.DrawSubset(0);
			}		
			d3dDevice.SetTexture(0,null);						

		}

		private void RenderCygs()
		{
			d3dDevice.RenderState.CullMode = Cull.None;
			if( plantPatch != null && plantPatch.Cygs.Count > 0 )
			{
				int i=0;
				foreach(CurrentYearGrowth cyg in plantPatch.Cygs)
				{
					// "other" cygs, the ones not selected
					if( cygIdx != i )
					{
						if( ! drawOnlySelectedCygs )
						{
							// if not selected, then draw normally
							d3dDevice.Material = cygMaterial;
							RenderCyg(cyg);
						}
					}
					else 
					{
						// since selected, blink & show ref point
						d3dDevice.Material = cygSelectedMaterial;
						if( ! blinkSelectedCyg || renderFrameCount % renderBlinkRate < (renderBlinkRate - (renderBlinkRate/4)) )
						{
							RenderCyg(cyg);
							RenderCygShadow(cyg);
							RenderCygReferencePoint(cyg);
						}
						if( drawAltimeter )
							RenderAxisAltimeter(cyg.ReferencePoint,Color.Red);
					}
					i++;
				}
			}
			d3dDevice.SetTexture(0,null );
			d3dDevice.RenderState.CullMode = Cull.CounterClockwise;
		}
		
		private void RenderShadowVolume()
		{
			// Setup render state
			// --------------------------

			// Disable z-buffer writes (note: z-testing still occurs), and enable the
			// stencil-buffer
			d3dDevice.RenderState.ZBufferEnable = false;

			// Don't bother with interpolating color
			d3dDevice.RenderState.ShadeMode = ShadeMode.Flat;

			// Set up stencil compare fuction, reference value, and masks.
			// Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
			// Note: since we set up the stencil-test to always pass, the STENCILFAIL
			// renderstate is really not needed.
			d3dDevice.RenderState.StencilFunction = Compare.Always;
			d3dDevice.RenderState.StencilZBufferFail = StencilOperation.Keep;
			d3dDevice.RenderState.StencilFail = StencilOperation.Keep;

			// If z-test passes, inc/decrement stencil buffer value
			//d3dDevice.RenderState.D3DRS_STENCILREF = false;
			//d3dDevice.RenderState.StencilMask = 0xffffffff;
			d3dDevice.RenderState.StencilPass = StencilOperation.Increment;

			// Make sure that no pixels get drawn to the frame buffer
			d3dDevice.RenderState.AlphaBlendEnable = true;
			d3dDevice.RenderState.SourceBlend = Blend.Zero;
			d3dDevice.RenderState.DestinationBlend = Blend.One;

			// render
			// --------------------------

			//// Draw front-side of shadow volume in stencil/z only
			//g_pd3dDevice->SetTransform( D3DTS_WORLD, &m_matTeapot );
			//m_pShadowVolume->render( g_pd3dDevice );
			//
			//// Now reverse cull order so back sides of shadow volume are written.
			//g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );
			//
			//// Decrement stencil buffer value
			//g_pd3dDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_DECR );
			//
			//// Draw back-side of shadow volume in stencil/z only
			//g_pd3dDevice->SetTransform( D3DTS_WORLD, &m_matTeapot );
			//m_pShadowVolume->render( g_pd3dDevice );
			//

			// Restore render states
			// --------------------------
			d3dDevice.RenderState.ShadeMode = ShadeMode.Gouraud;
			d3dDevice.RenderState.CullMode = Cull.CounterClockwise;
			d3dDevice.RenderState.ZBufferEnable = true;
		}

		private void RenderStylusContinuousMesh()
		{
			d3dDevice.Transform.World = Matrix.Identity;
			Matrix trans = Matrix.Identity;

			trans.Translate(stylusContinuousPosition * drawingScale);
			d3dDevice.Transform.World*=trans;

			d3dDevice.Material = stylusContinuousMaterial;
			stylusMesh.DrawSubset(0);

			// TODO jerk, find a better shadow
			d3dDevice.Transform.World=Matrix.Identity;
			trans = Matrix.Identity;
			trans.Translate(stylusContinuousPosition.X * drawingScale, stylusContinuousPosition.Y * drawingScale,0f);
			d3dDevice.Transform.World*=trans;
			d3dDevice.Material=stylusShadowMaterial;
			stylusShadowMesh.DrawSubset(0);
		}

		/// <summary>
		/// Ignore D3D Resizing. The engine handles this.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void d3dDevice_CancelResize(object sender, System.ComponentModel.CancelEventArgs e) 
		{
			e.Cancel = true;
		}


		/// <summary>
		/// When the device is lost dispose of buffers and meshes
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void d3dDevice_DeviceLost(object sender, EventArgs e)
		{
			// pause render
			RenderPaused = true;
			// free vertex buffers
			this.drawAxisVertexBuffer.Dispose();
			this.drawPolhemusBaseStationVertexBuffer.Dispose();
			// free meshes
			this.cygMesh.Dispose();
			this.stylusMesh.Dispose();
			this.stylusShadowMesh.Dispose();
			this.groundMesh.Dispose();
			this.axisFontMeshRX.Dispose();
			this.axisFontMeshRY.Dispose();
			this.axisFontMeshRZ.Dispose();
		}


		/// <summary>
		/// Event handler for when the device is reset. Allows recreation
		/// of Pool.Default resources.
		/// </summary>
		/// <param name="sender">the direct3d device</param>
		/// <param name="e">not used</param>
		private void d3dDevice_DeviceReset(object sender, EventArgs e)
		{
			Device dev = (Device)sender;

			// type of custom vertexes we'll use
			dev.VertexFormat = CustomVertex.PositionColored.Format;

			// setup the render state
			dev.RenderState.Lighting = true;
			dev.RenderState.ZBufferEnable = true;
			dev.RenderState.ZBufferFunction = Compare.LessEqual;
			dev.RenderState.StencilEnable = true;
			dev.RenderState.Ambient = Color.Black;
			dev.RenderState.ShadeMode = ShadeMode.Gouraud;

			// determine hardware capabilities
			Caps caps = Manager.GetDeviceCaps( Manager.Adapters.Default.Adapter, DeviceType.Hardware );

			// antialias if supported
			if( caps.LineCaps.SupportsAntiAlias == true )
				dev.RenderState.AntiAliasedLineEnable=true;

			// alpha, source, and destination blending if supported
			if( caps.SourceBlendCaps.SupportsSourceAlpha &&
				caps.DestinationBlendCaps.SupportsDestinationAlpha
				)
			{
				dev.RenderState.AlphaBlendEnable = true;
				dev.RenderState.SourceBlend = Blend.SourceAlpha;
				dev.RenderState.DestinationBlend = Blend.InvSourceAlpha;
				dev.RenderState.BlendOperation = BlendOperation.Add;
			}
			
			// Set up a white, directional light above 
			// the camera pointing at the scene.
			dev.Lights[0].Type		= LightType.Directional;
			float lightHorizontal = ModelParameters.PLANTPATCH_DIAMETER_CENTIMETERS * drawingScale + .5f;
			float lightVertical = ModelParameters.PLANTPATCH_HEIGHT_CENTIMETERS * drawingScale / 2f;
			dev.Lights[0].Position  = new Vector3(lightHorizontal,lightHorizontal,lightVertical);
			Vector3 lightDirection  = new Vector3(-lightHorizontal,-lightHorizontal,0f);
			dev.Lights[0].Direction = Vector3.Normalize(lightDirection);
			dev.Lights[0].Range		= lightVertical * 4f;
			dev.Lights[0].Ambient	= System.Drawing.Color.White;
			dev.Lights[0].Diffuse	= System.Drawing.Color.White;
			dev.Lights[0].Enabled	= true;//turn it on




			// initialize the D3d font from the 2d font
			postitionD3dFont2d = new D3D.Font(d3dDevice,positionFont2d);

			// setup the transformation pipeline
			SetupMatrices();

			// setup vertex buffers
			SetupVertexBuffers();

			// setup meshes
			SetupMeshes();
		}

		#endregion

	} // END class PlantPatchRenderer

} // END namespace PlantPatchRenderer

