context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Data;
  using System.Drawing;
  //using System.Linq;
  using System.Text;
  using System.Windows.Forms;
  using Microsoft.DirectX;
  using Microsoft.DirectX.Direct3D;
  using OpenDentBusiness;
  
  namespace SparksToothChart {
  
  	public partial class ToothChartDirectX:Control {
  
  		///<summary>DirectX handle to this control.</summary>
  		public Device device=null;
  		///<summary>This is a reference to the TcData object that's at the wrapper level.</summary>
  		public ToothChartData TcData;
  		public DirectXDeviceFormat deviceFormat=null;
  		//public bool showPerioLegend=false;
  		private Color specular_color_normal;
  		private Color specular_color_cementum;
  		private float specularSharpness;
  		private Microsoft.DirectX.Direct3D.Font xfont;
  		private Microsoft.DirectX.Direct3D.Font xSealantFont;
  		private Microsoft.DirectX.Direct3D.Font xWatchFont;
  		private Microsoft.DirectX.Direct3D.Font xWatchFontBig;
  		private bool MouseIsDown=false;
  		[Category("Action"),Description("Occurs when the mouse goes up ending a drawing segment.")]
  		public event ToothChartDrawEventHandler SegmentDrawn=null;
  		///<summary>Mouse move causes this variable to be updated with the current tooth that the mouse is hovering over.</summary>
  		private string hotTooth;
  		///<summary>The previous hotTooth.  If this is different than hotTooth, then mouse has just now moved to a new tooth.  Can be 0 to represent no previous.</summary>
  		private string hotToothOld;
  		private bool deviceLost=true;
  		//private DateTime frameBeginTime=DateTime.MinValue;
  		//private DateTime frameEndTime=DateTime.MinValue;
  
  		public class DirectXDeviceFormat{
  			public AdapterInformation adapter=null;
  			public DeviceType deviceType=DeviceType.NullReference;
  			public CreateFlags createFlags=CreateFlags.SoftwareVertexProcessing;
  			public DepthFormat depthStencilFormat=DepthFormat.D16;
  			public Format backBufferFormat=Format.R5G6B5;
  			public MultiSampleType maxMultiSampleType=MultiSampleType.None;
  
  			public DirectXDeviceFormat(){
  			}
  
  			///<summary>Inverse of ToString(). String to DirectXDeviceFormat.</summary>
  			public DirectXDeviceFormat(string directXFormat){
  				if(directXFormat.IndexOf(';')<0){
  					//Invalid format.
  					return;
  				}
  				string[] settings=directXFormat.Split(new char[] {';'});
  				adapter=Manager.Adapters[PIn.Int(settings[0])];
  				deviceType=(DeviceType)Enum.Parse(typeof(DeviceType),settings[1]);
  				createFlags=(CreateFlags)PIn.Int(settings[2]);
  				depthStencilFormat=(DepthFormat)Enum.Parse(typeof(DepthFormat),settings[3]);
  				backBufferFormat=(Format)Enum.Parse(typeof(Format),settings[4]);
  				maxMultiSampleType=(MultiSampleType)Enum.Parse(typeof(MultiSampleType),settings[5]);
  			}
  
  			public override string ToString() {
  				return ""+adapter.Adapter+";"+Enum.GetName(typeof(DeviceType),deviceType)+";"+((int)createFlags)+";"+
  					Enum.GetName(typeof(DepthFormat),depthStencilFormat)+";"+Enum.GetName(typeof(Format),backBufferFormat)+";"+
  					Enum.GetName(typeof(MultiSampleType),maxMultiSampleType);
  			}
  
  			public override bool Equals(object obj) {
  				if(obj.GetType()!=typeof(DirectXDeviceFormat)){
  					return false;
  				}
  				DirectXDeviceFormat xformat=(DirectXDeviceFormat)obj;
  				return(xformat.ToString()==this.ToString());
  			}
  
  			public override int GetHashCode() {
  				return base.GetHashCode();
  			}
  
  			public Device CreateDevice(Control control) {
  				try {
  					PresentParameters pp=new PresentParameters();
  					pp.AutoDepthStencilFormat=depthStencilFormat;
  					pp.BackBufferCount=1;
  					pp.BackBufferFormat=backBufferFormat;
  					pp.BackBufferHeight=control.Height;
  					pp.BackBufferWidth=control.Width;
  					pp.FullScreenRefreshRateInHz=0;//Must be 0 in windowed mode.
  					pp.DeviceWindow=control;
  					pp.DeviceWindowHandle=control.Handle;
  					pp.EnableAutoDepthStencil=true;
  					pp.ForceNoMultiThreadedFlag=false;
  					pp.MultiSample=maxMultiSampleType;
  					pp.MultiSampleQuality=0;
  					pp.PresentationInterval=PresentInterval.Default;
  					pp.PresentFlag=PresentFlag.None;
  					pp.SwapEffect=SwapEffect.Discard;//Required to be set to discard for anti-aliasing.
  					pp.Windowed=true;//Must be set to true for controls.
  					//When a Device object is created, the common language runtime will change the floating-point unit (FPU) to single precision to maintain better performance. 
  					//This was causing extensive rounding errors throughout OD.
  					//To maintain the default double precision FPU, which is default for the common language runtime, we use the CreateFlags.FpuPreserve flag in the constructor.
  					//HardwareVertexProcessing, MixedVertexProcessing, and SoftwareVertexProcessing constants (CreateFlags) are mutually exclusive.
  					//One of them must be specified during creation of a "device".
  					//Therefore, we pass in our variable createFlags (typically SoftwareVertexProcessing) along with FpuPreserve to maintain double precision.
  					Device dev=new Device(adapter.Adapter,deviceType,control,createFlags | CreateFlags.FpuPreserve,pp);
  					return dev;
  				} catch {
  				}
  				return null;
  			}
  
  
  		}
  
  		public ToothChartDirectX() {
  			InitializeComponent();
  		}
  
  		public static DirectXDeviceFormat[] GetStandardDeviceFormats(){
  			DeviceType[] deviceTypes=new DeviceType[] { DeviceType.Hardware,DeviceType.Reference };
  			Format[] backBufferFormats=new Format[] { 
  				//16bit formats
  				Format.R5G6B5,Format.A1R5G5B5,Format.X1R5G5B5,
  				//24bit formats
  				Format.R8G8B8,
  				//32bit formats
  				Format.A8R8G8B8,Format.X8R8G8B8,Format.A2R10G10B10,Format.A2B10G10R10,Format.A8B8G8R8,Format.G8R8G8B8,Format.R8G8B8G8,Format.X8B8G8R8,
  				//64bit formats
  				Format.A16B16G16R16,
  			};
  			DepthFormat[] depthFormats=new DepthFormat[] { DepthFormat.D16,DepthFormat.D32,DepthFormat.D24X8,DepthFormat.D15S1,DepthFormat.D24S8,
  				DepthFormat.D24SingleS8,DepthFormat.D24X4S4,DepthFormat.L16};
  			return ToothChartDirectX.GetPossibleDeviceFormats(deviceTypes,backBufferFormats,true,depthFormats);
  		}
  
  		private static bool IsDeviceMultiSampleOK(AdapterInformation adapter,DeviceType deviceType,DepthFormat depthFormat,
  			Format backbufferFormat,MultiSampleType multisampleType,bool windowed) {
  			int qualityLevels=0;
  			int result=0;
  			//Verify that the render target surface supports the given multisample type
  			if(Manager.CheckDeviceMultiSampleType(adapter.Adapter,deviceType,backbufferFormat,windowed,multisampleType,out result,out qualityLevels)) {
  				//Verify that the depth stencil surface supports the given multisample type
  				if(Manager.CheckDeviceMultiSampleType(adapter.Adapter,deviceType,(Format)depthFormat,windowed,multisampleType,out result,out qualityLevels)) {
  					return (result==((int)ResultCode.Success));
  				}
  			}
  			return false;
  		}
  
  		private static DirectXDeviceFormat[] GetPossibleDeviceFormats(DeviceType[] deviceTypes,Format[] backBufferFormats,bool windowed,DepthFormat[] depthStencilFormats) {
  			List<DirectXDeviceFormat> possibleFormats=new List<DirectXDeviceFormat>();
  			for(int a=0;a<Manager.Adapters.Count;a++) {
  				AdapterInformation adapter=Manager.Adapters[a];
  				for(int t=0;t<deviceTypes.Length;t++) {
  					DeviceType deviceType=deviceTypes[t];
  					for(int b=0;b<backBufferFormats.Length;b++) {
  						Format backBufferFormat=backBufferFormats[b];
  						foreach(DisplayMode displayMode in adapter.SupportedDisplayModes) {
  							if(displayMode.Format!=backBufferFormat) {
  								//We require the display buffer to have the same format as the back buffer,
  								//so that we know that a back buffer flip will work.
  								continue;
  							}
  							if(Manager.CheckDeviceType(
  								adapter.Adapter,
  								deviceType,
  								displayMode.Format,
  								displayMode.Format,
  								windowed)) {
  								//Now make sure the depth buffer meets one of the required formats.
  								foreach(DepthFormat depthStencilFormat in depthStencilFormats) {
  									if(Manager.CheckDeviceFormat(adapter.Adapter,deviceType,displayMode.Format,
  										Usage.DepthStencil,ResourceType.Surface,depthStencilFormat)) {
  										if(Manager.CheckDepthStencilMatch(adapter.Adapter,deviceType,displayMode.Format,
  											displayMode.Format,depthStencilFormat)) {
  											DirectXDeviceFormat format=new DirectXDeviceFormat();
  											format.adapter=adapter;
  											format.deviceType=deviceType;
  											format.createFlags=CreateFlags.SoftwareVertexProcessing;
  											//format.createFlags=CreateFlags.FpuPreserve;
  											format.depthStencilFormat=depthStencilFormat;
  											format.backBufferFormat=backBufferFormat;
  											format.maxMultiSampleType=MultiSampleType.None;
  											//Anti-aliasing/multi-sampling appears to only work on hardware devices.
  											if(deviceType==DeviceType.Hardware) {
  												Caps caps=Manager.GetDeviceCaps(adapter.Adapter,deviceType);
  												//An multisampling/anti-aliasing method must be supported both on the back buffer and with the depth buffer in order for it to be usable.
  												if(IsDeviceMultiSampleOK(adapter,deviceType,depthStencilFormat,backBufferFormat,MultiSampleType.FourSamples,windowed)) {
  													format.maxMultiSampleType=MultiSampleType.FourSamples;
  												} else if(IsDeviceMultiSampleOK(adapter,deviceType,depthStencilFormat,backBufferFormat,MultiSampleType.ThreeSamples,windowed)) {
  													format.maxMultiSampleType=MultiSampleType.ThreeSamples;
  												} else if(IsDeviceMultiSampleOK(adapter,deviceType,depthStencilFormat,backBufferFormat,MultiSampleType.TwoSamples,windowed)) {
  													format.maxMultiSampleType=MultiSampleType.TwoSamples;
  												}
  											}
  											if(possibleFormats.IndexOf(format)>-1) {
  												//Skip duplicate formats.
  												continue;
  											}
  											possibleFormats.Add(format);
  										}
  									}
  								}
  							}
  						}
  					}
  				}
  			}
  			return possibleFormats.ToArray();
  		}
  
  		///<summary>Returns the number of bits corresponding to the given format. Returns 0 if the format has not yet been accounted for.</summary>
  		public static int GetFormatBitCount(Format format){
  			Format[] eightBitFormats=new Format[]{
  				Format.A4L4,Format.A8,Format.L8,Format.P8,Format.R3G3B2,
  			};
  			Format[] sixteenBitFormats=new Format[]{
  				Format.A1R5G5B5,Format.A4R4G4B4,Format.A8L8,Format.A8P8,Format.A8R3G3B2,Format.D15S1,Format.D16,
  				Format.D16Lockable,Format.L16,Format.L6V5U5,Format.R16F,Format.R5G6B5,Format.V8U8,Format.X1R5G5B5,
  				Format.X4R4G4B4,
  			};
  			Format[] twentyFourBitFormats=new Format[]{
  				Format.R8G8B8,
  			};
  			Format[] thirtyTwoBitFormats=new Format[]{
  				Format.A2B10G10R10,Format.A2R10G10B10,Format.A2W10V10U10,Format.A8B8G8R8,Format.A8R8G8B8,
  				Format.D24S8,Format.D24SingleS8,Format.D24X4S4,Format.D24X8,Format.D32,Format.D32SingleLockable,
  				Format.G16R16,Format.G16R16F,Format.G8R8G8B8,Format.Q8W8V8U8,Format.R32F,Format.R8G8B8G8,Format.V16U16,
  				Format.X8B8G8R8,Format.X8L8V8U8,Format.X8R8G8B8,
  			};
  			Format[] sixtyFourBitFormats=new Format[]{
  				Format.A16B16G16R16,Format.A16B16G16R16F,Format.G32R32F,Format.Q16W16V16U16,
  			};
  			Format[] oneHundredTwentyEightBitFormats=new Format[]{
  				Format.A32B32G32R32F,
  			};
  			for(int i=0;i<eightBitFormats.Length;i++) {
  				if(format==eightBitFormats[i]) {
  					return 8;
  				}
  			}
  			for(int i=0;i<sixteenBitFormats.Length;i++){
  				if(format==sixteenBitFormats[i]){
  					return 16;
  				}
  			}
  			for(int i=0;i<twentyFourBitFormats.Length;i++){
  				if(format==twentyFourBitFormats[i]){
  					return 24;
  				}
  			}
  			for(int i=0;i<thirtyTwoBitFormats.Length;i++){
  				if(format==thirtyTwoBitFormats[i]){
  					return 32;
  				}
  			}
  			for(int i=0;i<sixtyFourBitFormats.Length;i++){
  				if(format==sixtyFourBitFormats[i]){
  					return 64;
  				}
  			}
  			for(int i=0;i<oneHundredTwentyEightBitFormats.Length;i++){
  				if(format==oneHundredTwentyEightBitFormats[i]){
  					return 128;
  				}
  			}
  			//Format has not yet been accounted for.
  			//Format.CxV8U8,Format.Dxt1,Format.Dxt2,Format.Dxt3,Format.Dxt4,Format.Dxt5,Format.Multi2Argb8,
  			//Format.Unknown,Format.Uyvy,Format.VertexData,Format.Yuy2
  			return 0;
  		}
  
  		///<summary>Returns the number of bits represented by the given depth format. Returns 0 if the given depth format is not yet accounted for.</summary>
  		public static int GetDepthFormatBitCount(DepthFormat depthFormat){
  			DepthFormat[] sixteenBitFormats=new DepthFormat[]{
  				DepthFormat.D15S1,DepthFormat.D16,DepthFormat.D16Lockable,DepthFormat.L16,
  			};
  			DepthFormat[] thirtyTwoBitFormats=new DepthFormat[] {
  				DepthFormat.D24S8,DepthFormat.D24SingleS8,DepthFormat.D24X4S4,DepthFormat.D24X8,
  				DepthFormat.D32,DepthFormat.D32SingleLockable,
  			};
  			for(int i=0;i<sixteenBitFormats.Length;i++){
  				if(depthFormat==sixteenBitFormats[i]){
  					return 16;
  				}
  			}
  			for(int i=0;i<thirtyTwoBitFormats.Length;i++){
  				if(depthFormat==thirtyTwoBitFormats[i]){
  					return 32;
  				}
  			}
  			//The specified format it not yet accounted for.
  			//DepthFormat.Unknown
  			return 0;
  		}
  
  		public static int GetMultiSampleNumberForType(MultiSampleType multiSampleType){
  			if(multiSampleType==MultiSampleType.TwoSamples){
  				return 2;
  			}else if(multiSampleType==MultiSampleType.ThreeSamples){
  				return 3;
  			} else if(multiSampleType==MultiSampleType.FourSamples) {
  				return 4;
  			} else if(multiSampleType==MultiSampleType.FiveSamples) {
  				return 5;
  			} else if(multiSampleType==MultiSampleType.SixSamples) {
  				return 6;
  			} else if(multiSampleType==MultiSampleType.SevenSamples) {
  				return 7;
  			} else if(multiSampleType==MultiSampleType.EightSamples) {
  				return 8;
  			} else if(multiSampleType==MultiSampleType.NineSamples) {
  				return 9;
  			} else if(multiSampleType==MultiSampleType.TenSamples) {
  				return 10;
  			} else if(multiSampleType==MultiSampleType.ElevenSamples) {
  				return 11;
  			} else if(multiSampleType==MultiSampleType.TwelveSamples) {
  				return 12;
  			} else if(multiSampleType==MultiSampleType.ThirteenSamples) {
  				return 13;
  			} else if(multiSampleType==MultiSampleType.FourteenSamples) {
  				return 14;
  			} else if(multiSampleType==MultiSampleType.FifteenSamples) {
  				return 15;
  			} else if(multiSampleType==MultiSampleType.SixteenSamples) {
  				return 16;
  			}
  			return 0;
  		}
  
  		///<summary>Must be called after the ToothChartDirectX control has been added to a form and should be called before it is drawn the first time.</summary>
  		public void InitializeGraphics(){
  			if(deviceLost && deviceFormat!=null){
  				device=deviceFormat.CreateDevice(this);
  				if(device==null) {
  					throw new Exception("Failed to create DirectX device.");
  				}
  				device.DeviceReset+=new EventHandler(this.OnDeviceReset);
  				device.DeviceLost+=new EventHandler(this.OnDeviceLost);
  				device.DeviceResizing+=new CancelEventHandler(this.OnDeviceResizing);
  				OnDeviceReset(device,null);
  			}
  		}
  
  		public void SetSize(Size size){
  			this.Size=size;
  			Reinitialize();
  		}
  
  		public void Reinitialize(){
  			CleanupDirectX();
  			if(device!=null) {
  				if(!deviceLost) {
  					device.Dispose();//This line causes OnDeviceLost() to fire.  If deviceLost, then device.Dispose() was already called.
  				}
  				device=null;
  			}
  			InitializeGraphics();
  		}
  
  		public void CleanupDirectX(){
  			if(xSealantFont!=null){
  				xSealantFont.Dispose();
  				xSealantFont=null;
  			}
  			if(xWatchFont!=null){
  				xWatchFont.Dispose();
  				xWatchFont=null;
  			}
  			if(xWatchFontBig!=null){
  				xWatchFontBig.Dispose();
  				xWatchFontBig=null;
  			}
  			if(xfont!=null){
  				xfont.Dispose();
  				xfont=null;
  			}
  			TcData.CleanupDirectX();
  		}
  
  		///<summary></summary>
  		public void OnDeviceReset(object sender,EventArgs e){
  			deviceLost=false;
  			CleanupDirectX();
  			device=sender as Device;
  			//Required for calculating font background rectangle size in ToothChartData.
  			TcData.Font=new System.Drawing.Font("Arial",9f);
  			xfont=new Microsoft.DirectX.Direct3D.Font(device,
  				(int)Math.Round((float)(14*Math.Sqrt(TcData.PixelScaleRatio))),
  				(int)Math.Round((float)(5*Math.Sqrt(TcData.PixelScaleRatio))),
  				FontWeight.Normal,1,false,CharacterSet.Ansi,Precision.Device,
  				FontQuality.ClearType,PitchAndFamily.DefaultPitch,"Arial");
  			xSealantFont=new Microsoft.DirectX.Direct3D.Font(device,
  				(int)Math.Round(25*TcData.PixelScaleRatio),
  				(int)Math.Round(9*TcData.PixelScaleRatio),
  				FontWeight.Regular,1,false,CharacterSet.Ansi,Precision.Device,
  				FontQuality.ClearType,PitchAndFamily.DefaultPitch,"Arial");
  			xWatchFont=new Microsoft.DirectX.Direct3D.Font(device,
  				(int)Math.Round(15*TcData.PixelScaleRatio),
  				(int)Math.Round(6*TcData.PixelScaleRatio),
  				FontWeight.Regular,1,false,CharacterSet.Ansi,Precision.Device,
  				FontQuality.ClearType,PitchAndFamily.DefaultPitch,"Arial");
  			xWatchFontBig=new Microsoft.DirectX.Direct3D.Font(device,
  				(int)Math.Round(19*TcData.PixelScaleRatio),
  				(int)Math.Round(7.4*TcData.PixelScaleRatio),
  				FontWeight.UltraBold,1,false,CharacterSet.Ansi,Precision.Device,
  				FontQuality.ClearType,PitchAndFamily.DefaultPitch,"Times New Roman");
  			TcData.PrepareForDirectX(device);
  		}
  
  		public void OnDeviceLost(object sender,EventArgs e){
  			deviceLost=true;
  		}
  
  		public void OnDeviceResizing(object sender,EventArgs e) {
  			//Hmm, is this function ever called? I couldn't make it fire with initial testing.
  		}
  
  		protected override void OnPaintBackground(PaintEventArgs e) {
  			//Do nothing to eliminate flicker. 
  		}
  
  		protected override void OnSizeChanged(EventArgs e) {
  			Invalidate();//Force the control to redraw. 
  		}
  
  		private Matrix ScreenSpaceMatrix() {
  			return device.Transform.World*device.Transform.View*device.Transform.Projection;
  		}
  
  		protected override void OnPaint(PaintEventArgs pe) {
  			//Color backColor=Color.FromArgb(150,145,152);
  			if(device==null || deviceLost) {
  				//When no rendering context has been set, simply display the control
  				//as a black rectangle. This will make the control draw as a blank
  				//rectangle when in the designer. 
  				pe.Graphics.FillRectangle(new SolidBrush(TcData.ColorBackground),new Rectangle(0,0,Width,Height));
  				return;
  			}
  			//When the OS user is switched then switched back or when coming back from stand by mode, the OS calls the OnPaint function
  			//even before it calls the OnDeviceLost() function. When this happens, the render will fail
  			//because the DirectX device is not in a valid state to be rendered to. Before the exception is returned from Render(), 
  			//a call is made by the OS to OnDeviceLost(), which sets deviceLost=true (when the OnPaint() function begins, deviceLost=false)
  			//so that further rendering will not occur with the device in its invalid state.elec
  			try {
  				Render();
  			}
  			catch { 
  				if(deviceLost){
  					//Rendering failed because our device is invalid. Reinitialize the device and cached objects and force
  					//the control to be rerendered.
  					Reinitialize();
  					Invalidate();
  				}
  			}
  		}
  
  		protected void Render() {
  			//Set the view and projection matricies for the camera.
  			float heightProj=TcData.SizeOriginalProjection.Height;
  			float widthProj=TcData.SizeOriginalProjection.Width;
  			if(TcData.IsWide) {
  				widthProj=heightProj*Width/Height;
  			} else {//tall
  				heightProj=widthProj*Height/Width;
  			}
  			device.Transform.Projection=Matrix.OrthoLH(widthProj,heightProj,0,1000.0f);
  			device.Transform.World=Matrix.Identity;
  			//viewport transformation not used. Default is to fill entire control.
  			device.RenderState.CullMode=Cull.None;//Do not cull triangles. Our triangles are too small for this feature to work reliably.
  			device.RenderState.ZBufferEnable=true;
  			device.RenderState.ZBufferFunction=Compare.Less;
  			//Blend mode settings.
  			device.RenderState.AlphaBlendEnable=false;//Disabled
  			//device.RenderState.AlphaFunction=Compare.Always;
  			//device.RenderState.AlphaTestEnable=true;
  			//device.RenderState.SourceBlend=Blend.SourceAlpha;
  			//device.RenderState.DestinationBlend=Blend.InvSourceAlpha;
  			//device.RenderState.AlphaBlendOperation=BlendOperation.Add;
  			//Lighting settings
  			device.RenderState.Lighting=true;
  			device.RenderState.SpecularEnable=true;
  			device.RenderState.SpecularMaterialSource=ColorSource.Material;
  			float ambI=.4f;//.2f;//Darker for testing
  			float difI=.6f;//.4f;//Darker for testing
  			float specI=.8f;//.2f;//Had to turn specular down to avoid bleedout.
  			if(TcData.PerioMode) {
  				//Want perio teeth to be more washed out so that other graphics are more visible.
  				ambI=.6f;
  				difI=.4f;
  			}
  			//I think we're going to need to eventually use shaders to get our pinpoint reflections.
  			//Set properties for light 0. Diffuse light.
  			device.Lights[0].Type=LightType.Directional;
  			device.Lights[0].Ambient=Color.FromArgb(255,(int)(255*ambI),(int)(255*ambI),(int)(255*ambI));
  			device.Lights[0].Diffuse=Color.FromArgb(255,(int)(255*difI),(int)(255*difI),(int)(255*difI));
  			device.Lights[0].Specular=Color.FromArgb(255,(int)(255*specI),(int)(255*specI),(int)(255*specI));
  			device.Lights[0].Direction=new Vector3(0.5f,-0.2f,1f);//(0.5f,0.1f,1f);
  			device.Lights[0].Enabled=true;
  			//Material settings
  			float specNorm=1f;
  			float specCem=.1f;
  			//Also, see DrawTooth for the specular color used for enamel.
  			specular_color_normal=Color.FromArgb(255,(int)(255*specNorm),(int)(255*specNorm),(int)(255*specNorm));
  			specular_color_cementum=Color.FromArgb(255,(int)(255*specCem),(int)(255*specCem),(int)(255*specCem));
  			specularSharpness=70f;//70f;//Not the same as in OpenGL. No maximum value. Smaller number means light is more spread out.
  			//Draw
  			if(TcData.PerioMode) {
  				DrawScenePerio();
  			}
  			else {
  				DrawScene();
  			}
  		}
  
  		private void DrawScene() {
  			device.Clear(ClearFlags.Target|ClearFlags.ZBuffer,TcData.ColorBackground,1.0f,0);
  			device.BeginScene();
  			//The Z values between OpenGL and DirectX are negated (the axis runs in the opposite direction).
  			//We reflect that difference here by negating the z values for all coordinates.
  			Matrix defOrient=Matrix.Identity;
  			defOrient.Scale(1f,1f,-1f);
  			//We make sure to move all teeth forward a large step so that specular lighting will calculate properly.
  			//This step does not affect the tooth locations on the screen because changes in z position for a tooth
  			//does not affect position in orthographic projections.
  			Matrix trans=Matrix.Identity;
  			trans.Translate(0f,0f,400f);
  			defOrient=defOrient*trans;
  			//frameBeginTime=DateTime.Now;
  			for(int t=0;t<TcData.ListToothGraphics.Count;t++) {//loop through each tooth
  				if(TcData.ListToothGraphics[t].ToothID=="implant") {//this is not an actual tooth.
  					continue;
  				}
  				DrawFacialView(TcData.ListToothGraphics[t],defOrient);
  				DrawOcclusalView(TcData.ListToothGraphics[t],defOrient);
  			}
  			//frameEndTime=DateTime.Now;
  			DrawWatches(defOrient);
  			DrawDrawingSegments();
  			DrawNumbersAndLines();//Numbers and center lines are top-most.			
  			device.EndScene();
  			device.Present();
  		}
  
  		private void DrawScenePerio() {
  			device.Clear(ClearFlags.Target|ClearFlags.ZBuffer,TcData.ColorBackground,1.0f,0);
  			device.BeginScene();
  			//The distance from y=0 to the tip of the highest root in the upper-most maxillary row.
  			const float maxYmm=63f;
  			//The baseY is used to force the perio chart to start as near to the top of the control as possible.
  			//This is helpful with the perio legend, because it allows one to increase the height of this control
  			//in order to give space for the perio legend to display at the bottom.
  			float baseY=Height/(2f*TcData.ScaleMmToPix)-maxYmm;
  			Matrix defOrient=Matrix.Scaling(1f,1f,-1f)*Matrix.Translation(0,baseY,400f);
  			List <ToothGraphic> maxillaryTeeth=new List <ToothGraphic> ();
  			List <ToothGraphic> mandibleTeeth=new List <ToothGraphic> ();
  			for(int t=0;t<TcData.ListToothGraphics.Count;t++) {//loop through each tooth
  				if(TcData.ListToothGraphics[t].ToothID=="implant") {//this is not an actual tooth.
  					continue;
  				}
  				if(ToothGraphic.IsMaxillary(TcData.ListToothGraphics[t].ToothID)){
  					maxillaryTeeth.Add(TcData.ListToothGraphics[t]);
  				}else{
  					mandibleTeeth.Add(TcData.ListToothGraphics[t]);
  				}
  			}
  			//Draw the maxillary upper-most row.
  			Matrix maxillaryUpperRowMat=Matrix.Translation(0,45f,0)*defOrient;
  			DrawPerioRow(maxillaryTeeth,maxillaryUpperRowMat,true,false);
  			//Draw the maxillary lower-most row with teeth which have negated z values.
  			Matrix maxillaryLowerRowMat=Matrix.Scaling(1f,1f,-1f)*Matrix.Translation(0,12f,0)*defOrient;
  			DrawPerioRow(maxillaryTeeth,maxillaryLowerRowMat,true,true);
  			//Draw the mandible upper-most row with teeth which have negated z values.
  			Matrix mandibleUpperRowMat=Matrix.Scaling(1f,1f,-1f)*Matrix.Translation(0,-12f,0)*defOrient;
  			DrawPerioRow(mandibleTeeth,mandibleUpperRowMat,false,true);
  			//Draw the mandible lower-most row.
  			Matrix mandibleLowerRowMat=Matrix.Translation(0,-45f,0)*defOrient;
  			DrawPerioRow(mandibleTeeth,mandibleLowerRowMat,false,false);
  			DrawNumbersAndLinesPerio(baseY);//Numbers and center lines are top-most.			
  			DrawPerioLegend(-60f,baseY-63f);
  			device.EndScene();
  			device.Present();
  		}
  
  		private void DrawPerioRow(List <ToothGraphic> toothGraphics,Matrix orientation,bool maxillary,bool lingual){
  			for(int t=0;t<toothGraphics.Count;t++){
  				device.RenderState.ZBufferEnable=true;
  				device.RenderState.Lighting=true;
  				device.Transform.World=Matrix.Translation(GetTransX(toothGraphics[t].ToothID),0,0)*orientation;
  				if(toothGraphics[t].Visible
  					|| (toothGraphics[t].IsCrown && toothGraphics[t].IsImplant)
  
gt: 					|| toothGraphics[t].IsPontic) {