Proper serialisation for morph animation

monkanim
Nehon 7 years ago
parent c2fcdefd0d
commit bcd97c22eb
  1. 4
      jme3-core/src/main/java/com/jme3/anim/AnimClip.java
  2. 37
      jme3-core/src/main/java/com/jme3/anim/Armature.java
  3. 40
      jme3-core/src/main/java/com/jme3/anim/Joint.java
  4. 107
      jme3-core/src/main/java/com/jme3/anim/MorphControl.java
  5. 2
      jme3-core/src/main/java/com/jme3/anim/MorphTrack.java
  6. 2
      jme3-core/src/main/java/com/jme3/anim/TransformTrack.java
  7. 2
      jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java
  8. 4
      jme3-core/src/main/java/com/jme3/animation/BoneTrack.java
  9. 7
      jme3-core/src/main/java/com/jme3/material/MatParamOverride.java
  10. 8
      jme3-core/src/main/java/com/jme3/material/Material.java
  11. 2
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java
  12. 44
      jme3-core/src/main/java/com/jme3/scene/Geometry.java
  13. 4
      jme3-core/src/main/java/com/jme3/scene/Mesh.java
  14. 9
      jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java
  15. 10
      jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java
  16. 36
      jme3-examples/src/main/java/jme3test/model/anim/TestAnimMorphSerialization.java
  17. 4
      jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java
  18. 4
      jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java
  19. 1
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
  20. 2
      jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java

