Hardware Skinning first commit, still non functionnal as no material implements it. also it's disabled by default in the skeleton control
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10537 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
e34d483973
commit
eb5525e581
@ -1,36 +1,55 @@
|
|||||||
#ifdef USE_HWSKINNING
|
#ifdef NUM_BONES
|
||||||
|
|
||||||
#ifndef NUM_BONES
|
|
||||||
#error A required pre-processor define "NUM_BONES" is not set!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
attribute vec4 inBoneWeight;
|
attribute vec4 inBoneWeight;
|
||||||
attribute vec4 inBoneIndices;
|
attribute vec4 inBoneIndex;
|
||||||
uniform mat4 m_BoneMatrices[NUM_BONES];
|
uniform mat4 m_BoneMatrices[NUM_BONES];
|
||||||
|
|
||||||
void Skinning_Compute(inout vec4 position, inout vec4 normal){
|
void Skinning_Compute(inout vec4 position){
|
||||||
vec4 index = inBoneIndices;
|
#if NUM_WEIGHTS_PER_VERT == 1
|
||||||
vec4 weight = inBoneWeight;
|
position = m_BoneMatrices[int(inBoneIndex.x)] * position;
|
||||||
|
#else
|
||||||
vec4 newPos = vec4(0.0);
|
mat4 mat = mat4(0.0);
|
||||||
vec4 newNormal = vec4(0.0);
|
mat += m_BoneMatrices[int(inBoneIndex.x)] * inBoneWeight.x;
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.y)] * inBoneWeight.y;
|
||||||
for (float i = 0.0; i < 4.0; i += 1.0){
|
mat += m_BoneMatrices[int(inBoneIndex.z)] * inBoneWeight.z;
|
||||||
mat4 skinMat = m_BoneMatrices[int(index.x)];
|
mat += m_BoneMatrices[int(inBoneIndex.w)] * inBoneWeight.w;
|
||||||
newPos += weight.x * (skinMat * position);
|
position = mat * position;
|
||||||
newNormal += weight.x * (skinMat * normal);
|
#endif
|
||||||
index = index.yzwx;
|
|
||||||
weight = weight.yzwx;
|
|
||||||
}
|
|
||||||
|
|
||||||
position = newPos;
|
|
||||||
normal = newNormal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
void Skinning_Compute(inout vec4 position, inout vec3 normal){
|
||||||
|
#if NUM_WEIGHTS_PER_VERT == 1
|
||||||
void Skinning_Compute(inout vec4 position, inout vec4 normal){
|
position = m_BoneMatrices[int(inBoneIndex.x)] * position;
|
||||||
// skinning disabled, leave position and normal unaltered
|
normal = (mat3(m_BoneMatrices[int(inBoneIndex.x)][0].xyz,
|
||||||
|
m_BoneMatrices[int(inBoneIndex.x)][1].xyz,
|
||||||
|
m_BoneMatrices[int(inBoneIndex.x)][2].xyz) * normal);
|
||||||
|
#else
|
||||||
|
mat4 mat = mat4(0.0);
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.x)] * inBoneWeight.x;
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.y)] * inBoneWeight.y;
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.z)] * inBoneWeight.z;
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.w)] * inBoneWeight.w;
|
||||||
|
position = mat * position;
|
||||||
|
normal = (mat3(mat[0].xyz,mat[1].xyz,mat[2].xyz) * normal);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Skinning_Compute(inout vec4 position, inout vec4 tangent, inout vec3 normal){
|
||||||
|
#if NUM_WEIGHTS_PER_VERT == 1
|
||||||
|
position = m_BoneMatrices[int(inBoneIndex.x)] * position;
|
||||||
|
tangent = m_BoneMatrices[int(inBoneIndex.x)] * tangent;
|
||||||
|
normal = (mat3(m_BoneMatrices[int(inBoneIndex.x)][0].xyz,
|
||||||
|
m_BoneMatrices[int(inBoneIndex.x)][1].xyz,
|
||||||
|
m_BoneMatrices[int(inBoneIndex.x)][2].xyz) * normal);
|
||||||
|
#else
|
||||||
|
mat4 mat = mat4(0.0);
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.x)] * inBoneWeight.x;
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.y)] * inBoneWeight.y;
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.z)] * inBoneWeight.z;
|
||||||
|
mat += m_BoneMatrices[int(inBoneIndex.w)] * inBoneWeight.w;
|
||||||
|
position = mat * position;
|
||||||
|
tangent = mat * tangent;
|
||||||
|
normal = (mat3(mat[0].xyz,mat[1].xyz,mat[2].xyz) * normal);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
@ -32,24 +32,31 @@
|
|||||||
package com.jme3.animation;
|
package com.jme3.animation;
|
||||||
|
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
|
import com.jme3.material.Material;
|
||||||
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;
|
||||||
|
import com.jme3.renderer.RendererException;
|
||||||
import com.jme3.renderer.ViewPort;
|
import com.jme3.renderer.ViewPort;
|
||||||
import com.jme3.scene.*;
|
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.shader.VarType;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.Buffer;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Skeleton control deforms a model according to a skeleton,
|
* The Skeleton control deforms a model according to a skeleton, It handles the
|
||||||
* It handles the computation of the deformation matrices and performs
|
* computation of the deformation matrices and performs the transformations on
|
||||||
* the transformations on the mesh
|
* the mesh
|
||||||
*
|
*
|
||||||
* @author Rémy Bouquet Based on AnimControl by Kirill Vainer
|
* @author Rémy Bouquet Based on AnimControl by Kirill Vainer
|
||||||
*/
|
*/
|
||||||
@ -64,10 +71,28 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
*/
|
*/
|
||||||
private Mesh[] targets;
|
private Mesh[] targets;
|
||||||
/**
|
/**
|
||||||
* Used to track when a mesh was updated. Meshes are only updated
|
* Used to track when a mesh was updated. Meshes are only updated if they
|
||||||
* if they are visible in at least one camera.
|
* are visible in at least one camera.
|
||||||
*/
|
*/
|
||||||
private boolean wasMeshUpdated = false;
|
private boolean wasMeshUpdated = false;
|
||||||
|
/**
|
||||||
|
* Flag to enable hardware/gpu skinning if available, disable for
|
||||||
|
* software/cpu skinning, enabled by default
|
||||||
|
*/
|
||||||
|
private boolean useHwSkinning = false;
|
||||||
|
/**
|
||||||
|
* Flag to check if we have to check the shader if it would work and on fail
|
||||||
|
* switch to sw skinning
|
||||||
|
*/
|
||||||
|
private boolean triedHwSkinning = false;
|
||||||
|
/**
|
||||||
|
* Bone offset matrices, recreated each frame
|
||||||
|
*/
|
||||||
|
private transient Matrix4f[] offsetMatrices;
|
||||||
|
/**
|
||||||
|
* Material references used for hardware skinning
|
||||||
|
*/
|
||||||
|
private Material[] materials;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization only. Do not use.
|
* Serialization only. Do not use.
|
||||||
@ -76,9 +101,49 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a skeleton control.
|
* Hint to use hardware/software skinning mode. If gpu skinning fails or is
|
||||||
* The list of targets will be acquired automatically when
|
* disabledIf in hardware mode all or some models display the same animation
|
||||||
* the control is attached to a node.
|
* cycle make sure your materials are not identical but clones
|
||||||
|
*
|
||||||
|
* @param useHwSkinning the useHwSkinning to set
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setUseHwSkinning(boolean useHwSkinning) {
|
||||||
|
this.useHwSkinning = useHwSkinning;
|
||||||
|
this.triedHwSkinning = false;
|
||||||
|
//next full 10 bones (e.g. 30 on 24 bones )
|
||||||
|
int bones = ((skeleton.getBoneCount() / 10) + 1) * 10;
|
||||||
|
for (Material m : materials) {
|
||||||
|
if (useHwSkinning) {
|
||||||
|
try {
|
||||||
|
m.setInt("NumberOfBones", bones);
|
||||||
|
} catch (java.lang.IllegalArgumentException e) {
|
||||||
|
Logger.getLogger(SkeletonControl.class.getName()).log(Level.INFO, "{0} material doesn't support Hardware Skinning reverting to software", new String[]{m.getName()});
|
||||||
|
setUseHwSkinning(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m.getParam("NumberOfBones") != null) {
|
||||||
|
m.clearParam("NumberOfBones");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Mesh mesh : targets) {
|
||||||
|
if (isMeshAnimated(mesh)) {
|
||||||
|
mesh.prepareForAnim(!useHwSkinning); // prepare for software animation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useHwSkinning) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseHwSkinning() {
|
||||||
|
return useHwSkinning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a skeleton control. The list of targets will be acquired
|
||||||
|
* automatically when the control is attached to a node.
|
||||||
*
|
*
|
||||||
* @param skeleton the skeleton
|
* @param skeleton the skeleton
|
||||||
*/
|
*/
|
||||||
@ -102,44 +167,45 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
return mesh.getBuffer(Type.BindPosePosition) != null;
|
return mesh.getBuffer(Type.BindPosePosition) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mesh[] findTargets(Node node) {
|
private void findTargets(Node node, ArrayList<Mesh> targets, HashSet<Material> materials) {
|
||||||
Mesh sharedMesh = null;
|
Mesh sharedMesh = null;
|
||||||
ArrayList<Mesh> animatedMeshes = new ArrayList<Mesh>();
|
|
||||||
|
|
||||||
for (Spatial child : node.getChildren()) {
|
for (Spatial child : node.getChildren()) {
|
||||||
if (!(child instanceof Geometry)) {
|
if (child instanceof Geometry) {
|
||||||
continue; // could be an attachment node, ignore.
|
Geometry geom = (Geometry) child;
|
||||||
}
|
|
||||||
|
|
||||||
Geometry geom = (Geometry) child;
|
// is this geometry using a shared mesh?
|
||||||
|
Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH);
|
||||||
|
|
||||||
// is this geometry using a shared mesh?
|
if (childSharedMesh != null) {
|
||||||
Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH);
|
// Don’t bother with non-animated shared meshes
|
||||||
|
if (isMeshAnimated(childSharedMesh)) {
|
||||||
if (childSharedMesh != null) {
|
// child is using shared mesh,
|
||||||
// Don't bother with non-animated shared meshes
|
// so animate the shared mesh but ignore child
|
||||||
if (isMeshAnimated(childSharedMesh)) {
|
if (sharedMesh == null) {
|
||||||
// child is using shared mesh,
|
sharedMesh = childSharedMesh;
|
||||||
// so animate the shared mesh but ignore child
|
} else if (sharedMesh != childSharedMesh) {
|
||||||
if (sharedMesh == null) {
|
throw new IllegalStateException("Two conflicting shared meshes for " + node);
|
||||||
sharedMesh = childSharedMesh;
|
}
|
||||||
} else if (sharedMesh != childSharedMesh) {
|
materials.add(geom.getMaterial());
|
||||||
throw new IllegalStateException("Two conflicting shared meshes for " + node);
|
}
|
||||||
|
} else {
|
||||||
|
Mesh mesh = geom.getMesh();
|
||||||
|
if (isMeshAnimated(mesh)) {
|
||||||
|
targets.add(mesh);
|
||||||
|
materials.add(geom.getMaterial());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (child instanceof Node) {
|
||||||
Mesh mesh = geom.getMesh();
|
findTargets((Node) child, targets, materials);
|
||||||
if (isMeshAnimated(mesh)) {
|
|
||||||
animatedMeshes.add(mesh);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sharedMesh != null) {
|
if (sharedMesh != null) {
|
||||||
animatedMeshes.add(sharedMesh);
|
targets.add(sharedMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
return animatedMeshes.toArray(new Mesh[animatedMeshes.size()]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -147,28 +213,51 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
super.setSpatial(spatial);
|
super.setSpatial(spatial);
|
||||||
if (spatial != null) {
|
if (spatial != null) {
|
||||||
Node node = (Node) spatial;
|
Node node = (Node) spatial;
|
||||||
targets = findTargets(node);
|
HashSet<Material> mats = new HashSet<Material>();
|
||||||
|
ArrayList<Mesh> meshes = new ArrayList<Mesh>();
|
||||||
|
findTargets(node, meshes, mats);
|
||||||
|
targets = meshes.toArray(new Mesh[meshes.size()]);
|
||||||
|
materials = mats.toArray(new Material[mats.size()]);
|
||||||
|
//try hw skinning, will be reset to sw skinning if render call fails
|
||||||
|
setUseHwSkinning(true);
|
||||||
} else {
|
} else {
|
||||||
targets = null;
|
targets = null;
|
||||||
|
materials = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
if (!wasMeshUpdated) {
|
if (!wasMeshUpdated) {
|
||||||
resetToBind(); // reset morph meshes to bind pose
|
if (useHwSkinning) {
|
||||||
|
//preload scene to check if shader won’t blow with too many bones
|
||||||
|
if (!triedHwSkinning) {
|
||||||
|
triedHwSkinning = true;
|
||||||
|
try {
|
||||||
|
rm.preloadScene(this.spatial);
|
||||||
|
} catch (RendererException e) {
|
||||||
|
//revert back to sw skinning for this model
|
||||||
|
setUseHwSkinning(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offsetMatrices = skeleton.computeSkinningMatrices();
|
||||||
|
|
||||||
Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices();
|
hardwareSkinUpdate();
|
||||||
|
} else {
|
||||||
|
resetToBind(); // reset morph meshes to bind pose
|
||||||
|
|
||||||
// if hardware skinning is supported, the matrices and weight buffer
|
offsetMatrices = skeleton.computeSkinningMatrices();
|
||||||
// will be sent by the SkinningShaderLogic object assigned to the shader
|
|
||||||
for (int i = 0; i < targets.length; i++) {
|
// if hardware skinning is supported, the matrices and weight buffer
|
||||||
// NOTE: This assumes that code higher up
|
// will be sent by the SkinningShaderLogic object assigned to the shader
|
||||||
// Already ensured those targets are animated
|
for (int i = 0; i < targets.length; i++) {
|
||||||
// otherwise a crash will happen in skin update
|
// NOTE: This assumes that code higher up
|
||||||
//if (isMeshAnimated(targets[i])) {
|
// Already ensured those targets are animated
|
||||||
softwareSkinUpdate(targets[i], offsetMatrices);
|
// otherwise a crash will happen in skin update
|
||||||
//}
|
//if (isMeshAnimated(targets)) {
|
||||||
|
softwareSkinUpdate(targets[i], offsetMatrices);
|
||||||
|
//}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wasMeshUpdated = true;
|
wasMeshUpdated = true;
|
||||||
@ -180,13 +269,14 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
wasMeshUpdated = false;
|
wasMeshUpdated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//only do this for software updates
|
||||||
void resetToBind() {
|
void resetToBind() {
|
||||||
for (Mesh mesh : targets) {
|
for (Mesh mesh : targets) {
|
||||||
if (isMeshAnimated(mesh)) {
|
if (isMeshAnimated(mesh)) {
|
||||||
FloatBuffer bwBuff = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
|
Buffer bwBuff = mesh.getBuffer(Type.BoneWeight).getData();
|
||||||
ByteBuffer biBuff = (ByteBuffer)mesh.getBuffer(Type.BoneIndex).getData();
|
Buffer biBuff = mesh.getBuffer(Type.BoneIndex).getData();
|
||||||
if (!biBuff.hasArray() || !bwBuff.hasArray()) {
|
if (!biBuff.hasArray() || !bwBuff.hasArray()) {
|
||||||
mesh.prepareForAnim(true); // prepare for software animation
|
mesh.prepareForAnim(!useHwSkinning); // prepare for software animation
|
||||||
}
|
}
|
||||||
VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
|
VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
|
||||||
VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
|
VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
|
||||||
@ -223,11 +313,10 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
Node clonedNode = (Node) spatial;
|
Node clonedNode = (Node) spatial;
|
||||||
AnimControl ctrl = spatial.getControl(AnimControl.class);
|
AnimControl ctrl = spatial.getControl(AnimControl.class);
|
||||||
SkeletonControl clone = new SkeletonControl();
|
SkeletonControl clone = new SkeletonControl();
|
||||||
clone.setSpatial(clonedNode);
|
|
||||||
|
|
||||||
clone.skeleton = ctrl.getSkeleton();
|
clone.skeleton = ctrl.getSkeleton();
|
||||||
// Fix animated targets for the cloned node
|
|
||||||
clone.targets = findTargets(clonedNode);
|
clone.setSpatial(clonedNode);
|
||||||
|
|
||||||
// Fix attachments for the cloned node
|
// Fix attachments for the cloned node
|
||||||
for (int i = 0; i < clonedNode.getQuantity(); i++) {
|
for (int i = 0; i < clonedNode.getQuantity(); i++) {
|
||||||
@ -269,6 +358,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the skeleton of this control
|
* returns the skeleton of this control
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Skeleton getSkeleton() {
|
public Skeleton getSkeleton() {
|
||||||
@ -277,6 +367,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the skeleton for this control
|
* sets the skeleton for this control
|
||||||
|
*
|
||||||
* @param skeleton
|
* @param skeleton
|
||||||
*/
|
*/
|
||||||
// public void setSkeleton(Skeleton skeleton) {
|
// public void setSkeleton(Skeleton skeleton) {
|
||||||
@ -284,6 +375,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
// }
|
// }
|
||||||
/**
|
/**
|
||||||
* returns the targets meshes of this control
|
* returns the targets meshes of this control
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Mesh[] getTargets() {
|
public Mesh[] getTargets() {
|
||||||
@ -291,7 +383,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the target meshes of this control
|
* sets the target meshes of this control
|
||||||
|
*
|
||||||
* @param targets
|
* @param targets
|
||||||
*/
|
*/
|
||||||
// public void setTargets(Mesh[] targets) {
|
// public void setTargets(Mesh[] targets) {
|
||||||
@ -299,6 +392,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
// }
|
// }
|
||||||
/**
|
/**
|
||||||
* Update the mesh according to the given transformation matrices
|
* Update the mesh according to the given transformation matrices
|
||||||
|
*
|
||||||
* @param mesh then mesh
|
* @param mesh then mesh
|
||||||
* @param offsetMatrices the transformation matrices to apply
|
* @param offsetMatrices the transformation matrices to apply
|
||||||
*/
|
*/
|
||||||
@ -316,8 +410,21 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the mesh according to the given transformation matrices
|
||||||
|
*
|
||||||
|
* @param mesh then mesh
|
||||||
|
* @param offsetMatrices the transformation matrices to apply
|
||||||
|
*/
|
||||||
|
private void hardwareSkinUpdate() {
|
||||||
|
for (Material m : materials) {
|
||||||
|
m.setParam("BoneMatrices", VarType.Matrix4Array, offsetMatrices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to apply skinning transforms to a mesh's buffers
|
* Method to apply skinning transforms to a mesh's buffers
|
||||||
|
*
|
||||||
* @param mesh the mesh
|
* @param mesh the mesh
|
||||||
* @param offsetMatrices the offset matices to apply
|
* @param offsetMatrices the offset matices to apply
|
||||||
*/
|
*/
|
||||||
@ -421,9 +528,12 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific method for skinning with tangents to avoid cluttering the classic skinning calculation with
|
* Specific method for skinning with tangents to avoid cluttering the
|
||||||
* null checks that would slow down the process even if tangents don't have to be computed.
|
* classic skinning calculation with null checks that would slow down the
|
||||||
* Also the iteration has additional indexes since tangent has 4 components instead of 3 for pos and norm
|
* process even if tangents don't have to be computed. Also the iteration
|
||||||
|
* has additional indexes since tangent has 4 components instead of 3 for
|
||||||
|
* pos and norm
|
||||||
|
*
|
||||||
* @param maxWeightsPerVert maximum number of weights per vertex
|
* @param maxWeightsPerVert maximum number of weights per vertex
|
||||||
* @param mesh the mesh
|
* @param mesh the mesh
|
||||||
* @param offsetMatrices the offsetMaytrices to apply
|
* @param offsetMatrices the offsetMaytrices to apply
|
||||||
@ -574,6 +684,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.write(targets, "targets", null);
|
oc.write(targets, "targets", null);
|
||||||
oc.write(skeleton, "skeleton", null);
|
oc.write(skeleton, "skeleton", null);
|
||||||
|
oc.write(materials, "materials", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -586,5 +697,10 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
System.arraycopy(sav, 0, targets, 0, sav.length);
|
System.arraycopy(sav, 0, targets, 0, sav.length);
|
||||||
}
|
}
|
||||||
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
||||||
|
sav = in.readSavableArray("materials", null);
|
||||||
|
if (sav != null) {
|
||||||
|
materials = new Material[sav.length];
|
||||||
|
System.arraycopy(sav, 0, materials, 0, sav.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
BufferUtils.clone(tangents.getData()));
|
BufferUtils.clone(tangents.getData()));
|
||||||
setBuffer(bindTangents);
|
setBuffer(bindTangents);
|
||||||
tangents.setUsage(Usage.Stream);
|
tangents.setUsage(Usage.Stream);
|
||||||
}
|
}// else hardware setup does nothing, mesh already in bind pose
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,22 +352,70 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
* @param forSoftwareAnim Should be true to enable the conversion.
|
* @param forSoftwareAnim Should be true to enable the conversion.
|
||||||
*/
|
*/
|
||||||
public void prepareForAnim(boolean forSoftwareAnim){
|
public void prepareForAnim(boolean forSoftwareAnim){
|
||||||
if (forSoftwareAnim){
|
if (forSoftwareAnim) {
|
||||||
// convert indices
|
// convert indices to ubytes on the heap or floats
|
||||||
VertexBuffer indices = getBuffer(Type.BoneIndex);
|
VertexBuffer indices = getBuffer(Type.BoneIndex);
|
||||||
ByteBuffer originalIndex = (ByteBuffer) indices.getData();
|
Buffer buffer = indices.getData();
|
||||||
ByteBuffer arrayIndex = ByteBuffer.allocate(originalIndex.capacity());
|
if (buffer instanceof ByteBuffer) {
|
||||||
originalIndex.clear();
|
ByteBuffer originalIndex = (ByteBuffer) buffer;
|
||||||
arrayIndex.put(originalIndex);
|
ByteBuffer arrayIndex = ByteBuffer.allocate(originalIndex.capacity());
|
||||||
indices.updateData(arrayIndex);
|
originalIndex.clear();
|
||||||
|
arrayIndex.put(originalIndex);
|
||||||
|
indices.updateData(arrayIndex);
|
||||||
|
} else if (buffer instanceof FloatBuffer) {
|
||||||
|
//Floats back to bytes
|
||||||
|
FloatBuffer originalIndex = (FloatBuffer) buffer;
|
||||||
|
ByteBuffer arrayIndex = ByteBuffer.allocate(originalIndex.capacity());
|
||||||
|
originalIndex.clear();
|
||||||
|
for (int i = 0; i < originalIndex.capacity(); i++) {
|
||||||
|
arrayIndex.put((byte) originalIndex.get(i));
|
||||||
|
}
|
||||||
|
indices.updateData(arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
// convert weights
|
// convert weights on the heap
|
||||||
VertexBuffer weights = getBuffer(Type.BoneWeight);
|
VertexBuffer weights = getBuffer(Type.BoneWeight);
|
||||||
FloatBuffer originalWeight = (FloatBuffer) weights.getData();
|
FloatBuffer originalWeight = (FloatBuffer) weights.getData();
|
||||||
FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());
|
FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());
|
||||||
originalWeight.clear();
|
originalWeight.clear();
|
||||||
arrayWeight.put(originalWeight);
|
arrayWeight.put(originalWeight);
|
||||||
weights.updateData(arrayWeight);
|
weights.updateData(arrayWeight);
|
||||||
|
} else {
|
||||||
|
//BoneIndex must be 32 bit for attribute type constraints in shaders
|
||||||
|
VertexBuffer indices = getBuffer(Type.BoneIndex);
|
||||||
|
Buffer buffer = indices.getData();
|
||||||
|
if (buffer instanceof ByteBuffer) {
|
||||||
|
ByteBuffer bIndex = (ByteBuffer) buffer;
|
||||||
|
final float[] rval = new float[bIndex.capacity()];
|
||||||
|
for (int i = 0; i < rval.length; i++) {
|
||||||
|
rval[i] = bIndex.get(i);
|
||||||
|
}
|
||||||
|
clearBuffer(Type.BoneIndex);
|
||||||
|
|
||||||
|
VertexBuffer ib = new VertexBuffer(Type.BoneIndex);
|
||||||
|
ib.setupData(Usage.Stream,
|
||||||
|
4,
|
||||||
|
Format.Float,
|
||||||
|
BufferUtils.createFloatBuffer(rval));
|
||||||
|
setBuffer(ib);
|
||||||
|
} else if (buffer instanceof FloatBuffer) {
|
||||||
|
//BoneWeights on DirectBuffer
|
||||||
|
FloatBuffer originalIndices = (FloatBuffer) buffer;
|
||||||
|
FloatBuffer arrayIndices = BufferUtils.createFloatBuffer(originalIndices.capacity());
|
||||||
|
originalIndices.clear();
|
||||||
|
arrayIndices.put(originalIndices);
|
||||||
|
indices.setUsage(Usage.Stream);
|
||||||
|
indices.updateData(arrayIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
//BoneWeights on DirectBuffer
|
||||||
|
VertexBuffer weights = getBuffer(Type.BoneWeight);
|
||||||
|
FloatBuffer originalWeight = (FloatBuffer) weights.getData();
|
||||||
|
FloatBuffer arrayWeight = BufferUtils.createFloatBuffer(originalWeight.capacity());
|
||||||
|
originalWeight.clear();
|
||||||
|
arrayWeight.put(originalWeight);
|
||||||
|
weights.setUsage(Usage.Static);
|
||||||
|
weights.updateData(arrayWeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +145,8 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
* Bone indices, used with animation (4 ubytes).
|
* Bone indices, used with animation (4 ubytes).
|
||||||
* If used with software skinning, the usage should be
|
* If used with software skinning, the usage should be
|
||||||
* {@link Usage#CpuOnly}, and the buffer should be allocated
|
* {@link Usage#CpuOnly}, and the buffer should be allocated
|
||||||
* on the heap.
|
* on the heap as a ubytes buffer. For Hardware skinning this should be
|
||||||
|
* either an int or float buffer due to shader attribute types restrictions.
|
||||||
*/
|
*/
|
||||||
BoneIndex,
|
BoneIndex,
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user