/Rendering/Basics/Drawing/DrawManager.cs
# · C# · 874 lines · 559 code · 108 blank · 207 comment · 65 complexity · f5344a4443585169a6c6976b75bc31f2 MD5 · raw file
- using System;
- using System.IO;
- using Delta.ContentSystem.Rendering;
- using Delta.Engine;
- using Delta.Engine.Dynamic;
- using Delta.Graphics;
- using Delta.Graphics.Basics;
- using Delta.Rendering.Basics.Materials;
- using Delta.Utilities;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Graphics;
- using Delta.Utilities.Graphics.ShaderFeatures;
- using Delta.Utilities.Helpers;
- using Delta.Rendering.Enums;
-
- namespace Delta.Rendering.Basics.Drawing
- {
- /// <summary>
- /// The DrawManager is used by all of the classes in this namespace to do
- /// all the line, box, sphere, circle, grid, etc. drawing of all kind of
- /// shapes for 2D and 3D drawing. Everything related with primitive drawing
- /// will be uniquely handled in here. This class is used as a singleton.
- /// Warning: This class is not thread safe, only have one thread draw stuff!
- /// </summary>
- public class DrawManager : DynamicModule
- {
- #region Constants
- /// <summary>
- /// Start with max. 120 lines initially (or 80 polygons). If we reach that
- /// limit we will increase the Mesh VertexData by 120 more lines.
- /// </summary>
- private const int InitialNumOfVertices = 240;
- #endregion
-
- #region MakeScreenshot (Static)
- /// <summary>
- /// Make a screenshot of the current screen.
- /// </summary>
- public static string MakeScreenshot()
- {
- return ScreenshotCapturer.MakeScreenshot();
- }
- #endregion
-
- #region Internal
-
- #region Instance (Internal)
- /// <summary>
- /// Instance for this Draw manager (handled only here privately)
- /// </summary>
- internal static DrawManager Instance
- {
- get
- {
- if (instance == null)
- {
- // Needs to be created via factory to make sure we only do this once
- instance = Factory.Create<DrawManager>();
- }
-
- return instance;
- } // get
- }
- #endregion
-
- #endregion
-
- #region Private
-
- #region instance (Private)
- /// <summary>
- /// Private instance
- /// </summary>
- private static DrawManager instance;
- #endregion
-
- #region screenshotCapturer (Private)
- /// <summary>
- /// Current screenshot capturer for the application
- /// </summary>
- private static ScreenshotCapturer screenshotCapturer;
- #endregion
-
- #region ScreenshotCapturer (Private)
- /// <summary>
- /// Screenshot capturer
- /// </summary>
- private static ScreenshotCapturer ScreenshotCapturer
- {
- get
- {
- if (screenshotCapturer == null)
- {
- screenshotCapturer = Factory.Create<ScreenshotCapturer>();
-
- if (screenshotCapturer == null)
- {
- Log.Warning("No 'ScreenshotCapturer' module was found," +
- " unable to start the screenshot capturer system. Please" +
- " provide a Settings module.");
- }
- } // if
-
- return screenshotCapturer;
- } // get
- }
- #endregion
-
- #region line2DMaterial (Private)
- /// <summary>
- /// Line material for rendering all lines (2D and 3D), contains the
- /// LineShader responsible for drawing everything.
- /// </summary>
- private readonly BaseMaterial line2DMaterial;
- #endregion
-
- #region line3DMaterial (Private)
- /// <summary>
- /// Line material for rendering all lines (2D and 3D), contains the
- /// LineShader responsible for drawing everything.
- /// </summary>
- private readonly BaseMaterial line3DMaterial;
- #endregion
-
- #region line2DMesh (Private)
- /// <summary>
- /// Line mesh for 2D lines, contains 2D position and color vertex data.
- /// Uncompressed this are just 12 bytes (8 for position, 4 for color),
- /// compressed this goes down to 8 bytes (4 for position, 4 for color).
- /// </summary>
- private readonly Geometry line2DMesh;
- #endregion
-
- #region line3DMesh (Private)
- /// <summary>
- /// Line mesh for 3D lines, contains 3D position and color vertex data.
- /// Uncompressed this are just 16 bytes (12 for position, 4 for color),
- /// compressed this goes down to 10 bytes (6 for position, 4 for color).
- /// </summary>
- private readonly Geometry line3DMesh;
- #endregion
-
- #region polygon2DMesh (Private)
- /// <summary>
- /// Polygon mesh for 2D shapes, contains 3 2D position and colors for
- /// each polygon. Same vertex data format is used as for line2DMesh!
- /// </summary>
- private readonly Geometry polygon2DMesh;
- #endregion
-
- #region polygon3DMesh (Private)
- /// <summary>
- /// Polygon mesh for 3D shapes, contains 3 3D position and colors for
- /// each polygon. Same vertex data format is used as for line3DMesh!
- /// </summary>
- private readonly Geometry polygon3DMesh;
- #endregion
-
- // Helpers for quickly saving out element data.
-
- #region position2DElement (Private)
- private VertexElement position2DElement;
- #endregion
-
- #region position3DElement (Private)
- private VertexElement position3DElement;
- #endregion
-
- // Note: Color is the same, it does not matter how much data is stored
- // in position 2d or 3d, if it is compressed or whatnot.
-
- #region colorElement (Private)
- private VertexElement colorElement;
- #endregion
-
- // Helper to make Draw2DLine much quicker if data does not change!
-
- #region lastTimeLine2DPos1 (Private)
- private Point[] lastTimeLine2DPos1 = new Point[InitialNumOfVertices / 2];
- #endregion
-
- #region lastTimeLine2DPos2 (Private)
- private Point[] lastTimeLine2DPos2 = new Point[InitialNumOfVertices / 2];
- #endregion
-
- #region lastTimeLine2DColor (Private)
- private Color[] lastTimeLine2DColor = new Color[InitialNumOfVertices / 2];
- #endregion
-
- // Helper to make DrawPolygon much quicker if data does not change!
-
- #region lastTimePolygonPos1 (Private)
- private Point[] lastTimePolygonPos1 = new Point[InitialNumOfVertices / 2];
- #endregion
-
- #region lastTimePolygonPos2 (Private)
- private Point[] lastTimePolygonPos2 = new Point[InitialNumOfVertices / 2];
- #endregion
-
- #region lastTimePolygonPos3 (Private)
- private Point[] lastTimePolygonPos3 = new Point[InitialNumOfVertices / 2];
- #endregion
-
- #region lastTimePolygonColor (Private)
- private Color[] lastTimePolygonColor = new Color[InitialNumOfVertices / 2];
- #endregion
-
- // Helper to make Draw3DLine much quicker if data does not change!
-
- #region lastTimeLine3DPos1 (Private)
- private Vector[] lastTimeLine3DPos1 = new Vector[InitialNumOfVertices / 2];
- #endregion
-
- #region lastTimeLine3DPos2 (Private)
- private Vector[] lastTimeLine3DPos2 = new Vector[InitialNumOfVertices / 2];
- #endregion
-
- #region lastTimeLine3DColor (Private)
- private uint[] lastTimeLine3DColor = new uint[InitialNumOfVertices / 2];
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create draw manager as a child of MaterialManager because we handle
- /// all rendering there (even line rendering, which is just meshes with
- /// materials too).
- /// </summary>
- protected DrawManager()
- : base("DrawManager", typeof(MaterialManager))
- {
- try
- {
- // The line material is an empty material with just the shader.
- // Note: 2D and 3D Positions are different in vertex data, both use
- // pretty much the same shader, but we need to 2 materials anyway.
- line2DMaterial = new Material((Texture)null, Shader.Create(
- // Note: This even works for VertexCompression because the Content.xml
- // does save the original shader flags, not the modified ones in the
- // case the VertexFormat of the shader uses vertex compression!
- ShaderFeatureFlags.Basic |
- ShaderFeatureFlags.UI2D |
- ShaderFeatureFlags.NoTexturing |
- ShaderFeatureFlags.ColoredVertices))
- {
- // Make sure that we always see the lines and aren't hidden by any
- // "normal" geometry because the lines are usually used as debug
- // information
- DrawLayer = RenderLayer.OverlayText,
- };
-
- line2DMesh = Geometry.Create(new GeometryData("<Line2DMesh>",
- InitialNumOfVertices, line2DMaterial.shader.VertexFormat,
- 0, false, true));
-
- // Check for invalid element count.
- if (line2DMesh.Data.Format.Elements.Length == 0)
- {
- // Set it to Position3D so it will launch warning later.
- position2DElement = new VertexElement(VertexElementType.Position3D);
- }
- else
- {
- position2DElement = line2DMesh.Data.Format.Elements[0];
- }
-
- // Create the polygon2DMesh in the same way, just not as line data.
- polygon2DMesh = Geometry.Create(new GeometryData("<Polygon2DMesh>",
- InitialNumOfVertices, line2DMaterial.shader.VertexFormat,
- 0, false, false));
-
- // Same for 3D, does not have the ShaderFeatureFlags.UI2D, everything
- // else is the same.
- line3DMaterial = new Material((Texture)null, Shader.Create(
- ShaderFeatureFlags.Basic |
- ShaderFeatureFlags.NoTexturing |
- ShaderFeatureFlags.ColoredVertices))
- {
- // Make sure that we always see the lines and aren't hidden by any
- // "normal" geometry because the lines are usually used as debug
- // information
- DrawLayer = RenderLayer.OverlayText,
- };
- line3DMesh = Geometry.Create(new GeometryData("<Line3DMesh>",
- InitialNumOfVertices, line3DMaterial.shader.VertexFormat,
- 0, false, true));
-
- // Check if valid vertex element is supplied.
- if (line3DMesh.Data.Format.Elements.Length < 2)
- {
- // By setting it as Position2D warning will be fired down.
- position3DElement = new VertexElement(VertexElementType.Position2D);
- }
- else
- {
- position3DElement = line3DMesh.Data.Format.Elements[0];
- // Color is the same for 2D and 3D
- colorElement = line2DMesh.Data.Format.Elements[1];
- }
-
- // Create the polygon3DMesh in the same way, just not as line data.
- polygon3DMesh = Geometry.Create(new GeometryData("<Polygon3DMesh>",
- InitialNumOfVertices, line3DMaterial.shader.VertexFormat,
- 2, false, false));
-
- } // try
- catch (Exception ex)
- {
- Log.Warning(
- "Creation of the 'DrawManager' has failed because of reason: " + ex);
- }
- }
- #endregion
-
- #region Run (Public)
- /// <summary>
- /// Show, just render all lines that have been collected this frame.
- /// </summary>
- public override void Run()
- {
-
- // Render all 2d and 3d lines and polygon geometries separately.
- if (polygon2DMesh.Data.NumberOfUsedVertices > 0)
- {
- line2DMaterial.Draw(polygon2DMesh);
- }
- if (line2DMesh.Data.NumberOfUsedVertices > 0)
- {
- line2DMaterial.Draw(line2DMesh);
- }
-
- // Did we have some 3D polygons or lines?
- if (polygon3DMesh.Data.NumberOfUsedVertices > 0)
- {
- line3DMaterial.Draw(polygon3DMesh);
- }
- if (line3DMesh.Data.NumberOfUsedVertices > 0)
- {
- line3DMaterial.Draw(line3DMesh);
- }
- }
- #endregion
-
- #region Methods (Private)
-
- #region Draw2DLine
- /// <summary>
- /// Draws a 2D line with a start and end point and a line color.
- /// </summary>
- /// <param name="linePoint1">Start of line</param>
- /// <param name="linePoint2">End of line</param>
- /// <param name="lineColor">The color of the line</param>
- internal void Draw2DLine(ref Point linePoint1, ref Point linePoint2,
- ref Color lineColor)
- {
- // Quickly check if this is the same line as last time, then skip!
- GeometryData data = line2DMesh.Data;
- int verticesIndex = data.NumberOfUsedVertices;
- // This only increases the max bounds if we run out of space (rarely)
- if (verticesIndex + 2 > data.MaxNumberOfVertices)
- {
- data.AddNumberOfUsedVertices(2);
- int maxCalls = data.MaxNumberOfVertices / 2;
- if (lastTimeLine2DPos1.Length < maxCalls)
- {
- lastTimeLine2DPos1 = new Point[maxCalls];
- lastTimeLine2DPos2 = new Point[maxCalls];
- lastTimeLine2DColor = new Color[maxCalls];
- }
- }
- else
- {
- data.AddNumberOfUsedVerticesFast(2);
- }
-
- // Next check if the data is the same as last time
- // Compare if data is different from last time (usually not)
- int index = 0;
- if (verticesIndex % 2 == 0)
- {
- index = verticesIndex / 2;
- }
- else
- {
- index = (verticesIndex / 2) - 1;
- }
-
- // This optimization makes rendering wrong, see bug: #3598
- bool isSame = false;
- //lastTimeLine2DPos1[index].X == linePoint1.X &&
- //lastTimeLine2DPos1[index].Y == linePoint1.Y &&
- //lastTimeLine2DPos2[index].X == linePoint2.X &&
- //lastTimeLine2DPos2[index].Y == linePoint2.Y &&
- //lastTimeLine2DColor[index].PackedRGBA == lineColor.PackedRGBA;
-
- // Store data for next time compare (only needed if it was different)
- // Note: Only continue running if application is not shutting down.
- if (isSame == false &&
- Application.IsShuttingDown == false)
- {
- lastTimeLine2DPos1[index] = linePoint1;
- lastTimeLine2DPos2[index] = linePoint2;
- lastTimeLine2DColor[index] = lineColor;
- }
- else
- {
- // If it is the same data as last time, skip adding!
- return;
- }
-
- // Quick check if we are at the beginning of the stream, if not
- // we need to modify the write position!
- if (data.HasDataChanged == false &&
- verticesIndex > 0)
- {
- data.writer.BaseStream.Seek(
- verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
- }
-
- // Mark data as dirty, geometry needs to be updated.
- data.HasDataChanged = true;
-
- // Save vertices directly into the VertexData stream, which is only a
- // little bit complicated if we want compressed vertices.
- position2DElement.SaveData(data.writer, linePoint1);
- colorElement.SaveData(data.writer, lineColor);
- position2DElement.SaveData(data.writer, linePoint2);
- colorElement.SaveData(data.writer, lineColor);
- }
- #endregion
-
- #region DrawPolygon
- /// <summary>
- /// Draws a polygon.
- /// </summary>
- /// <param name="polygonPoint1">Polygon point 1</param>
- /// <param name="polygonPoint2">Polygon point 2</param>
- /// <param name="polygonPoint3">Polygon point 3</param>
- /// <param name="polygonColor">The color of the polygon.</param>
- internal void DrawPolygon(ref Point polygonPoint1, ref Point polygonPoint2,
- ref Point polygonPoint3, ref Color polygonColor)
- {
- // Quickly check if this is the same line as last time, then skip!
- GeometryData data = polygon2DMesh.Data;
- int verticesIndex = data.NumberOfUsedVertices;
- // This only increases the max bounds if we run out of space (rarely)
- if (verticesIndex + 3 > data.MaxNumberOfVertices)
- {
- data.AddNumberOfUsedVertices(3);
- int maxCalls = data.MaxNumberOfVertices / 3;
- if (lastTimePolygonPos1.Length < maxCalls)
- {
- lastTimePolygonPos1 = new Point[maxCalls];
- lastTimePolygonPos2 = new Point[maxCalls];
- lastTimePolygonPos3 = new Point[maxCalls];
- lastTimePolygonColor = new Color[maxCalls];
- }
- }
- else
- {
- data.AddNumberOfUsedVerticesFast(3);
- }
-
- // Next check if the data is the same as last time
- // Compare if data is different from last time (usually not)
- int index = verticesIndex / 3;
- bool isSame = false;
-
- // This optimization makes rendering wrong, see bug: #3598
-
- //lastTimePolygonPos1[index].X == polygonPoint1.X &&
- //lastTimePolygonPos1[index].Y == polygonPoint1.Y &&
- //lastTimePolygonPos2[index].X == polygonPoint2.X &&
- //lastTimePolygonPos2[index].Y == polygonPoint2.Y &&
- //lastTimePolygonPos3[index].X == polygonPoint3.X &&
- //lastTimePolygonPos3[index].Y == polygonPoint3.Y &&
- //lastTimePolygonColor[index].PackedRGBA == polygonColor.PackedRGBA;
-
- // Store data for next time compare (only needed if it was different)
- if (isSame == false)
- {
- lastTimePolygonPos1[index] = polygonPoint1;
- lastTimePolygonPos2[index] = polygonPoint2;
- lastTimePolygonPos3[index] = polygonPoint3;
- lastTimePolygonColor[index] = polygonColor;
- }
- else
- {
- // If it is the same data as last time, skip adding!
- return;
- }
-
- // Quick check if we are at the beginning of the stream, if not
- // we need to modify the write position!
- if (data.HasDataChanged == false &&
- verticesIndex > 0)
- {
- data.writer.BaseStream.Seek(
- verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
- }
-
- // Mark data as dirty, geometry needs to be updated.
- data.HasDataChanged = true;
-
- // Save vertices directly into the VertexData stream, which is only a
- // little bit complicated if we want compressed vertices.
- position2DElement.SaveData(data.writer, polygonPoint1);
- colorElement.SaveData(data.writer, polygonColor);
- position2DElement.SaveData(data.writer, polygonPoint2);
- colorElement.SaveData(data.writer, polygonColor);
- position2DElement.SaveData(data.writer, polygonPoint3);
- colorElement.SaveData(data.writer, polygonColor);
- }
- #endregion
-
- #region Draw3DLine
- /// <summary>
- /// Draws a 3D line with a start and end vector and a line color.
- /// </summary>
- /// <param name="lineVector1">Start of line</param>
- /// <param name="lineVector2">End of line</param>
- /// <param name="lineColor">The color of the line.</param>
- internal void Draw3DLine(Vector lineVector1, Vector lineVector2,
- Color lineColor)
- {
- Draw3DLine(ref lineVector1, ref lineVector2, ref lineColor);
- }
-
- /// <summary>
- /// Draws a 3D line with a start and end vector and a line color.
- /// </summary>
- /// <param name="lineVector1">Start of line</param>
- /// <param name="lineVector2">End of line</param>
- /// <param name="lineColor">The color of the line.</param>
- internal void Draw3DLine(ref Vector lineVector1, ref Vector lineVector2,
- ref Color lineColor)
- {
- // Quickly check if this is the same line as last time, then skip!
- GeometryData data = line3DMesh.Data;
- int verticesIndex = data.NumberOfUsedVertices;
- // This only increases the max bounds if we run out of space (rarely)
- if (verticesIndex + 2 > data.MaxNumberOfVertices)
- {
- data.AddNumberOfUsedVertices(2);
- int maxCalls = data.MaxNumberOfVertices / 2;
- if (lastTimeLine3DPos1.Length < maxCalls)
- {
- lastTimeLine3DPos1 = new Vector[maxCalls];
- lastTimeLine3DPos2 = new Vector[maxCalls];
- lastTimeLine3DColor = new uint[maxCalls];
- }
- }
- else
- {
- data.AddNumberOfUsedVerticesFast(2);
- }
-
- // Next check if the data is the same as last time
- // Compare if data is different from last time (usually not)
- int index = verticesIndex / 2;
-
- bool isSame =
- lastTimeLine3DPos1[index].X == lineVector1.X &&
- lastTimeLine3DPos1[index].Y == lineVector1.Y &&
- lastTimeLine3DPos1[index].Z == lineVector1.Z &&
- lastTimeLine3DPos2[index].X == lineVector2.X &&
- lastTimeLine3DPos2[index].Y == lineVector2.Y &&
- lastTimeLine3DPos2[index].Z == lineVector2.Z &&
- lastTimeLine3DColor[index] == lineColor.PackedRGBA;
-
- // Store data for next time compare (only needed if it was different)
- if (isSame == false)
- {
- lastTimeLine3DPos1[index] = lineVector1;
- lastTimeLine3DPos2[index] = lineVector2;
- lastTimeLine3DColor[index] = lineColor.PackedRGBA;
- }
- else
- {
- // If it is the same data as last time, skip adding!
- return;
- }
-
- // Quick check if we are at the beginning of the stream, if not
- // we need to modify the write position!
- if (data.HasDataChanged == false &&
- verticesIndex > 0)
- {
- data.writer.BaseStream.Seek(
- verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
- }
-
- // Mark data as dirty, geometry needs to be updated.
- data.HasDataChanged = true;
-
- // Save vertices directly into the VertexData stream, which is only a
- // little bit complicated if we want compressed vertices.
- position3DElement.SaveData(data.writer, lineVector1);
- colorElement.SaveData(data.writer, lineColor);
- position3DElement.SaveData(data.writer, lineVector2);
- colorElement.SaveData(data.writer, lineColor);
-
- }
- #endregion
-
- #region DrawBox
- /// <summary>
- /// Draws a 3D box with a start vector, a size vector and a color.
- /// </summary>
- /// <param name="minimum">Minimum position of the box.</param>
- /// <param name="size">Size of the box.</param>
- /// <param name="fillColor">The color of the filled box.</param>
- internal void DrawBox(ref Vector minimum, ref Vector size,
- ref Color fillColor)
- {
- const int vertexCount = 8;
- const int numberOfIndices = 36;
- // Quickly check if this is the same line as last time, then skip!
- GeometryData data = polygon3DMesh.Data;
- int verticesIndex = data.NumberOfUsedVertices;
- int indicesIndex = data.NumberOfUsedIndices;
- bool needVertexResize =
- verticesIndex + vertexCount > data.MaxNumberOfVertices;
- bool needIndexResize =
- indicesIndex + numberOfIndices > data.Indices.Length;
-
- // This only increases the max bounds if we run out of space (rarely)
- if (needVertexResize)
- {
- data.AddNumberOfUsedVertices(vertexCount);
- }
- if (needIndexResize)
- {
- data.AddNumberOfUsedIndices(numberOfIndices);
- }
- if (needVertexResize == false &&
- needIndexResize == false)
- {
- data.AddNumberOfUsedVerticesAndIndicesFast(vertexCount,
- numberOfIndices);
- }
- else if (needVertexResize == false &&
- needIndexResize)
- {
- data.AddNumberOfUsedVerticesFast(vertexCount);
- }
-
- // Quick check if we are at the beginning of the stream, if not
- // we need to modify the write position!
- if (data.HasDataChanged == false &&
- verticesIndex > 0)
- {
- data.writer.BaseStream.Seek(
- verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
- }
-
- // Mark data as dirty, geometry needs to be updated.
- data.HasDataChanged = true;
-
- Vector corner000 = new Vector(minimum.X, minimum.Y, minimum.Z);
- Vector corner100 = new Vector(minimum.X + size.X, minimum.Y, minimum.Z);
- Vector corner010 = new Vector(minimum.X, minimum.Y + size.Y, minimum.Z);
- Vector corner110 = new Vector(minimum.X + size.X, minimum.Y + size.Y,
- minimum.Z);
-
- Vector corner001 = new Vector(minimum.X, minimum.Y, minimum.Z + size.Z);
- Vector corner101 = new Vector(minimum.X + size.X, minimum.Y,
- minimum.Z + size.Z);
- Vector corner011 = new Vector(minimum.X, minimum.Y + size.Y,
- minimum.Z + size.Z);
- Vector corner111 = new Vector(minimum.X + size.X, minimum.Y + size.Y,
- minimum.Z + size.Z);
-
- // Save vertices directly into the VertexData stream, which is only a
- // little bit complicated if we want compressed vertices.
-
- position3DElement.SaveData(data.writer, corner000);
- colorElement.SaveData(data.writer, fillColor);
- position3DElement.SaveData(data.writer, corner100);
- colorElement.SaveData(data.writer, fillColor);
- position3DElement.SaveData(data.writer, corner010);
- colorElement.SaveData(data.writer, fillColor);
- position3DElement.SaveData(data.writer, corner110);
- colorElement.SaveData(data.writer, fillColor);
-
- position3DElement.SaveData(data.writer, corner001);
- colorElement.SaveData(data.writer, fillColor);
- position3DElement.SaveData(data.writer, corner101);
- colorElement.SaveData(data.writer, fillColor);
- position3DElement.SaveData(data.writer, corner011);
- colorElement.SaveData(data.writer, fillColor);
- position3DElement.SaveData(data.writer, corner111);
- colorElement.SaveData(data.writer, fillColor);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 0);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 4);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 0);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 7);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 3);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 7);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 3);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 7);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 3);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 4);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 0);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 4);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 7);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 5);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 6);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 4);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 0);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
-
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 1);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 2);
- data.Indices[indicesIndex++] = (ushort)(verticesIndex + 3);
- }
- #endregion
-
- #region DrawSphere
- /// <summary>
- /// Draws a 3D sphere with a center vector, a radius and a color.
- /// </summary>
- /// <param name="minimum">Minimum position of the box.</param>
- /// <param name="size">Size of the box.</param>
- /// <param name="fillColor">The color of the filled box.</param>
- internal void DrawSphere(ref Vector center, float radius,
- ref Color fillColor)
- {
- // Set up the subdivision in which we build the geometry of the sphere.
- // The tessellation indicates in how many segments the sphere is
- // interpolated, smaller spheres have less tessellation, bigger spheres
- // use more tessellation, but we keep it between 4 and 32.
- int tessellation = (int)(MathHelper.Sqrt(radius) * 6.0f);
- tessellation = MathHelper.Clamp(tessellation, 6, 32);
- // Make multiple of 3 for better fitting texturing
- tessellation = (tessellation / 3) * 3;
- int verticalSegments = tessellation;
- // Add one horizontal segment to fit around a circle, good for UVs
- int horizontalSegments = tessellation * 2 + 1;
-
- int vertexCount = verticalSegments * horizontalSegments;
- int numberOfIndices = 6 * (verticalSegments - 1) * horizontalSegments;
- // Quickly check if this is the same line as last time, then skip!
- GeometryData data = polygon3DMesh.Data;
- int verticesIndex = data.NumberOfUsedVertices;
- int indicesIndex = data.NumberOfUsedIndices;
- bool needVertexResize =
- verticesIndex + vertexCount > data.MaxNumberOfVertices;
- bool needIndexResize =
- indicesIndex + numberOfIndices > data.Indices.Length;
-
- // This only increases the max bounds if we run out of space (rarely)
- if (needVertexResize)
- {
- data.AddNumberOfUsedVertices(vertexCount);
- }
- if (needIndexResize)
- {
- data.AddNumberOfUsedIndices(numberOfIndices);
- }
- if (needVertexResize == false &&
- needIndexResize == false)
- {
- data.AddNumberOfUsedVerticesAndIndicesFast(vertexCount,
- numberOfIndices);
- }
- else if (needVertexResize == false &&
- needIndexResize)
- {
- data.AddNumberOfUsedVerticesFast(vertexCount);
- }
-
- // Quick check if we are at the beginning of the stream, if not
- // we need to modify the write position!
- if (data.HasDataChanged == false &&
- verticesIndex > 0)
- {
- data.writer.BaseStream.Seek(
- verticesIndex * data.VertexDataLengthInBytes, SeekOrigin.Begin);
- }
-
- // Mark data as dirty, geometry needs to be updated.
- data.HasDataChanged = true;
-
- for (int index = 0; index < verticalSegments; index++)
- {
- // Lets begin with the latitude and divide each segment. As we are
- // using circular surface, we will split each position along the
- // vertical axis with the cosine. That is every position in the
- // vertical segment creates a ring with a maximum width stated by the
- // cosine (at the top width=0, in the medium reaches the maximum
- // width = 1, and at the bottom width=0).
- float latitude = index * 180f / (verticalSegments - 1) - 90f;
- float dy = MathHelper.Sin(latitude);
- float dxz = MathHelper.Cos(latitude);
-
- // Create a single ring of vertices at this latitude.
- for (int j = 0; j < horizontalSegments; j++)
- {
- // Next step is tessellation along horizontal axis in which we just
- // simple indicates the position of each vertex in the ring with the
- // previously established width along the surface of the sphere
- float longitude = j * 360f / (horizontalSegments - 1);
-
- float dx = MathHelper.Cos(longitude) * dxz;
- float dz = MathHelper.Sin(longitude) * dxz;
-
- // finally we got the correct position
- Vector normal = new Vector(dx, dy, dz);
-
- position3DElement.SaveData(data.writer, center + (normal * radius));
- colorElement.SaveData(data.writer, fillColor);
- }
- }
-
- // Create a fan connecting the bottom vertex to the bottom latitude ring
- // and finally set up the indices connecting each vertex.
- // Fill the sphere body with triangles joining each pair of rings.
- int num = indicesIndex;
- for (int index = 0; index < verticalSegments - 1; index++)
- {
- for (int j = 0; j < horizontalSegments; j++)
- {
- int nextI = (index + 1);
- int nextJ = (j + 1) % horizontalSegments;
-
- data.Indices[num++] = (ushort)(verticesIndex +
- (index * horizontalSegments + j));
- data.Indices[num++] = (ushort)(verticesIndex +
- (nextI * horizontalSegments + j));
- data.Indices[num++] = (ushort)(verticesIndex +
- (index * horizontalSegments + nextJ));
-
- data.Indices[num++] = (ushort)(verticesIndex +
- (index * horizontalSegments + nextJ));
- data.Indices[num++] = (ushort)(verticesIndex +
- (nextI * horizontalSegments + j));
- data.Indices[num++] = (ushort)(verticesIndex +
- (nextI * horizontalSegments + nextJ));
- }
- }
- }
- #endregion
-
- #endregion
- }
- }