Subversion Repositories AndroidProjects

Rev

Rev 1803 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.gebauz.bauzoid2.graphics.model;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.utils.JsonReader;
import com.badlogic.gdx.utils.JsonValue;
import com.gebauz.bauzoid2.file.FileUtil;
import com.gebauz.bauzoid2.game.Consts;
import com.gebauz.bauzoid2.game.Engine;
import com.gebauz.bauzoid2.graphics.animation.Animation;
import com.gebauz.bauzoid2.graphics.animation.BoneKeyframes;
import com.gebauz.bauzoid2.graphics.geometry.IndexStream;
import com.gebauz.bauzoid2.graphics.geometry.VertexAttribute;
import com.gebauz.bauzoid2.graphics.geometry.VertexStream;
import com.gebauz.bauzoid2.graphics.util.Transform;
import com.gebauz.bauzoid2.math.Matrix4;
import com.gebauz.bauzoid2.math.Quaternion;
import com.gebauz.bauzoid2.math.Vector3;

import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;

import sun.security.provider.certpath.Vertex;

public class GdxModelUtil
{
    public final static int NUM_POSITION_COORDS = 3;
    public final static int NUM_NORMAL_COORDS = 3;
    public final static int NUM_TEXCOORD_COORDS = 2;
    public final static int NUM_COLOR_COORDS = 4;
    public final static int NUM_COLOR_PACKED_COORDS = 1;
    public final static int NUM_TANGENT_COORDS = 3;
    public final static int NUM_BINORMAL_COORDS = 3;
    public final static int NUM_BLENDWEIGHT_COORDS = 2;

    public final static int NUM_COMBINED_BLENDINDICES_COORDS = 4;
    public final static int NUM_COMBINED_BLENDWEIGHTS_COORDS = 4;

    public static class MeshData
    {
        public String[] attributes = null;
        public float[] vertices = null;
        public int numIndices = 0;

        public ArrayList<PartData> parts = new ArrayList<PartData>();
    }

    public static class PartData
    {
        public String id = null;
        public String type = null;
        public short[] indices = null;
    }

    public static class MaterialData
    {
        public String id = null;

        public ArrayList<TextureData> textures = new ArrayList<TextureData>();
        float[] ambient = null;
        float[] diffuse = null;
        float[] emissive = null;
        float opacity = 1.0f;
        float[] specular = null;
        float shininess = 1.0f;
    }

    public static class TextureData
    {
        public String id = null;
        public String type = null;
        public String filename = null;

        public boolean isAsync = false;
    }

    public static class NodePartData
    {
        public String subMeshId = null;
        public String materialId = null;

        public ArrayList<BoneData> bones = new ArrayList<BoneData>();

        // TODO: uvmapping
    }

    public static class NodeData
    {
        public String id = null;

        public float[] translation = null;
        public float[] rotation = null;
        public float[] scale = null;

        public ArrayList<NodePartData> parts = new ArrayList<NodePartData>();

        public ArrayList<NodeData> children = new ArrayList<NodeData>();

        public NodeData parent = null;
    }

    public static class BoneData
    {
        String nodeId = null;

        public float[] translation = null;
        public float[] rotation = null;
        public float[] scale = null;
    }

    public static class KeyframeData
    {
        float time = 0.0f;

        public float[] translation = null;
        public float[] rotation = null;
        public float[] scale = null;
    }

    public static class BoneKeyframeData
    {
        public String boneId = null;
        public ArrayList<KeyframeData> keyframes = new ArrayList<KeyframeData>();
    }

    public static class AnimationData
    {
        public String id = null;
        public ArrayList<BoneKeyframeData> boneKeys = new ArrayList<BoneKeyframeData>();
    }

    public static class GdxModelDescriptor
    {
        public int[] version = null;

        public String id = null;
        public ArrayList<MeshData> meshes = new ArrayList<MeshData>();
        public ArrayList<MaterialData> materials = new ArrayList<MaterialData>();
        public ArrayList<NodeData> nodes = new ArrayList<NodeData>();
        public ArrayList<AnimationData> animations = new ArrayList<AnimationData>();

        public GdxModelDescriptor()
        {
        }
    }

    private GdxModelUtil() {}

