parent
e5057ad7fa
commit
728e28857b
@ -1,24 +1,189 @@ |
|||||||
package com.jme3.anim.util; |
package com.jme3.anim.util; |
||||||
|
|
||||||
import com.jme3.animation.AnimControl; |
import com.jme3.anim.*; |
||||||
import com.jme3.scene.SceneGraphVisitor; |
import com.jme3.animation.*; |
||||||
import com.jme3.scene.Spatial; |
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.*; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
public class AnimMigrationUtils { |
public class AnimMigrationUtils { |
||||||
|
|
||||||
|
private static AnimControlVisitor animControlVisitor = new AnimControlVisitor(); |
||||||
|
private static SkeletonControlVisitor skeletonControlVisitor = new SkeletonControlVisitor(); |
||||||
|
|
||||||
|
|
||||||
public static Spatial migrate(Spatial source) { |
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; |
return source; |
||||||
} |
} |
||||||
|
|
||||||
private class AnimControlVisitor implements SceneGraphVisitor { |
private static class AnimControlVisitor implements SceneGraphVisitor { |
||||||
|
|
||||||
|
Map<Skeleton, Armature> skeletonArmatureMap; |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void visit(Spatial spatial) { |
public void visit(Spatial spatial) { |
||||||
AnimControl control = spatial.getControl(AnimControl.class); |
AnimControl control = spatial.getControl(AnimControl.class); |
||||||
if (control != null) { |
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; |
||||||
|
} |
||||||
|
|
||||||
} |
} |
||||||
|
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue