Просмотр исходного кода

- Added tangent transforms support for skinning (normal mapped models with bone animation had incorrect tangents during animation)
- added a BindPoseTangent buffer type
- made generateBindPose generate a BindPoseTangent buffer if Tangent buffer is set in Mesh
- added a temp float array in TempVars to compute tangent skinning
- Generated bind pose for tangents after tangent generation in TangentBinormalGenerator

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8563 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

rem..om 14 лет назад
Родитель
Сommit
a5ff915fc1

+ 210 - 36
engine/src/core/com/jme3/animation/SkeletonControl.java

@@ -67,7 +67,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
     public SkeletonControl(Skeleton skeleton) {
         this.skeleton = skeleton;
     }
-    
+
     /**
      * Creates a skeleton control.
      * 
@@ -75,66 +75,66 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
      * @param skeleton the skeleton
      */
     @Deprecated
-    SkeletonControl(Mesh[] targets, Skeleton skeleton){
+    SkeletonControl(Mesh[] targets, Skeleton skeleton) {
         this.skeleton = skeleton;
         this.targets = targets;
     }
-    
-    private boolean isMeshAnimated(Mesh mesh){
+
+    private boolean isMeshAnimated(Mesh mesh) {
         return mesh.getBuffer(Type.BindPosePosition) != null;
     }
 
-    private Mesh[] findTargets(Node node){
+    private Mesh[] findTargets(Node node) {
         Mesh sharedMesh = null;
         ArrayList<Mesh> animatedMeshes = new ArrayList<Mesh>();
-        
-        for (Spatial child : node.getChildren()){
-            if (!(child instanceof Geometry)){
+
+        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?
             Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH);
-            
-            if (childSharedMesh != null){
+
+            if (childSharedMesh != null) {
                 // Don't bother with non-animated shared meshes
-                if (isMeshAnimated(childSharedMesh)){
+                if (isMeshAnimated(childSharedMesh)) {
                     // child is using shared mesh,
                     // so animate the shared mesh but ignore child
-                    if (sharedMesh == null){
+                    if (sharedMesh == null) {
                         sharedMesh = childSharedMesh;
-                    }else if (sharedMesh != childSharedMesh){
+                    } else if (sharedMesh != childSharedMesh) {
                         throw new IllegalStateException("Two conflicting shared meshes for " + node);
                     }
                 }
-            }else{
+            } else {
                 Mesh mesh = geom.getMesh();
-                if (isMeshAnimated(mesh)){
+                if (isMeshAnimated(mesh)) {
                     animatedMeshes.add(mesh);
                 }
             }
         }
-        
-        if (sharedMesh != null){
+
+        if (sharedMesh != null) {
             animatedMeshes.add(sharedMesh);
         }
-        
+
         return animatedMeshes.toArray(new Mesh[animatedMeshes.size()]);
     }
-    
+
     @Override
-    public void setSpatial(Spatial spatial){
+    public void setSpatial(Spatial spatial) {
         super.setSpatial(spatial);
-        if (spatial != null){
+        if (spatial != null) {
             Node node = (Node) spatial;
             targets = findTargets(node);
-        }else{
+        } else {
             targets = null;
         }
     }
-    
+
     @Override
     protected void controlRender(RenderManager rm, ViewPort vp) {
         if (!wasMeshUpdated) {
@@ -145,11 +145,11 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
             // 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
+                // NOTE: This assumes that code higher up
+                // Already ensured those targets are animated
+                // otherwise a crash will happen in skin update
                 //if (isMeshAnimated(targets[i])) {
-                    softwareSkinUpdate(targets[i], offsetMatrices);
+                softwareSkinUpdate(targets[i], offsetMatrices);
                 //}
             }
 
@@ -163,7 +163,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
     }
 
     void resetToBind() {
-        for (Mesh mesh : targets){
+        for (Mesh mesh : targets) {
             if (isMeshAnimated(mesh)) {
                 VertexBuffer bi = mesh.getBuffer(Type.BoneIndex);
                 ByteBuffer bib = (ByteBuffer) bi.getData();
@@ -182,6 +182,19 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
                 nb.clear();
                 bpb.clear();
                 bnb.clear();
+
+                //reseting bind tangents if there is a bind tangent buffer
+                VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent);
+                if (bindTangents != null) {
+                    VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
+                    FloatBuffer tb = (FloatBuffer) tangents.getData();
+                    FloatBuffer btb = (FloatBuffer) bindTangents.getData();
+                    tb.clear();
+                    btb.clear();
+                    tb.put(btb).clear();
+                }
+
+
                 pb.put(bpb).clear();
                 nb.put(bnb).clear();
             }
@@ -195,10 +208,10 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
         clone.setSpatial(clonedNode);
 
         clone.skeleton = ctrl.getSkeleton();
-		// Fix animated targets for the cloned node
+        // Fix animated targets for the cloned node
         clone.targets = findTargets(clonedNode);
 
-		// Fix attachments for the cloned node
+        // Fix attachments for the cloned node
         for (int i = 0; i < clonedNode.getQuantity(); i++) {
             // go through attachment nodes, apply them to correct bone
             Spatial child = clonedNode.getChild(i);
@@ -251,7 +264,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
 //    public void setSkeleton(Skeleton skeleton) {
 //        this.skeleton = skeleton;
 //    }
-
     /**
      * returns the targets meshes of this control
      * @return 
@@ -267,8 +279,31 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
 //    public void setTargets(Mesh[] targets) {
 //        this.targets = targets;
 //    }
-
+    /**
+     * Update the mesh according to the given transformation matrices
+     * @param mesh then mesh
+     * @param offsetMatrices the transformation matrices to apply 
+     */
     private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
+
+        VertexBuffer tb = mesh.getBuffer(Type.Tangent);
+        if (tb == null) {
+            //if there are no tangents use the classic skinning
+            applySkinning(mesh, offsetMatrices);
+        } else {
+            //if there are tangents use the skinning with tangents
+            applySkinningTangents(mesh, offsetMatrices, tb);
+        }
+
+
+    }
+
+    /**
+     * Method to apply skinning transforms to a mesh's buffers    
+     * @param mesh the mesh
+     * @param offsetMatrices the offset matices to apply
+     */
+    private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) {
         int maxWeightsPerVert = mesh.getMaxNumWeights();
         if (maxWeightsPerVert <= 0) {
             throw new IllegalStateException("Max weights per vert is incorrectly set!");
@@ -304,7 +339,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
         float[] normBuf = vars.skinNormals;
 
         int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
-        int bufLength = posBuf.length * 3;
+        int bufLength = posBuf.length;
         for (int i = iterations - 1; i >= 0; i--) {
             // read next set of positions and normals from native buffer
             bufLength = Math.min(posBuf.length, fvb.remaining());
@@ -359,7 +394,146 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
         vb.updateData(fvb);
         nb.updateData(fnb);
 
-//        mesh.updateBound();
+    }
+
+    /**
+     * Specific method for skinning with tangents to avoid cluttering the classic skinning calculation with
+     * null checks that would slow down the 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 mesh the mesh
+     * @param offsetMatrices the offsetMaytrices to apply
+     * @param tb the tangent vertexBuffer
+     */
+    private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) {
+        int maxWeightsPerVert = mesh.getMaxNumWeights();
+
+        if (maxWeightsPerVert <= 0) {
+            throw new IllegalStateException("Max weights per vert is incorrectly set!");
+        }
+
+        int fourMinusMaxWeights = 4 - maxWeightsPerVert;
+
+        // NOTE: This code assumes the vertex buffer is in bind pose
+        // resetToBind() has been called this frame
+        VertexBuffer vb = mesh.getBuffer(Type.Position);
+        FloatBuffer fvb = (FloatBuffer) vb.getData();
+        fvb.rewind();
+
+        VertexBuffer nb = mesh.getBuffer(Type.Normal);
+
+        FloatBuffer fnb = (FloatBuffer) nb.getData();
+        fnb.rewind();
+
+
+        FloatBuffer ftb = (FloatBuffer) tb.getData();
+        ftb.rewind();
+
+   
+        // get boneIndexes and weights for mesh
+        ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+        FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+        ib.rewind();
+        wb.rewind();
+
+        float[] weights = wb.array();
+        byte[] indices = ib.array();
+        int idxWeights = 0;
+
+        TempVars vars = TempVars.get();
+
+
+        float[] posBuf = vars.skinPositions;
+        float[] normBuf = vars.skinNormals;
+        float[] tanBuf = vars.skinTangents;
+
+        int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
+        int bufLength = 0;       
+        int tanLength = 0;
+        for (int i = iterations - 1; i >= 0; i--) {
+            // read next set of positions and normals from native buffer
+            bufLength = Math.min(posBuf.length, fvb.remaining());
+            tanLength = Math.min(tanBuf.length, ftb.remaining());
+            fvb.get(posBuf, 0, bufLength);
+            fnb.get(normBuf, 0, bufLength);
+            ftb.get(tanBuf, 0, tanLength);
+            int verts = bufLength / 3;
+            int idxPositions = 0;
+            //tangents has their own index because of the 4 components
+            int idxTangents = 0;
+
+            // iterate vertices and apply skinning transform for each effecting bone
+            for (int vert = verts - 1; vert >= 0; vert--) {
+                float nmx = normBuf[idxPositions];
+                float vtx = posBuf[idxPositions++];
+                float nmy = normBuf[idxPositions];
+                float vty = posBuf[idxPositions++];
+                float nmz = normBuf[idxPositions];
+                float vtz = posBuf[idxPositions++];
+
+                float tnx = tanBuf[idxTangents++];
+                float tny = tanBuf[idxTangents++];
+                float tnz = tanBuf[idxTangents++];
+
+                //skipping the 4th component of the tangent since it doesn't have to be transformed
+                idxTangents++;
+
+                float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0;
+
+                for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
+                    float weight = weights[idxWeights];
+                    Matrix4f mat = offsetMatrices[indices[idxWeights++]];
+
+                    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;
+                    rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
+
+                    rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
+                    rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
+                    rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
+
+                    rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight;
+                    rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight;
+                    rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight;
+                }
+
+                idxWeights += fourMinusMaxWeights;
+
+                idxPositions -= 3;
+
+                normBuf[idxPositions] = rnx;
+                posBuf[idxPositions++] = rx;
+                normBuf[idxPositions] = rny;
+                posBuf[idxPositions++] = ry;
+                normBuf[idxPositions] = rnz;
+                posBuf[idxPositions++] = rz;
+
+                idxTangents -= 4;
+
+                tanBuf[idxTangents++] = rtx;
+                tanBuf[idxTangents++] = rty;
+                tanBuf[idxTangents++] = rtz;
+
+                //once again skipping the 4th component of the tangent
+                idxTangents++;
+            }
+
+            fvb.position(fvb.position() - bufLength);
+            fvb.put(posBuf, 0, bufLength);
+            fnb.position(fnb.position() - bufLength);
+            fnb.put(normBuf, 0, bufLength);
+            ftb.position(ftb.position() - tanLength);
+            ftb.put(tanBuf, 0, tanLength);
+        }
+
+        vars.release();
+
+        vb.updateData(fvb);
+        nb.updateData(fnb);
+        tb.updateData(ftb);
+
+
     }
 
     @Override

+ 11 - 0
engine/src/core/com/jme3/scene/Mesh.java

@@ -334,6 +334,17 @@ public class Mesh implements Savable, Cloneable {
                 setBuffer(bindNorm);
                 norm.setUsage(Usage.Stream);
             }
+            
+            VertexBuffer tangents = getBuffer(Type.Tangent);
+            if (tangents != null) {
+                VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent);
+                bindTangents.setupData(Usage.CpuOnly,
+                        4,
+                        Format.Float,
+                        BufferUtils.clone(tangents.getData()));
+                setBuffer(bindTangents);
+                tangents.setUsage(Usage.Stream);
+            }
         }
     }
 

+ 12 - 1
engine/src/core/com/jme3/scene/VertexBuffer.java

@@ -94,7 +94,9 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
         Color,
 
         /**
-         * Tangent vector, normalized (3 floats)
+         * Tangent vector, normalized (4 floats) (x,y,z,w)
+         * the w component is called the binormal parity, is not normalized and is either 1f or -1f
+         * It's used to compuste the direction on the binormal verctor on the GPU at render time.
          */
         Tangent,
 
@@ -139,6 +141,15 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
          * on the heap.
          */
         BindPoseNormal,
+        
+         /** 
+         * Initial vertex tangents, used with animation.
+         * Should have the same format and size as {@link Type#Tangent}.
+         * If used with software skinning, the usage should be 
+         * {@link Usage#CpuOnly}, and the buffer should be allocated
+         * on the heap.
+         */
+        BindPoseTangent,
 
         /** 
          * Bone weights, used with animation (4 floats).

+ 190 - 174
engine/src/core/com/jme3/util/TangentBinormalGenerator.java

@@ -29,9 +29,11 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.util;
 
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Usage;
 import java.util.logging.Level;
 import com.jme3.scene.mesh.IndexBuffer;
 import com.jme3.scene.Geometry;
@@ -55,41 +57,40 @@ import static com.jme3.util.BufferUtils.*;
  * @author Lex
  */
 public class TangentBinormalGenerator {
-
+    
     private static final float ZERO_TOLERANCE = 0.0000001f;
     private static final Logger log = Logger.getLogger(
-			TangentBinormalGenerator.class.getName());
-
+            TangentBinormalGenerator.class.getName());
     private static float toleranceAngle;
     private static float toleranceDot;
-
+    
     static {
         setToleranceAngle(45);
     }
-
+    
     private static class VertexData {
+        
         public final Vector3f tangent = new Vector3f();
         public final Vector3f binormal = new Vector3f();
         public final List<TriangleData> triangles =
-                    new ArrayList<TriangleData>();
-
+                new ArrayList<TriangleData>();
+        
         public VertexData() {
-            
         }
     }
-
+    
     public static class TriangleData {
+        
         public final Vector3f tangent;
         public final Vector3f binormal;
         public final Vector3f normal;
         public int index0;
         public int index1;
         public int index2;
-
+        
         public TriangleData(Vector3f tangent, Vector3f binormal,
-                        Vector3f normal,
-                        int index0, int index1, int index2)
-        {
+                Vector3f normal,
+                int index0, int index1, int index2) {
             this.tangent = tangent;
             this.binormal = binormal;
             this.normal = normal;
@@ -99,7 +100,7 @@ public class TangentBinormalGenerator {
             this.index2 = index2;
         }
     }
-
+    
     private static VertexData[] initVertexData(int size) {
         VertexData[] vertices = new VertexData[size];
         for (int i = 0; i < size; i++) {
@@ -107,23 +108,23 @@ public class TangentBinormalGenerator {
         }
         return vertices;
     }
-
+    
     public static void generate(Mesh mesh) {
         generate(mesh, true);
     }
-
-    public static void generate(Spatial scene){
-        if (scene instanceof Node){
+    
+    public static void generate(Spatial scene) {
+        if (scene instanceof Node) {
             Node node = (Node) scene;
-            for (Spatial child : node.getChildren()){
+            for (Spatial child : node.getChildren()) {
                 generate(child);
             }
-        }else{
+        } else {
             Geometry geom = (Geometry) scene;
             generate(geom.getMesh());
         }
     }
-
+    
     public static void generate(Mesh mesh, boolean approxTangents) {
         int[] index = new int[3];
         Vector3f[] v = new Vector3f[3];
@@ -133,45 +134,68 @@ public class TangentBinormalGenerator {
             t[i] = new Vector2f();
         }
         
-        if (mesh.getBuffer(Type.Normal) == null){
+        if (mesh.getBuffer(Type.Normal) == null) {
             throw new IllegalArgumentException("The given mesh has no normal data!");
         }
-
+        
         VertexData[] vertices;
         switch (mesh.getMode()) {
             case Triangles:
-                vertices = processTriangles(mesh, index, v, t); break;
+                vertices = processTriangles(mesh, index, v, t);
+                break;
             case TriangleStrip:
-                vertices = processTriangleStrip(mesh, index, v, t); break;
+                vertices = processTriangleStrip(mesh, index, v, t);
+                break;
             case TriangleFan:
-                vertices = processTriangleFan(mesh, index, v, t); break;
-            default: throw new UnsupportedOperationException(
-                    mesh.getMode() + " is not supported.");
+                vertices = processTriangleFan(mesh, index, v, t);
+                break;
+            default:
+                throw new UnsupportedOperationException(
+                        mesh.getMode() + " is not supported.");
         }
-
+        
         processTriangleData(mesh, vertices, approxTangents);
-    }
 
+        //if the mesh has a bind pose, we need to generate the bind pose for the tangent buffer
+        if (mesh.getBuffer(Type.BindPosePosition) != null) {
+            
+            VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
+            if (tangents != null) {
+                VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent);
+                bindTangents.setupData(Usage.CpuOnly,
+                        4,
+                        Format.Float,
+                        BufferUtils.clone(tangents.getData()));
+                
+                if (mesh.getBuffer(Type.BindPoseTangent) != null) {
+                    mesh.clearBuffer(Type.BindPoseTangent);
+                }
+                mesh.setBuffer(bindTangents);
+                tangents.setUsage(Usage.Stream);
+            }
+        }
+    }
+    
     private static VertexData[] processTriangles(Mesh mesh,
-            int[] index, Vector3f[] v, Vector2f[] t)
-    {
-        IndexBuffer indexBuffer =  mesh.getIndexBuffer();
+            int[] index, Vector3f[] v, Vector2f[] t) {
+        IndexBuffer indexBuffer = mesh.getIndexBuffer();
         FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
-        if (mesh.getBuffer(Type.TexCoord) == null)
+        if (mesh.getBuffer(Type.TexCoord) == null) {
             throw new IllegalArgumentException("Can only generate tangents for "
-                                             + "meshes with texture coordinates");
+                    + "meshes with texture coordinates");
+        }
         
         FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
-
+        
         VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3);
-
+        
         for (int i = 0; i < indexBuffer.size() / 3; i++) {
             for (int j = 0; j < 3; j++) {
-                index[j] = indexBuffer.get(i*3 + j);
+                index[j] = indexBuffer.get(i * 3 + j);
                 populateFromBuffer(v[j], vertexBuffer, index[j]);
                 populateFromBuffer(t[j], textureBuffer, index[j]);
             }
-
+            
             TriangleData triData = processTriangle(index, v, t);
             if (triData != null) {
                 vertices[index[0]].triangles.add(triData);
@@ -182,21 +206,21 @@ public class TangentBinormalGenerator {
         
         return vertices;
     }
+    
     private static VertexData[] processTriangleStrip(Mesh mesh,
-            int[] index, Vector3f[] v, Vector2f[] t)
-    {
-        IndexBuffer indexBuffer =  mesh.getIndexBuffer();
+            int[] index, Vector3f[] v, Vector2f[] t) {
+        IndexBuffer indexBuffer = mesh.getIndexBuffer();
         FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
         FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
-
+        
         VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3);
-
+        
         index[0] = indexBuffer.get(0);
         index[1] = indexBuffer.get(1);
-
+        
         populateFromBuffer(v[0], vertexBuffer, index[0]);
         populateFromBuffer(v[1], vertexBuffer, index[1]);
-
+        
         populateFromBuffer(t[0], textureBuffer, index[0]);
         populateFromBuffer(t[1], textureBuffer, index[1]);
         
@@ -213,64 +237,64 @@ public class TangentBinormalGenerator {
                 vertices[index[1]].triangles.add(triData);
                 vertices[index[2]].triangles.add(triData);
             }
-
+            
             Vector3f vTemp = v[0];
             v[0] = v[1];
             v[1] = v[2];
             v[2] = vTemp;
-
+            
             Vector2f tTemp = t[0];
             t[0] = t[1];
             t[1] = t[2];
             t[2] = tTemp;
-
+            
             index[0] = index[1];
             index[1] = index[2];
         }
-
+        
         return vertices;
     }
+    
     private static VertexData[] processTriangleFan(Mesh mesh,
-            int[] index, Vector3f[] v, Vector2f[] t)
-    {
-        IndexBuffer indexBuffer =  mesh.getIndexBuffer();
+            int[] index, Vector3f[] v, Vector2f[] t) {
+        IndexBuffer indexBuffer = mesh.getIndexBuffer();
         FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
         FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
-
+        
         VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3);
-
+        
         index[0] = indexBuffer.get(0);
         index[1] = indexBuffer.get(1);
-
+        
         populateFromBuffer(v[0], vertexBuffer, index[0]);
         populateFromBuffer(v[1], vertexBuffer, index[1]);
-
+        
         populateFromBuffer(t[0], textureBuffer, index[0]);
         populateFromBuffer(t[1], textureBuffer, index[1]);
-
+        
         for (int i = 2; i < vertexBuffer.capacity() / 3; i++) {
             index[2] = indexBuffer.get(i);
             populateFromBuffer(v[2], vertexBuffer, index[2]);
             populateFromBuffer(t[2], textureBuffer, index[2]);
-
+            
             TriangleData triData = processTriangle(index, v, t);
             if (triData != null) {
                 vertices[index[0]].triangles.add(triData);
                 vertices[index[1]].triangles.add(triData);
                 vertices[index[2]].triangles.add(triData);
             }
-
+            
             Vector3f vTemp = v[1];
             v[1] = v[2];
             v[2] = vTemp;
-
+            
             Vector2f tTemp = t[1];
             t[1] = t[2];
             t[2] = tTemp;
-
+            
             index[1] = index[2];
         }
-
+        
         return vertices;
     }
 
@@ -278,87 +302,86 @@ public class TangentBinormalGenerator {
     private static boolean isDegenerateTriangle(Vector3f a, Vector3f b, Vector3f c) {
         return (a.subtract(b).cross(c.subtract(b))).lengthSquared() == 0;
     }
-
-
+    
     public static TriangleData processTriangle(int[] index,
-            Vector3f[] v, Vector2f[] t)
-    {
+            Vector3f[] v, Vector2f[] t) {
         Vector3f edge1 = new Vector3f();
         Vector3f edge2 = new Vector3f();
         Vector2f edge1uv = new Vector2f();
         Vector2f edge2uv = new Vector2f();
-
+        
         Vector3f tangent = new Vector3f();
         Vector3f binormal = new Vector3f();
         Vector3f normal = new Vector3f();
-
+        
         t[1].subtract(t[0], edge1uv);
         t[2].subtract(t[0], edge2uv);
-        float det = edge1uv.x*edge2uv.y - edge1uv.y*edge2uv.x;
-
+        float det = edge1uv.x * edge2uv.y - edge1uv.y * edge2uv.x;
+        
         boolean normalize = false;
         if (Math.abs(det) < ZERO_TOLERANCE) {
-            log.log(Level.WARNING, "Colinear uv coordinates for triangle " +
-                    "[{0}, {1}, {2}]; tex0 = [{3}, {4}], " +
-                    "tex1 = [{5}, {6}], tex2 = [{7}, {8}]",
-                    new Object[]{ index[0], index[1], index[2],
-                    t[0].x, t[0].y, t[1].x, t[1].y, t[2].x, t[2].y });
+            log.log(Level.WARNING, "Colinear uv coordinates for triangle "
+                    + "[{0}, {1}, {2}]; tex0 = [{3}, {4}], "
+                    + "tex1 = [{5}, {6}], tex2 = [{7}, {8}]",
+                    new Object[]{index[0], index[1], index[2],
+                        t[0].x, t[0].y, t[1].x, t[1].y, t[2].x, t[2].y});
             det = 1;
             normalize = true;
         }
-
+        
         v[1].subtract(v[0], edge1);
         v[2].subtract(v[0], edge2);
-
+        
         tangent.set(edge1);
         tangent.normalizeLocal();
         binormal.set(edge2);
         binormal.normalizeLocal();
-
+        
         if (Math.abs(Math.abs(tangent.dot(binormal)) - 1)
-                        < ZERO_TOLERANCE)
-        {
-            log.log(Level.WARNING, "Vertices are on the same line " +
-                    "for triangle [{0}, {1}, {2}].",
-                    new Object[]{ index[0], index[1], index[2] });
+                < ZERO_TOLERANCE) {
+            log.log(Level.WARNING, "Vertices are on the same line "
+                    + "for triangle [{0}, {1}, {2}].",
+                    new Object[]{index[0], index[1], index[2]});
         }
-
-        float factor = 1/det;
-        tangent.x = (edge2uv.y*edge1.x - edge1uv.y*edge2.x)*factor;
-        tangent.y = (edge2uv.y*edge1.y - edge1uv.y*edge2.y)*factor;
-        tangent.z = (edge2uv.y*edge1.z - edge1uv.y*edge2.z)*factor;
-        if (normalize) tangent.normalizeLocal();
-
-        binormal.x = (edge1uv.x*edge2.x - edge2uv.x*edge1.x)*factor;
-        binormal.y = (edge1uv.x*edge2.y - edge2uv.x*edge1.y)*factor;
-        binormal.z = (edge1uv.x*edge2.z - edge2uv.x*edge1.z)*factor;
-        if (normalize) binormal.normalizeLocal();
-
+        
+        float factor = 1 / det;
+        tangent.x = (edge2uv.y * edge1.x - edge1uv.y * edge2.x) * factor;
+        tangent.y = (edge2uv.y * edge1.y - edge1uv.y * edge2.y) * factor;
+        tangent.z = (edge2uv.y * edge1.z - edge1uv.y * edge2.z) * factor;
+        if (normalize) {
+            tangent.normalizeLocal();
+        }
+        
+        binormal.x = (edge1uv.x * edge2.x - edge2uv.x * edge1.x) * factor;
+        binormal.y = (edge1uv.x * edge2.y - edge2uv.x * edge1.y) * factor;
+        binormal.z = (edge1uv.x * edge2.z - edge2uv.x * edge1.z) * factor;
+        if (normalize) {
+            binormal.normalizeLocal();
+        }
+        
         tangent.cross(binormal, normal);
         normal.normalizeLocal();
-
+        
         return new TriangleData(
-                        tangent,
-                        binormal,
-                        normal,
-                        index[0], index[1], index[2]
-                    );
+                tangent,
+                binormal,
+                normal,
+                index[0], index[1], index[2]);
     }
-
+    
     public static void setToleranceAngle(float angle) {
         if (angle < 0 || angle > 179) {
             throw new IllegalArgumentException(
-                        "The angle must be between 0 and 179 degrees.");
+                    "The angle must be between 0 and 179 degrees.");
         }
-        toleranceDot = FastMath.cos(angle*FastMath.DEG_TO_RAD);
+        toleranceDot = FastMath.cos(angle * FastMath.DEG_TO_RAD);
         toleranceAngle = angle;
     }
-
+    
     private static void processTriangleData(Mesh mesh, VertexData[] vertices,
-            boolean approxTangent)
-    {
+            boolean approxTangent) {
         FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
-
+        
         FloatBuffer tangents = BufferUtils.createFloatBuffer(vertices.length * 4);
 //        FloatBuffer binormals = BufferUtils.createFloatBuffer(vertices.length * 3);
 
@@ -366,7 +389,7 @@ public class TangentBinormalGenerator {
         Vector3f binormal = new Vector3f();
         Vector3f normal = new Vector3f();
         Vector3f givenNormal = new Vector3f();
-
+        
         Vector3f tangentUnit = new Vector3f();
         Vector3f binormalUnit = new Vector3f();
         
@@ -375,7 +398,7 @@ public class TangentBinormalGenerator {
             
             populateFromBuffer(givenNormal, normalBuffer, i);
             givenNormal.normalizeLocal();
-
+            
             VertexData currentVertex = vertices[i];
             List<TriangleData> triangles = currentVertex.triangles;
 
@@ -384,26 +407,26 @@ public class TangentBinormalGenerator {
             tangent.normalizeLocal();
             binormal.set(triangles.get(0).binormal);
             binormal.normalizeLocal();
-
+            
             for (int j = 1; j < triangles.size(); j++) {
                 TriangleData triangleData = triangles.get(j);
-
+                
                 tangentUnit.set(triangleData.tangent);
                 tangentUnit.normalizeLocal();
                 if (tangent.dot(tangentUnit) < toleranceDot) {
                     log.log(Level.WARNING,
-                        "Angle between tangents exceeds tolerance " +
-                        "for vertex {0}.", i);
+                            "Angle between tangents exceeds tolerance "
+                            + "for vertex {0}.", i);
                     break;
                 }
-
+                
                 if (!approxTangent) {
                     binormalUnit.set(triangleData.binormal);
                     binormalUnit.normalizeLocal();
                     if (binormal.dot(binormalUnit) < toleranceDot) {
                         log.log(Level.WARNING,
-                                "Angle between binormals exceeds tolerance " +
-                                "for vertex {0}.", i);
+                                "Angle between binormals exceeds tolerance "
+                                + "for vertex {0}.", i);
                         break;
                     }
                 }
@@ -412,13 +435,13 @@ public class TangentBinormalGenerator {
             // find average tangent
             tangent.set(0, 0, 0);
             binormal.set(0, 0, 0);
-
+            
             boolean flippedNormal = false;
             for (int j = 0; j < triangles.size(); j++) {
                 TriangleData triangleData = triangles.get(j);
                 tangent.addLocal(triangleData.tangent);
                 binormal.addLocal(triangleData.binormal);
-
+                
                 if (givenNormal.dot(triangleData.normal) < 0) {
                     flippedNormal = true;
                 }
@@ -428,10 +451,10 @@ public class TangentBinormalGenerator {
                 // so binormal = normal.cross(tangent) will be flipped in the shader
 //                log.log(Level.WARNING,
 //                        "Binormal is flipped for vertex {0}.", i);
-                
+
                 wCoord = 1;
             }
-
+            
             if (tangent.length() < ZERO_TOLERANCE) {
                 log.log(Level.WARNING,
                         "Shared tangent is zero for vertex {0}.", i);
@@ -439,25 +462,22 @@ public class TangentBinormalGenerator {
                 if (binormal.length() >= ZERO_TOLERANCE) {
                     binormal.cross(givenNormal, tangent);
                     tangent.normalizeLocal();
-                }
-                // if all fails use the tangent from the first triangle
+                } // if all fails use the tangent from the first triangle
                 else {
                     tangent.set(triangles.get(0).tangent);
                 }
-            }
-            else {
+            } else {
                 tangent.divideLocal(triangles.size());
             }
-
+            
             tangentUnit.set(tangent);
             tangentUnit.normalizeLocal();
             if (Math.abs(Math.abs(tangentUnit.dot(givenNormal)) - 1)
-                        < ZERO_TOLERANCE)
-            {
+                    < ZERO_TOLERANCE) {
                 log.log(Level.WARNING,
                         "Normal and tangent are parallel for vertex {0}.", i);
             }
-
+            
             
             if (!approxTangent) {
                 if (binormal.length() < ZERO_TOLERANCE) {
@@ -467,33 +487,29 @@ public class TangentBinormalGenerator {
                     if (tangent.length() >= ZERO_TOLERANCE) {
                         givenNormal.cross(tangent, binormal);
                         binormal.normalizeLocal();
-                    }
-                    // if all fails use the binormal from the first triangle
+                    } // if all fails use the binormal from the first triangle
                     else {
                         binormal.set(triangles.get(0).binormal);
                     }
-                }
-                else {
+                } else {
                     binormal.divideLocal(triangles.size());
                 }
-
+                
                 binormalUnit.set(binormal);
                 binormalUnit.normalizeLocal();
                 if (Math.abs(Math.abs(binormalUnit.dot(givenNormal)) - 1)
-                            < ZERO_TOLERANCE)
-                {
+                        < ZERO_TOLERANCE) {
                     log.log(Level.WARNING,
                             "Normal and binormal are parallel for vertex {0}.", i);
                 }
                 
                 if (Math.abs(Math.abs(binormalUnit.dot(tangentUnit)) - 1)
-                            < ZERO_TOLERANCE)
-                {
+                        < ZERO_TOLERANCE) {
                     log.log(Level.WARNING,
                             "Tangent and binormal are parallel for vertex {0}.", i);
                 }
             }
-
+            
             if (approxTangent) {
 //                givenNormal.cross(tangent, binormal);
 //                binormal.cross(givenNormal, tangent);
@@ -503,67 +519,67 @@ public class TangentBinormalGenerator {
                 tangents.put((i * 4) + 1, tangent.y);
                 tangents.put((i * 4) + 2, tangent.z);
                 tangents.put((i * 4) + 3, wCoord);
-            }
-            else {
+            } else {
                 tangents.put((i * 4), tangent.x);
                 tangents.put((i * 4) + 1, tangent.y);
                 tangents.put((i * 4) + 2, tangent.z);
                 tangents.put((i * 4) + 3, wCoord);
-                
+
 //                setInBuffer(binormal, binormals, i);
             }
         }
-
-        mesh.setBuffer(Type.Tangent,  4, tangents);
+        
+        mesh.setBuffer(Type.Tangent, 4, tangents);
 //        if (!approxTangent) mesh.setBuffer(Type.Binormal, 3, binormals);
     }
-
+    
     public static Mesh genTbnLines(Mesh mesh, float scale) {
-        if (mesh.getBuffer(Type.Tangent) == null)
+        if (mesh.getBuffer(Type.Tangent) == null) {
             return genNormalLines(mesh, scale);
-        else
+        } else {
             return genTangentLines(mesh, scale);
+        }
     }
-
+    
     public static Mesh genNormalLines(Mesh mesh, float scale) {
         FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
         FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
-
+        
         ColorRGBA originColor = ColorRGBA.White;
         ColorRGBA normalColor = ColorRGBA.Blue;
-
+        
         Mesh lineMesh = new Mesh();
         lineMesh.setMode(Mesh.Mode.Lines);
-
+        
         Vector3f origin = new Vector3f();
         Vector3f point = new Vector3f();
-
+        
         FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.capacity() * 2);
         FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.capacity() / 3 * 4 * 2);
-
+        
         for (int i = 0; i < vertexBuffer.capacity() / 3; i++) {
             populateFromBuffer(origin, vertexBuffer, i);
             populateFromBuffer(point, normalBuffer, i);
-
+            
             int index = i * 2;
-
+            
             setInBuffer(origin, lineVertex, index);
             setInBuffer(originColor, lineColor, index);
-
+            
             point.multLocal(scale);
             point.addLocal(origin);
             setInBuffer(point, lineVertex, index + 1);
             setInBuffer(normalColor, lineColor, index + 1);
         }
-
+        
         lineMesh.setBuffer(Type.Position, 3, lineVertex);
         lineMesh.setBuffer(Type.Color, 4, lineColor);
-
+        
         lineMesh.setStatic();
         lineMesh.setInterleaved();
         return lineMesh;
     }
-
+    
     private static Mesh genTangentLines(Mesh mesh, float scale) {
         FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
         FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
@@ -573,15 +589,15 @@ public class TangentBinormalGenerator {
         if (mesh.getBuffer(Type.Binormal) != null) {
             binormalBuffer = (FloatBuffer) mesh.getBuffer(Type.Binormal).getData();
         }
-
+        
         ColorRGBA originColor = ColorRGBA.White;
         ColorRGBA tangentColor = ColorRGBA.Red;
         ColorRGBA binormalColor = ColorRGBA.Green;
         ColorRGBA normalColor = ColorRGBA.Blue;
-
+        
         Mesh lineMesh = new Mesh();
         lineMesh.setMode(Mesh.Mode.Lines);
-
+        
         Vector3f origin = new Vector3f();
         Vector3f point = new Vector3f();
         Vector3f tangent = new Vector3f();
@@ -598,17 +614,17 @@ public class TangentBinormalGenerator {
             populateFromBuffer(origin, vertexBuffer, i);
             populateFromBuffer(normal, normalBuffer, i);
             
-            if (hasParity){
+            if (hasParity) {
                 tangent.x = tangentBuffer.get(i * 4);
                 tangent.y = tangentBuffer.get(i * 4 + 1);
                 tangent.z = tangentBuffer.get(i * 4 + 2);
-                tangentW  = tangentBuffer.get(i * 4 + 3);
-            }else{
+                tangentW = tangentBuffer.get(i * 4 + 3);
+            } else {
                 populateFromBuffer(tangent, tangentBuffer, i);
             }
-
+            
             int index = i * 4;
-
+            
             int id = i * 6;
             lineIndex.put(id, index);
             lineIndex.put(id + 1, index + 1);
@@ -616,7 +632,7 @@ public class TangentBinormalGenerator {
             lineIndex.put(id + 3, index + 2);
             lineIndex.put(id + 4, index);
             lineIndex.put(id + 5, index + 3);
-
+            
             setInBuffer(origin, lineVertex, index);
             setInBuffer(originColor, lineColor, index);
             
@@ -627,7 +643,7 @@ public class TangentBinormalGenerator {
             setInBuffer(tangentColor, lineColor, index + 1);
 
             // wvBinormal = cross(wvNormal, wvTangent) * -inTangent.w
-            
+
             if (binormalBuffer == null) {
                 normal.cross(tangent, point);
                 point.multLocal(-tangentW);
@@ -640,18 +656,18 @@ public class TangentBinormalGenerator {
             point.addLocal(origin);
             setInBuffer(point, lineVertex, index + 2);
             setInBuffer(binormalColor, lineColor, index + 2);
-
+            
             point.set(normal);
             point.multLocal(scale);
             point.addLocal(origin);
             setInBuffer(point, lineVertex, index + 3);
             setInBuffer(normalColor, lineColor, index + 3);
         }
-
+        
         lineMesh.setBuffer(Type.Index, 1, lineIndex);
         lineMesh.setBuffer(Type.Position, 3, lineVertex);
         lineMesh.setBuffer(Type.Color, 4, lineColor);
-
+        
         lineMesh.setStatic();
         lineMesh.setInterleaved();
         return lineMesh;

+ 2 - 0
engine/src/core/com/jme3/util/TempVars.java

@@ -157,6 +157,8 @@ public class TempVars {
      */
     public final float[] skinPositions = new float[512 * 3];
     public final float[] skinNormals = new float[512 * 3];
+     //tangent buffer as 4 components by elements
+    public final float[] skinTangents = new float[512 * 4];
     /**
      * Fetching triangle from mesh
      */