Prechádzať zdrojové kódy

Use MPOs for skinning

Kirill Vainer 8 rokov pred
rodič
commit
bf18ef3048

+ 3 - 1
jme3-core/src/main/java/com/jme3/animation/Skeleton.java

@@ -70,7 +70,7 @@ public final class Skeleton implements Savable, JmeCloneable {
     public Skeleton(Bone[] boneList) {
         this.boneList = boneList;
 
-        List<Bone> rootBoneList = new ArrayList<Bone>();
+        List<Bone> rootBoneList = new ArrayList<>();
         for (int i = boneList.length - 1; i >= 0; i--) {
             Bone b = boneList[i];
             if (b.getParent() == null) {
@@ -289,6 +289,7 @@ public final class Skeleton implements Savable, JmeCloneable {
         return sb.toString();
     }
 
+    @Override
     public void read(JmeImporter im) throws IOException {
         InputCapsule input = im.getCapsule(this);
 
@@ -308,6 +309,7 @@ public final class Skeleton implements Savable, JmeCloneable {
         }
     }
 
+    @Override
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule output = ex.getCapsule(this);
         output.write(rootBones, "rootBones", null);

+ 52 - 83
jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java

@@ -32,8 +32,7 @@
 package com.jme3.animation;
 
 import com.jme3.export.*;
-import com.jme3.material.MatParam;
-import com.jme3.material.Material;
+import com.jme3.material.MatParamOverride;
 import com.jme3.math.FastMath;
 import com.jme3.math.Matrix4f;
 import com.jme3.renderer.RenderManager;
@@ -43,6 +42,7 @@ import com.jme3.scene.*;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.control.AbstractControl;
 import com.jme3.scene.control.Control;
+import com.jme3.scene.mesh.IndexBuffer;
 import com.jme3.shader.VarType;
 import com.jme3.util.*;
 import com.jme3.util.clone.Cloner;
@@ -51,8 +51,6 @@ import java.io.IOException;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.FloatBuffer;
-import java.util.HashSet;
-import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -107,13 +105,11 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
      * Bone offset matrices, recreated each frame
      */
     private transient Matrix4f[] offsetMatrices;
-    /**
-     * Material references used for hardware skinning
-     */
-    private Set<Material> materials = new HashSet<Material>();
 
-    //temp reader
-    private BufferUtils.ByteShortIntBufferReader indexReader = new BufferUtils.ByteShortIntBufferReader();
+    
+    private MatParamOverride numberOfBonesParam;
+    private MatParamOverride boneMatricesParam;
+    
     /**
      * Serialization only. Do not use.
      */
@@ -121,11 +117,13 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
     }
 
     private void switchToHardware() {
+        numberOfBonesParam.setEnabled(true);
+        boneMatricesParam.setEnabled(true);
+        
         // Next full 10 bones (e.g. 30 on 24 bones)
         int numBones = ((skeleton.getBoneCount() / 10) + 1) * 10;
-        for (Material m : materials) {
-            m.setInt("NumberOfBones", numBones);
-        }
+        numberOfBonesParam.setValue(numBones);
+        
         for (Geometry geometry : targets) {
             Mesh mesh = geometry.getMesh();
             if (mesh != null && mesh.isAnimated()) {
@@ -135,11 +133,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
     }
 
     private void switchToSoftware() {
-        for (Material m : materials) {
-            if (m.getParam("NumberOfBones") != null) {
-                m.clearParam("NumberOfBones");
-            }
-        }
+        numberOfBonesParam.setEnabled(false);
+        boneMatricesParam.setEnabled(false);
+        
         for (Geometry geometry : targets) {
             Mesh mesh = geometry.getMesh();
             if (mesh != null && mesh.isAnimated()) {
@@ -149,19 +145,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
     }
 
     private boolean testHardwareSupported(RenderManager rm) {
-        for (Material m : materials) {
-            // Some of the animated mesh(es) do not support hardware skinning,
-            // so it is not supported by the model.
-            if (m.getMaterialDef().getMaterialParam("NumberOfBones") == null) {
-                Logger.getLogger(SkeletonControl.class.getName()).log(Level.WARNING, 
-                        "Not using hardware skinning for {0}, " + 
-                        "because material {1} doesn''t support it.", 
-                        new Object[]{spatial, m.getMaterialDef().getName()});
-                
-                return false;
-            }
-        }
-
         switchToHardware();
         
         try {
@@ -178,6 +161,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
      * supported by GPU, it shall be enabled, if its not preferred, or not
      * supported by GPU, then it shall be disabled.
      * 
+     * @param preferred
      * @see #isHardwareSkinningUsed() 
      */
     public void setHardwareSkinningPreferred(boolean preferred) {
@@ -212,6 +196,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
             throw new IllegalArgumentException("skeleton cannot be null");
         }
         this.skeleton = skeleton;
+        this.numberOfBonesParam = new MatParamOverride(VarType.Int, "NumberOfBones", null);
+        this.boneMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null);
     }
 
     /**
@@ -222,8 +208,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         Mesh mesh = geometry.getMesh();
         if (mesh != null && mesh.isAnimated()) {
             targets.add(geometry);
-            materials.add(geometry.getMaterial());
         }
+        
     }
 
     private void findTargets(Node node) {
@@ -238,8 +224,21 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
     @Override
     public void setSpatial(Spatial spatial) {
+        Spatial oldSpatial = this.spatial;
         super.setSpatial(spatial);
         updateTargetsAndMaterials(spatial);
+        
+        if (oldSpatial != null) {
+            oldSpatial.removeMatParamOverride(numberOfBonesParam);
+            oldSpatial.removeMatParamOverride(boneMatricesParam);
+        }
+        
+        if (spatial != null) {
+            spatial.removeMatParamOverride(numberOfBonesParam);
+            spatial.removeMatParamOverride(boneMatricesParam);
+            spatial.addMatParamOverride(numberOfBonesParam);
+            spatial.addMatParamOverride(boneMatricesParam);
+        }
     }
 
     private void controlRenderSoftware() {
@@ -258,27 +257,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
     
     private void controlRenderHardware() {
         offsetMatrices = skeleton.computeSkinningMatrices();
-        for (Material m : materials) {
-            MatParam currentParam = m.getParam("BoneMatrices");
-
-            if (currentParam != null) {
-                if (currentParam.getValue() != offsetMatrices) {
-                    // Check to see if other SkeletonControl
-                    // is operating on this material, in that case, user
-                    // is sharing materials between models which is NOT allowed
-                    // when hardware skinning used.
-                    
-                    Logger.getLogger(SkeletonControl.class.getName()).log(Level.SEVERE,
-                            "Material instances cannot be shared when hardware skinning is used. " +
-                            "Ensure all models use unique material instances."
-                    );
-                }
-            }
-            
-            m.setParam("BoneMatrices", VarType.Matrix4Array, offsetMatrices);
-        }
+        boneMatricesParam.setValue(offsetMatrices);
     }
-
     
     @Override
     protected void controlRender(RenderManager rm, ViewPort vp) {
@@ -296,7 +276,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
                 if (hwSkinningSupported) {
                     hwSkinningEnabled = true;
                     
-                    Logger.getLogger(SkeletonControl.class.getName()).log(Level.INFO, "Hardware skinning engaged for " + spatial);
+                    Logger.getLogger(SkeletonControl.class.getName()).log(Level.INFO, "Hardware skinning engaged for {0}", spatial);
                 } else {
                     switchToSoftware();
                 }
@@ -420,28 +400,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         // were shared then this will share them.
         this.targets = cloner.clone(targets);
         
-        // Not automatic set cloning yet
-        Set<Material> newMaterials = new HashSet<Material>();
-        for( Material m : this.materials ) {
-            Material mClone = cloner.clone(m);
-            newMaterials.add(mClone);
-            if( mClone != m ) {
-                // Material was really cloned so clear the bone matrices in case
-                // this is hardware skinned.  This allows a local version to be
-                // used and will be reset on the material.  Really this just avoids
-                // the 'safety' check in controlRenderHardware().  Right now material
-                // doesn't clone itself with the cloner (and doesn't clone its parameters)
-                // else this would be unnecessary.
-                MatParam boneMatrices = mClone.getParam("BoneMatrices");
-                
-                // ...because for some strange reason you can't clear a non-existant 
-                // parameter.
-                if( boneMatrices != null ) {                    
-                    mClone.clearParam("BoneMatrices");
-                }
-            }
-        }
-        this.materials = newMaterials;
+        this.numberOfBonesParam = cloner.clone(numberOfBonesParam);
+        this.boneMatricesParam = cloner.clone(boneMatricesParam);
     }
          
     /**
@@ -547,10 +507,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         fnb.rewind();
 
         // get boneIndexes and weights for mesh
-        indexReader.setBuffer(mesh.getBuffer(Type.BoneIndex).getData());
+        IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData());
         FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
 
-        indexReader.rewind();
         wb.rewind();
 
         float[] weights = wb.array();
@@ -591,7 +550,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
                 for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
                     float weight = weights[idxWeights];
-                    Matrix4f mat = offsetMatrices[indexReader.getUnsigned(idxWeights++)];
+                    Matrix4f mat = offsetMatrices[ib.get(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;
@@ -664,10 +623,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
 
         // get boneIndexes and weights for mesh
-        indexReader.setBuffer(mesh.getBuffer(Type.BoneIndex).getData());
+        IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData());
         FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
 
-        indexReader.rewind();
         wb.rewind();
 
         float[] weights = wb.array();
@@ -723,7 +681,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
                 for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
                     float weight = weights[idxWeights];
-                    Matrix4f mat = offsetMatrices[indexReader.getUnsigned(idxWeights++)];
+                    Matrix4f mat = offsetMatrices[ib.get(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;
@@ -781,7 +739,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         super.write(ex);
         OutputCapsule oc = ex.getCapsule(this);
         oc.write(skeleton, "skeleton", null);
-        //Targets and materials don't need to be saved, they'll be gathered on each frame
+        
+        oc.write(numberOfBonesParam, "numberOfBonesParam", null);
+        oc.write(boneMatricesParam, "boneMatricesParam", null);
     }
 
     @Override
@@ -789,6 +749,16 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         super.read(im);
         InputCapsule in = im.getCapsule(this);
         skeleton = (Skeleton) in.readSavable("skeleton", null);
+        
+        numberOfBonesParam = (MatParamOverride) in.readSavable("numberOfBonesParam", null);
+        boneMatricesParam = (MatParamOverride) in.readSavable("boneMatricesParam", null);
+        
+        if (numberOfBonesParam == null) {
+            numberOfBonesParam = new MatParamOverride(VarType.Int, "NumberOfBones", null);
+            boneMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null);
+            getSpatial().addMatParamOverride(numberOfBonesParam);
+            getSpatial().addMatParamOverride(boneMatricesParam);
+        }
     }
 
     /**
@@ -798,7 +768,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
      */
     private void updateTargetsAndMaterials(Spatial spatial) {
         targets.clear();
-        materials.clear();
 
         if (spatial instanceof Node) {
             findTargets((Node) spatial);