PageRenderTime 46ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/Rendering/Basics/Drawing/DrawManager.cs

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