    public static GdxModelDescriptor readModel(String filename)
    {
        String modelFile = FileUtil.loadString(filename);

        JsonValue root = new JsonReader().parse(modelFile);

        GdxModelDescriptor descriptor = new GdxModelDescriptor();

        for (JsonValue entry : root)
        {
            if (entry.name.equalsIgnoreCase("version"))
            {
                try
                {
                    Field f = GdxModelDescriptor.class.getField(entry.name);
                    if (f.getType().isArray())
                    {
                        if (f.getType().getComponentType() == int.class)
                        {
                            f.set(descriptor, entry.asIntArray());
                        }
                    }
                    Engine.log("bla");
                }
                catch (Exception ex)
                {
                    Engine.log(ex.getMessage());
                }

                //int[] version = entry.asIntArray();

                Engine.log("G3DJ Version: " + descriptor.version[0] + "." + descriptor.version[1]);
            }
            else if (entry.name.equalsIgnoreCase("id"))
            {
                descriptor.id = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("meshes"))
            {
                for (JsonValue meshEntry : entry)
                {
                    descriptor.meshes.add(processMesh(meshEntry));
                }
            }
            else if (entry.name.equalsIgnoreCase("materials"))
            {
                for (JsonValue materialEntry : entry)
                {
                    descriptor.materials.add(processMaterial(materialEntry));
                }
            }
            else if (entry.name.equalsIgnoreCase("nodes"))
            {
                for (JsonValue nodeEntry : entry)
                {
                    descriptor.nodes.add(processNode(nodeEntry));
                }
            }
            else if (entry.name.equalsIgnoreCase("animations"))
            {
                for (JsonValue animationEntry : entry)
                {
                    descriptor.animations.add(processAnimation(animationEntry));
                }
            }
        }

        return descriptor;
    }

    public static Model buildModel(GdxModelDescriptor descriptor)
    {
        return buildModel(descriptor.id, descriptor.meshes, descriptor.materials, descriptor.nodes, descriptor.animations);
    }

