PageRenderTime 768ms CodeModel.GetById 159ms app.highlight 462ms RepoModel.GetById 83ms app.codeStats 2ms

/Rendering/Basics/Drawing/DrawManager.cs

#
C# | 874 lines | 559 code | 108 blank | 207 comment | 65 complexity | f5344a4443585169a6c6976b75bc31f2 MD5 | raw file
  1using System;
  2using System.IO;
  3using Delta.ContentSystem.Rendering;
  4using Delta.Engine;
  5using Delta.Engine.Dynamic;
  6using Delta.Graphics;
  7using Delta.Graphics.Basics;
  8using Delta.Rendering.Basics.Materials;
  9using Delta.Utilities;
 10using Delta.Utilities.Datatypes;
 11using Delta.Utilities.Graphics;
 12using Delta.Utilities.Graphics.ShaderFeatures;
 13using Delta.Utilities.Helpers;
 14using Delta.Rendering.Enums;
 15
 16namespace Delta.Rendering.Basics.Drawing
 17{
 18	/// <summary>
 19	/// The DrawManager is used by all of the classes in this namespace to do
 20	/// all the line, box, sphere, circle, grid, etc. drawing of all kind of
 21	/// shapes for 2D and 3D drawing. Everything related with primitive drawing
 22	/// will be uniquely handled in here. This class is used as a singleton.
 23	/// Warning: This class is not thread safe, only have one thread draw stuff!
 24	/// </summary>
 25	public class DrawManager : DynamicModule
 26	{
 27		#region Constants
 28		/// <summary>
 29		/// Start with max. 120 lines initially (or 80 polygons). If we reach that
 30		/// limit we will increase the Mesh VertexData by 120 more lines.
 31		/// </summary>
 32		private const int InitialNumOfVertices = 240;
 33		#endregion
 34
 35		#region MakeScreenshot (Static)
 36		/// <summary>
 37		/// Make a screenshot of the current screen.
 38		/// </summary>
 39		public static string MakeScreenshot()
 40		{
 41			return ScreenshotCapturer.MakeScreenshot();
 42		}
 43		#endregion
 44
 45		#region Internal
 46
 47		#region Instance (Internal)
 48		/// <summary>
 49		/// Instance for this Draw manager (handled only here privately)
 50		/// </summary>
 51		internal static DrawManager Instance
 52		{
 53			get
 54			{
 55				if (instance == null)
 56				{
 57					// Needs to be created via factory to make sure we only do this once
 58					instance = Factory.Create<DrawManager>();
 59				}
 60
 61				return instance;
 62			} // get
 63		}
 64		#endregion
 65
 66		#endregion
 67
 68		#region Private
 69
 70		#region instance (Private)
 71		/// <summary>
 72		/// Private instance
 73		/// </summary>
 74		private static DrawManager instance;
 75		#endregion
 76
 77		#region screenshotCapturer (Private)
 78		/// <summary>
 79		/// Current screenshot capturer for the application
 80		/// </summary>
 81		private static ScreenshotCapturer screenshotCapturer;
 82		#endregion
 83
 84		#region ScreenshotCapturer (Private)
 85		/// <summary>
 86		/// Screenshot capturer
 87		/// </summary>
 88		private static ScreenshotCapturer ScreenshotCapturer
 89		{
 90			get
 91			{
 92				if (screenshotCapturer == null)
 93				{
 94					screenshotCapturer = Factory.Create<ScreenshotCapturer>();
 95
 96					if (screenshotCapturer == null)
 97					{
 98						Log.Warning("No 'ScreenshotCapturer' module was found," +
 99												" unable to start the screenshot capturer system. Please" +
100												" provide a Settings module.");
101					}
102				} // if
103
104				return screenshotCapturer;
105			} // get
106		}
107		#endregion
108
109		#region line2DMaterial (Private)
110		/// <summary>
111		/// Line material for rendering all lines (2D and 3D), contains the
112		/// LineShader responsible for drawing everything.
113		/// </summary>
114		private readonly BaseMaterial line2DMaterial;
115		#endregion
116
117		#region line3DMaterial (Private)
118		/// <summary>
119		/// Line material for rendering all lines (2D and 3D), contains the
120		/// LineShader responsible for drawing everything.
121		/// </summary>
122		private readonly BaseMaterial line3DMaterial;
123		#endregion
124
125		#region line2DMesh (Private)
126		/// <summary>
127		/// Line mesh for 2D lines, contains 2D position and color vertex data.
128		/// Uncompressed this are just 12 bytes (8 for position, 4 for color),
129		/// compressed this goes down to 8 bytes (4 for position, 4 for color).
130		/// </summary>
131		private readonly Geometry line2DMesh;
132		#endregion
133
134		#region line3DMesh (Private)
135		/// <summary>
136		/// Line mesh for 3D lines, contains 3D position and color vertex data.
137		/// Uncompressed this are just 16 bytes (12 for position, 4 for color),
138		/// compressed this goes down to 10 bytes (6 for position, 4 for color).
139		/// </summary>
140		private readonly Geometry line3DMesh;
141		#endregion
142
143		#region polygon2DMesh (Private)
144		/// <summary>
145		/// Polygon mesh for 2D shapes, contains 3 2D position and colors for
146		/// each polygon. Same vertex data format is used as for line2DMesh!
147		/// </summary>
148		private readonly Geometry polygon2DMesh;
149		#endregion
150
151		#region polygon3DMesh (Private)
152		/// <summary>
153		/// Polygon mesh for 3D shapes, contains 3 3D position and colors for
154		/// each polygon. Same vertex data format is used as for line3DMesh!
155		/// </summary>
156		private readonly Geometry polygon3DMesh;
157		#endregion
158
159		// Helpers for quickly saving out element data.
160
161		#region position2DElement (Private)
162		private VertexElement position2DElement;
163		#endregion
164
165		#region position3DElement (Private)
166		private VertexElement position3DElement;
167		#endregion
168
169		// Note: Color is the same, it does not matter how much data is stored
170		// in position 2d or 3d, if it is compressed or whatnot.
171
172		#region colorElement (Private)
173		private VertexElement colorElement;
174		#endregion
175
176		// Helper to make Draw2DLine much quicker if data does not change!
177
178		#region lastTimeLine2DPos1 (Private)
179		private Point[] lastTimeLine2DPos1 = new Point[InitialNumOfVertices / 2];
180		#endregion
181
182		#region lastTimeLine2DPos2 (Private)
183		private Point[] lastTimeLine2DPos2 = new Point[InitialNumOfVertices / 2];
184		#endregion
185
186		#region lastTimeLine2DColor (Private)
187		private Color[] lastTimeLine2DColor = new Color[InitialNumOfVertices / 2];
188		#endregion
189
190		// Helper to make DrawPolygon much quicker if data does not change!
191
192		#region lastTimePolygonPos1 (Private)
193		private Point[] lastTimePolygonPos1 = new Point[InitialNumOfVertices / 2];
194		#endregion
195
196		#region lastTimePolygonPos2 (Private)
197		private Point[] lastTimePolygonPos2 = new Point[InitialNumOfVertices / 2];
198		#endregion
199
200		#region lastTimePolygonPos3 (Private)
201		private Point[] lastTimePolygonPos3 = new Point[InitialNumOfVertices / 2];
202		#endregion
203
204		#region lastTimePolygonColor (Private)
205		private Color[] lastTimePolygonColor = new Color[InitialNumOfVertices / 2];
206		#endregion
207
208		// Helper to make Draw3DLine much quicker if data does not change!
209
210		#region lastTimeLine3DPos1 (Private)
211		private Vector[] lastTimeLine3DPos1 = new Vector[InitialNumOfVertices / 2];
212		#endregion
213
214		#region lastTimeLine3DPos2 (Private)
215		private Vector[] lastTimeLine3DPos2 = new Vector[InitialNumOfVertices / 2];
216		#endregion
217
218		#region lastTimeLine3DColor (Private)
219		private uint[] lastTimeLine3DColor = new uint[InitialNumOfVertices / 2];
220		#endregion
221
222		#endregion
223
224		#region Constructors
225		/// <summary>
226		/// Create draw manager as a child of MaterialManager because we handle
227		/// all rendering there (even line rendering, which is just meshes with
228		/// materials too).
229		/// </summary>
230		protected DrawManager()
231			: base("DrawManager", typeof(MaterialManager))
232		{
233			try
234			{
235				// The line material is an empty material with just the shader.
236				// Note: 2D and 3D Positions are different in vertex data, both use
237				// pretty much the same shader, but we need to 2 materials anyway.
238				line2DMaterial = new Material((Texture)null, Shader.Create(
239					// Note: This even works for VertexCompression because the Content.xml
240					// does save the original shader flags, not the modified ones in the
241					// case the VertexFormat of the shader uses vertex compression!
242					ShaderFeatureFlags.Basic |
243					ShaderFeatureFlags.UI2D |
244					ShaderFeatureFlags.NoTexturing |
245					ShaderFeatureFlags.ColoredVertices))
246				{
247					// Make sure that we always see the lines and aren't hidden by any
248					// "normal" geometry because the lines are usually used as debug
249					// information
250					DrawLayer = RenderLayer.OverlayText,
251				};
252
253				line2DMesh = Geometry.Create(new GeometryData("<Line2DMesh>",
254					InitialNumOfVertices, line2DMaterial.shader.VertexFormat,
255					0, false, true));
256
257				// Check for invalid element count.
258				if (line2DMesh.Data.Format.Elements.Length == 0)
259				{
260					// Set it to Position3D so it will launch warning later.
261					position2DElement = new VertexElement(VertexElementType.Position3D);
262				}
263				else
264				{
265					position2DElement = line2DMesh.Data.Format.Elements[0];
266				}
267
268				// Create the polygon2DMesh in the same way, just not as line data.
269				polygon2DMesh = Geometry.Create(new GeometryData("<Polygon2DMesh>",
270					InitialNumOfVertices, line2DMaterial.shader.VertexFormat,
271					0, false, false));
272
273				// Same for 3D, does not have the ShaderFeatureFlags.UI2D, everything
274				// else is the same.
275				line3DMaterial = new Material((Texture)null, Shader.Create(
276					ShaderFeatureFlags.Basic |
277					ShaderFeatureFlags.NoTexturing |
278					ShaderFeatureFlags.ColoredVertices))
279				{
280					// Make sure that we always see the lines and aren't hidden by any
281					// "normal" geometry because the lines are usually used as debug
282					// information
283					DrawLayer = RenderLayer.OverlayText,
284				};
285				line3DMesh = Geometry.Create(new GeometryData("<Line3DMesh>",
286					InitialNumOfVertices, line3DMaterial.shader.VertexFormat,
287					0, false, true));
288
289				// Check if valid vertex element is supplied.
290				if (line3DMesh.Data.Format.Elements.Length < 2)
291				{
292					// By setting it as Position2D warning will be fired down.
293					position3DElement = new VertexElement(VertexElementType.Position2D);
294				}
295				else
296				{
297					position3DElement = line3DMesh.Data.Format.Elements[0];
298					// Color is the same for 2D and 3D
299					colorElement = line2DMesh.Data.Format.Elements[1];
300				}
301
302				// Create the polygon3DMesh in the same way, just not as line data.
303				polygon3DMesh = Geometry.Create(new GeometryData("<Polygon3DMesh>",
304					InitialNumOfVertices, line3DMaterial.shader.VertexFormat,
305					2, false, false));
306
307			} // try
308			catch (Exception ex)
309			{
310				Log.Warning(
311					"Creation of the 'DrawManager' has failed because of reason: " + ex);
312			}
313		}
314		#endregion
315
316		#region Run (Public)
317		/// <summary>
318		/// Show, just render all lines that have been collected this frame.
319		/// </summary>
320		public override void Run()
321		{
322
323			// Render all 2d and 3d lines and polygon geometries separately.
324			if (polygon2DMesh.Data.NumberOfUsedVertices > 0)
325			{
326				line2DMaterial.Draw(polygon2DMesh);
327			}
328			if (line2DMesh.Data.NumberOfUsedVertices > 0)
329			{
330				line2DMaterial.Draw(line2DMesh);
331			}
332
333			// Did we have some 3D polygons or lines?
334			if (polygon3DMesh.Data.NumberOfUsedVertices > 0)
335			{
336				line3DMaterial.Draw(polygon3DMesh);
337			}
338			if (line3DMesh.Data.NumberOfUsedVertices > 0)
339			{
340				line3DMaterial.Draw(line3DMesh);
341			}
342		}
343		#endregion
344
345		#region Methods (Private)
346
347		#region Draw2DLine
348		/// <summary>
349		/// Draws a 2D line with a start and end point and a line color.
350		/// </summary>
351		/// <param name="linePoint1">Start of line</param>
352		/// <param name="linePoint2">End of line</param>
353		/// <param name="lineColor">The color of the line</param>
354		internal void Draw2DLine(ref Point linePoint1, ref Point linePoint2,
355			ref Color lineColor)
356		{
357			// Quickly check if this is the same line as last time, then skip!
358			GeometryData data = line2DMesh.Data;
359			int verticesIndex = data.NumberOfUsedVertices;
360			// This only increases the max bounds if we run out of space (rarely)
361			if (verticesIndex + 2 > data.MaxNumberOfVertices)
362			{
363				data.AddNumberOfUsedVertices(2);
364				int maxCalls = data.MaxNumberOfVertices / 2;
365				if (lastTimeLine2DPos1.Length < maxCalls)
366				{
367					lastTimeLine2DPos1 = new Point[maxCalls];
368					lastTimeLine2DPos2 = new Point[maxCalls];
369					lastTimeLine2DColor = new Color[maxCalls];
370				}
371			}
372			else
373			{
374				data.AddNumberOfUsedVerticesFast(2);
375			}
376
377			// Next check if the data is the same as last time
378			// Compare if data is different from last time (usually not)
379			int index = 0;
380			if (verticesIndex % 2 == 0)
381			{
382				index = verticesIndex / 2;
383			}
384			else
385			{
386				index = (verticesIndex / 2) - 1;
387			}
388
389			// This optimization makes rendering wrong, see bug: #3598
390			bool isSame = false;
391			//lastTimeLine2DPos1[index].X == linePoint1.X &&
392			//lastTimeLine2DPos1[index].Y == linePoint1.Y &&
393			//lastTimeLine2DPos2[index].X == linePoint2.X &&
394			//lastTimeLine2DPos2[index].Y == linePoint2.Y &&
395			//lastTimeLine2DColor[index].PackedRGBA == lineColor.PackedRGBA;
396
397			// Store data for next time compare (only needed if it was different)
398			// Note: Only continue running if application is not shutting down.
399			if (isSame == false &&
400				Application.IsShuttingDown == false)
401			{
402				lastTimeLine2DPos1[index] = linePoint1;
403				lastTimeLine2DPos2[index] = linePoint2;
404				lastTimeLine2DColor[index] = lineColor;
405			}
406			else
407			{
408				// If it is the same data as last time, skip adding!
409				return;
410			}
411
412			// Quick check if we are at the beginning of the stream, if not
413			// we need to modify the write position!
414			if (data.HasDataChanged == false &&
415					verticesIndex > 0)
416			{
417				data.writer.BaseStream.Seek(
418					verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
419			}
420
421			// Mark data as dirty, geometry needs to be updated.
422			data.HasDataChanged = true;
423
424			// Save vertices directly into the VertexData stream, which is only a
425			// little bit complicated if we want compressed vertices.
426			position2DElement.SaveData(data.writer, linePoint1);
427			colorElement.SaveData(data.writer, lineColor);
428			position2DElement.SaveData(data.writer, linePoint2);
429			colorElement.SaveData(data.writer, lineColor);
430		}
431		#endregion
432
433		#region DrawPolygon
434		/// <summary>
435		/// Draws a polygon.
436		/// </summary>
437		/// <param name="polygonPoint1">Polygon point 1</param>
438		/// <param name="polygonPoint2">Polygon point 2</param>
439		/// <param name="polygonPoint3">Polygon point 3</param>
440		/// <param name="polygonColor">The color of the polygon.</param>
441		internal void DrawPolygon(ref Point polygonPoint1, ref Point polygonPoint2,
442			ref Point polygonPoint3, ref Color polygonColor)
443		{
444			// Quickly check if this is the same line as last time, then skip!
445			GeometryData data = polygon2DMesh.Data;
446			int verticesIndex = data.NumberOfUsedVertices;
447			// This only increases the max bounds if we run out of space (rarely)
448			if (verticesIndex + 3 > data.MaxNumberOfVertices)
449			{
450				data.AddNumberOfUsedVertices(3);
451				int maxCalls = data.MaxNumberOfVertices / 3;
452				if (lastTimePolygonPos1.Length < maxCalls)
453				{
454					lastTimePolygonPos1 = new Point[maxCalls];
455					lastTimePolygonPos2 = new Point[maxCalls];
456					lastTimePolygonPos3 = new Point[maxCalls];
457					lastTimePolygonColor = new Color[maxCalls];
458				}
459			}
460			else
461			{
462				data.AddNumberOfUsedVerticesFast(3);
463			}
464
465			// Next check if the data is the same as last time
466			// Compare if data is different from last time (usually not)
467			int index = verticesIndex / 3;
468			bool isSame = false;
469
470			// This optimization makes rendering wrong, see bug: #3598
471
472			//lastTimePolygonPos1[index].X == polygonPoint1.X &&
473			//lastTimePolygonPos1[index].Y == polygonPoint1.Y &&
474			//lastTimePolygonPos2[index].X == polygonPoint2.X &&
475			//lastTimePolygonPos2[index].Y == polygonPoint2.Y &&
476			//lastTimePolygonPos3[index].X == polygonPoint3.X &&
477			//lastTimePolygonPos3[index].Y == polygonPoint3.Y &&
478			//lastTimePolygonColor[index].PackedRGBA == polygonColor.PackedRGBA;
479
480			// Store data for next time compare (only needed if it was different)
481			if (isSame == false)
482			{
483				lastTimePolygonPos1[index] = polygonPoint1;
484				lastTimePolygonPos2[index] = polygonPoint2;
485				lastTimePolygonPos3[index] = polygonPoint3;
486				lastTimePolygonColor[index] = polygonColor;
487			}
488			else
489			{
490				// If it is the same data as last time, skip adding!
491				return;
492			}
493
494			// Quick check if we are at the beginning of the stream, if not
495			// we need to modify the write position!
496			if (data.HasDataChanged == false &&
497					verticesIndex > 0)
498			{
499				data.writer.BaseStream.Seek(
500					verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
501			}
502
503			// Mark data as dirty, geometry needs to be updated.
504			data.HasDataChanged = true;
505
506			// Save vertices directly into the VertexData stream, which is only a
507			// little bit complicated if we want compressed vertices.
508			position2DElement.SaveData(data.writer, polygonPoint1);
509			colorElement.SaveData(data.writer, polygonColor);
510			position2DElement.SaveData(data.writer, polygonPoint2);
511			colorElement.SaveData(data.writer, polygonColor);
512			position2DElement.SaveData(data.writer, polygonPoint3);
513			colorElement.SaveData(data.writer, polygonColor);
514		}
515		#endregion
516
517		#region Draw3DLine
518		/// <summary>
519		/// Draws a 3D line with a start and end vector and a line color.
520		/// </summary>
521		/// <param name="lineVector1">Start of line</param>
522		/// <param name="lineVector2">End of line</param>
523		/// <param name="lineColor">The color of the line.</param>
524		internal void Draw3DLine(Vector lineVector1, Vector lineVector2,
525			Color lineColor)
526		{
527			Draw3DLine(ref lineVector1, ref lineVector2, ref lineColor);
528		}
529
530		/// <summary>
531		/// Draws a 3D line with a start and end vector and a line color.
532		/// </summary>
533		/// <param name="lineVector1">Start of line</param>
534		/// <param name="lineVector2">End of line</param>
535		/// <param name="lineColor">The color of the line.</param>
536		internal void Draw3DLine(ref Vector lineVector1, ref Vector lineVector2,
537			ref Color lineColor)
538		{
539			// Quickly check if this is the same line as last time, then skip!
540			GeometryData data = line3DMesh.Data;
541			int verticesIndex = data.NumberOfUsedVertices;
542			// This only increases the max bounds if we run out of space (rarely)
543			if (verticesIndex + 2 > data.MaxNumberOfVertices)
544			{
545				data.AddNumberOfUsedVertices(2);
546				int maxCalls = data.MaxNumberOfVertices / 2;
547				if (lastTimeLine3DPos1.Length < maxCalls)
548				{
549					lastTimeLine3DPos1 = new Vector[maxCalls];
550					lastTimeLine3DPos2 = new Vector[maxCalls];
551					lastTimeLine3DColor = new uint[maxCalls];
552				}
553			}
554			else
555			{
556				data.AddNumberOfUsedVerticesFast(2);
557			}
558
559			// Next check if the data is the same as last time
560			// Compare if data is different from last time (usually not)
561			int index = verticesIndex / 2;
562
563			bool isSame =
564				lastTimeLine3DPos1[index].X == lineVector1.X &&
565				lastTimeLine3DPos1[index].Y == lineVector1.Y &&
566				lastTimeLine3DPos1[index].Z == lineVector1.Z &&
567				lastTimeLine3DPos2[index].X == lineVector2.X &&
568				lastTimeLine3DPos2[index].Y == lineVector2.Y &&
569				lastTimeLine3DPos2[index].Z == lineVector2.Z &&
570				lastTimeLine3DColor[index] == lineColor.PackedRGBA;
571
572			// Store data for next time compare (only needed if it was different)
573			if (isSame == false)
574			{
575				lastTimeLine3DPos1[index] = lineVector1;
576				lastTimeLine3DPos2[index] = lineVector2;
577				lastTimeLine3DColor[index] = lineColor.PackedRGBA;
578			}
579			else
580			{
581				// If it is the same data as last time, skip adding!
582				return;
583			}
584
585			// Quick check if we are at the beginning of the stream, if not
586			// we need to modify the write position!
587			if (data.HasDataChanged == false &&
588					verticesIndex > 0)
589			{
590				data.writer.BaseStream.Seek(
591					verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
592			}
593
594			// Mark data as dirty, geometry needs to be updated.
595			data.HasDataChanged = true;
596
597			// Save vertices directly into the VertexData stream, which is only a
598			// little bit complicated if we want compressed vertices.
599			position3DElement.SaveData(data.writer, lineVector1);
600			colorElement.SaveData(data.writer, lineColor);
601			position3DElement.SaveData(data.writer, lineVector2);
602			colorElement.SaveData(data.writer, lineColor);
603
604		}
605		#endregion
606
607		#region DrawBox
608		/// <summary>
609		/// Draws a 3D box with a start vector, a size vector and a color.
610		/// </summary>
611		/// <param name="minimum">Minimum position of the box.</param>
612		/// <param name="size">Size of the box.</param>
613		/// <param name="fillColor">The color of the filled box.</param>
614		internal void DrawBox(ref Vector minimum, ref Vector size,
615			ref Color fillColor)
616		{
617			const int vertexCount = 8;
618			const int numberOfIndices = 36;
619			// Quickly check if this is the same line as last time, then skip!
620			GeometryData data = polygon3DMesh.Data;
621			int verticesIndex = data.NumberOfUsedVertices;
622			int indicesIndex = data.NumberOfUsedIndices;
623			bool needVertexResize =
624				verticesIndex + vertexCount > data.MaxNumberOfVertices;
625			bool needIndexResize =
626				indicesIndex + numberOfIndices > data.Indices.Length;
627
628			// This only increases the max bounds if we run out of space (rarely)
629			if (needVertexResize)
630			{
631				data.AddNumberOfUsedVertices(vertexCount);
632			}
633			if (needIndexResize)
634			{
635				data.AddNumberOfUsedIndices(numberOfIndices);
636			}
637			if (needVertexResize == false &&
638					needIndexResize == false)
639			{
640				data.AddNumberOfUsedVerticesAndIndicesFast(vertexCount,
641					numberOfIndices);
642			}
643			else if (needVertexResize == false &&
644							 needIndexResize)
645			{
646				data.AddNumberOfUsedVerticesFast(vertexCount);
647			}
648
649			// Quick check if we are at the beginning of the stream, if not
650			// we need to modify the write position!
651			if (data.HasDataChanged == false &&
652					verticesIndex > 0)
653			{
654				data.writer.BaseStream.Seek(
655					verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
656			}
657
658			// Mark data as dirty, geometry needs to be updated.
659			data.HasDataChanged = true;
660
661			Vector corner000 = new Vector(minimum.X, minimum.Y, minimum.Z);
662			Vector corner100 = new Vector(minimum.X + size.X, minimum.Y, minimum.Z);
663			Vector corner010 = new Vector(minimum.X, minimum.Y + size.Y, minimum.Z);
664			Vector corner110 = new Vector(minimum.X + size.X, minimum.Y + size.Y,
665				minimum.Z);
666
667			Vector corner001 = new Vector(minimum.X, minimum.Y, minimum.Z + size.Z);
668			Vector corner101 = new Vector(minimum.X + size.X, minimum.Y,
669				minimum.Z + size.Z);
670			Vector corner011 = new Vector(minimum.X, minimum.Y + size.Y,
671				minimum.Z + size.Z);
672			Vector corner111 = new Vector(minimum.X + size.X, minimum.Y + size.Y,
673				minimum.Z + size.Z);
674
675			// Save vertices directly into the VertexData stream, which is only a
676			// little bit complicated if we want compressed vertices.
677
678			position3DElement.SaveData(data.writer, corner000);
679			colorElement.SaveData(data.writer, fillColor);
680			position3DElement.SaveData(data.writer, corner100);
681			colorElement.SaveData(data.writer, fillColor);
682			position3DElement.SaveData(data.writer, corner010);
683			colorElement.SaveData(data.writer, fillColor);
684			position3DElement.SaveData(data.writer, corner110);
685			colorElement.SaveData(data.writer, fillColor);
686
687			position3DElement.SaveData(data.writer, corner001);
688			colorElement.SaveData(data.writer, fillColor);
689			position3DElement.SaveData(data.writer, corner101);
690			colorElement.SaveData(data.writer, fillColor);
691			position3DElement.SaveData(data.writer, corner011);
692			colorElement.SaveData(data.writer, fillColor);
693			position3DElement.SaveData(data.writer, corner111);
694			colorElement.SaveData(data.writer, fillColor);
695
696			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 0);
697			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
698			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 4);
699
700			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 0);
701			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
702			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
703
704			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
705			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 7);
706			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
707
708			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
709			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 3);
710			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 7);
711
712			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 3);
713			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
714			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 7);
715
716			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 3);
717			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
718			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
719
720			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
721			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 4);
722			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
723
724			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
725			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 0);
726			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 4);
727
728			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
729			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 7);
730			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
731
732			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
733			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
734			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 4);
735
736			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
737			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 0);
738			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
739
740			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
741			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
742			data.Indices[indicesIndex++] = (ushort)(verticesIndex + 3);
743		}
744		#endregion
745
746		#region DrawSphere
747		/// <summary>
748		/// Draws a 3D sphere with a center vector, a radius and a color.
749		/// </summary>
750		/// <param name="minimum">Minimum position of the box.</param>
751		/// <param name="size">Size of the box.</param>
752		/// <param name="fillColor">The color of the filled box.</param>
753		internal void DrawSphere(ref Vector center, float radius,
754			ref Color fillColor)
755		{
756			// Set up the subdivision in which we build the geometry of the sphere.
757			// The tessellation indicates in how many segments the sphere is
758			// interpolated, smaller spheres have less tessellation, bigger spheres
759			// use more tessellation, but we keep it between 4 and 32.
760			int tessellation = (int)(MathHelper.Sqrt(radius) * 6.0f);
761			tessellation = MathHelper.Clamp(tessellation, 6, 32);
762			// Make multiple of 3 for better fitting texturing
763			tessellation = (tessellation / 3) * 3;
764			int verticalSegments = tessellation;
765			// Add one horizontal segment to fit around a circle, good for UVs
766			int horizontalSegments = tessellation * 2 + 1;
767
768			int vertexCount = verticalSegments * horizontalSegments;
769			int numberOfIndices = 6 * (verticalSegments - 1) * horizontalSegments;
770			// Quickly check if this is the same line as last time, then skip!
771			GeometryData data = polygon3DMesh.Data;
772			int verticesIndex = data.NumberOfUsedVertices;
773			int indicesIndex = data.NumberOfUsedIndices;
774			bool needVertexResize =
775				verticesIndex + vertexCount > data.MaxNumberOfVertices;
776			bool needIndexResize =
777				indicesIndex + numberOfIndices > data.Indices.Length;
778
779			// This only increases the max bounds if we run out of space (rarely)
780			if (needVertexResize)
781			{
782				data.AddNumberOfUsedVertices(vertexCount);
783			}
784			if (needIndexResize)
785			{
786				data.AddNumberOfUsedIndices(numberOfIndices);
787			}
788			if (needVertexResize == false &&
789					needIndexResize == false)
790			{
791				data.AddNumberOfUsedVerticesAndIndicesFast(vertexCount,
792					numberOfIndices);
793			}
794			else if (needVertexResize == false &&
795							 needIndexResize)
796			{
797				data.AddNumberOfUsedVerticesFast(vertexCount);
798			}
799
800			// Quick check if we are at the beginning of the stream, if not
801			// we need to modify the write position!
802			if (data.HasDataChanged == false &&
803					verticesIndex > 0)
804			{
805				data.writer.BaseStream.Seek(
806					verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
807			}
808
809			// Mark data as dirty, geometry needs to be updated.
810			data.HasDataChanged = true;
811
812			for (int index = 0; index < verticalSegments; index++)
813			{
814				// Lets begin with the latitude and divide each segment. As we are
815				// using circular surface, we will split each position along the
816				// vertical axis with the cosine. That is every position in the
817				// vertical segment creates a ring with a maximum width stated by the
818				// cosine (at the top width=0, in the medium reaches the maximum
819				// width = 1, and at the bottom width=0).
820				float latitude = index * 180f / (verticalSegments - 1) - 90f;
821				float dy = MathHelper.Sin(latitude);
822				float dxz = MathHelper.Cos(latitude);
823
824				// Create a single ring of vertices at this latitude.
825				for (int j = 0; j < horizontalSegments; j++)
826				{
827					// Next step is tessellation along horizontal axis in which we just 
828					// simple indicates the position of each vertex in the ring with the
829					// previously established width along the surface of the sphere
830					float longitude = j * 360f / (horizontalSegments - 1);
831
832					float dx = MathHelper.Cos(longitude) * dxz;
833					float dz = MathHelper.Sin(longitude) * dxz;
834
835					// finally we got the correct position 
836					Vector normal = new Vector(dx, dy, dz);
837
838					position3DElement.SaveData(data.writer, center + (normal * radius));
839					colorElement.SaveData(data.writer, fillColor);
840				}
841			}
842
843			// Create a fan connecting the bottom vertex to the bottom latitude ring
844			// and finally set up the indices connecting each vertex.
845			// Fill the sphere body with triangles joining each pair of rings.
846			int num = indicesIndex;
847			for (int index = 0; index < verticalSegments - 1; index++)
848			{
849				for (int j = 0; j < horizontalSegments; j++)
850				{
851					int nextI = (index + 1);
852					int nextJ = (j + 1) % horizontalSegments;
853
854					data.Indices[num++] = (ushort)(verticesIndex +
855																				 (index * horizontalSegments + j));
856					data.Indices[num++] = (ushort)(verticesIndex +
857																				 (nextI * horizontalSegments + j));
858					data.Indices[num++] = (ushort)(verticesIndex +
859																				 (index * horizontalSegments + nextJ));
860
861					data.Indices[num++] = (ushort)(verticesIndex +
862																				 (index * horizontalSegments + nextJ));
863					data.Indices[num++] = (ushort)(verticesIndex +
864																				 (nextI * horizontalSegments + j));
865					data.Indices[num++] = (ushort)(verticesIndex +
866																				 (nextI * horizontalSegments + nextJ));
867				}
868			}
869		}
870		#endregion
871
872		#endregion
873	}
874}