2
0

DebugDraw.cs 12 KB

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