    public static Model buildModel(String id, ArrayList<MeshData> meshDatas, ArrayList<MaterialData> materialDatas,
                                                                   ArrayList<NodeData> nodeDatas, ArrayList<AnimationData> anims)
    {
        Model model = new Model(id);

        Mesh[] meshes = new Mesh[meshDatas.size()];

        for (int meshIndex = 0; meshIndex < meshDatas.size(); meshIndex++)
        {
            MeshData meshData = meshDatas.get(meshIndex);
            meshes[meshIndex] = new Mesh();

            ArrayList<VertexAttribute> attribs = new ArrayList<VertexAttribute>();

            int byteOffset = 0;
            int coords = 0;

            ArrayList<VertexStream> vertexStreams = buildVertexStream(attribs, meshData.attributes, meshData.vertices);
            meshes[meshIndex].getGeometry().setVertexStreams(vertexStreams.toArray(new VertexStream[vertexStreams.size()]));

            short[] indices = new short[meshData.numIndices];
            int indexOffset = 0;

            MeshGroup[] subMeshes = new MeshGroup[meshData.parts.size()];
            for (int partIndex = 0; partIndex < meshData.parts.size(); partIndex++)
            {
                PartData part = meshData.parts.get(partIndex);

                System.arraycopy(part.indices, 0, indices, indexOffset, part.indices.length);

                subMeshes[partIndex] = new MeshGroup(meshes[meshIndex], part.id, indexOffset, indexOffset + part.indices.length);

                indexOffset += part.indices.length;
            }
            meshes[meshIndex].setMeshGroups(subMeshes);

            IndexStream is = new IndexStream();
            is.setData(indices);
            is.upload();
            meshes[meshIndex].getGeometry().setIndexStream(is);
        }

        Material[] materials = new Material[materialDatas.size()];
        for (int materialIndex = 0; materialIndex < materialDatas.size(); materialIndex++)
        {
            // TODO: for now, only take first texture, always diffuse
            MaterialData matData = materialDatas.get(materialIndex);
            String textureFile = null;
            Texture texture = null;
            if (matData.textures.size() > 0)
            {
                TextureData texData = matData.textures.get(0);
                textureFile = texData.filename;
                if (texData.isAsync)
                {
                    texture = Engine.assets.get(textureFile);
                }
                else
                {
                    texture = new Texture(Gdx.files.internal(textureFile));
                }
            }
            materials[materialIndex] = new Material(matData.id, texture);
            //matData.textures
        }

        model.setMeshes(meshes);
        model.setMaterials(materials);

        int nodeCount = getTotalNodeCount(nodeDatas);

                ArrayList<ModelPart> partList = new ArrayList<ModelPart>();
                ArrayList<NodePartData> partDataList = new ArrayList<NodePartData>();

        ModelNode[] nodes = new ModelNode[nodeCount];
        int newNodeIndex = 0;
        for (int nodeIndex = 0; nodeIndex < nodeDatas.size(); nodeIndex++)
        {
            Deque<NodeData> stack = new ArrayDeque<NodeData>();
            Deque<Integer> parentStack = new ArrayDeque<Integer>();
            stack.push(nodeDatas.get(nodeIndex));
            parentStack.push(-1);

            while (!stack.isEmpty())
            {
                NodeData nodeData = stack.pop();
                int parentIndex = parentStack.pop();

                nodes[newNodeIndex] = new ModelNode(nodeData.id, parentIndex);

                if (nodeData.parts.size() > 0)
                {
                    ModelPart[] parts = new ModelPart[nodeData.parts.size()];
                    for (int partIndex = 0; partIndex < nodeData.parts.size(); partIndex++)
                    {
                        NodePartData pair = nodeData.parts.get(partIndex);

                        MeshGroup group = findSubMesh(meshes, pair.subMeshId);
                        Material material = findMaterial(materials, pair.materialId);

                        parts[partIndex] = new ModelPart(pair.subMeshId, group, material);

                                                // for later wiring
                                                partList.add(parts[partIndex]);
                                                partDataList.add(pair);
                    }
                    nodes[newNodeIndex].parts = parts;
                }

                                if (nodeData.scale != null)
                                        nodes[newNodeIndex].bindPose.scale.set(nodeData.scale);
                                if (nodeData.rotation != null)
                                        nodes[newNodeIndex].bindPose.rotation.set(nodeData.rotation);
                                if (nodeData.translation != null)
                                        nodes[newNodeIndex].bindPose.translation.set(nodeData.translation);

                nodes[newNodeIndex].transform.setFrom(nodes[newNodeIndex].bindPose);

                for (NodeData child : nodeData.children)
                {
                    stack.push(child);
                    parentStack.push(newNodeIndex);
                }

                newNodeIndex++;
            }
        }

        model.setNodes(nodes);

                // wire together bone influences
                for (int i = 0; i < partList.size(); i++)
                {
                        ModelPart part = partList.get(i);
                        NodePartData partData = partDataList.get(i);

                        if (partData.bones.size() == 0)
                                continue;

                        ModelNode[] influences = new ModelNode[partData.bones.size()];
                        Transform[] transforms = new Transform[partData.bones.size()];
                        for (int j = 0; j < partData.bones.size(); j++)
                        {
                                influences[j] = model.findNode(partData.bones.get(j).nodeId);
                                transforms[j] = new Transform();
                                transforms[j].translation.set(partData.bones.get(j).translation);
                                transforms[j].rotation.set(partData.bones.get(j).rotation);
                                transforms[j].scale.set(partData.bones.get(j).scale);
                        }
                        part.setBones(influences, transforms);
                }

                Animation[] animations = new Animation[anims.size()];

                for (int i = 0; i < anims.size(); i++)
                {
                        AnimationData animData = anims.get(i);
                        animations[i] = new Animation(animData.id);

                        float totalTime = 0.0f;

                        BoneKeyframes[] boneKeys = new BoneKeyframes[animData.boneKeys.size()];
                        for (int j = 0; j < animData.boneKeys.size(); j++)
                        {
                                BoneKeyframeData boneKeyData = animData.boneKeys.get(j);

                                ModelNode bone = model.findNode(boneKeyData.boneId);
                                boneKeys[j] = new BoneKeyframes(bone);

                                ArrayList<BoneKeyframes.RotationKey> rotations = new ArrayList<BoneKeyframes.RotationKey>();

                                for (KeyframeData keyframe : boneKeyData.keyframes)
                                {
                                        if (keyframe.translation != null)
                                        {

                                        }
                                        if (keyframe.rotation != null)
                                        {
                                                rotations.add(new BoneKeyframes.RotationKey(keyframe.time, new Quaternion(keyframe.rotation)));
                                        }
                                        if (keyframe.scale != null)
                                        {

                                        }

                                        if (keyframe.time > totalTime)
                                                totalTime = keyframe.time;
                                }
                                boneKeys[j].setRotationKeys(rotations.toArray(new BoneKeyframes.RotationKey[rotations.size()]));
                        }

                        animations[i].setBoneKeys(boneKeys);
                        animations[i].setTotalTime(totalTime);
                }

                model.setAnimations(animations);

        return model;
    }

