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
3.0
rem..om 12 years ago
parent e34d483973
commit eb5525e581
  1. 81
      engine/src/core-data/Common/ShaderLib/Skinning.glsllib
  2. 262
      engine/src/core/com/jme3/animation/SkeletonControl.java
  3. 66
      engine/src/core/com/jme3/scene/Mesh.java
  4. 3
      engine/src/core/com/jme3/scene/VertexBuffer.java

@ -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,25 +32,32 @@
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
*/ */
public class SkeletonControl extends AbstractControl implements Cloneable { public class SkeletonControl extends AbstractControl implements Cloneable {
@ -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,10 +101,50 @@ 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
*/ */
public SkeletonControl(Skeleton skeleton) { public SkeletonControl(Skeleton skeleton) {
@ -88,7 +153,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
/** /**
* Creates a skeleton control. * Creates a skeleton control.
* *
* @param targets the meshes controlled by the skeleton * @param targets the meshes controlled by the skeleton
* @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()) {
if (!(child instanceof Geometry)) {
continue; // could be an attachment node, ignore.
}
Geometry geom = (Geometry) child;
// is this geometry using a shared mesh? for (Spatial child : node.getChildren()) {
Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH); if (child instanceof Geometry) {
Geometry geom = (Geometry) child;
if (childSharedMesh != null) {
// Don't bother with non-animated shared meshes // is this geometry using a shared mesh?
if (isMeshAnimated(childSharedMesh)) { Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH);
// child is using shared mesh,
// so animate the shared mesh but ignore child if (childSharedMesh != null) {
if (sharedMesh == null) { // Don’t bother with non-animated shared meshes
sharedMesh = childSharedMesh; if (isMeshAnimated(childSharedMesh)) {
} else if (sharedMesh != childSharedMesh) { // child is using shared mesh,
throw new IllegalStateException("Two conflicting shared meshes for " + node); // so animate the shared mesh but ignore child
if (sharedMesh == null) {
sharedMesh = childSharedMesh;
} else if (sharedMesh != childSharedMesh) {
throw new IllegalStateException("Two conflicting shared meshes for " + node);
}
materials.add(geom.getMaterial());
}
} 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
Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices(); if (!triedHwSkinning) {
triedHwSkinning = true;
// if hardware skinning is supported, the matrices and weight buffer try {
// will be sent by the SkinningShaderLogic object assigned to the shader rm.preloadScene(this.spatial);
for (int i = 0; i < targets.length; i++) { } catch (RendererException e) {
// NOTE: This assumes that code higher up //revert back to sw skinning for this model
// Already ensured those targets are animated setUseHwSkinning(false);
// otherwise a crash will happen in skin update }
//if (isMeshAnimated(targets[i])) { }
softwareSkinUpdate(targets[i], offsetMatrices); offsetMatrices = skeleton.computeSkinningMatrices();
//}
hardwareSkinUpdate();
} else {
resetToBind(); // reset morph meshes to bind pose
offsetMatrices = skeleton.computeSkinningMatrices();
// if hardware skinning is supported, the matrices and weight buffer
// will be sent by the SkinningShaderLogic object assigned to the shader
for (int i = 0; i < targets.length; i++) {
// NOTE: This assumes that code higher up
// Already ensured those targets are animated
// 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++) {
@ -250,9 +339,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
} }
/** /**
* *
* @param boneName the name of the bone * @param boneName the name of the bone
* @return the node attached to this bone * @return the node attached to this bone
*/ */
public Node getAttachmentsNode(String boneName) { public Node getAttachmentsNode(String boneName) {
Bone b = skeleton.getBone(boneName); Bone b = skeleton.getBone(boneName);
@ -269,7 +358,8 @@ 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() {
return skeleton; return skeleton;
@ -277,30 +367,34 @@ 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) {
// this.skeleton = skeleton; // this.skeleton = skeleton;
// } // }
/** /**
* returns the targets meshes of this control * returns the targets meshes of this control
* @return *
* @return
*/ */
public Mesh[] getTargets() { public Mesh[] getTargets() {
return targets; return targets;
} }
/** /**
* 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) {
// this.targets = targets; // this.targets = targets;
// } // }
/** /**
* 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
*/ */
private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) { private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
@ -317,7 +411,20 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
} }
/** /**
* Method to apply skinning transforms to a mesh's buffers * 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
*
* @param mesh the mesh * @param mesh the mesh
* @param offsetMatrices the offset matices to apply * @param offsetMatrices the offset matices to apply
*/ */
@ -373,7 +480,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
idxWeights += 4; idxWeights += 4;
continue; continue;
} }
float nmx = normBuf[idxPositions]; float nmx = normBuf[idxPositions];
float vtx = posBuf[idxPositions++]; float vtx = posBuf[idxPositions++];
float nmy = normBuf[idxPositions]; float nmy = normBuf[idxPositions];
@ -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
@ -496,7 +606,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
idxWeights += 4; idxWeights += 4;
continue; continue;
} }
float nmx = normBuf[idxPositions]; float nmx = normBuf[idxPositions];
float vtx = posBuf[idxPositions++]; float vtx = posBuf[idxPositions++];
float nmy = normBuf[idxPositions]; float nmy = normBuf[idxPositions];
@ -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…
Cancel
Save