Bläddra i källkod

* Removed deprecated AnimControl constructor
* Moved bind pose generation to Mesh away from Ogre loader as its a fairly common operation for animation loaders
* Added TestCustomAnim test which demonstrates how to generate an animated model from scratch

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

sha..rd 14 år sedan
förälder
incheckning
e9e64c7cb7

+ 2 - 2
engine/src/core/com/jme3/animation/AnimControl.java

@@ -111,7 +111,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
 
         this.skeleton = skeleton;
 
-        skeletonControl = new SkeletonControl(model, meshes, this.skeleton);
+        skeletonControl = new SkeletonControl(meshes, this.skeleton);
         reset();
     }
 
@@ -389,7 +389,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
             Mesh[] tg = null;
             tg = new Mesh[sav.length];
             System.arraycopy(sav, 0, tg, 0, sav.length);
-            skeletonControl = new SkeletonControl((Node) spatial, tg, skeleton);
+            skeletonControl = new SkeletonControl(tg, skeleton);
             spatial.addControl(skeletonControl);
         }
         //------

+ 4 - 2
engine/src/core/com/jme3/animation/Bone.java

@@ -382,11 +382,13 @@ public final class Bone implements Savable {
         worldRot.set(rotation);
     }
     
-    protected Vector3f tmpVec=new Vector3f();
-    protected Quaternion tmpQuat=new Quaternion();
+    protected Vector3f tmpVec = new Vector3f();
+    protected Quaternion tmpQuat = new Quaternion();
+
     public Quaternion getTmpQuat() {
         return tmpQuat;
     }
+
     public Vector3f getTmpVec() {
         return tmpVec;
     }

+ 4 - 2
engine/src/core/com/jme3/animation/SkeletonControl.java

@@ -44,8 +44,7 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab
     public SkeletonControl() {
     }
 
