|
@@ -32,8 +32,7 @@
|
|
package com.jme3.animation;
|
|
package com.jme3.animation;
|
|
|
|
|
|
import com.jme3.export.*;
|
|
import com.jme3.export.*;
|
|
-import com.jme3.material.MatParam;
|
|
|
|
-import com.jme3.material.Material;
|
|
|
|
|
|
+import com.jme3.material.MatParamOverride;
|
|
import com.jme3.math.FastMath;
|
|
import com.jme3.math.FastMath;
|
|
import com.jme3.math.Matrix4f;
|
|
import com.jme3.math.Matrix4f;
|
|
import com.jme3.renderer.RenderManager;
|
|
import com.jme3.renderer.RenderManager;
|
|
@@ -43,6 +42,7 @@ import com.jme3.scene.*;
|
|
import com.jme3.scene.VertexBuffer.Type;
|
|
import com.jme3.scene.VertexBuffer.Type;
|
|
import com.jme3.scene.control.AbstractControl;
|
|
import com.jme3.scene.control.AbstractControl;
|
|
import com.jme3.scene.control.Control;
|
|
import com.jme3.scene.control.Control;
|
|
|
|
+import com.jme3.scene.mesh.IndexBuffer;
|
|
import com.jme3.shader.VarType;
|
|
import com.jme3.shader.VarType;
|
|
import com.jme3.util.*;
|
|
import com.jme3.util.*;
|
|
import com.jme3.util.clone.Cloner;
|
|
import com.jme3.util.clone.Cloner;
|
|
@@ -51,8 +51,6 @@ import java.io.IOException;
|
|
import java.nio.Buffer;
|
|
import java.nio.Buffer;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.FloatBuffer;
|
|
import java.nio.FloatBuffer;
|
|
-import java.util.HashSet;
|
|
|
|
-import java.util.Set;
|
|
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
@@ -107,13 +105,11 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
* Bone offset matrices, recreated each frame
|
|
* Bone offset matrices, recreated each frame
|
|
*/
|
|
*/
|
|
private transient Matrix4f[] offsetMatrices;
|
|
private transient Matrix4f[] offsetMatrices;
|
|
- /**
|
|
|
|
- * Material references used for hardware skinning
|
|
|
|
- */
|
|
|
|
- private Set<Material> materials = new HashSet<Material>();
|
|
|
|
|
|
|
|
- //temp reader
|
|
|
|
- private BufferUtils.ByteShortIntBufferReader indexReader = new BufferUtils.ByteShortIntBufferReader();
|
|
|
|
|
|
+
|
|
|
|
+ private MatParamOverride numberOfBonesParam;
|
|
|
|
+ private MatParamOverride boneMatricesParam;
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Serialization only. Do not use.
|
|
* Serialization only. Do not use.
|
|
*/
|
|
*/
|
|
@@ -121,11 +117,13 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
}
|
|
}
|
|
|
|
|
|
private void switchToHardware() {
|
|
private void switchToHardware() {
|
|
|
|
+ numberOfBonesParam.setEnabled(true);
|
|
|
|
+ boneMatricesParam.setEnabled(true);
|
|
|
|
+
|
|
// Next full 10 bones (e.g. 30 on 24 bones)
|
|
// Next full 10 bones (e.g. 30 on 24 bones)
|
|
int numBones = ((skeleton.getBoneCount() / 10) + 1) * 10;
|
|
int numBones = ((skeleton.getBoneCount() / 10) + 1) * 10;
|
|
- for (Material m : materials) {
|
|
|
|
- m.setInt("NumberOfBones", numBones);
|
|
|
|
- }
|
|
|
|
|
|
+ numberOfBonesParam.setValue(numBones);
|
|
|
|
+
|
|
for (Geometry geometry : targets) {
|
|
for (Geometry geometry : targets) {
|
|
Mesh mesh = geometry.getMesh();
|
|
Mesh mesh = geometry.getMesh();
|
|
if (mesh != null && mesh.isAnimated()) {
|
|
if (mesh != null && mesh.isAnimated()) {
|
|
@@ -135,11 +133,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
}
|
|
}
|
|
|
|
|
|
private void switchToSoftware() {
|
|
private void switchToSoftware() {
|
|
- for (Material m : materials) {
|
|
|
|
- if (m.getParam("NumberOfBones") != null) {
|
|
|
|
- m.clearParam("NumberOfBones");
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ numberOfBonesParam.setEnabled(false);
|
|
|
|
+ boneMatricesParam.setEnabled(false);
|
|
|
|
+
|
|
for (Geometry geometry : targets) {
|
|
for (Geometry geometry : targets) {
|
|
Mesh mesh = geometry.getMesh();
|
|
Mesh mesh = geometry.getMesh();
|
|
if (mesh != null && mesh.isAnimated()) {
|
|
if (mesh != null && mesh.isAnimated()) {
|
|
@@ -149,19 +145,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
}
|
|
}
|
|
|
|
|
|
private boolean testHardwareSupported(RenderManager rm) {
|
|
private boolean testHardwareSupported(RenderManager rm) {
|
|
- for (Material m : materials) {
|
|
|
|
- // Some of the animated mesh(es) do not support hardware skinning,
|
|
|
|
- // so it is not supported by the model.
|
|
|
|
- if (m.getMaterialDef().getMaterialParam("NumberOfBones") == null) {
|
|
|
|
- Logger.getLogger(SkeletonControl.class.getName()).log(Level.WARNING,
|
|
|
|
- "Not using hardware skinning for {0}, " +
|
|
|
|
- "because material {1} doesn''t support it.",
|
|
|
|
- new Object[]{spatial, m.getMaterialDef().getName()});
|
|
|
|
-
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
switchToHardware();
|
|
switchToHardware();
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -178,6 +161,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
* supported by GPU, it shall be enabled, if its not preferred, or not
|
|
* supported by GPU, it shall be enabled, if its not preferred, or not
|
|
* supported by GPU, then it shall be disabled.
|
|
* supported by GPU, then it shall be disabled.
|
|
*
|
|
*
|
|
|
|
+ * @param preferred
|
|
* @see #isHardwareSkinningUsed()
|
|
* @see #isHardwareSkinningUsed()
|
|
*/
|
|
*/
|
|
public void setHardwareSkinningPreferred(boolean preferred) {
|
|
public void setHardwareSkinningPreferred(boolean preferred) {
|
|
@@ -212,6 +196,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
throw new IllegalArgumentException("skeleton cannot be null");
|
|
throw new IllegalArgumentException("skeleton cannot be null");
|
|
}
|
|
}
|
|
this.skeleton = skeleton;
|
|
this.skeleton = skeleton;
|
|
|
|
+ this.numberOfBonesParam = new MatParamOverride(VarType.Int, "NumberOfBones", null);
|
|
|
|
+ this.boneMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -222,8 +208,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
Mesh mesh = geometry.getMesh();
|
|
Mesh mesh = geometry.getMesh();
|
|
if (mesh != null && mesh.isAnimated()) {
|
|
if (mesh != null && mesh.isAnimated()) {
|
|
targets.add(geometry);
|
|
targets.add(geometry);
|
|
- materials.add(geometry.getMaterial());
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
private void findTargets(Node node) {
|
|
private void findTargets(Node node) {
|
|
@@ -238,8 +224,21 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public void setSpatial(Spatial spatial) {
|
|
public void setSpatial(Spatial spatial) {
|
|
|
|
+ Spatial oldSpatial = this.spatial;
|
|
super.setSpatial(spatial);
|
|
super.setSpatial(spatial);
|
|
updateTargetsAndMaterials(spatial);
|
|
updateTargetsAndMaterials(spatial);
|
|
|
|
+
|
|
|
|
+ if (oldSpatial != null) {
|
|
|
|
+ oldSpatial.removeMatParamOverride(numberOfBonesParam);
|
|
|
|
+ oldSpatial.removeMatParamOverride(boneMatricesParam);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (spatial != null) {
|
|
|
|
+ spatial.removeMatParamOverride(numberOfBonesParam);
|
|
|
|
+ spatial.removeMatParamOverride(boneMatricesParam);
|
|
|
|
+ spatial.addMatParamOverride(numberOfBonesParam);
|
|
|
|
+ spatial.addMatParamOverride(boneMatricesParam);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
private void controlRenderSoftware() {
|
|
private void controlRenderSoftware() {
|
|
@@ -258,27 +257,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
|
|
|
|
private void controlRenderHardware() {
|
|
private void controlRenderHardware() {
|
|
offsetMatrices = skeleton.computeSkinningMatrices();
|
|
offsetMatrices = skeleton.computeSkinningMatrices();
|
|
- for (Material m : materials) {
|
|
|
|
- MatParam currentParam = m.getParam("BoneMatrices");
|
|
|
|
-
|
|
|
|
- if (currentParam != null) {
|
|
|
|
- if (currentParam.getValue() != offsetMatrices) {
|
|
|
|
- // Check to see if other SkeletonControl
|
|
|
|
- // is operating on this material, in that case, user
|
|
|
|
- // is sharing materials between models which is NOT allowed
|
|
|
|
- // when hardware skinning used.
|
|
|
|
-
|
|
|
|
- Logger.getLogger(SkeletonControl.class.getName()).log(Level.SEVERE,
|
|
|
|
- "Material instances cannot be shared when hardware skinning is used. " +
|
|
|
|
- "Ensure all models use unique material instances."
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- m.setParam("BoneMatrices", VarType.Matrix4Array, offsetMatrices);
|
|
|
|
- }
|
|
|
|
|
|
+ boneMatricesParam.setValue(offsetMatrices);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
@@ -296,7 +276,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
if (hwSkinningSupported) {
|
|
if (hwSkinningSupported) {
|
|
hwSkinningEnabled = true;
|
|
hwSkinningEnabled = true;
|
|
|
|
|
|
- Logger.getLogger(SkeletonControl.class.getName()).log(Level.INFO, "Hardware skinning engaged for " + spatial);
|
|
|
|
|
|
+ Logger.getLogger(SkeletonControl.class.getName()).log(Level.INFO, "Hardware skinning engaged for {0}", spatial);
|
|
} else {
|
|
} else {
|
|
switchToSoftware();
|
|
switchToSoftware();
|
|
}
|
|
}
|
|
@@ -420,28 +400,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
// were shared then this will share them.
|
|
// were shared then this will share them.
|
|
this.targets = cloner.clone(targets);
|
|
this.targets = cloner.clone(targets);
|
|
|
|
|
|
- // Not automatic set cloning yet
|
|
|
|
- Set<Material> newMaterials = new HashSet<Material>();
|
|
|
|
- for( Material m : this.materials ) {
|
|
|
|
- Material mClone = cloner.clone(m);
|
|
|
|
- newMaterials.add(mClone);
|
|
|
|
- if( mClone != m ) {
|
|
|
|
- // Material was really cloned so clear the bone matrices in case
|
|
|
|
- // this is hardware skinned. This allows a local version to be
|
|
|
|
- // used and will be reset on the material. Really this just avoids
|
|
|
|
- // the 'safety' check in controlRenderHardware(). Right now material
|
|
|
|
- // doesn't clone itself with the cloner (and doesn't clone its parameters)
|
|
|
|
- // else this would be unnecessary.
|
|
|
|
- MatParam boneMatrices = mClone.getParam("BoneMatrices");
|
|
|
|
-
|
|
|
|
- // ...because for some strange reason you can't clear a non-existant
|
|
|
|
- // parameter.
|
|
|
|
- if( boneMatrices != null ) {
|
|
|
|
- mClone.clearParam("BoneMatrices");
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- this.materials = newMaterials;
|
|
|
|
|
|
+ this.numberOfBonesParam = cloner.clone(numberOfBonesParam);
|
|
|
|
+ this.boneMatricesParam = cloner.clone(boneMatricesParam);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -547,10 +507,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
fnb.rewind();
|
|
fnb.rewind();
|
|
|
|
|
|
// get boneIndexes and weights for mesh
|
|
// get boneIndexes and weights for mesh
|
|
- indexReader.setBuffer(mesh.getBuffer(Type.BoneIndex).getData());
|
|
|
|
|
|
+ IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData());
|
|
FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
|
|
FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
|
|
|
|
|
|
- indexReader.rewind();
|
|
|
|
wb.rewind();
|
|
wb.rewind();
|
|
|
|
|
|
float[] weights = wb.array();
|
|
float[] weights = wb.array();
|
|
@@ -591,7 +550,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
|
|
|
|
for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
|
|
for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
|
|
float weight = weights[idxWeights];
|
|
float weight = weights[idxWeights];
|
|
- Matrix4f mat = offsetMatrices[indexReader.getUnsigned(idxWeights++)];
|
|
|
|
|
|
+ Matrix4f mat = offsetMatrices[ib.get(idxWeights++)];
|
|
|
|
|
|
rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
|
|
rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
|
|
ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
|
|
ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
|
|
@@ -664,10 +623,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
|
|
|
|
|
|
|
|
// get boneIndexes and weights for mesh
|
|
// get boneIndexes and weights for mesh
|
|
- indexReader.setBuffer(mesh.getBuffer(Type.BoneIndex).getData());
|
|
|
|
|
|
+ IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData());
|
|
FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
|
|
FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
|
|
|
|
|
|
- indexReader.rewind();
|
|
|
|
wb.rewind();
|
|
wb.rewind();
|
|
|
|
|
|
float[] weights = wb.array();
|
|
float[] weights = wb.array();
|
|
@@ -723,7 +681,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
|
|
|
|
for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
|
|
for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
|
|
float weight = weights[idxWeights];
|
|
float weight = weights[idxWeights];
|
|
- Matrix4f mat = offsetMatrices[indexReader.getUnsigned(idxWeights++)];
|
|
|
|
|
|
+ Matrix4f mat = offsetMatrices[ib.get(idxWeights++)];
|
|
|
|
|
|
rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
|
|
rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
|
|
ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
|
|
ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
|
|
@@ -781,7 +739,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
super.write(ex);
|
|
super.write(ex);
|
|
OutputCapsule oc = ex.getCapsule(this);
|
|
OutputCapsule oc = ex.getCapsule(this);
|
|
oc.write(skeleton, "skeleton", null);
|
|
oc.write(skeleton, "skeleton", null);
|
|
- //Targets and materials don't need to be saved, they'll be gathered on each frame
|
|
|
|
|
|
+
|
|
|
|
+ oc.write(numberOfBonesParam, "numberOfBonesParam", null);
|
|
|
|
+ oc.write(boneMatricesParam, "boneMatricesParam", null);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -789,6 +749,16 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
super.read(im);
|
|
super.read(im);
|
|
InputCapsule in = im.getCapsule(this);
|
|
InputCapsule in = im.getCapsule(this);
|
|
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
|
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
|
|
|
+
|
|
|
|
+ numberOfBonesParam = (MatParamOverride) in.readSavable("numberOfBonesParam", null);
|
|
|
|
+ boneMatricesParam = (MatParamOverride) in.readSavable("boneMatricesParam", null);
|
|
|
|
+
|
|
|
|
+ if (numberOfBonesParam == null) {
|
|
|
|
+ numberOfBonesParam = new MatParamOverride(VarType.Int, "NumberOfBones", null);
|
|
|
|
+ boneMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null);
|
|
|
|
+ getSpatial().addMatParamOverride(numberOfBonesParam);
|
|
|
|
+ getSpatial().addMatParamOverride(boneMatricesParam);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -798,7 +768,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|
*/
|
|
*/
|
|
private void updateTargetsAndMaterials(Spatial spatial) {
|
|
private void updateTargetsAndMaterials(Spatial spatial) {
|
|
targets.clear();
|
|
targets.clear();
|
|
- materials.clear();
|
|
|
|
|
|
|
|
if (spatial instanceof Node) {
|
|
if (spatial instanceof Node) {
|
|
findTargets((Node) spatial);
|
|
findTargets((Node) spatial);
|