        private static float[] toFloatArray(ArrayList<Float> array)
        {
                float[] rt = new float[array.size()];
                for (int k = 0; k < array.size(); k++)
                        rt[k] = array.get(k);

                return rt;
        }

    private static ArrayList<VertexStream> buildVertexStream(ArrayList<VertexAttribute> attribs, String[] attributes, float[] vertices)
    {
        int coordsPerVertex = calcVertexSize(attributes);
        int numVertices = vertices.length / coordsPerVertex;

        ArrayList<VertexStream> result = new ArrayList<VertexStream>();

        float[] coords = null;
        float[] normals = null;
        float[] texcoords = null;
        float[] blendIndices = null;
        float[] blendWeights = null;

        int offset = 0;
                int blendOffset = 0;

        for (String attr : attributes)
        {
            if (attr.equalsIgnoreCase("POSITION"))
            {
                coords = new float[numVertices*NUM_POSITION_COORDS];
                extractInterleaved(coords, 0, NUM_POSITION_COORDS, vertices, offset, coordsPerVertex, NUM_POSITION_COORDS);
                offset += NUM_POSITION_COORDS;

                result.add(createIndividualVertexStream(coords, VertexAttribute.POSITION, NUM_POSITION_COORDS, "POSITION"));
            }
            else if (attr.equalsIgnoreCase("NORMAL"))
            {
                normals = new float[numVertices*NUM_NORMAL_COORDS];
                extractInterleaved(normals, 0, NUM_NORMAL_COORDS, vertices, offset, coordsPerVertex, NUM_NORMAL_COORDS);
                offset += NUM_NORMAL_COORDS;

                result.add(createIndividualVertexStream(normals, VertexAttribute.NORMAL, NUM_NORMAL_COORDS, "NORMAL"));
            }
            else if (attr.equalsIgnoreCase("TEXCOORD0"))
            {
                texcoords = new float[numVertices*NUM_TEXCOORD_COORDS];
                extractInterleaved(texcoords, 0, NUM_TEXCOORD_COORDS, vertices, offset, coordsPerVertex, NUM_TEXCOORD_COORDS);
                offset += NUM_TEXCOORD_COORDS;

                result.add(createIndividualVertexStream(texcoords, VertexAttribute.TEXCOORD0, NUM_TEXCOORD_COORDS, "TEXCOORD0"));
            }
            else if (attr.startsWith("BLENDWEIGHT"))
            {
                if (blendIndices == null)
                    blendIndices = new float[numVertices*NUM_COMBINED_BLENDINDICES_COORDS];
                if (blendWeights == null)
                    blendWeights = new float[numVertices*NUM_COMBINED_BLENDWEIGHTS_COORDS];
                extractInterleaved(blendIndices, blendOffset, NUM_COMBINED_BLENDINDICES_COORDS, vertices, offset, coordsPerVertex, 1);
                extractInterleaved(blendWeights, blendOffset, NUM_COMBINED_BLENDWEIGHTS_COORDS, vertices, offset+1, coordsPerVertex, 1);
                                blendOffset++;
                offset += NUM_BLENDWEIGHT_COORDS;
            }
        }

        if ((blendIndices != null) && (blendWeights != null))
        {
            result.add(createIndividualVertexStream(blendIndices, VertexAttribute.BLENDINDICES, NUM_COMBINED_BLENDINDICES_COORDS, "BLENDINDICES"));
            result.add(createIndividualVertexStream(blendWeights, VertexAttribute.BLENDWEIGHTS, NUM_COMBINED_BLENDWEIGHTS_COORDS, "BLENDWEIGHTS"));
        }

        return result;
    }

