Written by: Friday, August 12, 2011
Here is a code snippet that draws the bones of a 3D character skeleton for debugging (similar to the original SkeletonHelper.DrawBones method). The result looks like this:
(Click image to enlarge.)
Here is the code: (Click here to download.)
using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace DigitalRune.Animation.Character { public static class MySkeletonHelper { /// <summary> /// Draws the skeleton bones, bone space axes and bone names for debugging. /// </summary> /// <param name="skeletonPose">The skeleton pose.</param> /// <param name="graphicsDevice">The graphics device.</param> /// <param name="effect"> /// An initialized basic effect instance. BasicEffect.World, BasicEffect.View and /// BasicEffect.Projection must be correctly initialized before this method is called. /// </param> /// <param name="axisLength">The visible length of the bone space axes.</param> /// <param name="spriteBatch"> A SpriteBatch. Can be null to skip text rendering. </param> /// <param name="spriteFont"> A SpriteFont. Can be null to skip text rendering. </param> /// <param name="color">The color for the bones and the bone names.</param> public static void DrawBones(this SkeletonPose skeletonPose, GraphicsDevice graphicsDevice, BasicEffect effect, float axisLength, SpriteBatch spriteBatch, SpriteFont spriteFont, Color color) { if (skeletonPose == null) throw new ArgumentNullException("skeletonPose"); if (graphicsDevice == null) throw new ArgumentNullException("graphicsDevice"); if (effect == null) throw new ArgumentNullException("effect"); var oldVertexColorEnabled = effect.VertexColorEnabled; effect.VertexColorEnabled = true; // No font, then we don't need the sprite batch. if (spriteFont == null) spriteBatch = null; if (spriteBatch != null) spriteBatch.Begin(); List<VertexPositionColor> vertices = new List<VertexPositionColor>(); var skeleton = skeletonPose.Skeleton; for (int i = 0; i < skeleton.NumberOfBones; i++) { // Data of bone i: string name = skeleton.GetName(i); SrtTransform bonePose = skeletonPose.GetBonePoseAbsolute(i); var translation = (Vector3)bonePose.Translation; var rotation = (Quaternion)bonePose.Rotation; // Draw line to parent joint representing the parent bone. int parentIndex = skeleton.GetParent(i); if (parentIndex >= 0) { SrtTransform parentPose = skeletonPose.GetBonePoseAbsolute(parentIndex); vertices.Add(new VertexPositionColor(translation, color)); vertices.Add(new VertexPositionColor((Vector3)parentPose.Translation, color)); } // Add three lines in Red, Green and Blue that visualize the bone space. vertices.Add(new VertexPositionColor(translation, Color.Red)); vertices.Add(new VertexPositionColor( translation + Vector3.Transform(Vector3.UnitX, rotation) * axisLength, Color.Red)); vertices.Add(new VertexPositionColor(translation, Color.Green)); vertices.Add(new VertexPositionColor( translation + Vector3.Transform(Vector3.UnitY, rotation) * axisLength, Color.Green)); vertices.Add(new VertexPositionColor(translation, Color.Blue)); vertices.Add(new VertexPositionColor( translation + Vector3.Transform(Vector3.UnitZ, rotation) * axisLength, Color.Blue)); // Draw name. if (spriteBatch != null && !string.IsNullOrEmpty(name)) { // Compute the 3D position in view space. Text is rendered near drawn x axis. Vector3 textPosition = translation + Vector3.TransformNormal(Vector3.UnitX, bonePose) * axisLength * 0.5f; var textPositionWorld = Vector3.Transform(textPosition, effect.World); var textPositionView = Vector3.Transform(textPositionWorld, effect.View); // Check if the text is in front of the camera. if (textPositionView.Z < 0) { // Project text position to screen. Vector3 textPositionProjected = graphicsDevice.Viewport.Project( textPosition, effect.Projection, effect.View, effect.World); spriteBatch.DrawString(spriteFont, name + " " + i, new Vector2(textPositionProjected.X, textPositionProjected.Y), color); } } } if (spriteBatch != null) spriteBatch.End(); // Draw axis lines in one batch. graphicsDevice.DepthStencilState = DepthStencilState.None; effect.CurrentTechnique.Passes[0].Apply(); graphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); effect.VertexColorEnabled = oldVertexColorEnabled; } } }
0 comment(s) so far...
A collection of the most useful blog articles can be found here:
Article Collection (on Documentation page)
DigitalRune is a trademark of Garstenauer Information Technology OG.
Garstenauer Information Technology OG Weingartenstrasse 35, 4452 Ternberg Austria (EUROPE) office@digitalrune.com