@ -80,9 +80,9 @@ public class AnimClip implements JmeCloneable, Savable {
name = ic.readString("name", null);
Savable[] arr = ic.readSavableArray("tracks", null);
if (arr != null) {
tracks = new TransformTrack[arr.length];
tracks = new AnimTrack[arr.length];
for (int i = 0; i < arr.length; i++) {
TransformTrack t = (TransformTrack) arr[i];
AnimTrack t = (AnimTrack) arr[i];
tracks[i] = t;
if (t.getLength() > length) {
length = t.getLength();

@ -174,28 +174,49 @@ public class Armature implements JmeCloneable, Savable {
/**
* Saves the current Armature state as its bind pose.
* Note that the bind pose is supposed to be the one where the armature is aligned with the mesh to deform.
* Saving this pose will affect how skinning works.
*/
public void setBindPose() {
public void saveBindPose() {
//make sure all bones are updated
update();
//Save the current pose as bind pose
for (Joint joint : jointList) {
joint.setBindPose();
joint.saveBindPose();
}
}
/**
* This methods sets this armature in its bind pose (aligned with the undeformed mesh)
* Note that this is only useful for debugging porpose.
* This methods sets this armature in its bind pose (aligned with the mesh to deform)
* Note that this is only useful for debugging purpose.
*/
public void resetToBindPose() {
public void applyBindPose() {
for (Joint joint : rootJoints) {
joint.resetToBindPose();
joint.applyBindPose();
}
}
/**
* Compute the skining matrices for each bone of the armature that would be used to transform vertices of associated meshes
* Saves the current local transform as the initial transform.
* Initial transform is the one applied to the armature when loaded.
*/
public void saveInitialPose() {
for (Joint joint : jointList) {
joint.saveInitialPose();
}
}
/**
* Applies the initial pose to this armature
*/
public void applyInitialPose() {
for (Joint rootJoint : rootJoints) {
rootJoint.applyInitialPose();
}
}
/**
* Compute the skinning matrices for each bone of the armature that would be used to transform vertices of associated meshes
*
* @return
*/
@ -263,7 +284,7 @@ public class Armature implements JmeCloneable, Savable {
for (Joint rootJoint : rootJoints) {
rootJoint.update();
}
resetToBindPose();
applyInitialPose();
}
@Override

@ -37,6 +37,13 @@ public class Joint implements Savable, JmeCloneable, HasLocalTransform {
*/
private Transform localTransform = new Transform();
/**
* The initial transform of the joint in local space. Relative to its parent.
* Or relative to the model's origin for the root joint.
* this transform is the transform applied when the armature is loaded.
*/
private Transform initialTransform = new Transform();
/**
* The transform of the joint in model space. Relative to the origin of the model.
* this is either a MatrixJointModelTransform or a SeparateJointModelTransform
@ -127,18 +134,43 @@ public class Joint implements Savable, JmeCloneable, HasLocalTransform {
jointModelTransform.getOffsetTransform(outTransform, inverseModelBindMatrix);
}
protected void setBindPose() {
/**
* Sets the current localTransform as the Bind transform.
*/
protected void saveBindPose() {
//Note that the whole Armature must be updated before calling this method.
getModelTransform().toTransformMatrix(inverseModelBindMatrix);
inverseModelBindMatrix.invertLocal();
}
protected void resetToBindPose() {
/**
* Sets the current local transforms as the initial transform.
*/
protected void saveInitialPose() {
initialTransform.set(localTransform);
}
/**
* Sets the local transform with the bind transforms
*/
protected void applyBindPose() {
jointModelTransform.applyBindPose(localTransform, inverseModelBindMatrix, parent);
updateModelTransforms();
for (Joint child : children.getArray()) {
child.resetToBindPose();
child.applyBindPose();
}
}
/**
* Sets the local transform with the initial transform
*/
protected void applyInitialPose() {
setLocalTransform(initialTransform);
updateModelTransforms();
for (Joint child : children.getArray()) {
child.applyInitialPose();
}
}
@ -277,6 +309,7 @@ public class Joint implements Savable, JmeCloneable, HasLocalTransform {
name = input.readString("name", null);
attachedNode = (Node) input.readSavable("attachedNode", null);
targetGeometry = (Geometry) input.readSavable("targetGeometry", null);
initialTransform = (Transform) input.readSavable("initialTransform", new Transform());
inverseModelBindMatrix = (Matrix4f) input.readSavable("inverseModelBindMatrix", inverseModelBindMatrix);
ArrayList<Joint> childList = input.readSavableArrayList("children", null);
@ -292,6 +325,7 @@ public class Joint implements Savable, JmeCloneable, HasLocalTransform {
output.write(name, "name", null);
output.write(attachedNode, "attachedNode", null);
output.write(targetGeometry, "targetGeometry", null);
output.write(initialTransform, "initialTransform", new Transform());
output.write(inverseModelBindMatrix, "inverseModelBindMatrix", new Matrix4f());
output.writeSavableArrayList(new ArrayList(children), "children", null);
}

@ -1,11 +1,9 @@
package com.jme3.anim;
import com.jme3.export.*;
import com.jme3.material.*;
import com.jme3.renderer.*;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.SceneGraphVisitorAdapter;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.*;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.mesh.MorphTarget;
import com.jme3.shader.VarType;
@ -13,7 +11,10 @@ import com.jme3.util.BufferUtils;
import com.jme3.util.SafeArrayList;
import javafx.geometry.Pos;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A control that handle morph animation for Position, Normal and Tangent buffers.
@ -22,7 +23,9 @@ import java.nio.FloatBuffer;
*
* @author Rémy Bouquet
*/
public class MorphControl extends AbstractControl {
public class MorphControl extends AbstractControl implements Savable {
private static final Logger logger = Logger.getLogger(MorphControl.class.getName());
private static final int MAX_MORPH_BUFFERS = 14;
private final static float MIN_WEIGHT = 0.005f;
@ -55,33 +58,23 @@ public class MorphControl extends AbstractControl {
if (!enabled) {
return;
}
for (Geometry target : targets) {
Mesh mesh = target.getMesh();
if (!target.isDirtyMorph()) {
for (Geometry geom : targets) {
Mesh mesh = geom.getMesh();
if (!geom.isDirtyMorph()) {
continue;
}
int nbMaxBuffers = getRemainingBuffers(mesh, rm.getRenderer());
Material m = target.getMaterial();
float weights[] = target.getMorphState();
Material m = geom.getMaterial();
float weights[] = geom.getMorphState();
MorphTarget morphTargets[] = mesh.getMorphTargets();
float matWeights[];
MatParam param = m.getParam("MorphWeights");
//Number of buffer to handle for each morph target
int targetNumBuffers = getTargetNumBuffers(morphTargets[0]);
// compute the max number of targets to send to the GPU
int maxGPUTargets = Math.min(nbMaxBuffers, MAX_MORPH_BUFFERS) / targetNumBuffers;
if (param == null) {
matWeights = new float[maxGPUTargets];
m.setParam("MorphWeights", VarType.FloatArray, matWeights);
} else {
matWeights = (float[]) param.getValue();
}
// setting the maximum number as the real number may change every frame and trigger a shader recompilation since it's bound to a define.
m.setInt("NumberOfMorphTargets", maxGPUTargets);
m.setInt("NumberOfTargetsBuffers", targetNumBuffers);
int maxGPUTargets = getMaxGPUTargets(rm, geom, m, targetNumBuffers);
MatParam param2 = m.getParam("MorphWeights");
matWeights = (float[]) param2.getValue();
int nbGPUTargets = 0;
int lastGpuTargetIndex = 0;
@ -115,14 +108,14 @@ public class MorphControl extends AbstractControl {
} else if (cpuWeightSum > 0) {
// we have more simultaneous morph targets than available gpu slots,
// we merge the additional morph targets and bind them to the last gpu slot
MorphTarget mt = target.getFallbackMorphTarget();
MorphTarget mt = geom.getFallbackMorphTarget();
if (mt == null) {
mt = initCpuMorphTarget(target);
target.setFallbackMorphTarget(mt);
mt = initCpuMorphTarget(geom);
geom.setFallbackMorphTarget(mt);
}
// adding the last Gpu target weight
cpuWeightSum += matWeights[nbGPUTargets - 1];
ensureTmpArraysCapacity(target.getVertexCount() * 3, targetNumBuffers);
ensureTmpArraysCapacity(geom.getVertexCount() * 3, targetNumBuffers);
// merging all remaining targets in tmp arrays
for (int i = lastGpuTargetIndex; i < morphTargets.length; i++) {
@ -130,7 +123,7 @@ public class MorphControl extends AbstractControl {
continue;
}
float weight = weights[i] / cpuWeightSum;
MorphTarget t = target.getMesh().getMorphTargets()[i];
MorphTarget t = geom.getMesh().getMorphTargets()[i];
mergeMorphTargets(targetNumBuffers, weight, t, i == lastGpuTargetIndex);
}
@ -143,7 +136,52 @@ public class MorphControl extends AbstractControl {
// setting the eight of the merged targets
matWeights[nbGPUTargets - 1] = cpuWeightSum;
}
geom.setDirtyMorph(false);
}
}
private int getMaxGPUTargets(RenderManager rm, Geometry geom, Material mat, int targetNumBuffers) {
if (geom.getNbSimultaneousGPUMorph() > -1) {
return geom.getNbSimultaneousGPUMorph();
}
// Evaluate the number of CPU slots remaining for morph buffers.
int nbMaxBuffers = getRemainingBuffers(geom.getMesh(), rm.getRenderer());
int realNumTargetsBuffers = geom.getMesh().getMorphTargets().length * targetNumBuffers;
// compute the max number of targets to send to the GPU
int maxGPUTargets = Math.min(realNumTargetsBuffers, Math.min(nbMaxBuffers, MAX_MORPH_BUFFERS)) / targetNumBuffers;
MatParam param = mat.getParam("MorphWeights");
if (param == null) {
// init the mat param if it doesn't exists.
float[] wts = new float[maxGPUTargets];
mat.setParam("MorphWeights", VarType.FloatArray, wts);
}
mat.setInt("NumberOfTargetsBuffers", targetNumBuffers);
// test compile the shader to find the accurate number of remaining attributes slots
boolean compilationOk = false;
// Note that if ever the shader has an unrelated issue we want to break at some point, hence the maxGPUTargets > 0
while (!compilationOk && maxGPUTargets > 0) {
// setting the maximum number as the real number may change every frame and trigger a shader recompilation since it's bound to a define.
mat.setInt("NumberOfMorphTargets", maxGPUTargets);
try {
// preload the spatial. this will trigger a shader compilation that will fail if the number of attributes is over the limit.
rm.preloadScene(spatial);
compilationOk = true;
} catch (RendererException e) {
logger.log(Level.FINE, geom.getName() + ": failed at " + maxGPUTargets);
// the compilation failed let's decrement the number of targets an try again.
maxGPUTargets--;
}
}
logger.log(Level.FINE, geom.getName() + ": " + maxGPUTargets);
// set the number of GPU morph on the geom to not have to recompute it next frame.
geom.setNbSimultaneousGPUMorph(maxGPUTargets);
return maxGPUTargets;
}
private int bindMorphtargetBuffer(Mesh mesh, int targetNumBuffers, int boundBufferIdx, MorphTarget t) {
@ -263,6 +301,17 @@ public class MorphControl extends AbstractControl {
return num;
}
/**
* Computes the number of remaining buffers on this mesh.
* This is supposed to give a hint on how many attributes will be used in the material and computes the remaining available slots for the morph attributes.
* However, the shader can declare attributes that are not used and not bound to a real buffer.
* That's why we attempt to compile the shader later on to avoid any compilation crash.
* This method is here to avoid too much render test iteration.
*
* @param mesh
* @param renderer
* @return
*/
private int getRemainingBuffers(Mesh mesh, Renderer renderer) {
int nbUsedBuffers = 0;
for (VertexBuffer vb : mesh.getBufferList().getArray()) {

@ -187,6 +187,7 @@ public class MorphTrack implements AnimTrack<float[]> {
oc.write(weights, "weights", null);
oc.write(times, "times", null);
oc.write(target, "target", null);
oc.write(nbMorphTargets, "nbMorphTargets", 0);
}
@Override
@ -195,6 +196,7 @@ public class MorphTrack implements AnimTrack<float[]> {
weights = ic.readFloatArray("weights", null);
times = ic.readFloatArray("times", null);
target = (Geometry) ic.readSavable("target", null);
nbMorphTargets = ic.readInt("nbMorphTargets", 0);
setTimes(times);
}

@ -294,7 +294,7 @@ public class TransformTrack implements AnimTrack<Transform> {
rotations = (CompactQuaternionArray) ic.readSavable("rotations", null);
times = ic.readFloatArray("times", null);
scales = (CompactVector3Array) ic.readSavable("scales", null);
target = (Joint) ic.readSavable("target", null);
target = (HasLocalTransform) ic.readSavable("target", null);
setTimes(times);
}

@ -58,7 +58,7 @@ public class AnimMigrationUtils {
}
Armature armature = new Armature(joints);
armature.setBindPose();
armature.saveBindPose();
skeletonArmatureMap.put(skeleton, armature);
List<TransformTrack> tracks = new ArrayList<>();

@ -35,12 +35,8 @@ import com.jme3.export.*;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.util.TempVars;
<<<<<<< HEAD
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
=======
>>>>>>> Draft of the new animation system
import java.io.IOException;
import java.util.BitSet;

@ -140,6 +140,9 @@ public final class MatParamOverride extends MatParam {
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
oc.write(enabled, "enabled", true);
if (value == null) {
oc.write(true, "isNull", false);
}
}
@Override
@ -147,5 +150,9 @@ public final class MatParamOverride extends MatParam {
super.read(im);
InputCapsule ic = im.getCapsule(this);
enabled = ic.readBoolean("enabled", true);
boolean isNull = ic.readBoolean("isNull", false);
if (isNull) {
setValue(null);
}
}
}

@ -836,7 +836,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
*
* @param renderManager The render manager to preload for
*/
public void preload(RenderManager renderManager) {
public void preload(RenderManager renderManager, Geometry geometry) {
if (technique == null) {
selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
}
@ -847,9 +847,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
if (techniqueDef.isNoRender()) {
return;
}
// Get world overrides
SafeArrayList<MatParamOverride> overrides = geometry.getWorldMatParamOverrides();
Shader shader = technique.makeCurrent(renderManager, null, null, null, rendererCaps);
updateShaderMaterialParameters(renderer, shader, null, null);
Shader shader = technique.makeCurrent(renderManager, overrides, null, null, rendererCaps);
updateShaderMaterialParameters(renderer, shader, overrides, null);
renderManager.getRenderer().setShader(shader);
}

@ -660,7 +660,7 @@ public class RenderManager {
throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
}
gm.getMaterial().preload(this);
gm.getMaterial().preload(this, gm);
Mesh mesh = gm.getMesh();
if (mesh != null
&& mesh.getVertexCount() != 0

@ -95,6 +95,7 @@ public class Geometry extends Spatial {
// a Morph target that will be used to merge all targets that
// can't be handled on the cpu on each frame.
private MorphTarget fallbackMorphTarget;
private int nbSimultaneousGPUMorph = -1;
/**
* Serialization only. Do not use.
@ -258,7 +259,7 @@ public class Geometry extends Spatial {
@Override
public void setMaterial(Material material) {
this.material = material;
nbSimultaneousGPUMorph = -1;
if (isGrouped()) {
groupNode.onMaterialChange(this);
}
@ -600,10 +601,28 @@ public class Geometry extends Spatial {
this.dirtyMorph = true;
}
/**
* returns true if the morph state has changed on the last frame.
* @return
*/
public boolean isDirtyMorph() {
return dirtyMorph;
}
/**
* Seting this to true will stop this geometry morph buffer to be updated,
* unless the morph state changes
* @param dirtyMorph
*/
public void setDirtyMorph(boolean dirtyMorph) {
this.dirtyMorph = dirtyMorph;
}
/**
* returns the morph state of this Geometry.
* Used internally by the MorphControl.
* @return
*/
public float[] getMorphState() {
if (morphState == null) {
morphState = new float[mesh.getMorphTargets().length];
@ -611,6 +630,29 @@ public class Geometry extends Spatial {
return morphState;
}
/**
* Return the number of morph targets that can be handled on the GPU simultaneously for this geometry.
* Note that it depends on the material set on this geometry.
* This number is computed and set by the MorphControl, so it might be available only after the first frame.
* Else it's set to -1.
* @return the number of simultaneous morph targets handled on the GPU
*/
public int getNbSimultaneousGPUMorph() {
return nbSimultaneousGPUMorph;
}
/**
* Sets the number of morph targets that can be handled on the GPU simultaneously for this geometry.
* Note that it depends on the material set on this geometry.
* This number is computed and set by the MorphControl, so it might be available only after the first frame.
* Else it's set to -1.
* WARNING: setting this manually might crash the shader compilation if set too high. Do it at your own risk.
* @param nbSimultaneousGPUMorph the number of simultaneous morph targets to be handled on the GPU.
*/
public void setNbSimultaneousGPUMorph(int nbSimultaneousGPUMorph) {
this.nbSimultaneousGPUMorph = nbSimultaneousGPUMorph;
}
public MorphTarget getFallbackMorphTarget() {
return fallbackMorphTarget;
}

@ -1573,7 +1573,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
}
out.write(lodLevels, "lodLevels", null);
out.writeSavableArrayList(new ArrayList(morphTargets), "morphTargets", null);
if (morphTargets != null) {
out.writeSavableArrayList(new ArrayList(morphTargets), "morphTargets", null);
}
}
@Override

@ -216,15 +216,16 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
/**
* Morph animations targets.
* Supports up tp 8 morph target buffers at the same time
* Supports up tp 14 morph target buffers at the same time
* Limited due to the limited number of attributes you can bind to a vertex shader usually 16
* <p>
* MorphTarget buffers are either POSITION, NORMAL or TANGENT buffers.
* So we can support up to
* 10 simultaneous POSITION targets
* 5 simultaneous POSITION and NORMAL targets
* 3 simultaneous POSTION, NORMAL and TANGENT targets.
* 14 simultaneous POSITION targets
* 7 simultaneous POSITION and NORMAL targets
* 4 simultaneous POSTION, NORMAL and TANGENT targets.
* <p>
* Note that the MorphControl will find how many buffers can be supported for each mesh/material combination.
* Note that all buffers have 3 components (Vector3f) even the Tangent buffer that
* does not contain the w (handedness) component that will not be interpolated for morph animation.
* <p>

@ -116,15 +116,15 @@ public class TestGltfLoading extends SimpleApplication {
// rootNode.addLight(pl1);
//loadModel("Models/gltf/polly/project_polly.gltf", new Vector3f(0, 0, 0), 0.5f);
loadModel("Models/gltf/zophrac/scene.gltf", new Vector3f(0, 0, 0), 0.1f);
//loadModel("Models/gltf/zophrac/scene.gltf", new Vector3f(0, 0, 0), 0.1f);
// loadModel("Models/gltf/scifigirl/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
//loadModel("Models/gltf/man/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
//loadModel("Models/gltf/torus/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
//loadModel("Models/gltf/morph/scene.gltf", new Vector3f(0, 0, 0), 0.2f);
//loadModel("Models/gltf/morphCube/AnimatedMorphCube.gltf", new Vector3f(0, 0, 0), 1f);
// loadModel("Models/gltf/morph/SimpleMorph.gltf", new Vector3f(0, 0, 0), 0.1f);
// loadModel("Models/gltf/morph/SimpleMorph.gltf", new Vector3f(0, 0, 0), 0.1f);
//loadModel("Models/gltf/nier/scene.gltf", new Vector3f(0, -1.5f, 0), 0.01f);
//loadModel("Models/gltf/izzy/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
loadModel("Models/gltf/izzy/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
//loadModel("Models/gltf/darth/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
//loadModel("Models/gltf/mech/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
//loadModel("Models/gltf/elephant/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
@ -132,7 +132,7 @@ public class TestGltfLoading extends SimpleApplication {
//loadModel("Models/gltf/war/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
//loadModel("Models/gltf/ganjaarl/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
//loadModel("Models/gltf/hero/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
/// loadModel("Models/gltf/mercy/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
//loadModel("Models/gltf/mercy/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
//loadModel("Models/gltf/crab/scene.gltf", Vector3f.ZERO, 1);
//loadModel("Models/gltf/manta/scene.gltf", Vector3f.ZERO, 0.2f);
//loadModel("Models/gltf/bone/scene.gltf", Vector3f.ZERO, 0.1f);
@ -213,7 +213,7 @@ public class TestGltfLoading extends SimpleApplication {
dumpScene(rootNode, 0);
stateManager.attach(new DetailedProfilerState());
// stateManager.attach(new DetailedProfilerState());
}
private <T extends Control> T findControl(Spatial s, Class<T> controlClass) {

@ -1,7 +1,6 @@
package jme3test.model.anim;
import com.jme3.anim.AnimComposer;
import com.jme3.anim.SkinningControl;
import com.jme3.anim.*;
import com.jme3.anim.util.AnimMigrationUtils;
import com.jme3.app.ChaseCameraAppState;
import com.jme3.app.SimpleApplication;
@ -26,7 +25,7 @@ import java.util.Queue;
/**
* Created by Nehon on 18/12/2017.
*/
public class TestAnimSerialization extends SimpleApplication {
public class TestAnimMorphSerialization extends SimpleApplication {
ArmatureDebugAppState debugAppState;
AnimComposer composer;
@ -35,7 +34,7 @@ public class TestAnimSerialization extends SimpleApplication {
File file;
public static void main(String... argv) {
TestAnimSerialization app = new TestAnimSerialization();
TestAnimMorphSerialization app = new TestAnimMorphSerialization();
app.start();
}
@ -44,15 +43,14 @@ public class TestAnimSerialization extends SimpleApplication {
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");
AnimMigrationUtils.migrate(model);
//rootNode.addLight(new DirectionalLight(new Vector3f(-1, -1, -1).normalizeLocal()));
//rootNode.addLight(new AmbientLight(ColorRGBA.DarkGray));
Node probeNode = (Node) assetManager.loadModel("Scenes/defaultProbe.j3o");
rootNode.attachChild(probeNode);
Spatial model = assetManager.loadModel("Models/gltf/zophrac/scene.gltf");
File storageFolder = JmeSystem.getStorageFolder();
file = new File(storageFolder.getPath() + File.separator + "newJaime.j3o");
file = new File(storageFolder.getPath() + File.separator + "zophrac.j3o");
BinaryExporter be = new BinaryExporter();
try {
be.save(model, file);
@ -61,20 +59,20 @@ public class TestAnimSerialization extends SimpleApplication {
}
assetManager.registerLocator(storageFolder.getPath(), FileLocator.class);
model = assetManager.loadModel("newJaime.j3o");
rootNode.attachChild(model);
Spatial model2 = assetManager.loadModel("zophrac.j3o");
model2.setLocalScale(0.1f);
probeNode.attachChild(model2);
debugAppState = new ArmatureDebugAppState();
stateManager.attach(debugAppState);
setupModel(model);
setupModel(model2);
flyCam.setEnabled(false);
Node target = new Node("CamTarget");
//target.setLocalTransform(model.getLocalTransform());
target.move(0, 1, 0);
target.move(0, 0, 0);
ChaseCameraAppState chaseCam = new ChaseCameraAppState();
chaseCam.setTarget(target);
getStateManager().attach(chaseCam);
@ -130,10 +128,14 @@ public class TestAnimSerialization extends SimpleApplication {
}
composer = model.getControl(AnimComposer.class);
if (composer != null) {
// model.getControl(SkinningControl.class).setEnabled(false);
// model.getControl(MorphControl.class).setEnabled(false);
// composer.setEnabled(false);
SkinningControl sc = model.getControl(SkinningControl.class);
SkinningControl sc = model.getControl(SkinningControl.class);
debugAppState.addArmatureFrom(sc);
anims.clear();
for (String name : composer.getAnimClipsNames()) {
anims.add(name);

@ -52,7 +52,7 @@ public class TestArmature extends SimpleApplication {
final Armature armature = new Armature(joints);
//armature.setModelTransformClass(SeparateJointModelTransform.class);
armature.setBindPose();
armature.saveBindPose();
//create animations
AnimClip clip = new AnimClip("anim");
@ -131,7 +131,7 @@ public class TestArmature extends SimpleApplication {
public void onAction(String name, boolean isPressed, float tpf) {
if (isPressed) {
composer.reset();
armature.resetToBindPose();
armature.applyBindPose();
} else {
composer.setCurrentAction("anim");

@ -59,7 +59,7 @@ public class TestBaseAnimSerialization extends SimpleApplication {
armature = new Armature(joints);
//armature.setModelTransformClass(SeparateJointModelTransform.class);
armature.setBindPose();
armature.saveBindPose();
//create animations
AnimClip clip = new AnimClip("anim");
@ -153,7 +153,7 @@ public class TestBaseAnimSerialization extends SimpleApplication {
public void onAction(String name, boolean isPressed, float tpf) {
if (isPressed) {
composer.reset();
armature.resetToBindPose();
armature.applyBindPose();
} else {
composer.setCurrentAction("anim");

@ -1009,6 +1009,7 @@ public class GltfLoader implements AssetLoader {
skinnedSpatials.put(skinData, new ArrayList<Spatial>());
armature.update();
armature.saveInitialPose();
}
}

@ -161,7 +161,7 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader {
}
indexToJoint.clear();
armature = new Armature(joints);
armature.setBindPose();
armature.saveBindPose();
} else if (qName.equals("animation")) {
animClips.add(animClip);
animClip = null;

Loading…
Cancel
Save