Adds a MigrationUtil to migrate from old system to new system

shader-nodes-enhancement
Nehon 7 years ago committed by Rémy Bouquet
parent e5057ad7fa
commit 728e28857b
  1. 11
      jme3-core/src/main/java/com/jme3/anim/AnimComposer.java
  2. 2
      jme3-core/src/main/java/com/jme3/anim/Armature.java
  3. 11
      jme3-core/src/main/java/com/jme3/anim/Joint.java
  4. 175
      jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java
  5. 35
      jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java
  6. 147
      jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java
  7. 38
      jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java

@ -4,8 +4,7 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* Created by Nehon on 20/12/2017.
@ -65,6 +64,14 @@ public class AnimComposer extends AbstractControl {
time = 0;
}
public Collection<AnimClip> getAnimClips() {
return Collections.unmodifiableCollection(animClipMap.values());
}
public Collection<String> getAnimClipsNames() {
return Collections.unmodifiableCollection(animClipMap.keySet());
}
@Override
protected void controlUpdate(float tpf) {
if (currentAnimClip != null) {

@ -169,7 +169,7 @@ public class Armature implements JmeCloneable, Savable {
}
/**
* Saves the current Armature state as it's bind pose.
* Saves the current Armature state as its bind pose.
*/
public void setBindPose() {
//make sure all bones are updated

@ -34,11 +34,6 @@ public class Joint implements Savable, JmeCloneable {
* Or relative to the model's origin for the root joint.
*/
private Transform localTransform = new Transform();
/**
* The base transform of the joint in local space.
* Those transform are the joint's initial value.
*/
private Transform baseLocalTransform = new Transform();
/**
* The transform of the joint in model space. Relative to the origin of the model.
@ -134,7 +129,6 @@ public class Joint implements Savable, JmeCloneable {
//Note that the whole Armature must be updated before calling this method.
getModelTransform().toTransformMatrix(inverseModelBindMatrix);
inverseModelBindMatrix.invertLocal();
baseLocalTransform.set(localTransform);
}
protected void resetToBindPose() {
@ -267,8 +261,6 @@ public class Joint implements Savable, JmeCloneable {
this.children = cloner.clone(children);
this.attachedNode = cloner.clone(attachedNode);
this.targetGeometry = cloner.clone(targetGeometry);
this.baseLocalTransform = cloner.clone(baseLocalTransform);
this.localTransform = cloner.clone(localTransform);
this.jointModelTransform = cloner.clone(jointModelTransform);
this.inverseModelBindMatrix = cloner.clone(inverseModelBindMatrix);
@ -283,8 +275,6 @@ public class Joint implements Savable, JmeCloneable {
name = input.readString("name", null);
attachedNode = (Node) input.readSavable("attachedNode", null);
targetGeometry = (Geometry) input.readSavable("targetGeometry", null);
baseLocalTransform = (Transform) input.readSavable("baseLocalTransforms", baseLocalTransform);
localTransform.set(baseLocalTransform);
inverseModelBindMatrix = (Matrix4f) input.readSavable("inverseModelBindMatrix", inverseModelBindMatrix);
jointModelTransform = (JointModelTransform) input.readSavable("jointModelTransform", null);
@ -301,7 +291,6 @@ public class Joint implements Savable, JmeCloneable {
output.write(name, "name", null);
output.write(attachedNode, "attachedNode", null);
output.write(targetGeometry, "targetGeometry", null);
output.write(baseLocalTransform, "baseLocalTransform", new Transform());
output.write(inverseModelBindMatrix, "inverseModelBindMatrix", new Matrix4f());
output.writeSavableArrayList(children, "children", null);
output.write(jointModelTransform, "jointModelTransform", null);

@ -1,24 +1,189 @@
package com.jme3.anim.util;
import com.jme3.animation.AnimControl;
import com.jme3.scene.SceneGraphVisitor;
import com.jme3.scene.Spatial;
import com.jme3.anim.*;
import com.jme3.animation.*;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.*;
import java.util.*;
public class AnimMigrationUtils {
private static AnimControlVisitor animControlVisitor = new AnimControlVisitor();
private static SkeletonControlVisitor skeletonControlVisitor = new SkeletonControlVisitor();
public static Spatial migrate(Spatial source) {
//source.depthFirstTraversal();
Map<Skeleton, Armature> skeletonArmatureMap = new HashMap<>();
animControlVisitor.setMappings(skeletonArmatureMap);
source.depthFirstTraversal(animControlVisitor);
skeletonControlVisitor.setMappings(skeletonArmatureMap);
source.depthFirstTraversal(skeletonControlVisitor);
return source;
}
private class AnimControlVisitor implements SceneGraphVisitor {
private static class AnimControlVisitor implements SceneGraphVisitor {
Map<Skeleton, Armature> skeletonArmatureMap;
@Override
public void visit(Spatial spatial) {
AnimControl control = spatial.getControl(AnimControl.class);
if (control != null) {
AnimComposer composer = new AnimComposer();
Skeleton skeleton = control.getSkeleton();
if (skeleton == null) {
//only bone anim for now
return;
}
Joint[] joints = new Joint[skeleton.getBoneCount()];
for (int i = 0; i < skeleton.getBoneCount(); i++) {
Bone b = skeleton.getBone(i);
Joint j = joints[i];
if (j == null) {
j = fromBone(b);
joints[i] = j;
}
for (Bone bone : b.getChildren()) {
int index = skeleton.getBoneIndex(bone);
Joint joint = joints[index];
if (joint == null) {
joint = fromBone(bone);
}
j.addChild(joint);
joints[index] = joint;
}
}
Armature armature = new Armature(joints);
armature.setBindPose();
skeletonArmatureMap.put(skeleton, armature);
for (String animName : control.getAnimationNames()) {
Animation anim = control.getAnim(animName);
AnimClip clip = new AnimClip(animName);
Joint[] staticJoints = new Joint[joints.length];
System.arraycopy(joints, 0, staticJoints, 0, joints.length);
for (Track track : anim.getTracks()) {
if (track instanceof BoneTrack) {
BoneTrack boneTrack = (BoneTrack) track;
int index = boneTrack.getTargetBoneIndex();
Bone bone = skeleton.getBone(index);
Joint joint = joints[index];
JointTrack jointTrack = fromBoneTrack(boneTrack, bone, joint);
clip.addTrack(jointTrack);
//this joint is animated let's remove it from the static joints
staticJoints[index] = null;
}
}
for (int i = 0; i < staticJoints.length; i++) {
Joint j = staticJoints[i];
if (j != null) {
// joint has no track , we create one with the default pose
float[] times = new float[]{0};
Vector3f[] translations = new Vector3f[]{j.getLocalTranslation()};
Quaternion[] rotations = new Quaternion[]{j.getLocalRotation()};
Vector3f[] scales = new Vector3f[]{j.getLocalScale()};
JointTrack track = new JointTrack(j, times, translations, rotations, scales);
clip.addTrack(track);
}
}
composer.addAnimClip(clip);
}
spatial.removeControl(control);
spatial.addControl(composer);
}
}
public void setMappings(Map<Skeleton, Armature> skeletonArmatureMap) {
this.skeletonArmatureMap = skeletonArmatureMap;
}
}
private static class SkeletonControlVisitor implements SceneGraphVisitor {
Map<Skeleton, Armature> skeletonArmatureMap;
@Override
public void visit(Spatial spatial) {
SkeletonControl control = spatial.getControl(SkeletonControl.class);
if (control != null) {
Armature armature = skeletonArmatureMap.get(control.getSkeleton());
SkinningControl skinningControl = new SkinningControl(armature);
Map<String, List<Spatial>> attachedSpatials = new HashMap<>();
for (int i = 0; i < control.getSkeleton().getBoneCount(); i++) {
Bone b = control.getSkeleton().getBone(i);
Node n = control.getAttachmentsNode(b.getName());
n.removeFromParent();
if (!n.getChildren().isEmpty()) {
attachedSpatials.put(b.getName(), n.getChildren());
}
}
spatial.removeControl(control);
spatial.addControl(skinningControl);
for (String name : attachedSpatials.keySet()) {
List<Spatial> spatials = attachedSpatials.get(name);
for (Spatial child : spatials) {
skinningControl.getAttachmentsNode(name).attachChild(child);
}
}
}
}
public void setMappings(Map<Skeleton, Armature> skeletonArmatureMap) {
this.skeletonArmatureMap = skeletonArmatureMap;
}
}
public static JointTrack fromBoneTrack(BoneTrack boneTrack, Bone bone, Joint joint) {
float[] times = new float[boneTrack.getTimes().length];
int length = times.length;
System.arraycopy(boneTrack.getTimes(), 0, times, 0, length);
//translation
Vector3f[] translations = new Vector3f[length];
if (boneTrack.getTranslations() != null) {
for (int i = 0; i < boneTrack.getTranslations().length; i++) {
Vector3f oldTrans = boneTrack.getTranslations()[i];
Vector3f newTrans = new Vector3f();
newTrans.set(bone.getBindPosition()).addLocal(oldTrans);
translations[i] = newTrans;
}
}
//rotation
Quaternion[] rotations = new Quaternion[length];
if (boneTrack.getRotations() != null) {
for (int i = 0; i < boneTrack.getRotations().length; i++) {
Quaternion oldRot = boneTrack.getRotations()[i];
Quaternion newRot = new Quaternion();
newRot.set(bone.getBindRotation()).multLocal(oldRot);
rotations[i] = newRot;
}
}
//scale
Vector3f[] scales = new Vector3f[length];
if (boneTrack.getScales() != null) {
for (int i = 0; i < boneTrack.getScales().length; i++) {
Vector3f oldScale = boneTrack.getScales()[i];
Vector3f newScale = new Vector3f();
newScale.set(bone.getBindScale()).multLocal(oldScale);
scales[i] = newScale;
}
}
return new JointTrack(joint, times, translations, rotations, scales);
}
private static Joint fromBone(Bone b) {
Joint j = new Joint(b.getName());
j.setLocalTranslation(b.getBindPosition());
j.setLocalRotation(b.getBindRotation());
j.setLocalScale(b.getBindScale());
return j;
}
}

@ -6,8 +6,7 @@ package com.jme3.scene.debug.custom;
import com.jme3.anim.*;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.app.state.BaseAppState;
import com.jme3.collision.CollisionResults;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
@ -22,15 +21,17 @@ import java.util.*;
/**
* @author Nehon
*/
public class ArmatureDebugAppState extends AbstractAppState {
public class ArmatureDebugAppState extends BaseAppState {
private Node debugNode = new Node("debugNode");
private Map<Armature, ArmatureDebugger> armatures = new HashMap<>();
private Map<Armature, Joint> selectedBones = new HashMap<>();
private Application app;
ViewPort vp;
@Override
public void initialize(AppStateManager stateManager, Application app) {
ViewPort vp = app.getRenderManager().createMainView("debug", app.getCamera());
protected void initialize(Application app) {
vp = app.getRenderManager().createMainView("debug", app.getCamera());
vp.attachScene(debugNode);
vp.setClearDepth(true);
this.app = app;
@ -39,12 +40,26 @@ public class ArmatureDebugAppState extends AbstractAppState {
}
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)));
vp.setEnabled(false);
}
@Override
protected void cleanup(Application app) {
}
@Override
protected void onEnable() {
vp.setEnabled(true);
}
@Override
protected void onDisable() {
vp.setEnabled(false);
}
@Override
@ -53,13 +68,13 @@ public class ArmatureDebugAppState extends AbstractAppState {
debugNode.updateGeometricState();
}
public ArmatureDebugger addArmature(SkinningControl skinningControl) {
public ArmatureDebugger addArmatureFrom(SkinningControl skinningControl) {
Armature armature = skinningControl.getArmature();
Spatial forSpatial = skinningControl.getSpatial();
return addArmature(armature, forSpatial);
return addArmatureFrom(armature, forSpatial);
}
public ArmatureDebugger addArmature(Armature armature, Spatial forSpatial) {
public ArmatureDebugger addArmatureFrom(Armature armature, Spatial forSpatial) {
ArmatureDebugger ad = new ArmatureDebugger(forSpatial.getName() + "_Armature", armature);
ad.setLocalTransform(forSpatial.getWorldTransform());

@ -0,0 +1,147 @@
package jme3test.model.anim;
import com.jme3.anim.AnimComposer;
import com.jme3.anim.SkinningControl;
import com.jme3.anim.util.AnimMigrationUtils;
import com.jme3.app.ChaseCameraAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.*;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.debug.custom.ArmatureDebugAppState;
import java.util.LinkedList;
import java.util.Queue;
/**
* Created by Nehon on 18/12/2017.
*/
public class TestAnimMigration extends SimpleApplication {
ArmatureDebugAppState debugAppState;
AnimComposer composer;
Queue<String> anims = new LinkedList<>();
boolean playAnim = true;
public static void main(String... argv) {
TestAnimMigration app = new TestAnimMigration();
app.start();
}
@Override
public void simpleInitApp() {
setTimer(new EraseTimer());
//cam.setFrustumPerspective(90f, (float) cam.getWidth() / cam.getHeight(), 0.01f, 10f);
viewPort.setBackgroundColor(ColorRGBA.DarkGray);
rootNode.addLight(new DirectionalLight(new Vector3f(-1, -1, -1).normalizeLocal()));
rootNode.addLight(new AmbientLight(ColorRGBA.DarkGray));
Spatial model = assetManager.loadModel("Models/Jaime/Jaime.j3o");
//Spatial model = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
//Spatial model = assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
//Spatial model = assetManager.loadModel("Models/Elephant/Elephant.mesh.xml");
AnimMigrationUtils.migrate(model);
rootNode.attachChild(model);
debugAppState = new ArmatureDebugAppState();
stateManager.attach(debugAppState);
setupModel(model);
flyCam.setEnabled(false);
Node target = new Node("CamTarget");
//target.setLocalTransform(model.getLocalTransform());
target.move(0, 1, 0);
ChaseCameraAppState chaseCam = new ChaseCameraAppState();
chaseCam.setTarget(target);
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);
initInputs();
}
public void initInputs() {
inputManager.addMapping("toggleAnim", new KeyTrigger(KeyInput.KEY_RETURN));
inputManager.addListener(new ActionListener() {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (isPressed) {
playAnim = !playAnim;
if (playAnim) {
String anim = anims.poll();
anims.add(anim);
composer.setCurrentAnimClip(anim);
System.err.println(anim);
} else {
composer.reset();
}
}
}
}, "toggleAnim");
inputManager.addMapping("nextAnim", new KeyTrigger(KeyInput.KEY_RIGHT));
inputManager.addListener(new ActionListener() {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (isPressed && composer != null) {
String anim = anims.poll();
anims.add(anim);
composer.setCurrentAnimClip(anim);
System.err.println(anim);
}
}
}, "nextAnim");
}
private void setupModel(Spatial model) {
if (composer != null) {
return;
}
composer = model.getControl(AnimComposer.class);
if (composer != null) {
SkinningControl sc = model.getControl(SkinningControl.class);
debugAppState.addArmatureFrom(sc);
anims.clear();
for (String name : composer.getAnimClipsNames()) {
anims.add(name);
}
if (anims.isEmpty()) {
return;
}
if (playAnim) {
String anim = anims.poll();
anims.add(anim);
composer.setCurrentAnimClip(anim);
System.err.println(anim);
}
} else {
if (model instanceof Node) {
Node n = (Node) model;
for (Spatial child : n.getChildren()) {
setupModel(child);
}
}
}
}
}

@ -6,7 +6,6 @@ import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.*;
import com.jme3.scene.*;
@ -37,6 +36,7 @@ public class TestArmature extends SimpleApplication {
//cam.setFrustumPerspective(90f, (float) cam.getWidth() / cam.getHeight(), 0.01f, 10f);
viewPort.setBackgroundColor(ColorRGBA.DarkGray);
//create armature
Joint root = new Joint("Root_Joint");
j1 = new Joint("Joint_1");
j2 = new Joint("Joint_2");
@ -52,9 +52,9 @@ public class TestArmature extends SimpleApplication {
final Armature armature = new Armature(joints);
// armature.setModelTransformClass(SeparateJointModelTransform.class);
armature.setBindPose();
//create animations
AnimClip clip = new AnimClip("anim");
float[] times = new float[]{0, 2, 4};
Quaternion[] rotations = new Quaternion[]{
@ -83,15 +83,18 @@ public class TestArmature extends SimpleApplication {
clip.addTrack(track1);
clip.addTrack(track2);
//create the animComposer control
final AnimComposer composer = new AnimComposer();
composer.addAnimClip(clip);
//create the SkinningControl
SkinningControl ac = new SkinningControl(armature);
ac.setHardwareSkinningPreferred(false);
Node node = new Node("Test Armature");
rootNode.attachChild(node);
//Create the mesh to deform.
Geometry cylinder = new Geometry("cylinder", createMesh());
Material m = new Material(assetManager, "Common/MatDefs/Misc/fakeLighting.j3md");
m.setColor("Color", ColorRGBA.randomColor());
@ -103,14 +106,9 @@ public class TestArmature extends SimpleApplication {
composer.setCurrentAnimClip("anim");
ArmatureDebugAppState debugAppState = new ArmatureDebugAppState();
debugAppState.addArmature(ac);
debugAppState.addArmatureFrom(ac);
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();
@ -132,12 +130,10 @@ public class TestArmature extends SimpleApplication {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (isPressed) {
play = false;
composer.reset();
armature.resetToBindPose();
} else {
play = true;
composer.setCurrentAnimClip("anim");
}
}
@ -202,12 +198,10 @@ public class TestArmature extends SimpleApplication {
c.updateCounts();
c.updateBound();
//the mesh has some skinning let's create needed buffers for HW skinning
//creating empty buffers for HW skinning
//the buffers will be setup if ever used.
VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight);
VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex);
//setting usage to cpuOnly so that the buffer is not send empty to the GPU
indicesHW.setUsage(VertexBuffer.Usage.CpuOnly);
weightsHW.setUsage(VertexBuffer.Usage.CpuOnly);
c.setBuffer(weightsHW);
@ -220,20 +214,4 @@ public class TestArmature extends SimpleApplication {
}
float time = 0;
boolean play = true;
@Override
public void simpleUpdate(float tpf) {
// if (play == false) {
// return;
// }
// 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));
}
}

Loading…
Cancel
Save