From daba9b8bc745700e4b36dd27e04c0c06aafd6034 Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 3 Mar 2018 16:18:25 +0100 Subject: [PATCH] Proper serialisation for morph animation --- .../src/main/java/com/jme3/anim/AnimClip.java | 4 +- .../src/main/java/com/jme3/anim/Armature.java | 37 ++++-- .../src/main/java/com/jme3/anim/Joint.java | 40 ++++++- .../main/java/com/jme3/anim/MorphControl.java | 107 +++++++++++++----- .../main/java/com/jme3/anim/MorphTrack.java | 2 + .../java/com/jme3/anim/TransformTrack.java | 2 +- .../jme3/anim/util/AnimMigrationUtils.java | 2 +- .../java/com/jme3/animation/BoneTrack.java | 4 - .../com/jme3/material/MatParamOverride.java | 7 ++ .../main/java/com/jme3/material/Material.java | 8 +- .../java/com/jme3/renderer/RenderManager.java | 2 +- .../main/java/com/jme3/scene/Geometry.java | 44 ++++++- .../src/main/java/com/jme3/scene/Mesh.java | 4 +- .../java/com/jme3/scene/VertexBuffer.java | 9 +- .../java/jme3test/model/TestGltfLoading.java | 10 +- .../anim/TestAnimMorphSerialization.java | 36 +++--- .../jme3test/model/anim/TestArmature.java | 4 +- .../model/anim/TestBaseAnimSerialization.java | 4 +- .../jme3/scene/plugins/gltf/GltfLoader.java | 1 + .../scene/plugins/ogre/SkeletonLoader.java | 2 +- 20 files changed, 244 insertions(+), 85 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/anim/AnimClip.java b/jme3-core/src/main/java/com/jme3/anim/AnimClip.java index 0baf7b873..7f8c022c5 100644 --- a/jme3-core/src/main/java/com/jme3/anim/AnimClip.java +++ b/jme3-core/src/main/java/com/jme3/anim/AnimClip.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(); diff --git a/jme3-core/src/main/java/com/jme3/anim/Armature.java b/jme3-core/src/main/java/com/jme3/anim/Armature.java index 582dc8bce..39a664ede 100644 --- a/jme3-core/src/main/java/com/jme3/anim/Armature.java +++ b/jme3-core/src/main/java/com/jme3/anim/Armature.java @@ -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 diff --git a/jme3-core/src/main/java/com/jme3/anim/Joint.java b/jme3-core/src/main/java/com/jme3/anim/Joint.java index eaa7ac145..f4c0a0d1b 100644 --- a/jme3-core/src/main/java/com/jme3/anim/Joint.java +++ b/jme3-core/src/main/java/com/jme3/anim/Joint.java @@ -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 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); } diff --git a/jme3-core/src/main/java/com/jme3/anim/MorphControl.java b/jme3-core/src/main/java/com/jme3/anim/MorphControl.java index 7af716b53..80398ce6c 100644 --- a/jme3-core/src/main/java/com/jme3/anim/MorphControl.java +++ b/jme3-core/src/main/java/com/jme3/anim/MorphControl.java @@ -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()) { diff --git a/jme3-core/src/main/java/com/jme3/anim/MorphTrack.java b/jme3-core/src/main/java/com/jme3/anim/MorphTrack.java index b1968df36..cd662c11e 100644 --- a/jme3-core/src/main/java/com/jme3/anim/MorphTrack.java +++ b/jme3-core/src/main/java/com/jme3/anim/MorphTrack.java @@ -187,6 +187,7 @@ public class MorphTrack implements AnimTrack { 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 { weights = ic.readFloatArray("weights", null); times = ic.readFloatArray("times", null); target = (Geometry) ic.readSavable("target", null); + nbMorphTargets = ic.readInt("nbMorphTargets", 0); setTimes(times); } diff --git a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java index b5e5fb85f..dee2aefb9 100644 --- a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java +++ b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java @@ -294,7 +294,7 @@ public class TransformTrack implements AnimTrack { 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); } diff --git a/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java b/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java index c9e65b08f..d50bf90b8 100644 --- a/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java +++ b/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java @@ -58,7 +58,7 @@ public class AnimMigrationUtils { } Armature armature = new Armature(joints); - armature.setBindPose(); + armature.saveBindPose(); skeletonArmatureMap.put(skeleton, armature); List tracks = new ArrayList<>(); diff --git a/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java b/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java index 32f5e6b7b..a39108c5a 100644 --- a/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java +++ b/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/material/MatParamOverride.java b/jme3-core/src/main/java/com/jme3/material/MatParamOverride.java index 8a7355b87..1b4aad480 100644 --- a/jme3-core/src/main/java/com/jme3/material/MatParamOverride.java +++ b/jme3-core/src/main/java/com/jme3/material/MatParamOverride.java @@ -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); + } } } diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index a14bc357d..e2b030e93 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -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 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); } diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index 238445b6c..cc86b5ad4 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -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 diff --git a/jme3-core/src/main/java/com/jme3/scene/Geometry.java b/jme3-core/src/main/java/com/jme3/scene/Geometry.java index e68d75969..437036222 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Geometry.java +++ b/jme3-core/src/main/java/com/jme3/scene/Geometry.java @@ -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; } diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java index 889963697..8dececf26 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java @@ -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 diff --git a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java index 9753850a1..bb5d70afb 100644 --- a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java @@ -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 *

* 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. *

+ * 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. *

diff --git a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java index 61219cae2..8aa044d55 100644 --- a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java +++ b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java @@ -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 findControl(Spatial s, Class controlClass) { diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMorphSerialization.java b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMorphSerialization.java index 1d3551df2..2e122a35e 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMorphSerialization.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMorphSerialization.java @@ -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); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java index f6b85674a..f7a839327 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java @@ -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"); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java b/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java index 748bb43f3..24d8b21e5 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java @@ -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"); diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java index fce2bf8ad..260e018e6 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java @@ -1009,6 +1009,7 @@ public class GltfLoader implements AssetLoader { skinnedSpatials.put(skinData, new ArrayList()); armature.update(); + armature.saveInitialPose(); } } diff --git a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java index 88ab22853..d9977d3a9 100644 --- a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java +++ b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java @@ -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;