From 2acbdf9f84ebdce2aea43b38a411ffdf7828d5ac Mon Sep 17 00:00:00 2001 From: "rem..om" Date: Sat, 22 Jun 2013 10:03:40 +0000 Subject: [PATCH] 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 --- .../blender/modifiers/ArmatureModifier.java | 10 ++++ .../Common/ShaderLib/Skinning.glsllib | 56 +++++++++--------- engine/src/core/com/jme3/scene/Mesh.java | 42 ++++---------- .../src/core/com/jme3/scene/VertexBuffer.java | 20 +++++-- .../jme3/scene/plugins/ogre/MeshLoader.java | 58 ++++++++----------- 5 files changed, 90 insertions(+), 96 deletions(-) diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java index 40b36ac22..2eedcc37d 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java @@ -200,6 +200,8 @@ import com.jme3.util.BufferUtils; mesh.setBuffer(buffers[0]); mesh.setBuffer(buffers[1]); + //FIXME @Kaelthas this should be replaced by a call to + //mesh.generateBindPos(true) VertexBuffer bindNormalBuffer = meshContext.getBindNormalBuffer(materialIndex); if (bindNormalBuffer != null) { mesh.setBuffer(bindNormalBuffer); @@ -212,6 +214,14 @@ import com.jme3.util.BufferUtils; // Static to Stream mesh.getBuffer(Type.Position).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) { LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); diff --git a/engine/src/core-data/Common/ShaderLib/Skinning.glsllib b/engine/src/core-data/Common/ShaderLib/Skinning.glsllib index 399e9e3fb..66a9ff73b 100644 --- a/engine/src/core-data/Common/ShaderLib/Skinning.glsllib +++ b/engine/src/core-data/Common/ShaderLib/Skinning.glsllib @@ -6,38 +6,38 @@ #define NUM_WEIGHTS_PER_VERT 4 -attribute vec4 inBoneWeight; -attribute vec4 inBoneIndex; +attribute vec4 inHWBoneWeight; +attribute vec4 inHWBoneIndex; uniform mat4 m_BoneMatrices[NUM_BONES]; - + void Skinning_Compute(inout vec4 position){ - if (inBoneWeight.x != 0.0) { + if (inHWBoneWeight.x != 0.0) { #if NUM_WEIGHTS_PER_VERT == 1 - position = m_BoneMatrices[int(inBoneIndex.x)] * position; + position = m_BoneMatrices[int(inHWBoneIndex.x)] * position; #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; + mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x; + mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y; + mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z; + mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w; position = mat * position; #endif } } 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 - position = m_BoneMatrices[int(inBoneIndex.x)] * position; - normal = (mat3(m_BoneMatrices[int(inBoneIndex.x)][0].xyz, - m_BoneMatrices[int(inBoneIndex.x)][1].xyz, - m_BoneMatrices[int(inBoneIndex.x)][2].xyz) * normal); + position = m_BoneMatrices[int(inHWBoneIndex.x)] * position; + normal = (mat3(m_BoneMatrices[int(inHWBoneIndex.x)][0].xyz, + m_BoneMatrices[int(inHWBoneIndex.x)][1].xyz, + m_BoneMatrices[int(inHWBoneIndex.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; + mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x; + mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y; + mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z; + mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w; position = mat * position; 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){ - if (inBoneWeight.x != 0.0) { + if (inHWBoneWeight.x != 0.0) { #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); + position = m_BoneMatrices[int(inHWBoneIndex.x)] * position; + tangent = m_BoneMatrices[int(inHWBoneIndex.x)] * tangent; + normal = (mat3(m_BoneMatrices[int(inHWBoneIndex.x)][0].xyz, + m_BoneMatrices[int(inHWBoneIndex.x)][1].xyz, + m_BoneMatrices[int(inHWBoneIndex.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; + mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x; + mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y; + mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z; + mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w; position = mat * position; mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz); diff --git a/engine/src/core/com/jme3/scene/Mesh.java b/engine/src/core/com/jme3/scene/Mesh.java index 7076b7fdc..527e103c5 100644 --- a/engine/src/core/com/jme3/scene/Mesh.java +++ b/engine/src/core/com/jme3/scene/Mesh.java @@ -353,28 +353,6 @@ public class Mesh implements Savable, Cloneable { */ public void prepareForAnim(boolean 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 VertexBuffer positions = getBuffer(Type.Position); VertexBuffer normals = getBuffer(Type.Normal); @@ -387,25 +365,27 @@ public class Mesh implements Savable, Cloneable { tangents.setUsage(Usage.Stream); } } else { - VertexBuffer indices = getBuffer(Type.BoneIndex); - if (!indices.getData().isDirect()) { + //if HWBoneIndex and HWBoneWieght are empty, we setup them as direct + //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 directIndex = BufferUtils.createByteBuffer(originalIndex.capacity()); originalIndex.clear(); directIndex.put(originalIndex); - indices.updateData(directIndex); + indicesHW.setupData(Usage.Static, indices.getNumComponents(), indices.getFormat(), directIndex); } - indices.setUsage(Usage.Static); - VertexBuffer weights = getBuffer(Type.BoneWeight); - if (!weights.getData().isDirect()) { + VertexBuffer weightsHW = getBuffer(Type.HWBoneWeight); + if(weightsHW.getData() == null){ + VertexBuffer weights = getBuffer(Type.BoneWeight); FloatBuffer originalWeight = (FloatBuffer) weights.getData(); FloatBuffer directWeight = BufferUtils.createFloatBuffer(originalWeight.capacity()); originalWeight.clear(); directWeight.put(originalWeight); - weights.updateData(directWeight); - } - weights.setUsage(Usage.Static); + weightsHW.setupData(Usage.Static, weights.getNumComponents(), weights.getFormat(), directWeight); + } // position, normal, and tanget buffers to be in "Static" mode VertexBuffer positions = getBuffer(Type.Position); diff --git a/engine/src/core/com/jme3/scene/VertexBuffer.java b/engine/src/core/com/jme3/scene/VertexBuffer.java index 6405fd7aa..2ea6ab871 100644 --- a/engine/src/core/com/jme3/scene/VertexBuffer.java +++ b/engine/src/core/com/jme3/scene/VertexBuffer.java @@ -135,7 +135,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { /** * 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 * on the heap. */ @@ -143,10 +143,9 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { /** * 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 - * 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. + * on the heap as a ubytes buffer. */ BoneIndex, @@ -193,6 +192,19 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { * on the heap. */ 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, } /** diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java index 70e73709b..5f2653281 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java @@ -72,7 +72,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private static final Logger logger = Logger.getLogger(MeshLoader.class.getName()); public static boolean AUTO_INTERLEAVE = true; - public static boolean HARDWARE_SKINNING = false; private static final Type[] TEXCOORD_TYPES = new Type[]{ Type.TexCoord, @@ -371,24 +370,28 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { // each vertex has // - 4 bone weights // - 4 bone indices - if (HARDWARE_SKINNING) { - weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4); - indicesData = BufferUtils.createByteBuffer(vertCount * 4); - } else { - // create array-backed buffers if software skinning for access speed - weightsFloatData = FloatBuffer.allocate(vertCount * 4); - indicesData = ByteBuffer.allocate(vertCount * 4); - } + // create array-backed buffers for software skinning for access speed + weightsFloatData = FloatBuffer.allocate(vertCount * 4); + indicesData = ByteBuffer.allocate(vertCount * 4); VertexBuffer weights = new VertexBuffer(Type.BoneWeight); VertexBuffer indices = new VertexBuffer(Type.BoneIndex); - Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly; - weights.setupData(usage, 4, Format.Float, weightsFloatData); - indices.setupData(usage, 4, Format.UnsignedByte, indicesData); - + weights.setupData(Usage.CpuOnly, 4, Format.Float, weightsFloatData); + indices.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indicesData); + mesh.setBuffer(weights); 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 { @@ -773,10 +776,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { m.extractVertexData(sharedMesh); } - // Old code for buffer sharer - //if (sharedMesh != null && isUsingSharedVerts(g)) { - // m.setBound(sharedMesh.getBound().clone()); - //} model.attachChild(geoms.get(i)); } @@ -785,26 +784,19 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { if (animData != null) { // 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++) { Geometry g = geoms.get(i); Mesh m = geoms.get(i).getMesh(); + + //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); - m.generateBindPose(!HARDWARE_SKINNING); - - // Old code for buffer sharer - //boolean useShared = isUsingSharedVerts(g); - //if (!useShared) { - // create bind pose - //m.generateBindPose(!HARDWARE_SKINNING); - //} } // Put the animations in the AnimControl