//-----------------------------------------------------------------------------
// DebugDraw.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace CollisionSample
{
///
/// Debug drawing routines for common collision shapes. These are not designed to be the most
/// efficent way to submit geometry to the graphics device as they are intended for use in
/// visualizing collision for debugging purposes.
///
public class DebugDraw : IDisposable
{
#region Constants
public const int MAX_VERTS = 2000;
public const int MAX_INDICES = 2000;
// Indices for drawing the edges of a cube, given the vertex ordering
// used by Bounding(Frustum|Box|OrientedBox).GetCorners()
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 };
#endregion
#region Fields
BasicEffect basicEffect;
DynamicVertexBuffer vertexBuffer;
DynamicIndexBuffer indexBuffer;
ushort[] Indices = new ushort[MAX_INDICES];
VertexPositionColor[] Vertices = new VertexPositionColor[MAX_VERTS];
int IndexCount;
int VertexCount;
#endregion
#region Initialization
public DebugDraw(GraphicsDevice device)
{
vertexBuffer = new DynamicVertexBuffer(device, typeof(VertexPositionColor), MAX_VERTS, BufferUsage.WriteOnly);
indexBuffer = new DynamicIndexBuffer(device, typeof(ushort), MAX_INDICES, BufferUsage.WriteOnly);
basicEffect = new BasicEffect(device); //(device, null);
basicEffect.LightingEnabled = false;
basicEffect.VertexColorEnabled = true;
basicEffect.TextureEnabled = false;
}
#endregion
#region Dispose
~DebugDraw()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (vertexBuffer != null)
vertexBuffer.Dispose();
if (indexBuffer != null)
indexBuffer.Dispose();
if (basicEffect != null)
basicEffect.Dispose();
}
}
#endregion
#region Draw
///
/// Starts debug drawing by setting the required render states and camera information
///
public void Begin(Matrix view, Matrix projection)
{
basicEffect.World = Matrix.Identity;
basicEffect.View = view;
basicEffect.Projection = projection;
VertexCount = 0;
IndexCount = 0;
}
///
/// Ends debug drawing and restores standard render states
///
public void End()
{
FlushDrawing();
}
public void DrawWireShape(Vector3[] positionArray, ushort[] indexArray, Color color)
{
if (Reserve(positionArray.Length, indexArray.Length))
{
for (int i = 0; i < indexArray.Length; i++)
Indices[IndexCount++] = (ushort)(VertexCount + indexArray[i]);
for (int i = 0; i < positionArray.Length; i++)
Vertices[VertexCount++] = new VertexPositionColor(positionArray[i], color);
}
}
// Draw any queued objects and reset our line buffers
private void FlushDrawing()
{
if (IndexCount > 0)
{
vertexBuffer.SetData(Vertices, 0, VertexCount, SetDataOptions.Discard);
indexBuffer.SetData(Indices, 0, IndexCount, SetDataOptions.Discard);
GraphicsDevice device = basicEffect.GraphicsDevice;
device.SetVertexBuffer(vertexBuffer);
device.Indices = indexBuffer;
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
pass.Apply();
device.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, VertexCount, 0, IndexCount / 2);
}
device.SetVertexBuffer(null);
device.Indices = null;
}
IndexCount = 0;
VertexCount = 0;
}
// Check if there's enough space to draw an object with the given vertex/index counts.
// If necessary, call FlushDrawing() to make room.
private bool Reserve(int numVerts, int numIndices)
{
if(numVerts > MAX_VERTS || numIndices > MAX_INDICES)
{
// Whatever it is, we can't draw it
return false;
}
if (VertexCount + numVerts > MAX_VERTS || IndexCount + numIndices >= MAX_INDICES)
{
// We can draw it, but we need to make room first
FlushDrawing();
}
return true;
}
///
/// Renders a 2D grid (must be called within a Begin/End pair)
///
/// Vector direction for the local X-axis direction of the grid
/// Vector direction for the local Y-axis of the grid
/// 3D starting anchor point for the grid
/// Number of divisions in the local X-axis direction
/// Number of divisions in the local Y-axis direction
/// Color of the grid lines
public void DrawWireGrid(Vector3 xAxis, Vector3 yAxis, Vector3 origin, int iXDivisions, int iYDivisions, Color color)
{
Vector3 pos, step;
pos = origin;
step = xAxis / iXDivisions;
for (int i = 0; i <= iXDivisions; i++)
{
DrawLine(pos, pos + yAxis, color);
pos += step;
}
pos = origin;
step = yAxis / iYDivisions;
for (int i = 0; i <= iYDivisions; i++)
{
DrawLine(pos, pos + xAxis, color);
pos += step;
}
}
///
/// Renders the outline of a bounding frustum
///
/// Bounding frustum to render
/// Color of the frustum lines
public void DrawWireFrustum(BoundingFrustum frustum, Color color)
{
DrawWireShape(frustum.GetCorners(), cubeIndices, color);
}
///
/// Renders the outline of an axis-aligned bounding box
///
/// Bounding box to render
/// Color of the box lines
public void DrawWireBox(BoundingBox box, Color color)
{
DrawWireShape(box.GetCorners(), cubeIndices, color);
}
///
/// Renders the outline of an oriented bounding box
///
/// Oriented bounding box to render
/// Color of the box lines
public void DrawWireBox(BoundingOrientedBox box, Color color)
{
DrawWireShape(box.GetCorners(), cubeIndices, color);
}
///
/// Renders a circular ring (tessellated circle)
///
/// Center point for the ring
/// Direction of the major-axis of the circle
/// Direction of hte minor-axis of the circle
/// Color of the ring lines
public void DrawRing(Vector3 origin, Vector3 majorAxis, Vector3 minorAxis, Color color)
{
const int RING_SEGMENTS = 32;
const float fAngleDelta = 2.0F * (float)Math.PI / RING_SEGMENTS;
if (Reserve(RING_SEGMENTS, RING_SEGMENTS * 2))
{
for (int i = 0; i < RING_SEGMENTS; i++)
{
Indices[IndexCount++] = (ushort)(VertexCount + i);
Indices[IndexCount++] = (ushort)(VertexCount + (i + 1) % RING_SEGMENTS);
}
float cosDelta = (float)Math.Cos(fAngleDelta);
float sinDelta = (float)Math.Sin(fAngleDelta);
float cosAcc = 1;
float sinAcc = 0;
for (int i = 0; i < RING_SEGMENTS; ++i)
{
Vector3 pos = new Vector3(majorAxis.X * cosAcc + minorAxis.X * sinAcc + origin.X,
majorAxis.Y * cosAcc + minorAxis.Y * sinAcc + origin.Y,
majorAxis.Z * cosAcc + minorAxis.Z * sinAcc + origin.Z);
Vertices[VertexCount++] = new VertexPositionColor(pos, color);
float newCos = cosAcc * cosDelta - sinAcc * sinDelta;
float newSin = cosAcc * sinDelta + sinAcc * cosDelta;
cosAcc = newCos;
sinAcc = newSin;
}
}
}
///
/// Renders the outline of a bounding sphere.
///
/// This code assumes that the model and view matrices contain only rigid motion.
///
/// Bounding sphere to render
/// Color of the outline lines
public void DrawWireSphere(BoundingSphere sphere, Color color)
{
// Invert the modelview matrix to get direction vectors
// in screen space, so we can draw a circle that always
// faces the camera.
Matrix view = basicEffect.World * basicEffect.View;
Matrix.Transpose(ref view, out view);
DrawRing(sphere.Center, view.Right * sphere.Radius, view.Up * sphere.Radius, color);
}
///
/// Draw a ray of the given length
///
///
///
///
public void DrawRay(Ray ray, Color color, float length)
{
DrawLine(ray.Position, ray.Position + ray.Direction * length, color);
}
public void DrawLine(Vector3 v0, Vector3 v1, Color color)
{
if(Reserve(2, 2))
{
Indices[IndexCount++] = (ushort)VertexCount;
Indices[IndexCount++] = (ushort)(VertexCount+1);
Vertices[VertexCount++] = new VertexPositionColor(v0, color);
Vertices[VertexCount++] = new VertexPositionColor(v1, color);
}
}
public void DrawWireTriangle(Vector3 v0, Vector3 v1, Vector3 v2, Color color)
{
if(Reserve(3, 6))
{
Indices[IndexCount++] = (ushort)(VertexCount+0);
Indices[IndexCount++] = (ushort)(VertexCount+1);
Indices[IndexCount++] = (ushort)(VertexCount+1);
Indices[IndexCount++] = (ushort)(VertexCount+2);
Indices[IndexCount++] = (ushort)(VertexCount+2);
Indices[IndexCount++] = (ushort)(VertexCount+0);
Vertices[VertexCount++] = new VertexPositionColor(v0, color);
Vertices[VertexCount++] = new VertexPositionColor(v1, color);
Vertices[VertexCount++] = new VertexPositionColor(v2, color);
}
}
public void DrawWireTriangle(Triangle t, Color color)
{
DrawWireTriangle(t.V0, t.V1, t.V2, color);
}
#endregion
}
}