2 * Copyright (c) 2003-2009 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 import com.jme.animation.Bone;
35 import com.jme.math.Quaternion;
36 import com.jme.math.Vector3f;
37 import com.jme.renderer.ColorRGBA;
38 import com.jme.renderer.Renderer;
39 import com.jme.scene.Node;
40 import com.jme.scene.Spatial;
41 import com.jme.scene.shape.Box;
42 import com.jme.scene.shape.Cylinder;
43 import com.jme.scene.shape.Sphere;
44 import com.jme.scene.state.TextureState;
45 import com.jme.scene.state.ZBufferState;
48 * BoneDebugger is responsible for visually representing a skeletal system (a
49 * heirarchy of Bone nodes). To visualize the skeleton, a Bone is represented
50 * as a red sphere and the connection between bones as a white cylinder. Additionally,
51 * hardpoints are presented as green boxes.
53 * Standard usage requires the passing in of the model that contains a skeleton
54 * to the drawBones method. This method will render the bones on screen as
57 * @see com.jme.animation.Bone
60 public final class BoneDebugger {
62 private static Sphere boneSphere = new Sphere("boneSphere", new Vector3f(), 6, 6, .125f);
63 private static Box hardpointBox = new Box("hardpoint", new Vector3f(), 0.125f, 0.125f, 0.125f);
64 private static Cylinder boneCylinder = new Cylinder("boneCylinder", 3, 8, .03f, 1f);
66 boneSphere.setLightCombineMode(Spatial.LightCombineMode.Off);
67 boneSphere.setTextureCombineMode(Spatial.TextureCombineMode.Off);
68 boneSphere.setSolidColor(ColorRGBA.red.clone());
69 boneSphere.setRenderQueueMode(Renderer.QUEUE_SKIP);
71 hardpointBox.setLightCombineMode(Spatial.LightCombineMode.Off);
72 hardpointBox.setTextureCombineMode(Spatial.TextureCombineMode.Off);
73 hardpointBox.setSolidColor(ColorRGBA.green.clone());
74 hardpointBox.setRenderQueueMode(Renderer.QUEUE_SKIP);
76 boneCylinder.setLightCombineMode(Spatial.LightCombineMode.Off);
77 boneCylinder.setTextureCombineMode(Spatial.TextureCombineMode.Off);
78 boneCylinder.setSolidColor(ColorRGBA.white.clone());
79 boneCylinder.setRenderQueueMode(Renderer.QUEUE_SKIP);
82 private static boolean inited = false;
83 private static Vector3f tempTrans = new Vector3f();
84 private static Vector3f tempScale = new Vector3f();
85 private static Quaternion tempRot = new Quaternion();
86 private static Quaternion tempQ = new Quaternion();
87 private static Vector3f tempA = new Vector3f();
88 private static Vector3f tempB = new Vector3f();
89 private static Vector3f tempC = new Vector3f();
90 private static Vector3f tempD = new Vector3f();
92 public static void drawBones(Spatial spat, Renderer r) {
93 drawBones(spat, r, true);
96 public static void drawBones(Spatial spat, Renderer r, boolean drawChildren) {
98 TextureState noTextureState = r.createTextureState();
99 noTextureState.setEnabled(false);
100 boneSphere.setRenderState(noTextureState);
101 hardpointBox.setRenderState(noTextureState);
102 boneCylinder.setRenderState(noTextureState);
104 ZBufferState noBufferState = r.createZBufferState();
105 noBufferState.setEnabled(true);
106 noBufferState.setWritable(true);
107 noBufferState.setFunction(ZBufferState.TestFunction.Always);
108 boneSphere.setRenderState(noBufferState);
109 hardpointBox.setRenderState(noBufferState);
110 boneCylinder.setRenderState(noBufferState);
112 boneSphere.updateRenderState();
113 boneSphere.updateGeometricState(0, false);
114 hardpointBox.updateRenderState();
115 hardpointBox.updateGeometricState(0, false);
116 boneCylinder.updateRenderState();
117 boneCylinder.updateGeometricState(0, false);
118 boneSphere.lockMeshes();
119 hardpointBox.lockMeshes();
120 boneCylinder.lockMeshes();
124 if (spat instanceof Bone) {
125 drawTheBones(null, (Bone)spat, r);
128 if ((spat instanceof Node) && drawChildren) {
129 Node n = (Node) spat;
130 for (int x = 0, count = n.getQuantity(); x < count; x++) {
131 drawBones(n.getChild(x), r, true);
136 private static void drawTheBones(Spatial skin, Bone bone, Renderer r) {
138 tempTrans.set(0,0,0);
139 tempRot.set(0, 0, 0, 1);
140 tempScale.set(1,1,1);
142 tempTrans.set(skin.getWorldTranslation());
143 tempRot.set(skin.getWorldRotation());
144 tempScale.set(skin.getWorldScale());
147 if(bone.isHardpoint()) {
148 hardpointBox.getWorldTranslation().set(tempTrans).addLocal(tempRot.mult(bone.getWorldTranslation(), tempA));
149 hardpointBox.getWorldRotation().set(tempRot).multLocal(bone.getWorldRotation());
150 hardpointBox.getWorldScale().set(tempScale).multLocal(bone.getWorldScale());
152 hardpointBox.draw(r);
154 boneSphere.getWorldTranslation().set(tempTrans).addLocal(tempRot.mult(bone.getWorldTranslation(), tempA));
155 boneSphere.getWorldRotation().set(tempRot).multLocal(bone.getWorldRotation());
156 boneSphere.getWorldScale().set(tempScale).multLocal(bone.getWorldScale());
161 Vector3f here = tempA;
162 Vector3f there = tempB;
163 Vector3f diff = tempC;
165 if (bone.getQuantity() > 0) {
166 bone.localToWorld(Vector3f.ZERO, here);
171 for (int x = 0, count = bone.getQuantity(); x < count; x++) {
172 Spatial child = bone.getChild(x);
173 if (child instanceof Bone) {
174 child.localToWorld(Vector3f.ZERO, there);
175 diff.set(there).subtractLocal(here);
177 float distance = here.distance(there);
179 boneCylinder.getWorldScale().set(1, 1, distance);
180 boneCylinder.getWorldTranslation().set(diff).multLocal(0.5f).addLocal(here);
181 tempD.set(boneCylinder.getWorldTranslation());
182 boneCylinder.getWorldTranslation().set(tempTrans).addLocal(tempRot.mult(tempD, tempD));
184 diff.normalizeLocal();
185 boneCylinder.getWorldRotation().set(bone.getWorldRotation()).lookAt(diff, Vector3f.UNIT_Z);
186 tempQ.set(boneCylinder.getWorldRotation());
187 boneCylinder.getWorldRotation().set(tempRot).multLocal(tempQ);
190 boneCylinder.draw(r);
191 drawTheBones(skin, (Bone)child, r);
192 here.set(hX, hY, hZ);