-    public SkeletonControl(Node model, Mesh[] targets, Skeleton skeleton) {
-        super(model);
+    public SkeletonControl(Mesh[] targets, Skeleton skeleton) {
         this.skeleton = skeleton;
         this.targets = targets;
     }
@@ -95,6 +94,9 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab
 
     private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
         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

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

@@ -168,6 +168,41 @@ public class Mesh implements Savable, Cloneable {
         return clone;
     }
 
+    public void generateBindPose(boolean swAnim){
+        if (swAnim){
+            VertexBuffer pos = getBuffer(Type.Position);
+            if (pos == null || getBuffer(Type.BoneIndex) == null) {
+                // ignore, this mesh doesn't have positional data
+                // or it doesn't have bone-vertex assignments, so its not animated
+                return;
+            }
+
+            VertexBuffer bindPos = new VertexBuffer(Type.BindPosePosition);
+            bindPos.setupData(Usage.CpuOnly,
+                    3,
+                    Format.Float,
+                    BufferUtils.clone(pos.getData()));
+            setBuffer(bindPos);
+
+            // XXX: note that this method also sets stream mode
+            // so that animation is faster. this is not needed for hardware skinning
+            pos.setUsage(Usage.Stream);
+
+            VertexBuffer norm = getBuffer(Type.Normal);
+            if (norm != null) {
+                VertexBuffer bindNorm = new VertexBuffer(Type.BindPoseNormal);
+                bindNorm.setupData(Usage.CpuOnly,
+                        3,
+                        Format.Float,
+                        BufferUtils.clone(norm.getData()));
+                setBuffer(bindNorm);
+                norm.setUsage(Usage.Stream);
+            }
+
+            norm.setUsage(Usage.Stream);
+        }
+    }
+
     public void prepareForAnim(boolean swAnim){
         if (swAnim){
             // convert indices

+ 1 - 32
engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java

@@ -705,37 +705,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
     public void characters(char ch[], int start, int length) {
     }
 
-    private void createBindPose(Mesh mesh) {
-        VertexBuffer pos = mesh.getBuffer(Type.Position);
-        if (pos == null || mesh.getBuffer(Type.BoneIndex) == null) {
-            // ignore, this mesh doesn't have positional data
-            // or it doesn't have bone-vertex assignments, so its not animated
-            return;
-        }
-
-        VertexBuffer bindPos = new VertexBuffer(Type.BindPosePosition);
-        bindPos.setupData(Usage.CpuOnly,
-                3,
-                Format.Float,
-                BufferUtils.clone(pos.getData()));
-        mesh.setBuffer(bindPos);
-
-        // XXX: note that this method also sets stream mode
-        // so that animation is faster. this is not needed for hardware skinning
-        pos.setUsage(Usage.Stream);
-
-        VertexBuffer norm = mesh.getBuffer(Type.Normal);
-        if (norm != null) {
-            VertexBuffer bindNorm = new VertexBuffer(Type.BindPoseNormal);
-            bindNorm.setupData(Usage.CpuOnly,
-                    3,
-                    Format.Float,
-                    BufferUtils.clone(norm.getData()));
-            mesh.setBuffer(bindNorm);
-            norm.setUsage(Usage.Stream);
-        }
-    }
-
     private Node compileModel() {
         String nodeName;
         if (meshName == null) {
@@ -757,7 +726,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
                 boolean useShared = usesSharedGeom.get(i);
                 // create bind pose
                 if (!useShared) {
-                    createBindPose(m);
+                    m.generateBindPose(!HARDWARE_SKINNING);
                     newMeshes.add(m);
                 } else {
                     VertexBuffer bindPos = sharedmesh.getBuffer(Type.BindPosePosition);

+ 145 - 0
engine/src/test/jme3test/model/anim/TestCustomAnim.java

@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.anim;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.Skeleton;
+import com.jme3.animation.SkeletonControl;
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.shape.Box;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+
+public class TestCustomAnim extends SimpleApplication {
+
+    private Bone bone;
+    private Skeleton skeleton;
+    private Quaternion rotation = new Quaternion();
+
+    public static void main(String[] args) {
+        TestCustomAnim app = new TestCustomAnim();
+        app.start();
+    }
+
+    @Override
+    public void simpleInitApp() {
+
+        AmbientLight al = new AmbientLight();
+        rootNode.addLight(al);
+
+        DirectionalLight dl = new DirectionalLight();
+        dl.setDirection(Vector3f.UNIT_XYZ.negate());
+        rootNode.addLight(dl);
+
+        Box box = new Box(1, 1, 1);
+
+        // Setup bone weight buffer
+        FloatBuffer weights = FloatBuffer.allocate( box.getVertexCount() * 4 );
+        VertexBuffer weightsBuf = new VertexBuffer(Type.BoneWeight);
+        weightsBuf.setupData(Usage.CpuOnly, 4, Format.Float, weights);
+        box.setBuffer(weightsBuf);
+
+        // Setup bone index buffer
+        ByteBuffer indices = ByteBuffer.allocate( box.getVertexCount() * 4 );
+        VertexBuffer indicesBuf = new VertexBuffer(Type.BoneIndex);
+        indicesBuf.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indices);
+        box.setBuffer(indicesBuf);
+
+        // Create bind pose buffers
+        box.generateBindPose(true);
+
+        // Create skeleton
+        bone = new Bone("root");
+        bone.setBindTransforms(Vector3f.ZERO, Quaternion.IDENTITY, Vector3f.UNIT_XYZ);
+        bone.setUserControl(true);
+        skeleton = new Skeleton(new Bone[]{ bone });
+
+        // Assign all verticies to bone 0 with weight 1
+        for (int i = 0; i < box.getVertexCount() * 4; i += 4){
+            // assign vertex to bone index 0
+            indices.array()[i+0] = 0;
+            indices.array()[i+1] = 0;
+            indices.array()[i+2] = 0;
+            indices.array()[i+3] = 0;
+
+            // set weight to 1 only for first entry
+            weights.array()[i+0] = 1;
+            weights.array()[i+1] = 0;
+            weights.array()[i+2] = 0;
+            weights.array()[i+3] = 0;
+        }
+
+        // Maximum number of weights per bone is 1
+        box.setMaxNumWeights(1);
+
+        // Create model
+        Geometry geom = new Geometry("box", box);
+        geom.setMaterial(assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"));
+        Node model = new Node("model");
+        model.attachChild(geom);
+
+        // Create skeleton control
+        SkeletonControl skeletonControl = new SkeletonControl(new Mesh[]{ box }, skeleton);
+        model.addControl(skeletonControl);
+
+        rootNode.attachChild(model);
+    }
+
+    @Override
+    public void simpleUpdate(float tpf){
+        // Rotate around X axis
+        Quaternion rotate = new Quaternion();
+        rotate.fromAngleAxis(tpf, Vector3f.UNIT_X);
+
+        // Combine rotation with previous
+        rotation.multLocal(rotate);
+
+        // Set new rotation into bone
+        bone.setUserTransforms(Vector3f.ZERO, rotation, Vector3f.UNIT_XYZ);
+
+        // After changing skeleton transforms, must update world data
+        skeleton.updateWorldVectors();
+    }
+
+}