Hardware Skinning now uses its own bone index and bone weight buffers. The vertex buffers are initialized empty when the model is loaded and placed in the cache.

They are populated only if hardware skinning is used with the model.

BoneIndex and BoneWeight buffers are now always CpuOnly and only used for Software Skinning.
Some enhancement could be done to save memory by not generating the bindPose buffers if hardware skinning is used as it doesn't need them.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10657 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 12 years ago
parent b5d1672f5e
commit 2acbdf9f84
  1. 10
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
  2. 54
      engine/src/core-data/Common/ShaderLib/Skinning.glsllib
  3. 40
      engine/src/core/com/jme3/scene/Mesh.java
  4. 20
      engine/src/core/com/jme3/scene/VertexBuffer.java
  5. 54
      engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java

@ -200,6 +200,8 @@ import com.jme3.util.BufferUtils;
mesh.setBuffer(buffers[0]); mesh.setBuffer(buffers[0]);
mesh.setBuffer(buffers[1]); mesh.setBuffer(buffers[1]);
//FIXME @Kaelthas this should be replaced by a call to
//mesh.generateBindPos(true)
VertexBuffer bindNormalBuffer = meshContext.getBindNormalBuffer(materialIndex); VertexBuffer bindNormalBuffer = meshContext.getBindNormalBuffer(materialIndex);
if (bindNormalBuffer != null) { if (bindNormalBuffer != null) {
mesh.setBuffer(bindNormalBuffer); mesh.setBuffer(bindNormalBuffer);
@ -212,6 +214,14 @@ import com.jme3.util.BufferUtils;
// Static to Stream // Static to Stream
mesh.getBuffer(Type.Position).setUsage(Usage.Stream); mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
mesh.getBuffer(Type.Normal).setUsage(Usage.Stream); mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
//creating empty buffers for HW skinning
//the buffers will be setup if ever used.
VertexBuffer verticesWeightsHW = new VertexBuffer(Type.HWBoneWeight);
VertexBuffer verticesWeightsIndicesHW = new VertexBuffer(Type.HWBoneIndex);
mesh.setBuffer(verticesWeightsHW);
mesh.setBuffer(verticesWeightsIndicesHW);
} }
} catch (BlenderFileException e) { } catch (BlenderFileException e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);

