Skip to content

Feat: Improve ArmatureDebugger #2493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.jme3.scene.debug.custom;

/*
* Copyright (c) 2009-2021 jMonkeyEngine
* Copyright (c) 2009-2025 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -31,9 +29,11 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.scene.debug.custom;

import com.jme3.anim.Armature;
import com.jme3.anim.Joint;
import com.jme3.anim.SkinningControl;
import com.jme3.asset.AssetManager;
import com.jme3.collision.Collidable;
import com.jme3.collision.CollisionResults;
Expand All @@ -55,104 +55,166 @@
public class ArmatureDebugger extends Node {

/**
* The lines of the bones or the wires between their heads.
* The node responsible for rendering the bones/wires and their outlines.
*/
private ArmatureNode armatureNode;

/**
* The {@link Armature} instance being debugged.
*/
private Armature armature;

/**
* A node containing all {@link Geometry} objects representing the joint points.
*/
private Node joints;
/**
* A node containing all {@link Geometry} objects representing the bone outlines.
*/
private Node outlines;
/**
* A node containing all {@link Geometry} objects representing the bone wires/lines.
*/
private Node wires;
/**
* The dotted lines between a bone's tail and the had of its children. Not
* available if the length data was not provided.
*/
private ArmatureInterJointsWire interJointWires;

/**
* Default constructor for `ArmatureDebugger`.
* Use {@link #ArmatureDebugger(String, Armature, List)} for a functional instance.
*/
public ArmatureDebugger() {
}

/**
* Creates a debugger with no length data. The wires will be a connection
* between the bones' heads only. The points will show the bones' heads only
* and no dotted line of inter bones connection will be visible.
* Convenience constructor that creates an {@code ArmatureDebugger} and immediately
* initializes its materials based on the provided {@code AssetManager}
* and {@code SkinningControl}.
*
* @param assetManager The {@link AssetManager} used to load textures and materials
* for the debug visualization.
* @param skControl The {@link SkinningControl} from which to extract the
* {@link Armature} and its associated joints.
*/
public ArmatureDebugger(AssetManager assetManager, SkinningControl skControl) {
this(null, skControl.getArmature(), skControl.getArmature().getJointList());
initialize(assetManager, null);
}

/**
* Creates an `ArmatureDebugger` instance without explicit bone length data.
* In this configuration, the visual representation will consist of wires
* connecting the bone heads, and points representing the bone heads.
* No dotted lines for inter-bone connections will be visible.
*
* @param name the name of the debugger's node
* @param armature the armature that will be shown
* @param deformingJoints a list of joints
* @param name The name of this debugger's root node.
* @param armature The {@link Armature} to be visualized.
* @param deformingJoints A {@link List} of {@link Joint} objects that are
* considered deforming joints.
*/
public ArmatureDebugger(String name, Armature armature, List<Joint> deformingJoints) {
super(name);
this.armature = armature;
// Ensure the armature's world transforms are up-to-date before visualization.
armature.update();

// Initialize the main container nodes for different visual elements.
joints = new Node("joints");
outlines = new Node("outlines");
wires = new Node("bones");
this.attachChild(joints);
this.attachChild(outlines);
this.attachChild(wires);
Node ndJoints = new Node("non deforming Joints");
Node ndOutlines = new Node("non deforming Joints outlines");
Node ndWires = new Node("non deforming Joints wires");

// Create child nodes specifically for non-deforming joints' visualization
Node ndJoints = new Node("NonDeformingJoints");
Node ndOutlines = new Node("NonDeformingOutlines");
Node ndWires = new Node("NonDeformingWires");
joints.attachChild(ndJoints);
outlines.attachChild(ndOutlines);
wires.attachChild(ndWires);
Node outlineDashed = new Node("Outlines Dashed");
Node wiresDashed = new Node("Wires Dashed");
wiresDashed.attachChild(new Node("dashed non defrom"));
outlineDashed.attachChild(new Node("dashed non defrom"));

Node outlineDashed = new Node("DashedOutlines");
Node wiresDashed = new Node("DashedWires");
wiresDashed.attachChild(new Node("DashedNonDeformingWires"));
outlineDashed.attachChild(new Node("DashedNonDeformingOutlines"));
outlines.attachChild(outlineDashed);
wires.attachChild(wiresDashed);

// Initialize the core ArmatureNode which handles the actual mesh generation.
armatureNode = new ArmatureNode(armature, joints, wires, outlines, deformingJoints);

this.attachChild(armatureNode);

// By default, non-deforming joints are hidden.
displayNonDeformingJoint(false);
}

