Proper serialisation for morph animation
This commit is contained in:
parent
4048f8ba26
commit
daba9b8bc7
@ -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,9 +136,54 @@ 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) {
|
||||
int start = VertexBuffer.Type.MorphTarget0.ordinal();
|
||||
if (targetNumBuffers >= 1) {
|
||||
@ -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);
|
||||
|
||||
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…
x
Reference in New Issue
Block a user