@ -6,38 +6,38 @@
#define NUM_WEIGHTS_PER_VERT 4 #define NUM_WEIGHTS_PER_VERT 4
attribute vec4 inBoneWeight; attribute vec4 inHWBoneWeight;
attribute vec4 inBoneIndex; attribute vec4 inHWBoneIndex;
uniform mat4 m_BoneMatrices[NUM_BONES]; uniform mat4 m_BoneMatrices[NUM_BONES];
void Skinning_Compute(inout vec4 position){ void Skinning_Compute(inout vec4 position){
if (inBoneWeight.x != 0.0) { if (inHWBoneWeight.x != 0.0) {
#if NUM_WEIGHTS_PER_VERT == 1 #if NUM_WEIGHTS_PER_VERT == 1
position = m_BoneMatrices[int(inBoneIndex.x)] * position; position = m_BoneMatrices[int(inHWBoneIndex.x)] * position;
#else #else
mat4 mat = mat4(0.0); mat4 mat = mat4(0.0);
mat += m_BoneMatrices[int(inBoneIndex.x)] * inBoneWeight.x; mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x;
mat += m_BoneMatrices[int(inBoneIndex.y)] * inBoneWeight.y; mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y;
mat += m_BoneMatrices[int(inBoneIndex.z)] * inBoneWeight.z; mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z;
mat += m_BoneMatrices[int(inBoneIndex.w)] * inBoneWeight.w; mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w;
position = mat * position; position = mat * position;
#endif #endif
} }
} }
void Skinning_Compute(inout vec4 position, inout vec3 normal){ void Skinning_Compute(inout vec4 position, inout vec3 normal){
if (inBoneWeight.x != 0.0) { if (inHWBoneWeight.x != 0.0) {
#if NUM_WEIGHTS_PER_VERT == 1 #if NUM_WEIGHTS_PER_VERT == 1
position = m_BoneMatrices[int(inBoneIndex.x)] * position; position = m_BoneMatrices[int(inHWBoneIndex.x)] * position;
normal = (mat3(m_BoneMatrices[int(inBoneIndex.x)][0].xyz, normal = (mat3(m_BoneMatrices[int(inHWBoneIndex.x)][0].xyz,
m_BoneMatrices[int(inBoneIndex.x)][1].xyz, m_BoneMatrices[int(inHWBoneIndex.x)][1].xyz,
m_BoneMatrices[int(inBoneIndex.x)][2].xyz) * normal); m_BoneMatrices[int(inHWBoneIndex.x)][2].xyz) * normal);
#else #else
mat4 mat = mat4(0.0); mat4 mat = mat4(0.0);
mat += m_BoneMatrices[int(inBoneIndex.x)] * inBoneWeight.x; mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x;
mat += m_BoneMatrices[int(inBoneIndex.y)] * inBoneWeight.y; mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y;
mat += m_BoneMatrices[int(inBoneIndex.z)] * inBoneWeight.z; mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z;
mat += m_BoneMatrices[int(inBoneIndex.w)] * inBoneWeight.w; mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w;
position = mat * position; position = mat * position;
mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz); mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz);
@ -47,19 +47,19 @@ void Skinning_Compute(inout vec4 position, inout vec3 normal){
} }
void Skinning_Compute(inout vec4 position, inout vec3 tangent, inout vec3 normal){ void Skinning_Compute(inout vec4 position, inout vec3 tangent, inout vec3 normal){
if (inBoneWeight.x != 0.0) { if (inHWBoneWeight.x != 0.0) {
#if NUM_WEIGHTS_PER_VERT == 1 #if NUM_WEIGHTS_PER_VERT == 1
position = m_BoneMatrices[int(inBoneIndex.x)] * position; position = m_BoneMatrices[int(inHWBoneIndex.x)] * position;
tangent = m_BoneMatrices[int(inBoneIndex.x)] * tangent; tangent = m_BoneMatrices[int(inHWBoneIndex.x)] * tangent;
normal = (mat3(m_BoneMatrices[int(inBoneIndex.x)][0].xyz, normal = (mat3(m_BoneMatrices[int(inHWBoneIndex.x)][0].xyz,
m_BoneMatrices[int(inBoneIndex.x)][1].xyz, m_BoneMatrices[int(inHWBoneIndex.x)][1].xyz,
m_BoneMatrices[int(inBoneIndex.x)][2].xyz) * normal); m_BoneMatrices[int(inHWBoneIndex.x)][2].xyz) * normal);
#else #else
mat4 mat = mat4(0.0); mat4 mat = mat4(0.0);
mat += m_BoneMatrices[int(inBoneIndex.x)] * inBoneWeight.x; mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x;
mat += m_BoneMatrices[int(inBoneIndex.y)] * inBoneWeight.y; mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y;
mat += m_BoneMatrices[int(inBoneIndex.z)] * inBoneWeight.z; mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z;
mat += m_BoneMatrices[int(inBoneIndex.w)] * inBoneWeight.w; mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w;
position = mat * position; position = mat * position;
mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz); mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz);

@ -353,28 +353,6 @@ public class Mesh implements Savable, Cloneable {
*/ */
public void prepareForAnim(boolean forSoftwareAnim){ public void prepareForAnim(boolean forSoftwareAnim){
if (forSoftwareAnim) { if (forSoftwareAnim) {
// convert indices to ubytes on the heap
VertexBuffer indices = getBuffer(Type.BoneIndex);
if (!indices.getData().hasArray()) {
ByteBuffer originalIndex = (ByteBuffer) indices.getData();
ByteBuffer arrayIndex = ByteBuffer.allocate(originalIndex.capacity());
originalIndex.clear();
arrayIndex.put(originalIndex);
indices.updateData(arrayIndex);
}
indices.setUsage(Usage.CpuOnly);
// convert weights on the heap
VertexBuffer weights = getBuffer(Type.BoneWeight);
if (!weights.getData().hasArray()) {
FloatBuffer originalWeight = (FloatBuffer) weights.getData();
FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());
originalWeight.clear();
arrayWeight.put(originalWeight);
weights.updateData(arrayWeight);
}
weights.setUsage(Usage.CpuOnly);
// position, normal, and tanget buffers to be in "Stream" mode // position, normal, and tanget buffers to be in "Stream" mode
VertexBuffer positions = getBuffer(Type.Position); VertexBuffer positions = getBuffer(Type.Position);
VertexBuffer normals = getBuffer(Type.Normal); VertexBuffer normals = getBuffer(Type.Normal);
@ -387,25 +365,27 @@ public class Mesh implements Savable, Cloneable {
tangents.setUsage(Usage.Stream); tangents.setUsage(Usage.Stream);
} }
} else { } else {
VertexBuffer indices = getBuffer(Type.BoneIndex); //if HWBoneIndex and HWBoneWieght are empty, we setup them as direct
if (!indices.getData().isDirect()) { //buffers with software anim buffers data
VertexBuffer indicesHW = getBuffer(Type.HWBoneIndex);
if(indicesHW.getData() == null){
VertexBuffer indices = getBuffer(Type.BoneIndex);
ByteBuffer originalIndex = (ByteBuffer) indices.getData(); ByteBuffer originalIndex = (ByteBuffer) indices.getData();
ByteBuffer directIndex = BufferUtils.createByteBuffer(originalIndex.capacity()); ByteBuffer directIndex = BufferUtils.createByteBuffer(originalIndex.capacity());
originalIndex.clear(); originalIndex.clear();
directIndex.put(originalIndex); directIndex.put(originalIndex);
indices.updateData(directIndex); indicesHW.setupData(Usage.Static, indices.getNumComponents(), indices.getFormat(), directIndex);
} }
indices.setUsage(Usage.Static);
VertexBuffer weights = getBuffer(Type.BoneWeight); VertexBuffer weightsHW = getBuffer(Type.HWBoneWeight);
if (!weights.getData().isDirect()) { if(weightsHW.getData() == null){
VertexBuffer weights = getBuffer(Type.BoneWeight);
FloatBuffer originalWeight = (FloatBuffer) weights.getData(); FloatBuffer originalWeight = (FloatBuffer) weights.getData();
FloatBuffer directWeight = BufferUtils.createFloatBuffer(originalWeight.capacity()); FloatBuffer directWeight = BufferUtils.createFloatBuffer(originalWeight.capacity());
originalWeight.clear(); originalWeight.clear();
directWeight.put(originalWeight); directWeight.put(originalWeight);
weights.updateData(directWeight); weightsHW.setupData(Usage.Static, weights.getNumComponents(), weights.getFormat(), directWeight);
} }
weights.setUsage(Usage.Static);
// position, normal, and tanget buffers to be in "Static" mode // position, normal, and tanget buffers to be in "Static" mode
VertexBuffer positions = getBuffer(Type.Position); VertexBuffer positions = getBuffer(Type.Position);

@ -135,7 +135,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
/** /**
* Bone weights, used with animation (4 floats). * Bone weights, used with animation (4 floats).
* If used with software skinning, the usage should be * Only used for 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.
*/ */
@ -143,10 +143,9 @@ 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 * Only used for 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 as a ubytes buffer. For Hardware skinning this should be * on the heap as a ubytes buffer.
* either an int or float buffer due to shader attribute types restrictions.
*/ */
BoneIndex, BoneIndex,
@ -193,6 +192,19 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
* on the heap. * on the heap.
*/ */
BindPoseTangent, BindPoseTangent,
/**
* Bone weights, used with animation (4 floats).
* for Hardware Skinning only
*/
HWBoneWeight,
/**
* Bone indices, used with animation (4 ubytes).
* for Hardware Skinning only
* either an int or float buffer due to shader attribute types restrictions.
*/
HWBoneIndex,
} }
/** /**

@ -72,7 +72,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
private static final Logger logger = Logger.getLogger(MeshLoader.class.getName()); private static final Logger logger = Logger.getLogger(MeshLoader.class.getName());
public static boolean AUTO_INTERLEAVE = true; public static boolean AUTO_INTERLEAVE = true;
public static boolean HARDWARE_SKINNING = false;
private static final Type[] TEXCOORD_TYPES = private static final Type[] TEXCOORD_TYPES =
new Type[]{ new Type[]{
Type.TexCoord, Type.TexCoord,
@ -371,24 +370,28 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
// each vertex has // each vertex has
// - 4 bone weights // - 4 bone weights
// - 4 bone indices // - 4 bone indices
if (HARDWARE_SKINNING) { // create array-backed buffers for software skinning for access speed
weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4); weightsFloatData = FloatBuffer.allocate(vertCount * 4);
indicesData = BufferUtils.createByteBuffer(vertCount * 4); indicesData = ByteBuffer.allocate(vertCount * 4);
} else {
// create array-backed buffers if software skinning for access speed
weightsFloatData = FloatBuffer.allocate(vertCount * 4);
indicesData = ByteBuffer.allocate(vertCount * 4);
}
VertexBuffer weights = new VertexBuffer(Type.BoneWeight); VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
VertexBuffer indices = new VertexBuffer(Type.BoneIndex); VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly; weights.setupData(Usage.CpuOnly, 4, Format.Float, weightsFloatData);
weights.setupData(usage, 4, Format.Float, weightsFloatData); indices.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indicesData);
indices.setupData(usage, 4, Format.UnsignedByte, indicesData);
mesh.setBuffer(weights); mesh.setBuffer(weights);
mesh.setBuffer(indices); mesh.setBuffer(indices);
//creating empty buffers for HW skinning
//the buffers will be setup if ever used.
VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight);
VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex);
//setting usage to cpuOnly so that the buffer is not send empty to the GPU
indicesHW.setUsage(Usage.CpuOnly);
weightsHW.setUsage(Usage.CpuOnly);
mesh.setBuffer(weightsHW);
mesh.setBuffer(indicesHW);
} }
private void startVertexBuffer(Attributes attribs) throws SAXException { private void startVertexBuffer(Attributes attribs) throws SAXException {
@ -773,10 +776,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
m.extractVertexData(sharedMesh); m.extractVertexData(sharedMesh);
} }
// Old code for buffer sharer
//if (sharedMesh != null && isUsingSharedVerts(g)) {
// m.setBound(sharedMesh.getBound().clone());
//}
model.attachChild(geoms.get(i)); model.attachChild(geoms.get(i));
} }
@ -785,26 +784,19 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
if (animData != null) { if (animData != null) {
// This model uses animation // This model uses animation
// Old code for buffer sharer
// generate bind pose for mesh
// ONLY if not using shared geometry
// This includes the shared geoemtry itself actually
//if (sharedMesh != null) {
// sharedMesh.generateBindPose(!HARDWARE_SKINNING);
//}
for (int i = 0; i < geoms.size(); i++) { for (int i = 0; i < geoms.size(); i++) {
Geometry g = geoms.get(i); Geometry g = geoms.get(i);
Mesh m = geoms.get(i).getMesh(); Mesh m = geoms.get(i).getMesh();
m.generateBindPose(!HARDWARE_SKINNING); //FIXME the parameter is now useless.
//It was !HADWARE_SKINNING before, but since toggleing
//HW skinning does not happen at load time it was always true.
//We should use something similar as for the HWBoneIndex and
//HWBoneWeight : create the vertex buffers empty so that they
//are put in the cache, and really populate them the first time
//software skinning is used on the mesh.
m.generateBindPose(true);
// Old code for buffer sharer
//boolean useShared = isUsingSharedVerts(g);
//if (!useShared) {
// create bind pose
//m.generateBindPose(!HARDWARE_SKINNING);
//}
} }
// Put the animations in the AnimControl // Put the animations in the AnimControl

Loading…
Cancel
Save