    private static VertexStream createIndividualVertexStream(float[] data, int attribute, int numCoords, String name)
    {
        VertexAttribute[] vertexAttribs = new VertexAttribute[]
            {
                new VertexAttribute(attribute, numCoords, 0)
            };
        VertexStream vs = new VertexStream(VertexStream.StreamType.INDIVIDUAL, name, vertexAttribs, false);
        vs.setData(data, numCoords, numCoords * Consts.SIZEOF_FLOAT);
        vs.upload();

        return vs;
    }

    private static void extractInterleaved(float[] dest, int destOffset, int destStride, float[] src, int srcOffset, int srcStride, int count)
    {
        int targetIndex = 0;
        int sourceIndex = 0;
        while (sourceIndex < src.length)
        {
            for (int j = 0; j < count; j++)
                        {
                                dest[targetIndex + destOffset + j] = src[sourceIndex + srcOffset + j];
                        }

                        targetIndex += destStride;
            sourceIndex += srcStride;
        }
    }

    public static int calcVertexSize(String[] attribs)
    {
        int result = 0;

        for (String attr : attribs)
        {
            if (attr.equalsIgnoreCase("POSITION"))
                result += NUM_POSITION_COORDS;
            else if (attr.equalsIgnoreCase("NORMAL"))
                result += NUM_NORMAL_COORDS;
            else if (attr.equalsIgnoreCase("COLOR"))
                result += NUM_COLOR_COORDS;
            else if (attr.equalsIgnoreCase("COLORPACKED"))
                result += NUM_COLOR_PACKED_COORDS;
            else if (attr.equalsIgnoreCase("TANGENT"))
                result += NUM_TANGENT_COORDS;
            else if (attr.equalsIgnoreCase("BINORMAL"))
                result += NUM_BINORMAL_COORDS;
            else if (attr.startsWith("TEXCOORD"))
                result += NUM_TEXCOORD_COORDS;
            else if (attr.startsWith("BLENDWEIGHT"))
                result += NUM_BLENDWEIGHT_COORDS;
        }

        return result;
    }

    public static int getTotalNodeCount(ArrayList<NodeData> nodeDatas)
    {
        int nodeCount = 0;
        for (int nodeIndex = 0; nodeIndex < nodeDatas.size(); nodeIndex++)
        {
            NodeData rootNode = nodeDatas.get(nodeIndex);

            Deque<NodeData> stack = new ArrayDeque<NodeData>();
            stack.push(rootNode);
            while (!stack.isEmpty())
            {
                NodeData node = stack.pop();
                nodeCount++;

                for (int j = 0; j < node.children.size(); j++)
                {
                    stack.push(node.children.get(j));
                }
            }
        }
        return nodeCount;
    }

    public static MeshGroup findSubMesh(Mesh[] meshes, String id)
    {
        for (Mesh mesh : meshes)
        {
            for (MeshGroup group : mesh.getMeshGroups())
            {
                if (id.equalsIgnoreCase(group.getName()))
                    return group;
            }
        }
        return null;
    }

    public static Material findMaterial(Material[] materials, String id)
    {
        for (Material mat : materials)
        {
            if (id.equalsIgnoreCase(mat.getName()))
                return mat;
        }
        return null;
    }

    public static MeshData processMesh(JsonValue meshesEntry)
    {
        MeshData result = new MeshData();

        for (JsonValue entry : meshesEntry)
        {
            if (entry.name.equalsIgnoreCase("attributes"))
            {
                result.attributes = entry.asStringArray();
            }
            else if (entry.name.equalsIgnoreCase("vertices"))
            {
                result.vertices = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("parts"))
            {
                result.numIndices = 0;
                for (JsonValue partEntry : entry)
                {
                    result.parts.add(processParts(partEntry));
                    result.numIndices += result.parts.get(result.parts.size()-1).indices.length;
                }
            }
        }

        return result;
    }

    public static PartData processParts(JsonValue partsEntry)
    {
        PartData result = new PartData();

        for (JsonValue entry : partsEntry)
        {
            if (entry.name.equalsIgnoreCase("id"))
            {
                result.id = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("type"))
            {
                result.type = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("indices"))
            {
                result.indices = entry.asShortArray();
            }
        }

        return result;
    }

