DebugDraw.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. //-----------------------------------------------------------------------------
  2. // DebugDraw.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Diagnostics;
  9. using System.Collections.Generic;
  10. using Microsoft.Xna.Framework;
  11. using Microsoft.Xna.Framework.Graphics;
  12. namespace CollisionSample
  13. {
  14. /// <summary>
  15. /// Debug drawing routines for common collision shapes. These are not designed to be the most
  16. /// efficent way to submit geometry to the graphics device as they are intended for use in
  17. /// visualizing collision for debugging purposes.
  18. /// </summary>
  19. public class DebugDraw : IDisposable
  20. {
  21. public const int MAX_VERTS = 2000;
  22. public const int MAX_INDICES = 2000;
  23. // Indices for drawing the edges of a cube, given the vertex ordering
  24. // used by Bounding(Frustum|Box|OrientedBox).GetCorners()
  25. static ushort[] cubeIndices = new ushort[] { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 };
  26. BasicEffect basicEffect;
  27. DynamicVertexBuffer vertexBuffer;
  28. DynamicIndexBuffer indexBuffer;
  29. ushort[] Indices = new ushort[MAX_INDICES];
  30. VertexPositionColor[] Vertices = new VertexPositionColor[MAX_VERTS];
  31. int IndexCount;
  32. int VertexCount;
  33. public DebugDraw(GraphicsDevice device)
  34. {
  35. vertexBuffer = new DynamicVertexBuffer(device, typeof(VertexPositionColor), MAX_VERTS, BufferUsage.WriteOnly);
  36. indexBuffer = new DynamicIndexBuffer(device, typeof(ushort), MAX_INDICES, BufferUsage.WriteOnly);
  37. basicEffect = new BasicEffect(device); //(device, null);
  38. basicEffect.LightingEnabled = false;
  39. basicEffect.VertexColorEnabled = true;
  40. basicEffect.TextureEnabled = false;
  41. }
  42. ~DebugDraw()
  43. {
  44. Dispose(false);
  45. }
  46. public void Dispose()
  47. {
  48. Dispose(true);
  49. GC.SuppressFinalize(this);
  50. }
  51. protected virtual void Dispose(bool disposing)
  52. {
  53. if (disposing)
  54. {
  55. if (vertexBuffer != null)
  56. vertexBuffer.Dispose();
  57. if (indexBuffer != null)
  58. indexBuffer.Dispose();
  59. if (basicEffect != null)
  60. basicEffect.Dispose();
  61. }
  62. }
  63. /// <summary>
  64. /// Starts debug drawing by setting the required render states and camera information
  65. /// </summary>
  66. public void Begin(Matrix view, Matrix projection)
  67. {
  68. basicEffect.World = Matrix.Identity;
  69. basicEffect.View = view;
  70. basicEffect.Projection = projection;
  71. VertexCount = 0;
  72. IndexCount = 0;
  73. }
  74. /// <summary>
  75. /// Ends debug drawing and restores standard render states
  76. /// </summary>
  77. public void End()
  78. {
  79. FlushDrawing();
  80. }
  81. public void DrawWireShape(Vector3[] positionArray, ushort[] indexArray, Color color)
  82. {
  83. if (Reserve(positionArray.Length, indexArray.Length))
  84. {
  85. for (int i = 0; i < indexArray.Length; i++)
  86. Indices[IndexCount++] = (ushort)(VertexCount + indexArray[i]);
  87. for (int i = 0; i < positionArray.Length; i++)
  88. Vertices[VertexCount++] = new VertexPositionColor(positionArray[i], color);
  89. }
  90. }
  91. // Draw any queued objects and reset our line buffers
  92. private void FlushDrawing()
  93. {
  94. if (IndexCount > 0)
  95. {
  96. vertexBuffer.SetData(Vertices, 0, VertexCount, SetDataOptions.Discard);
  97. indexBuffer.SetData(Indices, 0, IndexCount, SetDataOptions.Discard);
  98. GraphicsDevice device = basicEffect.GraphicsDevice;
  99. device.SetVertexBuffer(vertexBuffer);
  100. device.Indices = indexBuffer;
  101. foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
  102. {
  103. pass.Apply();
  104. // Updated to new overload: DrawIndexedPrimitives(PrimitiveType primitiveType, int baseVertex, int startIndex, int primitiveCount)
  105. device.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, IndexCount / 2);
  106. }
  107. device.SetVertexBuffer(null);
  108. device.Indices = null;
  109. }
  110. IndexCount = 0;
  111. VertexCount = 0;
  112. }
  113. // Check if there's enough space to draw an object with the given vertex/index counts.
  114. // If necessary, call FlushDrawing() to make room.
  115. private bool Reserve(int numVerts, int numIndices)
  116. {
  117. if(numVerts > MAX_VERTS || numIndices > MAX_INDICES)
  118. {
  119. // Whatever it is, we can't draw it
  120. return false;
  121. }
  122. if (VertexCount + numVerts > MAX_VERTS || IndexCount + numIndices >= MAX_INDICES)
  123. {
  124. // We can draw it, but we need to make room first
  125. FlushDrawing();
  126. }
  127. return true;
  128. }
  129. /// <summary>
  130. /// Renders a 2D grid (must be called within a Begin/End pair)
  131. /// </summary>
  132. /// <param name="xAxis">Vector direction for the local X-axis direction of the grid</param>
  133. /// <param name="yAxis">Vector direction for the local Y-axis of the grid</param>
  134. /// <param name="origin">3D starting anchor point for the grid</param>
  135. /// <param name="iXDivisions">Number of divisions in the local X-axis direction</param>
  136. /// <param name="iYDivisions">Number of divisions in the local Y-axis direction</param>
  137. /// <param name="color">Color of the grid lines</param>
  138. public void DrawWireGrid(Vector3 xAxis, Vector3 yAxis, Vector3 origin, int iXDivisions, int iYDivisions, Color color)
  139. {
  140. Vector3 pos, step;
  141. pos = origin;
  142. step = xAxis / iXDivisions;
  143. for (int i = 0; i <= iXDivisions; i++)
  144. {
  145. DrawLine(pos, pos + yAxis, color);
  146. pos += step;
  147. }
  148. pos = origin;
  149. step = yAxis / iYDivisions;
  150. for (int i = 0; i <= iYDivisions; i++)
  151. {
  152. DrawLine(pos, pos + xAxis, color);
  153. pos += step;
  154. }
  155. }
  156. /// <summary>
  157. /// Renders the outline of a bounding frustum
  158. /// </summary>
  159. /// <param name="frustum">Bounding frustum to render</param>
  160. /// <param name="color">Color of the frustum lines</param>
  161. public void DrawWireFrustum(BoundingFrustum frustum, Color color)
  162. {
  163. DrawWireShape(frustum.GetCorners(), cubeIndices, color);
  164. }
  165. /// <summary>
  166. /// Renders the outline of an axis-aligned bounding box
  167. /// </summary>
  168. /// <param name="box">Bounding box to render</param>
  169. /// <param name="color">Color of the box lines</param>
  170. public void DrawWireBox(BoundingBox box, Color color)
  171. {
  172. DrawWireShape(box.GetCorners(), cubeIndices, color);
  173. }
  174. /// <summary>
  175. /// Renders the outline of an oriented bounding box
  176. /// </summary>
  177. /// <param name="box">Oriented bounding box to render</param>
  178. /// <param name="color">Color of the box lines</param>
  179. public void DrawWireBox(BoundingOrientedBox box, Color color)
  180. {
  181. DrawWireShape(box.GetCorners(), cubeIndices, color);
  182. }
  183. /// <summary>
  184. /// Renders a circular ring (tessellated circle)
  185. /// </summary>
  186. /// <param name="origin">Center point for the ring</param>
  187. /// <param name="majorAxis">Direction of the major-axis of the circle</param>
  188. /// <param name="minorAxis">Direction of hte minor-axis of the circle</param>
  189. /// <param name="color">Color of the ring lines</param>
  190. public void DrawRing(Vector3 origin, Vector3 majorAxis, Vector3 minorAxis, Color color)
  191. {
  192. const int RING_SEGMENTS = 32;
  193. const float fAngleDelta = 2.0F * (float)Math.PI / RING_SEGMENTS;
  194. if (Reserve(RING_SEGMENTS, RING_SEGMENTS * 2))
  195. {
  196. for (int i = 0; i < RING_SEGMENTS; i++)
  197. {
  198. Indices[IndexCount++] = (ushort)(VertexCount + i);
  199. Indices[IndexCount++] = (ushort)(VertexCount + (i + 1) % RING_SEGMENTS);
  200. }
  201. float cosDelta = (float)Math.Cos(fAngleDelta);
  202. float sinDelta = (float)Math.Sin(fAngleDelta);
  203. float cosAcc = 1;
  204. float sinAcc = 0;
  205. for (int i = 0; i < RING_SEGMENTS; ++i)
  206. {
  207. Vector3 pos = new Vector3(majorAxis.X * cosAcc + minorAxis.X * sinAcc + origin.X,
  208. majorAxis.Y * cosAcc + minorAxis.Y * sinAcc + origin.Y,
  209. majorAxis.Z * cosAcc + minorAxis.Z * sinAcc + origin.Z);
  210. Vertices[VertexCount++] = new VertexPositionColor(pos, color);
  211. float newCos = cosAcc * cosDelta - sinAcc * sinDelta;
  212. float newSin = cosAcc * sinDelta + sinAcc * cosDelta;
  213. cosAcc = newCos;
  214. sinAcc = newSin;
  215. }
  216. }
  217. }
  218. /// <summary>
  219. /// Renders the outline of a bounding sphere.
  220. ///
  221. /// This code assumes that the model and view matrices contain only rigid motion.
  222. /// </summary>
  223. /// <param name="sphere">Bounding sphere to render</param>
  224. /// <param name="color">Color of the outline lines</param>
  225. public void DrawWireSphere(BoundingSphere sphere, Color color)
  226. {
  227. // Invert the modelview matrix to get direction vectors
  228. // in screen space, so we can draw a circle that always
  229. // faces the camera.
  230. Matrix view = basicEffect.World * basicEffect.View;
  231. Matrix.Transpose(ref view, out view);
  232. DrawRing(sphere.Center, view.Right * sphere.Radius, view.Up * sphere.Radius, color);
  233. }
  234. /// <summary>
  235. /// Draw a ray of the given length
  236. /// </summary>
  237. /// <param name="ray"></param>
  238. /// <param name="color"></param>
  239. /// <param name="length"></param>
  240. public void DrawRay(Ray ray, Color color, float length)
  241. {
  242. DrawLine(ray.Position, ray.Position + ray.Direction * length, color);
  243. }
  244. public void DrawLine(Vector3 v0, Vector3 v1, Color color)
  245. {
  246. if(Reserve(2, 2))
  247. {
  248. Indices[IndexCount++] = (ushort)VertexCount;
  249. Indices[IndexCount++] = (ushort)(VertexCount+1);
  250. Vertices[VertexCount++] = new VertexPositionColor(v0, color);
  251. Vertices[VertexCount++] = new VertexPositionColor(v1, color);
  252. }
  253. }
  254. public void DrawWireTriangle(Vector3 v0, Vector3 v1, Vector3 v2, Color color)
  255. {
  256. if(Reserve(3, 6))
  257. {
  258. Indices[IndexCount++] = (ushort)(VertexCount+0);
  259. Indices[IndexCount++] = (ushort)(VertexCount+1);
  260. Indices[IndexCount++] = (ushort)(VertexCount+1);
  261. Indices[IndexCount++] = (ushort)(VertexCount+2);
  262. Indices[IndexCount++] = (ushort)(VertexCount+2);
  263. Indices[IndexCount++] = (ushort)(VertexCount+0);
  264. Vertices[VertexCount++] = new VertexPositionColor(v0, color);
  265. Vertices[VertexCount++] = new VertexPositionColor(v1, color);
  266. Vertices[VertexCount++] = new VertexPositionColor(v2, color);
  267. }
  268. }
  269. public void DrawWireTriangle(Triangle t, Color color)
  270. {
  271. DrawWireTriangle(t.V0, t.V1, t.V2, color);
  272. }
  273. }
  274. }