From 4040a1e412c78eec2dd0a36b31194f3e703fb53d Mon Sep 17 00:00:00 2001 From: Nehon Date: Mon, 18 Dec 2017 17:41:13 +0100 Subject: [PATCH] Adds an ArmatureDebugger --- .../{SkeletonBone.java => ArmatureBone.java} | 159 +++---- .../debug/custom/ArmatureDebugAppState.java | 154 ++++++ ...tonDebugger.java => ArmatureDebugger.java} | 113 +++-- ...Wire.java => ArmatureInterJointsWire.java} | 60 ++- .../jme3/scene/debug/custom/BoneShape.java | 448 ++++-------------- .../debug/custom/SkeletonDebugAppState.java | 156 ------ .../com/jme3/scene/shape/AbstractBox.java | 22 +- .../main/java/com/jme3/scene/shape/Box.java | 9 +- .../java/com/jme3/scene/shape/StripBox.java | 9 +- .../Common/MatDefs/Misc/fakeLighting.j3md | 49 ++ .../ShaderNodes/Basic/Mat3Vec3Mult.j3sn | 31 ++ .../ShaderNodes/Basic/Mat3Vec3Mult100.frag | 3 + .../ShaderNodes/Misc/fakeLighting.j3sn | 33 ++ .../ShaderNodes/Misc/fakeLighting100.frag | 9 + .../jme3test/model/anim/TestArmature.java | 112 +++++ 15 files changed, 655 insertions(+), 712 deletions(-) rename jme3-core/src/main/java/com/jme3/scene/debug/custom/{SkeletonBone.java => ArmatureBone.java} (51%) create mode 100644 jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java rename jme3-core/src/main/java/com/jme3/scene/debug/custom/{SkeletonDebugger.java => ArmatureDebugger.java} (63%) rename jme3-core/src/main/java/com/jme3/scene/debug/custom/{SkeletonInterBoneWire.java => ArmatureInterJointsWire.java} (70%) delete mode 100644 jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonDebugAppState.java create mode 100644 jme3-core/src/main/resources/Common/MatDefs/Misc/fakeLighting.j3md create mode 100644 jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult.j3sn create mode 100644 jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult100.frag create mode 100644 jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/fakeLighting.j3sn create mode 100644 jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/fakeLighting100.frag create mode 100644 jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonBone.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureBone.java similarity index 51% rename from jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonBone.java rename to jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureBone.java index b4f21ec54..b0b6e7e27 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonBone.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureBone.java @@ -32,58 +32,51 @@ package com.jme3.scene.debug.custom; * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import java.util.Map; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; +import com.jme3.animation.Armature; +import com.jme3.animation.Joint; import com.jme3.bounding.*; -import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.Node; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.*; import com.jme3.scene.shape.Sphere; -import static com.jme3.util.BufferUtils.createFloatBuffer; - import java.nio.FloatBuffer; import java.util.HashMap; +import java.util.Map; + +import static com.jme3.util.BufferUtils.createFloatBuffer; /** * The class that displays either wires between the bones' heads if no length * data is supplied and full bones' shapes otherwise. */ -public class SkeletonBone extends Node { +public class ArmatureBone extends Node { /** - * The skeleton to be displayed. + * The armature to be displayed. */ - private Skeleton skeleton; + private Armature armature; /** * The map between the bone index and its length. */ - private Map boneNodes = new HashMap(); - private Map nodeBones = new HashMap(); + private Map jointNode = new HashMap<>(); + private Map nodeJoint = new HashMap<>(); private Node selectedNode = null; - private boolean guessBonesOrientation = false; + private boolean guessJointsOrientation = false; /** * Creates a wire with bone lengths data. If the data is supplied then the * wires will show each full bone (from head to tail). * - * @param skeleton the skeleton that will be shown + * @param armature the armature that will be shown * @param boneLengths a map between the bone's index and the bone's length */ - public SkeletonBone(Skeleton skeleton, Map boneLengths, boolean guessBonesOrientation) { - this.skeleton = skeleton; - this.skeleton.reset(); - this.skeleton.updateWorldVectors(); - this.guessBonesOrientation = guessBonesOrientation; - - BoneShape boneShape = new BoneShape(5, 12, 0.02f, 0.07f, 1f, false, false); - Sphere jointShape = new Sphere(10, 10, 0.1f); + public ArmatureBone(Armature armature, Map boneLengths, boolean guessJointsOrientation) { + this.armature = armature; + this.guessJointsOrientation = guessJointsOrientation; + + BoneShape boneShape = new BoneShape(); + Sphere jointShape = new Sphere(16, 16, 0.05f); jointShape.setBuffer(VertexBuffer.Type.Color, 4, createFloatBuffer(jointShape.getVertexCount() * 4)); FloatBuffer cb = jointShape.getFloatBuffer(VertexBuffer.Type.Color); @@ -92,13 +85,13 @@ public class SkeletonBone extends Node { cb.put(0.05f).put(0.05f).put(0.05f).put(1f); } - for (Bone bone : skeleton.getRoots()) { - createSkeletonGeoms(bone, boneShape, jointShape, boneLengths, skeleton, this, guessBonesOrientation); + for (Joint joint : armature.getRoots()) { + createSkeletonGeoms(joint, boneShape, jointShape, boneLengths, armature, this, guessJointsOrientation); } this.updateModelBound(); - Sphere originShape = new Sphere(10, 10, 0.02f); + Sphere originShape = new Sphere(16, 16, 0.02f); originShape.setBuffer(VertexBuffer.Type.Color, 4, createFloatBuffer(originShape.getVertexCount() * 4)); cb = originShape.getFloatBuffer(VertexBuffer.Type.Color); cb.rewind(); @@ -118,91 +111,65 @@ public class SkeletonBone extends Node { } origin.scale(scale); attachChild(origin); - - - } - protected final void createSkeletonGeoms(Bone bone, Mesh boneShape, Mesh jointShape, Map boneLengths, Skeleton skeleton, Node parent, boolean guessBonesOrientation) { + protected final void createSkeletonGeoms(Joint joint, Mesh boneShape, Mesh jointShape, Map boneLengths, Armature armature, Node parent, boolean guessBonesOrientation) { - if (guessBonesOrientation && bone.getName().equalsIgnoreCase("Site")) { - //BVH skeleton have a useless end point bone named Site - return; - } - Node n = new Node(bone.getName() + "Node"); - Geometry bGeom = new Geometry(bone.getName(), boneShape); - Geometry jGeom = new Geometry(bone.getName() + "Joint", jointShape); - n.setLocalTranslation(bone.getLocalPosition()); - n.setLocalRotation(bone.getLocalRotation()); + Node n = new Node(joint.getName() + "Node"); + Geometry bGeom = new Geometry(joint.getName() + "Bone", boneShape); + Geometry jGeom = new Geometry(joint.getName() + "Joint", jointShape); + n.setLocalTransform(joint.getLocalTransform()); - float boneLength = boneLengths.get(skeleton.getBoneIndex(bone)); - n.setLocalScale(bone.getLocalScale()); - - bGeom.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X).normalizeLocal()); + float boneLength = boneLengths.get(armature.getJointIndex(joint)); if (guessBonesOrientation) { //One child only, the bone direction is from the parent joint to the child joint. - if (bone.getChildren().size() == 1) { - Vector3f v = bone.getChildren().get(0).getLocalPosition(); + if (joint.getChildren().size() == 1) { + Vector3f v = joint.getChildren().get(0).getLocalTranslation(); Quaternion q = new Quaternion(); q.lookAt(v, Vector3f.UNIT_Z); bGeom.setLocalRotation(q); - boneLength = v.length(); } //no child, the bone has the same direction as the parent bone. - if (bone.getChildren().isEmpty()) { - if (parent.getChildren().size() > 0) { - bGeom.setLocalRotation(parent.getChild(0).getLocalRotation()); - } else { - //no parent, let's use the bind orientation of the bone - bGeom.setLocalRotation(bone.getBindRotation()); + if (joint.getChildren().isEmpty()) { + //no parent, let's use the bind orientation of the bone + Spatial s = parent.getChild(0); + if (s != null) { + bGeom.setLocalRotation(s.getLocalRotation()); } } } - bGeom.setLocalScale(boneLength); - jGeom.setLocalScale(boneLength); - - n.attachChild(bGeom); - n.attachChild(jGeom); - //tip - if (bone.getChildren().size() != 1) { - Geometry gt = jGeom.clone(); - gt.scale(0.8f); - Vector3f v = new Vector3f(0, boneLength, 0); - if (guessBonesOrientation) { - if (bone.getChildren().isEmpty()) { - if (parent.getChildren().size() > 0) { - gt.setLocalTranslation(bGeom.getLocalRotation().mult(parent.getChild(0).getLocalRotation()).mult(v, v)); - } else { - gt.setLocalTranslation(bGeom.getLocalRotation().mult(bone.getBindRotation()).mult(v, v)); - } - } - } else { - gt.setLocalTranslation(v); - } + float boneScale = boneLength * 0.8f; + float scale = boneScale / 8f; + bGeom.setLocalScale(new Vector3f(scale, scale, boneScale)); + Vector3f offset = new Vector3f(0, 0, boneLength * 0.1f); + bGeom.getLocalRotation().multLocal(offset); + bGeom.setLocalTranslation(offset); + jGeom.setLocalScale(boneLength); - n.attachChild(gt); + if (joint.getChildren().size() <= 1) { + n.attachChild(bGeom); } + n.attachChild(jGeom); - - boneNodes.put(bone, n); - nodeBones.put(n, bone); + jointNode.put(joint, n); + nodeJoint.put(n, joint); parent.attachChild(n); - for (Bone childBone : bone.getChildren()) { - createSkeletonGeoms(childBone, boneShape, jointShape, boneLengths, skeleton, n, guessBonesOrientation); + for (Joint child : joint.getChildren()) { + createSkeletonGeoms(child, boneShape, jointShape, boneLengths, armature, n, guessBonesOrientation); } } - protected Bone select(Geometry g) { + protected Joint select(Geometry g) { Node parentNode = g.getParent(); if (parent != null) { - Bone b = nodeBones.get(parentNode); - if (b != null) { + Joint j = nodeJoint.get(parentNode); + if (j != null) { selectedNode = parentNode; } - return b; + return j; } return null; } @@ -212,17 +179,12 @@ public class SkeletonBone extends Node { } - protected final void updateSkeletonGeoms(Bone bone) { - if (guessBonesOrientation && bone.getName().equalsIgnoreCase("Site")) { - return; - } - Node n = boneNodes.get(bone); - n.setLocalTranslation(bone.getLocalPosition()); - n.setLocalRotation(bone.getLocalRotation()); - n.setLocalScale(bone.getLocalScale()); + protected final void updateSkeletonGeoms(Joint joint) { + Node n = jointNode.get(joint); + n.setLocalTransform(joint.getLocalTransform()); - for (Bone childBone : bone.getChildren()) { - updateSkeletonGeoms(childBone); + for (Joint child : joint.getChildren()) { + updateSkeletonGeoms(child); } } @@ -230,9 +192,8 @@ public class SkeletonBone extends Node { * The method updates the geometry according to the positions of the bones. */ public void updateGeometry() { - - for (Bone bone : skeleton.getRoots()) { - updateSkeletonGeoms(bone); + for (Joint joint : armature.getRoots()) { + updateSkeletonGeoms(joint); } } } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java new file mode 100644 index 000000000..73d0a7b01 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java @@ -0,0 +1,154 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.scene.debug.custom; + +import com.jme3.animation.*; +import com.jme3.app.Application; +import com.jme3.app.state.AbstractAppState; +import com.jme3.app.state.AppStateManager; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.MouseButtonTrigger; +import com.jme3.light.DirectionalLight; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.*; + +import java.util.*; + +/** + * @author Nehon + */ +public class ArmatureDebugAppState extends AbstractAppState { + + private Node debugNode = new Node("debugNode"); + private Map armatures = new HashMap<>(); + private Map selectedBones = new HashMap<>(); + private Application app; + + @Override + public void initialize(AppStateManager stateManager, Application app) { + ViewPort vp = app.getRenderManager().createMainView("debug", app.getCamera()); + vp.attachScene(debugNode); + vp.setClearDepth(true); + this.app = app; + for (ArmatureDebugger armatureDebugger : armatures.values()) { + armatureDebugger.initialize(app.getAssetManager()); + } + app.getInputManager().addListener(actionListener, "shoot"); + app.getInputManager().addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT), new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); + super.initialize(stateManager, app); + + + debugNode.addLight(new DirectionalLight(new Vector3f(-1f, -1f, -1f).normalizeLocal())); + + debugNode.addLight(new DirectionalLight(new Vector3f(1f, 1f, 1f).normalizeLocal(), new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f))); + + } + + @Override + public void update(float tpf) { + debugNode.updateLogicalState(tpf); + debugNode.updateGeometricState(); + } + + public ArmatureDebugger addArmature(ArmatureControl armatureControl, boolean guessJointsOrientation) { + Armature armature = armatureControl.getArmature(); + Spatial forSpatial = armatureControl.getSpatial(); + return addArmature(armature, forSpatial, guessJointsOrientation); + } + + public ArmatureDebugger addArmature(Armature armature, Spatial forSpatial, boolean guessJointsOrientation) { + + ArmatureDebugger ad = new ArmatureDebugger(forSpatial.getName() + "_Armature", armature, guessJointsOrientation); + ad.setLocalTransform(forSpatial.getWorldTransform()); + if (forSpatial instanceof Node) { + List geoms = new ArrayList<>(); + findGeoms((Node) forSpatial, geoms); + if (geoms.size() == 1) { + ad.setLocalTransform(geoms.get(0).getWorldTransform()); + } + } + armatures.put(armature, ad); + debugNode.attachChild(ad); + if (isInitialized()) { + ad.initialize(app.getAssetManager()); + } + return ad; + } + + private void findGeoms(Node node, List geoms) { + for (Spatial spatial : node.getChildren()) { + if (spatial instanceof Geometry) { + geoms.add((Geometry) spatial); + } else if (spatial instanceof Node) { + findGeoms((Node) spatial, geoms); + } + } + } + + /** + * Pick a Target Using the Mouse Pointer.
  1. Map "pick target" action + * to a MouseButtonTrigger.
  2. flyCam.setEnabled(false); + *
  3. inputManager.setCursorVisible(true);
  4. Implement action in + * AnalogListener (TODO).
+ */ + private ActionListener actionListener = new ActionListener() { + public void onAction(String name, boolean isPressed, float tpf) { + //if (name.equals("shoot") && isPressed) { +// CollisionResults results = new CollisionResults(); +// Vector2f click2d = app.getInputManager().getCursorPosition(); +// Vector3f click3d = app.getCamera().getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone(); +// Vector3f dir = app.getCamera().getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d); +// Ray ray = new Ray(click3d, dir); +// +// debugNode.collideWith(ray, results); +// +// if (results.size() > 0) { +// // The closest result is the target that the player picked: +// Geometry target = results.getClosestCollision().getGeometry(); +// for (ArmatureDebugger skeleton : armatures.values()) { +// Joint selectedBone = skeleton.select(target); +// if (selectedBone != null) { +// selectedBones.put(skeleton.getArmature(), selectedBone); +// System.err.println("-----------------------"); +// System.err.println("Selected Bone : " + selectedBone.getName() + " in skeleton " + skeleton.getName()); +// System.err.println("Root Bone : " + (selectedBone.getParent() == null)); +// System.err.println("-----------------------"); +// System.err.println("Bind translation: " + selectedBone.getBindPosition()); +// System.err.println("Bind rotation: " + selectedBone.getBindRotation()); +// System.err.println("Bind scale: " + selectedBone.getBindScale()); +// System.err.println("---"); +// System.err.println("Local translation: " + selectedBone.getLocalPosition()); +// System.err.println("Local rotation: " + selectedBone.getLocalRotation()); +// System.err.println("Local scale: " + selectedBone.getLocalScale()); +// System.err.println("---"); +// System.err.println("Model translation: " + selectedBone.getModelSpacePosition()); +// System.err.println("Model rotation: " + selectedBone.getModelSpaceRotation()); +// System.err.println("Model scale: " + selectedBone.getModelSpaceScale()); +// System.err.println("---"); +// System.err.println("Bind inverse Transform: "); +// System.err.println(selectedBone.getBindInverseTransform()); +// return; +// } +// } +// } +// } + } + }; + +// public Map getSelectedBones() { +// return selectedBones; +// } + + public Node getDebugNode() { + return debugNode; + } + + public void setDebugNode(Node debugNode) { + this.debugNode = debugNode; + } +} diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonDebugger.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java similarity index 63% rename from jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonDebugger.java rename to jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java index 1336b9d1b..7899cabac 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonDebugger.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java @@ -32,46 +32,37 @@ package com.jme3.scene.debug.custom; * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import com.jme3.animation.Bone; - -import java.util.Map; - -import com.jme3.animation.Skeleton; +import com.jme3.animation.*; import com.jme3.asset.AssetManager; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; -import com.jme3.scene.BatchNode; -import com.jme3.scene.Geometry; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.*; import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.util.*; /** * The class that creates a mesh to display how bones behave. If it is supplied * with the bones' lengths it will show exactly how the bones look like on the * scene. If not then only connections between each bone heads will be shown. */ -public class SkeletonDebugger extends BatchNode { +public class ArmatureDebugger extends BatchNode { /** * The lines of the bones or the wires between their heads. */ - private SkeletonBone bones; + private ArmatureBone bones; - private Skeleton skeleton; + private Armature armature; /** * The dotted lines between a bone's tail and the had of its children. Not * available if the length data was not provided. */ - private SkeletonInterBoneWire interBoneWires; - private List selectedBones = new ArrayList(); + private ArmatureInterJointsWire interJointWires; + private Geometry wires; + private List selectedJoints = new ArrayList(); - public SkeletonDebugger() { + public ArmatureDebugger() { } /** @@ -80,38 +71,42 @@ public class SkeletonDebugger extends BatchNode { * and no dotted line of inter bones connection will be visible. * * @param name the name of the debugger's node - * @param skeleton the skeleton that will be shown + * @param armature the armature that will be shown */ - public SkeletonDebugger(String name, Skeleton skeleton, boolean guessBonesOrientation) { + public ArmatureDebugger(String name, Armature armature, boolean guessJointsOrientation) { super(name); - this.skeleton = skeleton; - skeleton.reset(); - skeleton.updateWorldVectors(); - Map boneLengths = new HashMap(); - - for (Bone bone : skeleton.getRoots()) { - computeLength(bone, boneLengths, skeleton); + this.armature = armature; +// armature.reset(); + armature.update(); + //Joints have no length we want to display the as bones so we compute their length + Map bonesLength = new HashMap(); + + for (Joint joint : armature.getRoots()) { + computeLength(joint, bonesLength, armature); } - bones = new SkeletonBone(skeleton, boneLengths, guessBonesOrientation); + bones = new ArmatureBone(armature, bonesLength, guessJointsOrientation); this.attachChild(bones); - interBoneWires = new SkeletonInterBoneWire(skeleton, boneLengths, guessBonesOrientation); - Geometry g = new Geometry(name + "_interwires", interBoneWires); - g.setBatchHint(BatchHint.Never); - this.attachChild(g); + interJointWires = new ArmatureInterJointsWire(armature, bonesLength, guessJointsOrientation); + wires = new Geometry(name + "_interwires", interJointWires); + this.attachChild(wires); } protected void initialize(AssetManager assetManager) { - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", new ColorRGBA(0.05f, 0.05f, 0.05f, 1.0f));//new ColorRGBA(0.1f, 0.1f, 0.1f, 1.0f) + Material mat = new Material(assetManager, "Common/MatDefs/Misc/fakeLighting.j3md"); + mat.setColor("Color", new ColorRGBA(0.2f, 0.2f, 0.2f, 1)); setMaterial(mat); - Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat2.setBoolean("VertexColor", true); - bones.setMaterial(mat2); - batch(); + Material matWires = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + matWires.setColor("Color", ColorRGBA.Black); + wires.setMaterial(matWires); + //wires.setQueueBucket(RenderQueue.Bucket.Transparent); +// Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); +// mat2.setBoolean("VertexColor", true); +// bones.setMaterial(mat2); +// batch(); } @Override @@ -125,29 +120,29 @@ public class SkeletonDebugger extends BatchNode { } } - public Skeleton getSkeleton() { - return skeleton; + public Armature getArmature() { + return armature; } - private void computeLength(Bone b, Map boneLengths, Skeleton skeleton) { - if (b.getChildren().isEmpty()) { - if (b.getParent() != null) { - boneLengths.put(skeleton.getBoneIndex(b), boneLengths.get(skeleton.getBoneIndex(b.getParent())) * 0.75f); + private void computeLength(Joint joint, Map jointsLength, Armature armature) { + if (joint.getChildren().isEmpty()) { + if (joint.getParent() != null) { + jointsLength.put(armature.getJointIndex(joint), jointsLength.get(armature.getJointIndex(joint.getParent())) * 0.75f); } else { - boneLengths.put(skeleton.getBoneIndex(b), 0.1f); + jointsLength.put(armature.getJointIndex(joint), 0.1f); } } else { float length = Float.MAX_VALUE; - for (Bone bone : b.getChildren()) { - float len = b.getModelSpacePosition().subtract(bone.getModelSpacePosition()).length(); + for (Joint child : joint.getChildren()) { + float len = joint.getModelTransform().getTranslation().subtract(child.getModelTransform().getTranslation()).length(); if (len < length) { length = len; } } - boneLengths.put(skeleton.getBoneIndex(b), length); - for (Bone bone : b.getChildren()) { - computeLength(bone, boneLengths, skeleton); + jointsLength.put(armature.getJointIndex(joint), length); + for (Joint child : joint.getChildren()) { + computeLength(child, jointsLength, armature); } } } @@ -156,17 +151,17 @@ public class SkeletonDebugger extends BatchNode { public void updateLogicalState(float tpf) { super.updateLogicalState(tpf); bones.updateGeometry(); - if (interBoneWires != null) { - interBoneWires.updateGeometry(); + if (interJointWires != null) { + interJointWires.updateGeometry(); } } ColorRGBA selectedColor = ColorRGBA.Orange; ColorRGBA baseColor = new ColorRGBA(0.05f, 0.05f, 0.05f, 1f); - protected Bone select(Geometry g) { + protected Joint select(Geometry g) { Node oldNode = bones.getSelectedNode(); - Bone b = bones.select(g); + Joint b = bones.select(g); if (b == null) { return null; } @@ -178,17 +173,17 @@ public class SkeletonDebugger extends BatchNode { } /** - * @return the skeleton wires + * @return the armature wires */ - public SkeletonBone getBoneShapes() { + public ArmatureBone getBoneShapes() { return bones; } /** * @return the dotted line between bones (can be null) */ - public SkeletonInterBoneWire getInterBoneWires() { - return interBoneWires; + public ArmatureInterJointsWire getInterJointWires() { + return interJointWires; } protected void markSelected(Node n, boolean selected) { diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonInterBoneWire.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java similarity index 70% rename from jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonInterBoneWire.java rename to jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java index 504c81fe2..fbd97c1e0 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonInterBoneWire.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java @@ -33,34 +33,32 @@ package com.jme3.scene.debug.custom; */ -import java.nio.FloatBuffer; -import java.util.Map; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; +import com.jme3.animation.Armature; +import com.jme3.animation.Joint; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.VertexBuffer.*; import com.jme3.util.BufferUtils; +import java.nio.FloatBuffer; +import java.util.Map; + /** * A class that displays a dotted line between a bone tail and its childrens' heads. * * @author Marcin Roguski (Kaelthas) */ -public class SkeletonInterBoneWire extends Mesh { - private static final int POINT_AMOUNT = 10; +public class ArmatureInterJointsWire extends Mesh { + private static final int POINT_AMOUNT = 50; /** * The amount of connections between bones. */ private int connectionsAmount; /** - * The skeleton that will be showed. + * The armature that will be showed. */ - private Skeleton skeleton; + private Armature armature; /** * The map between the bone index and its length. */ @@ -71,18 +69,17 @@ public class SkeletonInterBoneWire extends Mesh { /** * Creates buffers for points. Each line has POINT_AMOUNT of points. * - * @param skeleton the skeleton that will be showed + * @param armature the armature that will be showed * @param boneLengths the lengths of the bones */ - public SkeletonInterBoneWire(Skeleton skeleton, Map boneLengths, boolean guessBonesOrientation) { - this.skeleton = skeleton; + public ArmatureInterJointsWire(Armature armature, Map boneLengths, boolean guessBonesOrientation) { + this.armature = armature; - for (Bone bone : skeleton.getRoots()) { - this.countConnections(bone); + for (Joint joint : armature.getRoots()) { + this.countConnections(joint); } this.setMode(Mode.Points); - this.setPointSize(2); this.boneLengths = boneLengths; VertexBuffer pb = new VertexBuffer(Type.Position); @@ -95,27 +92,28 @@ public class SkeletonInterBoneWire extends Mesh { } /** - * The method updates the geometry according to the poitions of the bones. + * The method updates the geometry according to the positions of the bones. */ public void updateGeometry() { VertexBuffer vb = this.getBuffer(Type.Position); FloatBuffer posBuf = this.getFloatBuffer(Type.Position); posBuf.clear(); - for (int i = 0; i < skeleton.getBoneCount(); ++i) { - Bone bone = skeleton.getBone(i); - Vector3f parentTail = bone.getModelSpacePosition().add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(boneLengths.get(i)))); + for (int i = 0; i < armature.getJointCount(); ++i) { + Joint joint = armature.getJoint(i); + Vector3f parentTail = joint.getModelTransform().getTranslation().add(joint.getModelTransform().getRotation().mult(Vector3f.UNIT_Y.mult(boneLengths.get(i)))); if (guessBonesOrientation) { - parentTail = bone.getModelSpacePosition(); + parentTail = joint.getModelTransform().getTranslation(); } - for (Bone child : bone.getChildren()) { - Vector3f childHead = child.getModelSpacePosition(); + for (Joint child : joint.getChildren()) { + Vector3f childHead = child.getModelTransform().getTranslation(); Vector3f v = childHead.subtract(parentTail); - float pointDelta = v.length() / POINT_AMOUNT; + float len = v.length(); + float pointDelta = 1f / POINT_AMOUNT; v.normalizeLocal().multLocal(pointDelta); Vector3f pointPosition = parentTail.clone(); - for (int j = 0; j < POINT_AMOUNT; ++j) { + for (int j = 0; j < POINT_AMOUNT * len; ++j) { posBuf.put(pointPosition.getX()).put(pointPosition.getY()).put(pointPosition.getZ()); pointPosition.addLocal(v); } @@ -128,12 +126,12 @@ public class SkeletonInterBoneWire extends Mesh { } /** - * Th method couns the connections between bones. + * Th method counts the connections between bones. * - * @param bone the bone where counting starts + * @param joint the bone where counting starts */ - private void countConnections(Bone bone) { - for (Bone child : bone.getChildren()) { + private void countConnections(Joint joint) { + for (Joint child : joint.getChildren()) { ++connectionsAmount; this.countConnections(child); } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/BoneShape.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/BoneShape.java index 1b6208619..702ec5613 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/BoneShape.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/BoneShape.java @@ -32,20 +32,11 @@ // $Id: Cylinder.java 4131 2009-03-19 20:15:28Z blaine.dev $ package com.jme3.scene.debug.custom; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.math.FastMath; -import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; +import com.jme3.math.*; import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.mesh.IndexBuffer; +import com.jme3.scene.shape.AbstractBox; import com.jme3.util.BufferUtils; -import static com.jme3.util.BufferUtils.*; - -import java.io.IOException; import java.nio.FloatBuffer; /** @@ -55,362 +46,125 @@ import java.nio.FloatBuffer; * @author Mark Powell * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $ */ -public class BoneShape extends Mesh { - - private int axisSamples; - - private int radialSamples; - - private float radius; - private float radius2; - - private float height; - private boolean closed; - private boolean inverted; - - /** - * Default constructor for serialization only. Do not use. - */ - public BoneShape() { +public class BoneShape extends AbstractBox { + + private static Vector3f topN = new Vector3f(0, 1, 0); + private static Vector3f botN = new Vector3f(0, -1, 0); + private static Vector3f rigN = new Vector3f(1, 0, 0); + private static Vector3f lefN = new Vector3f(-1, 0, 0); + + static { + Quaternion q = new Quaternion().fromAngleAxis(-FastMath.PI / 16f, Vector3f.UNIT_X); + q.multLocal(topN); + q.inverseLocal(); + q.multLocal(botN); + q = new Quaternion().fromAngleAxis(FastMath.PI / 16f, Vector3f.UNIT_Y); + q.multLocal(rigN); + q.inverseLocal(); + q.multLocal(lefN); } - /** - * Creates a new Cylinder. By default its center is the origin. Usually, a - * higher sample number creates a better looking cylinder, but at the cost - * of more vertex information. - * - * @param axisSamples Number of triangle samples along the axis. - * @param radialSamples Number of triangle samples along the radial. - * @param radius The radius of the cylinder. - * @param height The cylinder's height. - */ - public BoneShape(int axisSamples, int radialSamples, - float radius, float height) { - this(axisSamples, radialSamples, radius, height, false); - } - - /** - * Creates a new Cylinder. By default its center is the origin. Usually, a - * higher sample number creates a better looking cylinder, but at the cost - * of more vertex information.
- * If the cylinder is closed the texture is split into axisSamples parts: - * top most and bottom most part is used for top and bottom of the cylinder, - * rest of the texture for the cylinder wall. The middle of the top is - * mapped to texture coordinates (0.5, 1), bottom to (0.5, 0). Thus you need - * a suited distorted texture. - * - * @param axisSamples Number of triangle samples along the axis. - * @param radialSamples Number of triangle samples along the radial. - * @param radius The radius of the cylinder. - * @param height The cylinder's height. - * @param closed true to create a cylinder with top and bottom surface - */ - public BoneShape(int axisSamples, int radialSamples, - float radius, float height, boolean closed) { - this(axisSamples, radialSamples, radius, height, closed, false); - } + private static final short[] GEOMETRY_INDICES_DATA = { + 2, 1, 0, 3, 2, 0, // back + 6, 5, 4, 7, 6, 4, // right + 10, 9, 8, 11, 10, 8, // front + 14, 13, 12, 15, 14, 12, // left + 18, 17, 16, 19, 18, 16, // top + 22, 21, 20, 23, 22, 20 // bottom + }; + + private static final float[] GEOMETRY_NORMALS_DATA = { + 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, // back + rigN.x, rigN.y, rigN.z, rigN.x, rigN.y, rigN.z, rigN.x, rigN.y, rigN.z, rigN.x, rigN.y, rigN.z, // right + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // front + lefN.x, lefN.y, lefN.z, lefN.x, lefN.y, lefN.z, lefN.x, lefN.y, lefN.z, lefN.x, lefN.y, lefN.z, // left + topN.x, topN.y, topN.z, topN.x, topN.y, topN.z, topN.x, topN.y, topN.z, topN.x, topN.y, topN.z, // top + botN.x, botN.y, botN.z, botN.x, botN.y, botN.z, botN.x, botN.y, botN.z, botN.x, botN.y, botN.z // bottom + }; + + private static final float[] GEOMETRY_TEXTURE_DATA = { + 1, 0, 0, 0, 0, 1, 1, 1, // back + 1, 0, 0, 0, 0, 1, 1, 1, // right + 1, 0, 0, 0, 0, 1, 1, 1, // front + 1, 0, 0, 0, 0, 1, 1, 1, // left + 1, 0, 0, 0, 0, 1, 1, 1, // top + 1, 0, 0, 0, 0, 1, 1, 1 // bottom + }; + + private static final float[] GEOMETRY_POSITION_DATA = { + -0.5f, -0.5f, 0, 0.5f, -0.5f, 0, 0.5f, 0.5f, 0, -0.5f, 0.5f, 0, //back + 0.5f, -0.5f, 0, 0.25f, -0.25f, 1, 0.25f, 0.25f, 1, 0.5f, 0.5f, 0, //right + 0.25f, -0.25f, 1, -0.25f, -0.25f, 1, -0.25f, 0.25f, 1, 0.25f, 0.25f, 1, //front + -0.25f, -0.25f, 1, -0.5f, -0.5f, 0, -0.5f, 0.5f, 0, -0.25f, 0.25f, 1, //left + 0.5f, 0.5f, 0, 0.25f, 0.25f, 1, -0.25f, 0.25f, 1, -0.5f, 0.5f, 0, // top + -0.5f, -0.5f, 0, -0.25f, -0.25f, 1, 0.25f, -0.25f, 1, 0.5f, -0.5f, 0 // bottom + }; + + //0,1,2,3 + //1,4,6,2 + //4,5,7,6 + //5,0,3,7, + //2,6,7,3 + //0,5,4,1 + + +// v[0].x, v[0].y, v[0].z, v[1].x, v[1].y, v[1].z, v[2].x, v[2].y, v[2].z, v[3].x, v[3].y, v[3].z, // back +// v[1].x, v[1].y, v[1].z, v[4].x, v[4].y, v[4].z, v[6].x, v[6].y, v[6].z, v[2].x, v[2].y, v[2].z, // right +// v[4].x, v[4].y, v[4].z, v[5].x, v[5].y, v[5].z, v[7].x, v[7].y, v[7].z, v[6].x, v[6].y, v[6].z, // front +// v[5].x, v[5].y, v[5].z, v[0].x, v[0].y, v[0].z, v[3].x, v[3].y, v[3].z, v[7].x, v[7].y, v[7].z, // left +// v[2].x, v[2].y, v[2].z, v[6].x, v[6].y, v[6].z, v[7].x, v[7].y, v[7].z, v[3].x, v[3].y, v[3].z, // top +// v[0].x, v[0].y, v[0].z, v[5].x, v[5].y, v[5].z, v[4].x, v[4].y, v[4].z, v[1].x, v[1].y, v[1].z // bottom /** - * Creates a new Cylinder. By default its center is the origin. Usually, a - * higher sample number creates a better looking cylinder, but at the cost - * of more vertex information.
- * If the cylinder is closed the texture is split into axisSamples parts: - * top most and bottom most part is used for top and bottom of the cylinder, - * rest of the texture for the cylinder wall. The middle of the top is - * mapped to texture coordinates (0.5, 1), bottom to (0.5, 0). Thus you need - * a suited distorted texture. + * Creates a new box. + *

+ * The box has a center of 0,0,0 and extends in the out from the center by + * the given amount in each direction. So, for example, a box + * with extent of 0.5 would be the unit cube. * - * @param axisSamples Number of triangle samples along the axis. - * @param radialSamples Number of triangle samples along the radial. - * @param radius The radius of the cylinder. - * @param height The cylinder's height. - * @param closed true to create a cylinder with top and bottom surface - * @param inverted true to create a cylinder that is meant to be viewed from the - * interior. + * @param x the size of the box along the x axis, in both directions. + * @param y the size of the box along the y axis, in both directions. + * @param z the size of the box along the z axis, in both directions. */ - public BoneShape(int axisSamples, int radialSamples, - float radius, float height, boolean closed, boolean inverted) { - this(axisSamples, radialSamples, radius, radius, height, closed, inverted); - } - - public BoneShape(int axisSamples, int radialSamples, - float radius, float radius2, float height, boolean closed, boolean inverted) { + public BoneShape() { super(); - updateGeometry(axisSamples, radialSamples, radius, radius2, height, closed, inverted); - } - - /** - * @return the number of samples along the cylinder axis - */ - public int getAxisSamples() { - return axisSamples; - } - - /** - * @return Returns the height. - */ - public float getHeight() { - return height; - } - - /** - * @return number of samples around cylinder - */ - public int getRadialSamples() { - return radialSamples; - } - - /** - * @return Returns the radius. - */ - public float getRadius() { - return radius; - } - - public float getRadius2() { - return radius2; - } - - /** - * @return true if end caps are used. - */ - public boolean isClosed() { - return closed; + updateGeometry(); } /** - * @return true if normals and uvs are created for interior use + * Creates a clone of this box. + *

+ * The cloned box will have '_clone' appended to it's name, but all other + * properties will be the same as this box. */ - public boolean isInverted() { - return inverted; + @Override + public BoneShape clone() { + return new BoneShape(); } - /** - * Rebuilds the cylinder based on a new set of parameters. - * - * @param axisSamples the number of samples along the axis. - * @param radialSamples the number of samples around the radial. - * @param radius the radius of the bottom of the cylinder. - * @param radius2 the radius of the top of the cylinder. - * @param height the cylinder's height. - * @param closed should the cylinder have top and bottom surfaces. - * @param inverted is the cylinder is meant to be viewed from the inside. - */ - public void updateGeometry(int axisSamples, int radialSamples, - float radius, float radius2, float height, boolean closed, boolean inverted) { - this.axisSamples = axisSamples + (closed ? 2 : 0); - this.radialSamples = radialSamples; - this.radius = radius; - this.radius2 = radius2; - this.height = height; - this.closed = closed; - this.inverted = inverted; - -// VertexBuffer pvb = getBuffer(Type.Position); -// VertexBuffer nvb = getBuffer(Type.Normal); -// VertexBuffer tvb = getBuffer(Type.TexCoord); - - // Vertices - int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0); - - setBuffer(Type.Position, 3, createVector3Buffer(getFloatBuffer(Type.Position), vertCount)); - - // Normals - setBuffer(Type.Normal, 3, createVector3Buffer(getFloatBuffer(Type.Normal), vertCount)); - - // Texture co-ordinates - setBuffer(Type.TexCoord, 2, createVector2Buffer(vertCount)); - - int triCount = ((closed ? 2 : 0) + 2 * (axisSamples - 1)) * radialSamples; - - setBuffer(Type.Index, 3, createShortBuffer(getShortBuffer(Type.Index), 3 * triCount)); - - //Color - setBuffer(Type.Color, 4, createFloatBuffer(vertCount * 4)); - - // generate geometry - float inverseRadial = 1.0f / radialSamples; - float inverseAxisLess = 1.0f / (closed ? axisSamples - 3 : axisSamples - 1); - float inverseAxisLessTexture = 1.0f / (axisSamples - 1); - float halfHeight = 0.5f * height; - - // Generate points on the unit circle to be used in computing the mesh - // points on a cylinder slice. - float[] sin = new float[radialSamples + 1]; - float[] cos = new float[radialSamples + 1]; - - for (int radialCount = 0; radialCount < radialSamples; radialCount++) { - float angle = FastMath.TWO_PI * inverseRadial * radialCount; - cos[radialCount] = FastMath.cos(angle); - sin[radialCount] = FastMath.sin(angle); - } - sin[radialSamples] = sin[0]; - cos[radialSamples] = cos[0]; - - // calculate normals - Vector3f[] vNormals = null; - Vector3f vNormal = Vector3f.UNIT_Z; - - if ((height != 0.0f) && (radius != radius2)) { - vNormals = new Vector3f[radialSamples]; - Vector3f vHeight = Vector3f.UNIT_Z.mult(height); - Vector3f vRadial = new Vector3f(); - - for (int radialCount = 0; radialCount < radialSamples; radialCount++) { - vRadial.set(cos[radialCount], sin[radialCount], 0.0f); - Vector3f vRadius = vRadial.mult(radius); - Vector3f vRadius2 = vRadial.mult(radius2); - Vector3f vMantle = vHeight.subtract(vRadius2.subtract(vRadius)); - Vector3f vTangent = vRadial.cross(Vector3f.UNIT_Z); - vNormals[radialCount] = vMantle.cross(vTangent).normalize(); - } - } - - FloatBuffer nb = getFloatBuffer(Type.Normal); - FloatBuffer pb = getFloatBuffer(Type.Position); - FloatBuffer tb = getFloatBuffer(Type.TexCoord); - FloatBuffer cb = getFloatBuffer(Type.Color); - - cb.rewind(); - for (int i = 0; i < vertCount; i++) { - cb.put(0.05f).put(0.05f).put(0.05f).put(1f); - } - - // generate the cylinder itself - Vector3f tempNormal = new Vector3f(); - for (int axisCount = 0, i = 0; axisCount < axisSamples; axisCount++, i++) { - float axisFraction; - float axisFractionTexture; - int topBottom = 0; - if (!closed) { - axisFraction = axisCount * inverseAxisLess; // in [0,1] - axisFractionTexture = axisFraction; - } else { - if (axisCount == 0) { - topBottom = -1; // bottom - axisFraction = 0; - axisFractionTexture = inverseAxisLessTexture; - } else if (axisCount == axisSamples - 1) { - topBottom = 1; // top - axisFraction = 1; - axisFractionTexture = 1 - inverseAxisLessTexture; - } else { - axisFraction = (axisCount - 1) * inverseAxisLess; - axisFractionTexture = axisCount * inverseAxisLessTexture; - } - } - - // compute center of slice - float z = height * axisFraction; - Vector3f sliceCenter = new Vector3f(0, 0, z); - - // compute slice vertices with duplication at end point - int save = i; - for (int radialCount = 0; radialCount < radialSamples; radialCount++, i++) { - float radialFraction = radialCount * inverseRadial; // in [0,1) - tempNormal.set(cos[radialCount], sin[radialCount], 0.0f); - - if (vNormals != null) { - vNormal = vNormals[radialCount]; - } else if (radius == radius2) { - vNormal = tempNormal; - } - - if (topBottom == 0) { - if (!inverted) - nb.put(vNormal.x).put(vNormal.y).put(vNormal.z); - else - nb.put(-vNormal.x).put(-vNormal.y).put(-vNormal.z); - } else { - nb.put(0).put(0).put(topBottom * (inverted ? -1 : 1)); - } - - tempNormal.multLocal((radius - radius2) * axisFraction + radius2) - .addLocal(sliceCenter); - pb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z); - - tb.put((inverted ? 1 - radialFraction : radialFraction)) - .put(axisFractionTexture); - } - - BufferUtils.copyInternalVector3(pb, save, i); - BufferUtils.copyInternalVector3(nb, save, i); - - tb.put((inverted ? 0.0f : 1.0f)) - .put(axisFractionTexture); - } - - if (closed) { - pb.put(0).put(0).put(-halfHeight); // bottom center - nb.put(0).put(0).put(-1 * (inverted ? -1 : 1)); - tb.put(0.5f).put(0); - pb.put(0).put(0).put(halfHeight); // top center - nb.put(0).put(0).put(1 * (inverted ? -1 : 1)); - tb.put(0.5f).put(1); + protected void doUpdateGeometryIndices() { + if (getBuffer(Type.Index) == null) { + setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(GEOMETRY_INDICES_DATA)); } + } - IndexBuffer ib = getIndexBuffer(); - int index = 0; - // Connectivity - for (int axisCount = 0, axisStart = 0; axisCount < axisSamples - 1; axisCount++) { - int i0 = axisStart; - int i1 = i0 + 1; - axisStart += radialSamples + 1; - int i2 = axisStart; - int i3 = i2 + 1; - for (int i = 0; i < radialSamples; i++) { - if (closed && axisCount == 0) { - if (!inverted) { - ib.put(index++, i0++); - ib.put(index++, vertCount - 2); - ib.put(index++, i1++); - } else { - ib.put(index++, i0++); - ib.put(index++, i1++); - ib.put(index++, vertCount - 2); - } - } else if (closed && axisCount == axisSamples - 2) { - ib.put(index++, i2++); - ib.put(index++, inverted ? vertCount - 1 : i3++); - ib.put(index++, inverted ? i3++ : vertCount - 1); - } else { - ib.put(index++, i0++); - ib.put(index++, inverted ? i2 : i1); - ib.put(index++, inverted ? i1 : i2); - ib.put(index++, i1++); - ib.put(index++, inverted ? i2++ : i3++); - ib.put(index++, inverted ? i3++ : i2++); - } - } + protected void doUpdateGeometryNormals() { + if (getBuffer(Type.Normal) == null) { + setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(GEOMETRY_NORMALS_DATA)); } - - updateBound(); } - @Override - public void read(JmeImporter e) throws IOException { - super.read(e); - InputCapsule capsule = e.getCapsule(this); - axisSamples = capsule.readInt("axisSamples", 0); - radialSamples = capsule.readInt("radialSamples", 0); - radius = capsule.readFloat("radius", 0); - radius2 = capsule.readFloat("radius2", 0); - height = capsule.readFloat("height", 0); - closed = capsule.readBoolean("closed", false); - inverted = capsule.readBoolean("inverted", false); + protected void doUpdateGeometryTextures() { + if (getBuffer(Type.TexCoord) == null) { + setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(GEOMETRY_TEXTURE_DATA)); + } } - @Override - public void write(JmeExporter e) throws IOException { - super.write(e); - OutputCapsule capsule = e.getCapsule(this); - capsule.write(axisSamples, "axisSamples", 0); - capsule.write(radialSamples, "radialSamples", 0); - capsule.write(radius, "radius", 0); - capsule.write(radius2, "radius2", 0); - capsule.write(height, "height", 0); - capsule.write(closed, "closed", false); - capsule.write(inverted, "inverted", false); + protected void doUpdateGeometryVertices() { + FloatBuffer fpb = BufferUtils.createVector3Buffer(24); + fpb.put(GEOMETRY_POSITION_DATA); + setBuffer(Type.Position, 3, fpb); + updateBound(); } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonDebugAppState.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonDebugAppState.java deleted file mode 100644 index 43778f5ce..000000000 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/SkeletonDebugAppState.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.jme3.scene.debug.custom; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.animation.SkeletonControl; -import com.jme3.app.Application; -import com.jme3.app.state.AbstractAppState; -import com.jme3.app.state.AppStateManager; -import com.jme3.collision.CollisionResults; -import com.jme3.input.MouseInput; -import com.jme3.input.controls.ActionListener; -import com.jme3.input.controls.MouseButtonTrigger; -import com.jme3.math.Ray; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.Geometry; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author Nehon - */ -public class SkeletonDebugAppState extends AbstractAppState { - - private Node debugNode = new Node("debugNode"); - private Map skeletons = new HashMap(); - private Map selectedBones = new HashMap(); - private Application app; - - @Override - public void initialize(AppStateManager stateManager, Application app) { - ViewPort vp = app.getRenderManager().createMainView("debug", app.getCamera()); - vp.attachScene(debugNode); - vp.setClearDepth(true); - this.app = app; - for (SkeletonDebugger skeletonDebugger : skeletons.values()) { - skeletonDebugger.initialize(app.getAssetManager()); - } - app.getInputManager().addListener(actionListener, "shoot"); - app.getInputManager().addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT), new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); - super.initialize(stateManager, app); - } - - @Override - public void update(float tpf) { - debugNode.updateLogicalState(tpf); - debugNode.updateGeometricState(); - } - - public SkeletonDebugger addSkeleton(SkeletonControl skeletonControl, boolean guessBonesOrientation) { - Skeleton skeleton = skeletonControl.getSkeleton(); - Spatial forSpatial = skeletonControl.getSpatial(); - return addSkeleton(skeleton, forSpatial, guessBonesOrientation); - } - - public SkeletonDebugger addSkeleton(Skeleton skeleton, Spatial forSpatial, boolean guessBonesOrientation) { - - SkeletonDebugger sd = new SkeletonDebugger(forSpatial.getName() + "_Skeleton", skeleton, guessBonesOrientation); - sd.setLocalTransform(forSpatial.getWorldTransform()); - if (forSpatial instanceof Node) { - List geoms = new ArrayList<>(); - findGeoms((Node) forSpatial, geoms); - if (geoms.size() == 1) { - sd.setLocalTransform(geoms.get(0).getWorldTransform()); - } - } - skeletons.put(skeleton, sd); - debugNode.attachChild(sd); - if (isInitialized()) { - sd.initialize(app.getAssetManager()); - } - return sd; - } - - private void findGeoms(Node node, List geoms) { - for (Spatial spatial : node.getChildren()) { - if (spatial instanceof Geometry) { - geoms.add((Geometry) spatial); - } else if (spatial instanceof Node) { - findGeoms((Node) spatial, geoms); - } - } - } - - /** - * Pick a Target Using the Mouse Pointer.

  1. Map "pick target" action - * to a MouseButtonTrigger.
  2. flyCam.setEnabled(false); - *
  3. inputManager.setCursorVisible(true);
  4. Implement action in - * AnalogListener (TODO).
- */ - private ActionListener actionListener = new ActionListener() { - public void onAction(String name, boolean isPressed, float tpf) { - if (name.equals("shoot") && isPressed) { - CollisionResults results = new CollisionResults(); - Vector2f click2d = app.getInputManager().getCursorPosition(); - Vector3f click3d = app.getCamera().getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone(); - Vector3f dir = app.getCamera().getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d); - Ray ray = new Ray(click3d, dir); - - debugNode.collideWith(ray, results); - - if (results.size() > 0) { - // The closest result is the target that the player picked: - Geometry target = results.getClosestCollision().getGeometry(); - for (SkeletonDebugger skeleton : skeletons.values()) { - Bone selectedBone = skeleton.select(target); - if (selectedBone != null) { - selectedBones.put(skeleton.getSkeleton(), selectedBone); - System.err.println("-----------------------"); - System.err.println("Selected Bone : " + selectedBone.getName() + " in skeleton " + skeleton.getName()); - System.err.println("Root Bone : " + (selectedBone.getParent() == null)); - System.err.println("-----------------------"); - System.err.println("Bind translation: " + selectedBone.getBindPosition()); - System.err.println("Bind rotation: " + selectedBone.getBindRotation()); - System.err.println("Bind scale: " + selectedBone.getBindScale()); - System.err.println("---"); - System.err.println("Local translation: " + selectedBone.getLocalPosition()); - System.err.println("Local rotation: " + selectedBone.getLocalRotation()); - System.err.println("Local scale: " + selectedBone.getLocalScale()); - System.err.println("---"); - System.err.println("Model translation: " + selectedBone.getModelSpacePosition()); - System.err.println("Model rotation: " + selectedBone.getModelSpaceRotation()); - System.err.println("Model scale: " + selectedBone.getModelSpaceScale()); - System.err.println("---"); - System.err.println("Bind inverse Transform: "); - System.err.println(selectedBone.getBindInverseTransform()); - return; - } - } - } - } - } - }; - - public Map getSelectedBones() { - return selectedBones; - } - - public Node getDebugNode() { - return debugNode; - } - - public void setDebugNode(Node debugNode) { - this.debugNode = debugNode; - } -} diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/AbstractBox.java b/jme3-core/src/main/java/com/jme3/scene/shape/AbstractBox.java index 7ffee1905..1571823a7 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/AbstractBox.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/AbstractBox.java @@ -31,12 +31,10 @@ */ package com.jme3.scene.shape; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; +import com.jme3.export.*; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; + import java.io.IOException; /** @@ -88,12 +86,12 @@ public abstract class AbstractBox extends Mesh { /** * Convert the indices into the list of vertices that define the box's geometry. */ - protected abstract void duUpdateGeometryIndices(); + protected abstract void doUpdateGeometryIndices(); /** * Update the normals of each of the box's planes. */ - protected abstract void duUpdateGeometryNormals(); + protected abstract void doUpdateGeometryNormals(); /** * Update the points that define the texture of the box. @@ -101,14 +99,14 @@ public abstract class AbstractBox extends Mesh { * It's a one-to-one ratio, where each plane of the box has its own copy * of the texture. That is, the texture is repeated one time for each face. */ - protected abstract void duUpdateGeometryTextures(); + protected abstract void doUpdateGeometryTextures(); /** * Update the position of the vertices that define the box. *

* These eight points are determined from the minimum and maximum point. */ - protected abstract void duUpdateGeometryVertices(); + protected abstract void doUpdateGeometryVertices(); /** * Get the center point of this box. @@ -145,10 +143,10 @@ public abstract class AbstractBox extends Mesh { * need to call this method afterwards in order to update the box. */ public final void updateGeometry() { - duUpdateGeometryVertices(); - duUpdateGeometryNormals(); - duUpdateGeometryTextures(); - duUpdateGeometryIndices(); + doUpdateGeometryVertices(); + doUpdateGeometryNormals(); + doUpdateGeometryTextures(); + doUpdateGeometryIndices(); setStatic(); } diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Box.java b/jme3-core/src/main/java/com/jme3/scene/shape/Box.java index f94611de3..15b25d24b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Box.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Box.java @@ -35,6 +35,7 @@ package com.jme3.scene.shape; import com.jme3.math.Vector3f; import com.jme3.scene.VertexBuffer.Type; import com.jme3.util.BufferUtils; + import java.nio.FloatBuffer; /** @@ -144,25 +145,25 @@ public class Box extends AbstractBox { return new Box(center.clone(), xExtent, yExtent, zExtent); } - protected void duUpdateGeometryIndices() { + protected void doUpdateGeometryIndices() { if (getBuffer(Type.Index) == null){ setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(GEOMETRY_INDICES_DATA)); } } - protected void duUpdateGeometryNormals() { + protected void doUpdateGeometryNormals() { if (getBuffer(Type.Normal) == null){ setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(GEOMETRY_NORMALS_DATA)); } } - protected void duUpdateGeometryTextures() { + protected void doUpdateGeometryTextures() { if (getBuffer(Type.TexCoord) == null){ setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(GEOMETRY_TEXTURE_DATA)); } } - protected void duUpdateGeometryVertices() { + protected void doUpdateGeometryVertices() { FloatBuffer fpb = BufferUtils.createVector3Buffer(24); Vector3f[] v = computeVertices(); fpb.put(new float[] { diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/StripBox.java b/jme3-core/src/main/java/com/jme3/scene/shape/StripBox.java index 312650caa..5ac256e93 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/StripBox.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/StripBox.java @@ -35,6 +35,7 @@ package com.jme3.scene.shape; import com.jme3.math.Vector3f; import com.jme3.scene.VertexBuffer.Type; import com.jme3.util.BufferUtils; + import java.nio.FloatBuffer; /** @@ -138,13 +139,13 @@ public class StripBox extends AbstractBox { return new StripBox(center.clone(), xExtent, yExtent, zExtent); } - protected void duUpdateGeometryIndices() { + protected void doUpdateGeometryIndices() { if (getBuffer(Type.Index) == null){ setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(GEOMETRY_INDICES_DATA)); } } - protected void duUpdateGeometryNormals() { + protected void doUpdateGeometryNormals() { if (getBuffer(Type.Normal) == null){ float[] normals = new float[8 * 3]; @@ -163,13 +164,13 @@ public class StripBox extends AbstractBox { } } - protected void duUpdateGeometryTextures() { + protected void doUpdateGeometryTextures() { if (getBuffer(Type.TexCoord) == null){ setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(GEOMETRY_TEXTURE_DATA)); } } - protected void duUpdateGeometryVertices() { + protected void doUpdateGeometryVertices() { FloatBuffer fpb = BufferUtils.createVector3Buffer(8 * 3); Vector3f[] v = computeVertices(); fpb.put(new float[] { diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/fakeLighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Misc/fakeLighting.j3md new file mode 100644 index 000000000..58797c112 --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/fakeLighting.j3md @@ -0,0 +1,49 @@ +MaterialDef FakeLighting { + MaterialParameters { + Vector4 Color + } + + Technique { + WorldParameters { + WorldViewProjectionMatrix + NormalMatrix + } + + VertexShaderNodes { + ShaderNode Mat3Vec3Mult { + Definition: Mat3Vec3Mult: Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult.j3sn + InputMappings { + matrix3 = WorldParam.NormalMatrix + vector3 = Attr.inNormal + } + OutputMappings { + } + } + ShaderNode CommonVert { + Definition: CommonVert: Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn + InputMappings { + worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix + modelPosition = Attr.inPosition + } + OutputMappings { + Global.position = projPosition + } + } + } + + + FragmentShaderNodes { + ShaderNode FakeLighting { + Definition: FakeLighting: Common/MatDefs/ShaderNodes/Misc/fakeLighting.j3sn + InputMappings { + inColor = MatParam.Color + normal = Mat3Vec3Mult.outVector3.xyz + } + OutputMappings { + Global.color = outColor + } + } + } + + } +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult.j3sn b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult.j3sn new file mode 100644 index 000000000..019edb839 --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult.j3sn @@ -0,0 +1,31 @@ +ShaderNodeDefinitions{ + ShaderNodeDefinition Mat3Vec3Mult { + //Vertex/Fragment + Type: Vertex + + //Shader GLSL: + Shader GLSL100: Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult100.frag + + Documentation{ + //type documentation here. This is optional but recommended + + //@input + @input mat3 matrix3 the mat3 + @input vec3 vector3 the vec3 + + //@output + @output vec3 outVector3 the output vector + } + Input { + //all the node inputs + // + mat3 matrix3 + vec3 vector3 + } + Output { + //all the node outputs + // + vec3 outVector3 + } + } +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult100.frag b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult100.frag new file mode 100644 index 000000000..f1a29fd2c --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/Mat3Vec3Mult100.frag @@ -0,0 +1,3 @@ +void main(){ + outVector3 = matrix3 * vector3; +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/fakeLighting.j3sn b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/fakeLighting.j3sn new file mode 100644 index 000000000..c47e1bf61 --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/fakeLighting.j3sn @@ -0,0 +1,33 @@ +ShaderNodeDefinitions{ + ShaderNodeDefinition FakeLighting { + //Vertex/Fragment + Type: Fragment + + //Shader GLSL: + Shader GLSL100: Common/MatDefs/ShaderNodes/Misc/fakeLighting100.frag + + Documentation{ + //type documentation here. This is optional but recommended + + //@input + @input vec4 inColor The input color + @input vec3 normal The normal in view space + + + //@output + @output vec4 outColor The modified output color + } + Input { + //all the node inputs + // + vec4 inColor + vec3 normal + + } + Output { + //all the node outputs + // + vec4 outColor + } + } +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/fakeLighting100.frag b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/fakeLighting100.frag new file mode 100644 index 000000000..9b6b98b09 --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/fakeLighting100.frag @@ -0,0 +1,9 @@ +void main(){ + + vec4 dark = inColor * 0.3; + vec4 bright = min(inColor * 4.0, 1.0); + normal = normalize(normal); + vec3 dir = vec3(0,0,1); + float factor = dot(dir, normal); + outColor = mix(dark, bright, factor); +} diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java new file mode 100644 index 000000000..c4f23e882 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java @@ -0,0 +1,112 @@ +package jme3test.model.anim; + +import com.jme3.animation.*; +import com.jme3.app.ChaseCameraAppState; +import com.jme3.app.SimpleApplication; +import com.jme3.light.DirectionalLight; +import com.jme3.material.Material; +import com.jme3.math.*; +import com.jme3.scene.*; +import com.jme3.scene.debug.custom.ArmatureDebugAppState; +import com.jme3.util.TangentBinormalGenerator; + +/** + * Created by Nehon on 18/12/2017. + */ +public class TestArmature extends SimpleApplication { + + Joint j1; + Joint j2; + + public static void main(String... argv) { + TestArmature app = new TestArmature(); + app.start(); + } + + @Override + public void simpleInitApp() { + renderManager.setSinglePassLightBatchSize(2); + //cam.setFrustumPerspective(90f, (float) cam.getWidth() / cam.getHeight(), 0.01f, 10f); + viewPort.setBackgroundColor(ColorRGBA.DarkGray); + + Joint root = new Joint("Root_Joint"); + j1 = new Joint("Joint_1"); + j2 = new Joint("Joint_2"); + root.addChild(j1); + j1.addChild(j2); + j1.setLocalTranslation(new Vector3f(0, 0.5f, 0)); + j1.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI * 0.3f, Vector3f.UNIT_Z)); + j2.setLocalTranslation(new Vector3f(0, 0.2f, 0)); + Joint[] joints = new Joint[]{root, j1, j2}; + + Armature armature = new Armature(joints); + armature.setBindPose(); + + ArmatureControl ac = new ArmatureControl(armature); + Node node = new Node("Test Armature"); + rootNode.attachChild(node); + + node.addControl(ac); + + ArmatureDebugAppState debugAppState = new ArmatureDebugAppState(); + debugAppState.addArmature(ac, true); + stateManager.attach(debugAppState); + + rootNode.addLight(new DirectionalLight(new Vector3f(-1f, -1f, -1f).normalizeLocal())); + + rootNode.addLight(new DirectionalLight(new Vector3f(1f, 1f, 1f).normalizeLocal(), new ColorRGBA(0.7f, 0.7f, 0.7f, 1.0f))); + + + flyCam.setEnabled(false); + + ChaseCameraAppState chaseCam = new ChaseCameraAppState(); + chaseCam.setTarget(node); + getStateManager().attach(chaseCam); + chaseCam.setInvertHorizontalAxis(true); + chaseCam.setInvertVerticalAxis(true); + chaseCam.setZoomSpeed(0.5f); + chaseCam.setMinVerticalRotation(-FastMath.HALF_PI); + chaseCam.setRotationSpeed(3); + chaseCam.setDefaultDistance(3); + chaseCam.setMinDistance(0.01f); + chaseCam.setZoomSpeed(0.01f); + chaseCam.setDefaultVerticalRotation(0.3f); + } + + + private void displayNormals(Spatial s) { + final Node debugTangents = new Node("debug tangents"); + debugTangents.setCullHint(Spatial.CullHint.Never); + + rootNode.attachChild(debugTangents); + + final Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m"); + debugMat.getAdditionalRenderState().setLineWidth(2); + + s.depthFirstTraversal(new SceneGraphVisitorAdapter() { + @Override + public void visit(Geometry g) { + Mesh m = g.getMesh(); + Geometry debug = new Geometry( + "debug tangents geom", + TangentBinormalGenerator.genNormalLines(m, 0.1f) + ); + debug.setMaterial(debugMat); + debug.setCullHint(Spatial.CullHint.Never); + debug.setLocalTransform(g.getWorldTransform()); + debugTangents.attachChild(debug); + } + }); + } + + float time = 0; + + @Override + public void simpleUpdate(float tpf) { + time += tpf; + float rot = FastMath.sin(time); + j1.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI * rot, Vector3f.UNIT_Z)); + j2.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI * rot, Vector3f.UNIT_Z)); + + } +}