com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonModel.java Source code

Java tutorial

Introduction

Here is the source code for com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonModel.java

Source

/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.graphics.g3d.model.skeleton;

import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.materials.Material;
import com.badlogic.gdx.graphics.g3d.model.AnimatedModel;
import com.badlogic.gdx.graphics.g3d.model.Model;
import com.badlogic.gdx.graphics.g3d.model.SubMesh;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.utils.Array;

public class SkeletonModel implements AnimatedModel {
    public final Skeleton skeleton;
    public final SkeletonSubMesh[] subMeshes;

    public SkeletonModel(Skeleton skeleton, SubMesh[] subMeshes) {
        this.skeleton = skeleton;
        this.subMeshes = new SkeletonSubMesh[subMeshes.length];

        for (int i = 0; i < subMeshes.length; ++i) {
            this.subMeshes[i] = (SkeletonSubMesh) subMeshes[i];
        }
        setMaterial(new Material("default"));
    }

    public void setBindPose() {
        skeleton.setBindPose();
        for (int i = 0; i < subMeshes.length; i++) {
            skin(subMeshes[i], skeleton.combinedMatrices);
        }
    }

    @Override
    public void setAnimation(String animation, float time, boolean loop) {
        skeleton.setAnimation(animation, time);
        for (int i = 0; i < subMeshes.length; i++) {
            skin(subMeshes[i], skeleton.combinedMatrices);
        }
    }

    final Vector3 v = new Vector3();

    public void skin(SkeletonSubMesh subMesh, Array<Matrix4> boneMatrices) {
        final int stride = subMesh.mesh.getVertexSize() / 4;
        final int numVertices = subMesh.mesh.getNumVertices();
        int idx = 0;
        int nidx = subMesh.mesh.getVertexAttribute(Usage.Normal) == null ? -1
                : subMesh.mesh.getVertexAttribute(Usage.Normal).offset / 4;
        final float[] vertices = subMesh.vertices;
        final float[] skinnedVertices = subMesh.skinnedVertices;

        System.arraycopy(subMesh.vertices, 0, skinnedVertices, 0, subMesh.vertices.length);

        for (int i = 0; i < numVertices; i++, idx += stride, nidx += stride) {
            final int[] boneIndices = subMesh.boneAssignments[i];
            final float[] boneWeights = subMesh.boneWeights[i];

            final float ox = vertices[idx], oy = vertices[idx + 1], oz = vertices[idx + 2];
            float x = 0, y = 0, z = 0;
            float onx = 0, ony = 0, onz = 0;
            float nx = 0, ny = 0, nz = 0;

            if (nidx != -1) {
                onx = vertices[nidx];
                ony = vertices[nidx + 1];
                onz = vertices[nidx + 2];
            }

            for (int j = 0; j < boneIndices.length; j++) {
                int boneIndex = boneIndices[j];
                float weight = boneWeights[j];
                v.set(ox, oy, oz);
                v.mul(boneMatrices.get(boneIndex));
                x += v.x * weight;
                y += v.y * weight;
                z += v.z * weight;

                if (nidx != -1) {
                    v.set(onx, ony, onz);
                    v.rot(boneMatrices.get(boneIndex));
                    nx += v.x * weight;
                    ny += v.y * weight;
                    nz += v.z * weight;
                }
            }

            skinnedVertices[idx] = x;
            skinnedVertices[idx + 1] = y;
            skinnedVertices[idx + 2] = z;

            if (nidx != -1) {
                skinnedVertices[nidx] = nx;
                skinnedVertices[nidx + 1] = ny;
                skinnedVertices[nidx + 2] = nz;
            }
        }

        subMesh.mesh.setVertices(skinnedVertices);
    }

    @Override
    public void render() {
        int len = subMeshes.length;
        for (int i = 0; i < len; i++) {
            SkeletonSubMesh subMesh = subMeshes[i];
            if (i == 0) {
                subMesh.material.bind();
            } else if (!subMeshes[i - 1].material.equals(subMesh.material)) {
                subMesh.material.bind();
            }
            subMesh.mesh.render(subMesh.primitiveType);
        }
    }

    @Override
    public void render(ShaderProgram program) {
        int len = subMeshes.length;
        for (int i = 0; i < len; i++) {
            SkeletonSubMesh subMesh = subMeshes[i];
            if (i == 0) {
                subMesh.material.bind(program);
            } else if (!subMeshes[i - 1].material.equals(subMesh.material)) {
                subMesh.material.bind(program);
            }
            subMesh.mesh.render(program, subMesh.primitiveType);
        }
    }

    @Override
    public void setMaterials(Material... materials) {
        if (materials.length != subMeshes.length)
            throw new UnsupportedOperationException("number of materials must equal number of sub-meshes");
        int len = materials.length;
        for (int i = 0; i < len; i++) {
            subMeshes[i].material = materials[i];
        }
    }

    @Override
    public void setMaterial(Material material) {
        int len = subMeshes.length;
        for (int i = 0; i < len; i++) {
            subMeshes[i].material = material;
        }
    }

    @Override
    public SubMesh getSubMesh(String name) {
        int len = subMeshes.length;
        for (int i = 0; i < len; i++) {
            if (subMeshes[i].name.equals(name))
                return subMeshes[i];
        }
        return null;
    }

    @Override
    public SubMesh[] getSubMeshes() {
        return subMeshes;
    }

    @Override
    public SkeletonAnimation getAnimation(String name) {
        return skeleton.animations.get(name);
    }

    // FIXME, ugh...
    protected SkeletonAnimation[] animations;

    @Override
    public SkeletonAnimation[] getAnimations() {
        if (animations == null || animations.length != skeleton.animations.size) {
            animations = new SkeletonAnimation[skeleton.animations.size];
            int i = 0;
            for (SkeletonAnimation anim : skeleton.animations.values()) {
                animations[i++] = anim;
            }
        }
        return animations;
    }

    @Override
    public Model getSubModel(String... subMeshNames) {
        // FIXME
        return null;
    }

    private final static BoundingBox tmpBox = new BoundingBox();

    @Override
    public void getBoundingBox(BoundingBox bbox) {
        bbox.inf();
        for (int i = 0; i < subMeshes.length; i++) {
            subMeshes[i].mesh.calculateBoundingBox(tmpBox);
            bbox.ext(tmpBox);
        }
    }

    @Override
    public void dispose() {
        for (int i = 0; i < subMeshes.length; i++) {
            subMeshes[i].mesh.dispose();
        }
    }
}