159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/*
259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * To change this template, choose Tools | Templates
359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * and open the template in the editor.
459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.animation;
659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.*;
859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.FastMath;
959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Matrix4f;
1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.renderer.RenderManager;
1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.renderer.ViewPort;
1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.*;
1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer.Type;
1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.control.AbstractControl;
1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.control.Control;
1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.util.TempVars;
1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.IOException;
1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.ByteBuffer;
1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.FloatBuffer;
2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.ArrayList;
2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/**
2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The Skeleton control deforms a model according to a skeleton,
2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * It handles the computation of the deformation matrices and performs
2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the transformations on the mesh
2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Rémy Bouquet Based on AnimControl by Kirill Vainer
2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class SkeletonControl extends AbstractControl implements Cloneable {
3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * The skeleton of the model
3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Skeleton skeleton;
3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * List of targets which this controller effects.
3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Mesh[] targets;
3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Used to track when a mesh was updated. Meshes are only updated
4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * if they are visible in at least one camera.
4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private boolean wasMeshUpdated = false;
4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Serialization only. Do not use.
4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public SkeletonControl() {
4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Creates a skeleton control.
5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * The list of targets will be acquired automatically when
5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * the control is attached to a node.
5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param skeleton the skeleton
5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public SkeletonControl(Skeleton skeleton) {
5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.skeleton = skeleton;
6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Creates a skeleton control.
6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param targets the meshes controlled by the skeleton
6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param skeleton the skeleton
6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Deprecated
6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    SkeletonControl(Mesh[] targets, Skeleton skeleton) {
7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.skeleton = skeleton;
7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.targets = targets;
7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private boolean isMeshAnimated(Mesh mesh) {
7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return mesh.getBuffer(Type.BindPosePosition) != null;
7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Mesh[] findTargets(Node node) {
7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Mesh sharedMesh = null;
8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        ArrayList<Mesh> animatedMeshes = new ArrayList<Mesh>();
8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (Spatial child : node.getChildren()) {
8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (!(child instanceof Geometry)) {
8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                continue; // could be an attachment node, ignore.
8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Geometry geom = (Geometry) child;
8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // is this geometry using a shared mesh?
9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH);
9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (childSharedMesh != null) {
9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // Don't bother with non-animated shared meshes
9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (isMeshAnimated(childSharedMesh)) {
9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    // child is using shared mesh,
9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    // so animate the shared mesh but ignore child
9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (sharedMesh == null) {
9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        sharedMesh = childSharedMesh;
9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    } else if (sharedMesh != childSharedMesh) {
10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        throw new IllegalStateException("Two conflicting shared meshes for " + node);
10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else {
10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Mesh mesh = geom.getMesh();
10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (isMeshAnimated(mesh)) {
10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    animatedMeshes.add(mesh);
10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (sharedMesh != null) {
11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            animatedMeshes.add(sharedMesh);
11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return animatedMeshes.toArray(new Mesh[animatedMeshes.size()]);
11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setSpatial(Spatial spatial) {
12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super.setSpatial(spatial);
12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (spatial != null) {
12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Node node = (Node) spatial;
12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            targets = findTargets(node);
12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            targets = null;
12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void controlRender(RenderManager rm, ViewPort vp) {
13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (!wasMeshUpdated) {
13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            resetToBind(); // reset morph meshes to bind pose
13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices();
13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // if hardware skinning is supported, the matrices and weight buffer
13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // will be sent by the SkinningShaderLogic object assigned to the shader
13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = 0; i < targets.length; i++) {
13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // NOTE: This assumes that code higher up
14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // Already ensured those targets are animated
14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // otherwise a crash will happen in skin update
14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                //if (isMeshAnimated(targets[i])) {
14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                softwareSkinUpdate(targets[i], offsetMatrices);
14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                //}
14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            wasMeshUpdated = true;
14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void controlUpdate(float tpf) {
15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        wasMeshUpdated = false;
15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    void resetToBind() {
15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (Mesh mesh : targets) {
15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (isMeshAnimated(mesh)) {
15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                VertexBuffer bi = mesh.getBuffer(Type.BoneIndex);
16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ByteBuffer bib = (ByteBuffer) bi.getData();
16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (!bib.hasArray()) {
16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    mesh.prepareForAnim(true); // prepare for software animation
16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                VertexBuffer pos = mesh.getBuffer(Type.Position);
16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                VertexBuffer norm = mesh.getBuffer(Type.Normal);
16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                FloatBuffer pb = (FloatBuffer) pos.getData();
16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                FloatBuffer nb = (FloatBuffer) norm.getData();
17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                FloatBuffer bpb = (FloatBuffer) bindPos.getData();
17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                FloatBuffer bnb = (FloatBuffer) bindNorm.getData();
17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                pb.clear();
17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                nb.clear();
17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                bpb.clear();
17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                bnb.clear();
17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                //reseting bind tangents if there is a bind tangent buffer
17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent);
17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (bindTangents != null) {
18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    FloatBuffer tb = (FloatBuffer) tangents.getData();
18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    FloatBuffer btb = (FloatBuffer) bindTangents.getData();
18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    tb.clear();
18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    btb.clear();
18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    tb.put(btb).clear();
18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                pb.put(bpb).clear();
19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                nb.put(bnb).clear();
19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Control cloneForSpatial(Spatial spatial) {
19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Node clonedNode = (Node) spatial;
19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        AnimControl ctrl = spatial.getControl(AnimControl.class);
19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        SkeletonControl clone = new SkeletonControl();
19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.setSpatial(clonedNode);
20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.skeleton = ctrl.getSkeleton();
20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // Fix animated targets for the cloned node
20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.targets = findTargets(clonedNode);
20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // Fix attachments for the cloned node
20659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i < clonedNode.getQuantity(); i++) {
20759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // go through attachment nodes, apply them to correct bone
20859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Spatial child = clonedNode.getChild(i);
20959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (child instanceof Node) {
21059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Node clonedAttachNode = (Node) child;
21159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Bone originalBone = (Bone) clonedAttachNode.getUserData("AttachedBone");
21259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
21359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (originalBone != null) {
21459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    Bone clonedBone = clone.skeleton.getBone(originalBone.getName());
21559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
21659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    clonedAttachNode.setUserData("AttachedBone", clonedBone);
21759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    clonedBone.setAttachmentsNode(clonedAttachNode);
21859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
21959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
22059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
22159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
22259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return clone;
22359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
22459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
22559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
22659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
22759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param boneName the name of the bone
22859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return the node attached to this bone
22959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
23059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Node getAttachmentsNode(String boneName) {
23159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Bone b = skeleton.getBone(boneName);
23259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (b == null) {
23359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalArgumentException("Given bone name does not exist "
23459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    + "in the skeleton.");
23559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
23659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
23759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Node n = b.getAttachmentsNode();
23859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Node model = (Node) spatial;
23959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        model.attachChild(n);
24059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return n;
24159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
24259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
24359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
24459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * returns the skeleton of this control
24559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return
24659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
24759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Skeleton getSkeleton() {
24859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return skeleton;
24959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
25059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
25159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
25259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * sets the skeleton for this control
25359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param skeleton
25459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
25559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta//    public void setSkeleton(Skeleton skeleton) {
25659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta//        this.skeleton = skeleton;
25759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta//    }
25859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
25959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * returns the targets meshes of this control
26059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return
26159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
26259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Mesh[] getTargets() {
26359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return targets;
26459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
26559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
26659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
26759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * sets the target  meshes of this control
26859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param targets
26959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
27059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta//    public void setTargets(Mesh[] targets) {
27159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta//        this.targets = targets;
27259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta//    }
27359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
27459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Update the mesh according to the given transformation matrices
27559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param mesh then mesh
27659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param offsetMatrices the transformation matrices to apply
27759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
27859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
27959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
28059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        VertexBuffer tb = mesh.getBuffer(Type.Tangent);
28159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (tb == null) {
28259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //if there are no tangents use the classic skinning
28359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            applySkinning(mesh, offsetMatrices);
28459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
28559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //if there are tangents use the skinning with tangents
28659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            applySkinningTangents(mesh, offsetMatrices, tb);
28759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
28859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
28959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
29059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
29159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
29259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
29359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Method to apply skinning transforms to a mesh's buffers
29459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param mesh the mesh
29559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param offsetMatrices the offset matices to apply
29659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
29759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) {
29859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int maxWeightsPerVert = mesh.getMaxNumWeights();
29959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (maxWeightsPerVert <= 0) {
30059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Max weights per vert is incorrectly set!");
30159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
30259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int fourMinusMaxWeights = 4 - maxWeightsPerVert;
30459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // NOTE: This code assumes the vertex buffer is in bind pose
30659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // resetToBind() has been called this frame
30759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        VertexBuffer vb = mesh.getBuffer(Type.Position);
30859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer fvb = (FloatBuffer) vb.getData();
30959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        fvb.rewind();
31059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
31159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        VertexBuffer nb = mesh.getBuffer(Type.Normal);
31259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer fnb = (FloatBuffer) nb.getData();
31359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        fnb.rewind();
31459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
31559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // get boneIndexes and weights for mesh
31659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
31759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
31859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
31959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        ib.rewind();
32059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        wb.rewind();
32159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
32259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] weights = wb.array();
32359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        byte[] indices = ib.array();
32459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int idxWeights = 0;
32559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
32659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TempVars vars = TempVars.get();
32759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
32859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
32959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] posBuf = vars.skinPositions;
33059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] normBuf = vars.skinNormals;
33159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
33259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
33359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int bufLength = posBuf.length;
33459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = iterations - 1; i >= 0; i--) {
33559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // read next set of positions and normals from native buffer
33659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            bufLength = Math.min(posBuf.length, fvb.remaining());
33759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fvb.get(posBuf, 0, bufLength);
33859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fnb.get(normBuf, 0, bufLength);
33959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int verts = bufLength / 3;
34059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int idxPositions = 0;
34159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
34259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // iterate vertices and apply skinning transform for each effecting bone
34359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int vert = verts - 1; vert >= 0; vert--) {
34459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float nmx = normBuf[idxPositions];
34559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float vtx = posBuf[idxPositions++];
34659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float nmy = normBuf[idxPositions];
34759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float vty = posBuf[idxPositions++];
34859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float nmz = normBuf[idxPositions];
34959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float vtz = posBuf[idxPositions++];
35059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
35159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0;
35259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
35359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
35459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    float weight = weights[idxWeights];
35559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    Matrix4f mat = offsetMatrices[indices[idxWeights++]];
35659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
35759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
35859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
35959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
36059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
36159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
36259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
36359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
36459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
36559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
36659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                idxWeights += fourMinusMaxWeights;
36759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
36859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                idxPositions -= 3;
36959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                normBuf[idxPositions] = rnx;
37059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                posBuf[idxPositions++] = rx;
37159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                normBuf[idxPositions] = rny;
37259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                posBuf[idxPositions++] = ry;
37359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                normBuf[idxPositions] = rnz;
37459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                posBuf[idxPositions++] = rz;
37559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
37659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
37759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fvb.position(fvb.position() - bufLength);
37859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fvb.put(posBuf, 0, bufLength);
37959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fnb.position(fnb.position() - bufLength);
38059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fnb.put(normBuf, 0, bufLength);
38159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
38259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
38359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        vars.release();
38459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
38559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        vb.updateData(fvb);
38659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        nb.updateData(fnb);
38759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
38859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
38959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
39059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
39159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Specific method for skinning with tangents to avoid cluttering the classic skinning calculation with
39259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * null checks that would slow down the process even if tangents don't have to be computed.
39359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Also the iteration has additional indexes since tangent has 4 components instead of 3 for pos and norm
39459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param maxWeightsPerVert maximum number of weights per vertex
39559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param mesh the mesh
39659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param offsetMatrices the offsetMaytrices to apply
39759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param tb the tangent vertexBuffer
39859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
39959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) {
40059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int maxWeightsPerVert = mesh.getMaxNumWeights();
40159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
40259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (maxWeightsPerVert <= 0) {
40359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Max weights per vert is incorrectly set!");
40459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
40559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
40659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int fourMinusMaxWeights = 4 - maxWeightsPerVert;
40759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
40859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // NOTE: This code assumes the vertex buffer is in bind pose
40959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // resetToBind() has been called this frame
41059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        VertexBuffer vb = mesh.getBuffer(Type.Position);
41159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer fvb = (FloatBuffer) vb.getData();
41259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        fvb.rewind();
41359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
41459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        VertexBuffer nb = mesh.getBuffer(Type.Normal);
41559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
41659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer fnb = (FloatBuffer) nb.getData();
41759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        fnb.rewind();
41859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
41959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer ftb = (FloatBuffer) tb.getData();
42159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        ftb.rewind();
42259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // get boneIndexes and weights for mesh
42559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
42659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
42759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        ib.rewind();
42959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        wb.rewind();
43059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] weights = wb.array();
43259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        byte[] indices = ib.array();
43359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int idxWeights = 0;
43459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TempVars vars = TempVars.get();
43659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] posBuf = vars.skinPositions;
43959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] normBuf = vars.skinNormals;
44059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] tanBuf = vars.skinTangents;
44159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
44259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
44359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int bufLength = 0;
44459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int tanLength = 0;
44559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = iterations - 1; i >= 0; i--) {
44659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // read next set of positions and normals from native buffer
44759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            bufLength = Math.min(posBuf.length, fvb.remaining());
44859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            tanLength = Math.min(tanBuf.length, ftb.remaining());
44959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fvb.get(posBuf, 0, bufLength);
45059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fnb.get(normBuf, 0, bufLength);
45159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            ftb.get(tanBuf, 0, tanLength);
45259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int verts = bufLength / 3;
45359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int idxPositions = 0;
45459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //tangents has their own index because of the 4 components
45559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int idxTangents = 0;
45659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
45759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // iterate vertices and apply skinning transform for each effecting bone
45859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int vert = verts - 1; vert >= 0; vert--) {
45959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float nmx = normBuf[idxPositions];
46059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float vtx = posBuf[idxPositions++];
46159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float nmy = normBuf[idxPositions];
46259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float vty = posBuf[idxPositions++];
46359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float nmz = normBuf[idxPositions];
46459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float vtz = posBuf[idxPositions++];
46559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
46659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float tnx = tanBuf[idxTangents++];
46759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float tny = tanBuf[idxTangents++];
46859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float tnz = tanBuf[idxTangents++];
46959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
47059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                //skipping the 4th component of the tangent since it doesn't have to be transformed
47159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                idxTangents++;
47259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
47359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0;
47459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
47559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
47659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    float weight = weights[idxWeights];
47759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    Matrix4f mat = offsetMatrices[indices[idxWeights++]];
47859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
47959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
48059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
48159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
48259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
48359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
48459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
48559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
48659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
48759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight;
48859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight;
48959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight;
49059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
49159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                idxWeights += fourMinusMaxWeights;
49359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                idxPositions -= 3;
49559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                normBuf[idxPositions] = rnx;
49759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                posBuf[idxPositions++] = rx;
49859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                normBuf[idxPositions] = rny;
49959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                posBuf[idxPositions++] = ry;
50059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                normBuf[idxPositions] = rnz;
50159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                posBuf[idxPositions++] = rz;
50259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
50359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                idxTangents -= 4;
50459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
50559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                tanBuf[idxTangents++] = rtx;
50659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                tanBuf[idxTangents++] = rty;
50759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                tanBuf[idxTangents++] = rtz;
50859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
50959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                //once again skipping the 4th component of the tangent
51059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                idxTangents++;
51159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
51259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
51359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fvb.position(fvb.position() - bufLength);
51459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fvb.put(posBuf, 0, bufLength);
51559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fnb.position(fnb.position() - bufLength);
51659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fnb.put(normBuf, 0, bufLength);
51759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            ftb.position(ftb.position() - tanLength);
51859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            ftb.put(tanBuf, 0, tanLength);
51959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
52059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        vars.release();
52259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        vb.updateData(fvb);
52459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        nb.updateData(fnb);
52559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tb.updateData(ftb);
52659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
52959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
53159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void write(JmeExporter ex) throws IOException {
53259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super.write(ex);
53359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        OutputCapsule oc = ex.getCapsule(this);
53459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(targets, "targets", null);
53559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(skeleton, "skeleton", null);
53659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
53759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
53959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void read(JmeImporter im) throws IOException {
54059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super.read(im);
54159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        InputCapsule in = im.getCapsule(this);
54259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Savable[] sav = in.readSavableArray("targets", null);
54359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (sav != null) {
54459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            targets = new Mesh[sav.length];
54559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            System.arraycopy(sav, 0, targets, 0, sav.length);
54659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
54759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        skeleton = (Skeleton) in.readSavable("skeleton", null);
54859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
54959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta}
550