    public static MaterialData processMaterial(JsonValue materialsEntry)
    {
        MaterialData result = new MaterialData();

        for (JsonValue entry : materialsEntry)
        {
            if (entry.name.equalsIgnoreCase("id"))
            {
                result.id = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("textures"))
            {
                for (JsonValue textureEntry : entry)
                {
                    result.textures.add(processTexture(textureEntry));
                }
            }
            else if (entry.name.equalsIgnoreCase("ambient"))
            {
                result.ambient = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("diffuse"))
            {
                result.diffuse = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("emissive"))
            {
                result.emissive = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("opacity"))
            {
                result.opacity = entry.asFloat();
            }
            else if (entry.name.equalsIgnoreCase("specular"))
            {
                result.specular = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("shininess"))
            {
                result.shininess = entry.asFloat();
            }
        }

        return result;
    }

    public static TextureData processTexture(JsonValue textureEntry)
    {
        TextureData result = new TextureData();

        for (JsonValue entry : textureEntry)
        {
            if (entry.name.equalsIgnoreCase("id"))
            {
                result.id = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("filename"))
            {
                result.filename = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("type"))
            {
                result.type = entry.asString();
            }
        }

        return result;
    }

    public static NodeData processNode(JsonValue nodeEntry)
    {
        NodeData result = new NodeData();

        for (JsonValue entry : nodeEntry)
        {
            if (entry.name.equalsIgnoreCase("id"))
            {
                result.id = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("translation"))
            {
                result.translation = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("rotation"))
            {
                result.rotation = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("scale"))
            {
                result.scale = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("parts"))
            {
                for (JsonValue partEntry : entry)
                {
                    result.parts.add(processNodePart(partEntry));
                }
            }
            else if (entry.name.equalsIgnoreCase("children"))
            {
                for (JsonValue childEntry : entry)
                {
                    NodeData node = processNode(childEntry);
                    node.parent = result;
                    result.children.add(node);
                }
            }
        }

        return result;
    }

    public static NodePartData processNodePart(JsonValue partEntry)
    {
        NodePartData result = new NodePartData();

        for (JsonValue entry : partEntry)
        {
            if (entry.name.equalsIgnoreCase("meshpartid"))
            {
                result.subMeshId = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("materialid"))
            {
                result.materialId = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("bones"))
            {
                for (JsonValue boneEntry : entry)
                {
                    result.bones.add(processBone(boneEntry));
                }
            }
        }

        return result;
    }

    public static BoneData processBone(JsonValue boneEntry)
    {
        BoneData result = new BoneData();

        for (JsonValue entry : boneEntry)
        {
            if (entry.name.equalsIgnoreCase("node"))
            {
                result.nodeId = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("translation"))
            {
                result.translation = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("rotation"))
            {
                result.rotation = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("scale"))
            {
                result.scale = entry.asFloatArray();
            }
        }

        return result;
    }

    public static AnimationData processAnimation(JsonValue animEntry)
    {
        AnimationData result = new AnimationData();

        for (JsonValue entry : animEntry)
        {
            if (entry.name.equalsIgnoreCase("id"))
            {
                result.id = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("bones"))
            {
                for (JsonValue boneKeyEntry : entry)
                {
                    result.boneKeys.add(processBoneKeyframe(boneKeyEntry));
                }
            }
        }

        return result;
    }

    public static BoneKeyframeData processBoneKeyframe(JsonValue boneKeyEntry)
    {
        BoneKeyframeData result = new BoneKeyframeData();

        for (JsonValue entry : boneKeyEntry)
        {
            if (entry.name.equalsIgnoreCase("boneId"))
            {
                result.boneId = entry.asString();
            }
            else if (entry.name.equalsIgnoreCase("keyframes"))
            {
                for (JsonValue keyframeEntry : entry)
                {
                    result.keyframes.add(processKeyframe(keyframeEntry));
                }
            }
        }

        return result;
    }

    public static KeyframeData processKeyframe(JsonValue keyframeEntry)
    {
        KeyframeData result = new KeyframeData();

        for (JsonValue entry : keyframeEntry)
        {
            if (entry.name.equalsIgnoreCase("keytime"))
            {
                result.time = entry.asFloat();
            }
            else if (entry.name.equalsIgnoreCase("translation"))
            {
                result.translation = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("rotation"))
            {
                result.rotation = entry.asFloatArray();
            }
            else if (entry.name.equalsIgnoreCase("scale"))
            {
                result.scale = entry.asFloatArray();
            }
        }

        return result;
    }

}