/**
* Sets the visibility of non-deforming joints and their associated outlines and wires.
*
* @param display `true` to make non-deforming joints visible, `false` to hide them.
*/
public void displayNonDeformingJoint(boolean display) {
joints.getChild(0).setCullHint(display ? CullHint.Dynamic : CullHint.Always);
outlines.getChild(0).setCullHint(display ? CullHint.Dynamic : CullHint.Always);
wires.getChild(0).setCullHint(display ? CullHint.Dynamic : CullHint.Always);
((Node) outlines.getChild(1)).getChild(0).setCullHint(display ? CullHint.Dynamic : CullHint.Always);
((Node) wires.getChild(1)).getChild(0).setCullHint(display ? CullHint.Dynamic : CullHint.Always);
CullHint cullHint = display ? CullHint.Dynamic : CullHint.Always;

joints.getChild(0).setCullHint(cullHint);
outlines.getChild(0).setCullHint(cullHint);
wires.getChild(0).setCullHint(cullHint);

((Node) outlines.getChild(1)).getChild(0).setCullHint(cullHint);
((Node) wires.getChild(1)).getChild(0).setCullHint(cullHint);
}

/**
* Initializes the materials and camera for the debugger's visual components.
* This method should be called after the `ArmatureDebugger` is added to a scene graph
* and an {@link AssetManager} and {@link Camera} are available.
*
* @param assetManager The {@link AssetManager} to load textures and materials.
* @param camera The scene's primary {@link Camera}, used by the `ArmatureNode`
* for billboard rendering of joint points.
*/
public void initialize(AssetManager assetManager, Camera camera) {

armatureNode.setCamera(camera);

// Material for joint points (billboarded dots).
Material matJoints = new Material(assetManager, "Common/MatDefs/Misc/Billboard.j3md");
Texture t = assetManager.loadTexture("Common/Textures/dot.png");
matJoints.setTexture("Texture", t);
Texture tex = assetManager.loadTexture("Common/Textures/dot.png");
matJoints.setTexture("Texture", tex);
matJoints.getAdditionalRenderState().setDepthTest(false);
matJoints.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
joints.setQueueBucket(RenderQueue.Bucket.Translucent);
joints.setMaterial(matJoints);

// Material for bone wires/lines (unshaded, vertex colored).
Material matWires = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
matWires.setBoolean("VertexColor", true);
matWires.getAdditionalRenderState().setLineWidth(1f);
matWires.getAdditionalRenderState().setDepthTest(false);
wires.setMaterial(matWires);

// Material for bone outlines (unshaded, vertex colored).
Material matOutline = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
matOutline.setBoolean("VertexColor", true);
matOutline.getAdditionalRenderState().setLineWidth(1f);
matOutline.getAdditionalRenderState().setDepthTest(false);
outlines.setMaterial(matOutline);

// Material for dashed outlines. This assumes a "DashedLine.j3md" shader.
Material matOutline2 = new Material(assetManager, "Common/MatDefs/Misc/DashedLine.j3md");
matOutline2.getAdditionalRenderState().setLineWidth(1);
matOutline2.getAdditionalRenderState().setDepthTest(false);
outlines.getChild(1).setMaterial(matOutline2);

// Material for dashed wires. This assumes a "DashedLine.j3md" shader.
Material matWires2 = new Material(assetManager, "Common/MatDefs/Misc/DashedLine.j3md");
matWires2.getAdditionalRenderState().setLineWidth(1);
matWires2.getAdditionalRenderState().setDepthTest(false);
wires.getChild(1).setMaterial(matWires2);

}

/**
* Returns the {@link Armature} instance associated with this debugger.
*
* @return The {@link Armature} being debugged.
*/
public Armature getArmature() {
return armature;
}
Expand All @@ -168,21 +230,35 @@ public int collideWith(Collidable other, CollisionResults results) {
return armatureNode.collideWith(other, results);
}

protected Joint select(Geometry g) {
return armatureNode.select(g);
/**
* Selects and returns the {@link Joint} associated with a given {@link Geometry}.
* This is an internal helper method, likely used for picking operations.
*
* @param geo The {@link Geometry} representing a part of a joint.
* @return The {@link Joint} corresponding to the geometry, or `null` if not found.
*/
protected Joint select(Geometry geo) {
return armatureNode.select(geo);
}

/**
* @return the armature wires
* Returns the {@link ArmatureNode} which is responsible for generating and
* managing the visual mesh of the bones and wires.
*
* @return The {@link ArmatureNode} instance.
*/
public ArmatureNode getBoneShapes() {
return armatureNode;
}

/**
* @return the dotted line between bones (can be null)
* Returns the {@link ArmatureInterJointsWire} instance, which represents the
* dotted lines connecting a bone's tail to the head of its children.
* This will be `null` if the debugger was created without bone length data.
*
* @return The {@link ArmatureInterJointsWire} instance, or `null` if not present.
*/
public ArmatureInterJointsWire getInterJointWires() {
return interJointWires;
}
}
}
Loading
Loading