Commits

Michael Ludwig committed 407e7c6

Clean up unnecessary, old components and implementation details.

  • Participants
  • Parent commits f3114ef

Comments (0)

Files changed (24)

File ferox-scene/src/main/java/com/ferox/anim/AcclaimSkeleton.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.ferox.math.Matrix3;
-import com.ferox.math.Matrix4;
-import com.ferox.math.Vector3;
-import com.ferox.math.Vector4;
-import com.lhkbob.entreri.Entity;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class AcclaimSkeleton {
-    private String name;
-    private ASFRoot root;
-    private List<ASFBone> boneData;
-    private Map<Object, List<ASFBone>> hierarchy; // key is Bone or Root instance
-
-    public String getName() {
-        return name;
-    }
-
-    public Skeleton addSkeleton(Entity entity) {
-        Skeleton skeleton = entity.add(Skeleton.class).getData();
-
-        Bone rootBone = createRootBone();
-        skeleton.addBone(rootBone);
-        skeleton.setRootBone(rootBone);
-
-        addBones(root, rootBone, skeleton);
-
-        return skeleton;
-    }
-
-    public SkeletonAnimation loadAnimation(InputStream acmFile, double frameRate) throws IOException {
-        BufferedReader in = new BufferedReader(new InputStreamReader(acmFile));
-
-        SkeletonAnimation animation = new SkeletonAnimation();
-        KeyFrame.Builder currentKeyFrame = null;
-        boolean inDegrees = true; // assume this is the default just like in ASF
-
-        String line;
-        while ((line = in.readLine()) != null) {
-            line = line.replaceAll("#.*", "").trim();
-            if (line.isEmpty()) {
-                continue;
-            }
-
-            String[] tokens = line.split("\\s+");
-
-            if (tokens[0].equalsIgnoreCase(":degrees")) {
-                inDegrees = true;
-            } else if (tokens[0].equalsIgnoreCase(":radians")) {
-                inDegrees = false;
-            } else if (tokens[0].startsWith(":")) {
-                // ignore
-            } else if (tokens.length == 1) {
-                try {
-                    int frame = Integer.parseInt(tokens[0]);
-                    if (currentKeyFrame != null && (frame - 1) % 5 == 0) {
-                        animation.addKeyFrame(currentKeyFrame.build());
-                    }
-                    currentKeyFrame = new KeyFrame.Builder(frameRate * (frame - 1));
-                } catch (NumberFormatException nfe) {
-                    throw new IOException("Expected single integer on line: " + line);
-                }
-            } else {
-                if (tokens[0].equals("root")) {
-                    double[] dofValues = new double[root.order.length];
-                    if (dofValues.length != tokens.length - 1) {
-                        throw new IOException("Incorrect number of motion values");
-                    }
-
-                    for (int i = 0; i < dofValues.length; i++) {
-                        dofValues[i] = Double.parseDouble(tokens[i + 1]);
-                        if (inDegrees && (root.order[i] == DoF.RX || root.order[i] == DoF.RY ||
-                                          root.order[i] == DoF.RZ)) {
-                            dofValues[i] = Math.toRadians(dofValues[i]);
-                        }
-                    }
-
-                    currentKeyFrame.setBone("root", buildRoot(root.order, dofValues));
-                } else {
-                    ASFBone bone = getBone(tokens[0]);
-                    if (bone == null) {
-                        throw new IOException("Bone with name: " + tokens[0] + " is undefined");
-                    }
-
-                    double[] dofValues = new double[bone.motionDoF.length];
-                    if (dofValues.length != tokens.length - 1) {
-                        throw new IOException("Incorrect number of motion values");
-                    }
-
-                    for (int i = 0; i < dofValues.length; i++) {
-                        dofValues[i] = Double.parseDouble(tokens[i + 1]);
-                        if (inDegrees && (bone.motionDoF[i] == DoF.RX || bone.motionDoF[i] == DoF.RY ||
-                                          bone.motionDoF[i] == DoF.RZ)) {
-                            dofValues[i] = Math.toRadians(dofValues[i]);
-                        }
-                    }
-
-                    Matrix4 localBasis = new Matrix4().setIdentity().setUpper(bone.childToParentBasis);
-                    Matrix4 motion = new Matrix4().setIdentity()
-                                                  .setUpper(buildBasis(bone.motionDoF, dofValues));
-                    Matrix4 translate = new Matrix4().setIdentity().setCol(3, new Vector4(
-                            bone.localDirection.x * bone.length, bone.localDirection.y * bone.length,
-                            bone.localDirection.z * bone.length, 1));
-
-                    localBasis.mul(motion).mul(translate);
-                    currentKeyFrame.setBone(bone.name, localBasis);
-                }
-            }
-        }
-
-        if (currentKeyFrame != null) {
-            animation.addKeyFrame(currentKeyFrame.build());
-        }
-        return animation;
-    }
-
-    private void addBones(Object asfParent, Bone parent, Skeleton skeleton) {
-        List<ASFBone> children = hierarchy.get(asfParent);
-        if (children != null) {
-            for (ASFBone bone : children) {
-                Bone childBone = createBone(bone);
-                skeleton.addBone(childBone);
-                skeleton.connect(parent, childBone);
-
-                addBones(bone, childBone, skeleton);
-            }
-        }
-    }
-
-    private Bone createBone(ASFBone bone) {
-        Matrix4 translate = new Matrix4().setIdentity();
-        translate.m03 = bone.localDirection.x * bone.length;
-        translate.m13 = bone.localDirection.y * bone.length;
-        translate.m23 = bone.localDirection.z * bone.length;
-
-        Matrix4 rotate = new Matrix4().setIdentity().setUpper(bone.childToParentBasis);
-        rotate.mul(translate);
-
-        Bone newBone = new Bone(bone.name);
-        newBone.setRelativeBoneTransform(rotate);
-        return newBone;
-    }
-
-    private void computeLocalSpace() {
-        List<ASFBone> rootChildren = hierarchy.get(root);
-        if (rootChildren != null) {
-            Matrix3 inverseParent;
-            if (root.axis != null) {
-                inverseParent = buildBasis(root.axis, root.presetOrientation).inverse();
-            } else {
-                inverseParent = new Matrix3().setIdentity();
-            }
-
-            for (ASFBone child : rootChildren) {
-                computeLocalSpace(child, inverseParent);
-            }
-        }
-    }
-
-    private void computeLocalSpace(ASFBone bone, Matrix3 inverseParent) {
-        Matrix3 boneGlobalBasis = buildBasis(bone.axis, bone.axisRotation);
-        bone.childToParentBasis = new Matrix3().mul(inverseParent, boneGlobalBasis);
-
-        // this now holds the inverse of the bone's global basis
-        bone.localDirection = new Vector3().mul(boneGlobalBasis.inverse(), bone.direction);
-        bone.localDirection.normalize();
-
-        List<ASFBone> children = hierarchy.get(bone);
-        if (children != null) {
-            for (ASFBone child : children) {
-                computeLocalSpace(child, boneGlobalBasis);
-            }
-        }
-    }
-
-    private Bone createRootBone() {
-        Bone rootBone = new Bone("root");
-        Matrix4 m = rootBone.getRelativeBoneTransform();
-        m.setIdentity();
-
-        if (root.axis != null) {
-            m.setUpper(buildBasis(root.axis, root.presetOrientation));
-        }
-
-        if (root.presetPosition != null) {
-            m.m03 = root.presetPosition.x;
-            m.m13 = root.presetPosition.y;
-            m.m23 = root.presetPosition.z;
-        }
-        rootBone.setRelativeBoneTransform(m);
-        return rootBone;
-    }
-
-    private Matrix3 buildBasis(DoF[] axis, Vector3 values) {
-        return buildBasis(axis, new double[] { values.x, values.y, values.z });
-    }
-
-    private Matrix4 buildRoot(DoF[] order, double[] values) {
-        //        Matrix3 temp = new Matrix3();
-        Matrix4 root = new Matrix4().setIdentity();
-
-        // FIXME this is brittle, and odd, because it expects the rotations to
-        // be transposed and reversed like in buildBasis(), but the translation
-        // must be at the end, even though the  provided order is different.
-
-        // What they give: TX TY TZ RX RY RZ
-        // So what the expected translation would be: RZ RY RX TZ TY TX
-        // What works: TX TY TZ RZ RY RX
-        //
-        // Perhaps the site is incorrect, and you just use the order values
-        // but construct it using the same axis definition for presetOrientation, etc.?
-        // seems plausible
-        root.setUpper(buildBasis(new DoF[] { order[3], order[4], order[5] },
-                                 new double[] { values[3], values[4], values[5] }));
-        root.m03 = values[0];
-        root.m13 = values[1];
-        root.m23 = values[2];
-
-        //        Matrix4 dof = new Matrix4();
-        //
-        //        // FIXME order?
-        //        for (int i = 0; i < order.length; i++) {
-        //            dof.setIdentity();
-        //            switch (order[i]) {
-        //            case RX:
-        //                temp.rotateX(values[i]);
-        //                dof.setUpper(temp);
-        //                break;
-        //            case RY:
-        //                temp.rotateY(values[i]);
-        //                dof.setUpper(temp);
-        //                break;
-        //            case RZ:
-        //                temp.rotateZ(values[i]);
-        //                dof.setUpper(temp);
-        //                break;
-        //            case TX:
-        //                dof.m03 = values[i];
-        //                break;
-        //            case TY:
-        //                dof.m13 = values[i];
-        //                break;
-        //            case TZ:
-        //                dof.m23 = values[i];
-        //                break;
-        //            default:
-        //                throw new IllegalStateException("Bad degree of freedom: " + order[i]);
-        //            }
-        //
-        //            root.mul(dof);
-        //        }
-
-        return root;
-    }
-
-    private Matrix3 buildBasis(DoF[] axis, double[] values) {
-        Matrix3 basis = new Matrix3().setIdentity();
-        Matrix3 axisAngle = new Matrix3();
-
-        for (int i = axis.length - 1; i >= 0; i--) {
-            switch (axis[i]) {
-            case RX:
-                axisAngle.rotateX(values[i]);
-                break;
-            case RY:
-                axisAngle.rotateY(values[i]);
-                break;
-            case RZ:
-                axisAngle.rotateZ(values[i]);
-                break;
-            default:
-                throw new IllegalStateException("Bad degree of freedom: " + axis[i]);
-            }
-
-            basis.mul(axisAngle);
-        }
-
-        return basis;
-    }
-
-    public void load(InputStream asf) throws IOException {
-        BufferedReader in = new BufferedReader(new InputStreamReader(asf));
-
-        // state tracking while progressively loading
-        FileSection section = null;
-
-        // when in units
-        boolean anglesInDegrees = true; // degrees is default
-
-        // when in bone data
-        ASFBone currentBone = null;
-        List<Double> minLimits = null;
-        List<Double> maxLimits = null;
-
-        // when in hierarchy
-        boolean buildingHierarchy = false;
-
-        String line;
-        while ((line = in.readLine()) != null) {
-            line = line.replaceAll("#.*", "").trim();
-            if (line.isEmpty()) {
-                continue;
-            }
-
-            String[] tokens = line.split("\\s+");
-
-            if (tokens[0].equalsIgnoreCase(":version")) {
-                if (tokens.length != 2) {
-                    throw new IOException("Malformed :version token: " + line);
-                }
-                if (!tokens[1].equals("1.10") && !tokens[1].equals("1.1")) {
-                    throw new IOException("Unsupported version: " + tokens[1]);
-                }
-                section = FileSection.VERSION;
-            } else if (tokens[0].equalsIgnoreCase(":name")) {
-                if (tokens.length != 2) {
-                    throw new IOException("Malformed :name token: " + line);
-                }
-                name = tokens[1];
-                section = FileSection.NAME;
-            } else if (tokens[0].equalsIgnoreCase(":units")) {
-                section = FileSection.UNITS;
-            } else if (tokens[0].equalsIgnoreCase(":documentation")) {
-                section = FileSection.DOCUMENTATION;
-            } else if (tokens[0].equalsIgnoreCase(":root")) {
-                root = new ASFRoot();
-                section = FileSection.ROOT;
-            } else if (tokens[0].equalsIgnoreCase(":bonedata")) {
-                boneData = new ArrayList<ASFBone>();
-                section = FileSection.BONE_DATA;
-            } else if (tokens[0].equalsIgnoreCase(":hierarchy")) {
-                section = FileSection.HIERARCHY;
-            } else if (tokens[0].equalsIgnoreCase(":skin")) {
-                section = FileSection.SKIN;
-            } else if (section == FileSection.UNITS) {
-                if (tokens[0].equalsIgnoreCase("angle")) {
-                    if (tokens.length != 2) {
-                        throw new IOException("Malformed angle token: " + line);
-                    }
-                    if (!tokens[1].equalsIgnoreCase("deg") && !tokens[1].equalsIgnoreCase("rad")) {
-                        throw new IOException("Invalid angle unit: " + tokens[1]);
-                    }
-                    anglesInDegrees = tokens[1].equalsIgnoreCase("deg");
-                }
-            } else if (section == FileSection.ROOT) {
-                if (tokens[0].equals("order")) {
-                    root.order = new DoF[tokens.length - 1];
-
-                    for (int i = 0; i < root.order.length; i++) {
-                        root.order[i] = DoF.byToken(tokens[i + 1]);
-                    }
-                } else if (tokens[0].equals("axis")) {
-                    if (tokens.length != 2 || tokens[1].length() != 3) {
-                        throw new IOException("Unexpected axis format: " + line);
-                    }
-
-                    root.axis = new DoF[3];
-                    String axisOrder = tokens[1].toLowerCase();
-                    for (int i = 0; i < 3; i++) {
-                        if (axisOrder.charAt(i) == 'x') {
-                            root.axis[i] = DoF.RX;
-                        } else if (axisOrder.charAt(i) == 'y') {
-                            root.axis[i] = DoF.RY;
-                        } else if (axisOrder.charAt(i) == 'z') {
-                            root.axis[i] = DoF.RZ;
-                        } else {
-                            throw new IOException("Unknown axis token: " + tokens[1]);
-                        }
-                    }
-                } else if (tokens[0].equals("position")) {
-                    if (tokens.length != 4) {
-                        throw new IOException("Badly formed position specifier: " + line);
-                    }
-                    root.presetPosition = new Vector3(Double.parseDouble(tokens[1]),
-                                                      Double.parseDouble(tokens[2]),
-                                                      Double.parseDouble(tokens[3]));
-                } else if (tokens[0].equals("orientation")) {
-                    if (tokens.length != 4) {
-                        throw new IOException("Badly formed orientation specifier: " + line);
-                    }
-                    root.presetOrientation = new Vector3(Double.parseDouble(tokens[1]),
-                                                         Double.parseDouble(tokens[2]),
-                                                         Double.parseDouble(tokens[3]));
-                    toRadians(root.presetOrientation, anglesInDegrees);
-                }
-            } else if (section == FileSection.BONE_DATA) {
-                if (currentBone != null) {
-                    if (tokens[0].equals("name")) {
-                        if (tokens.length != 2) {
-                            throw new IOException("Malformed name token: " + line);
-                        }
-                        currentBone.name = tokens[1];
-                    } else if (tokens[0].equals("direction")) {
-                        if (tokens.length != 4) {
-                            throw new IOException("Badly formed direction specifier: " + line);
-                        }
-                        currentBone.direction = new Vector3(Double.parseDouble(tokens[1]),
-                                                            Double.parseDouble(tokens[2]),
-                                                            Double.parseDouble(tokens[3]));
-                        currentBone.direction.normalize();
-                    } else if (tokens[0].equals("length")) {
-                        if (tokens.length != 2) {
-                            throw new IOException("Malformed length token: " + line);
-                        }
-                        currentBone.length = Double.parseDouble(tokens[1]);
-                    } else if (tokens[0].equals("axis")) {
-                        if (tokens.length != 5 || tokens[4].length() != 3) {
-                            throw new IOException("Malformed axis token: " + line);
-                        }
-
-                        currentBone.axis = new DoF[3];
-                        currentBone.axisRotation = new Vector3(Double.parseDouble(tokens[1]),
-                                                               Double.parseDouble(tokens[2]),
-                                                               Double.parseDouble(tokens[3]));
-                        toRadians(currentBone.axisRotation, anglesInDegrees);
-
-                        String axisOrder = tokens[4].toLowerCase();
-                        for (int i = 0; i < 3; i++) {
-                            if (axisOrder.charAt(i) == 'x') {
-                                currentBone.axis[i] = DoF.RX;
-                            } else if (axisOrder.charAt(i) == 'y') {
-                                currentBone.axis[i] = DoF.RY;
-                            } else if (axisOrder.charAt(i) == 'z') {
-                                currentBone.axis[i] = DoF.RZ;
-                            } else {
-                                throw new IOException("Unknown axis token: " + tokens[1]);
-                            }
-                        }
-                    } else if (tokens[0].equals("dof")) {
-                        currentBone.motionDoF = new DoF[tokens.length - 1];
-
-                        for (int i = 0; i < currentBone.motionDoF.length; i++) {
-                            currentBone.motionDoF[i] = DoF.byToken(tokens[i + 1]);
-                        }
-                    } else if (tokens[0].equals("limits")) {
-                        minLimits = new ArrayList<Double>();
-                        maxLimits = new ArrayList<Double>();
-
-                        String min, max;
-                        if (tokens.length == 3) {
-                            // tokens are ['limits', '(number', 'number)']
-                            min = tokens[1].substring(1);
-                            max = tokens[2].substring(0, tokens[2].length() - 1);
-                        } else if (tokens.length == 2) {
-                            // tokens are ['limits', '(number,number)']
-                            String[] split = tokens[1].split(",");
-                            min = split[0].substring(1);
-                            max = split[1].substring(0, split[1].length() - 1);
-                        } else {
-                            throw new IOException("Malformed limits token: " + line);
-                        }
-
-                        parseLimits(min, max, minLimits, maxLimits);
-                    } else if (tokens[0].startsWith("(")) {
-                        if (minLimits == null) {
-                            throw new IOException("Unexpected limit specifier: " + line);
-                        }
-
-                        String min, max;
-                        if (tokens.length == 2) {
-                            // tokens are ['(number', 'number)']
-                            min = tokens[0].substring(1);
-                            max = tokens[1].substring(0, tokens[1].length() - 1);
-                        } else if (tokens.length == 1) {
-                            // tokens are ['(number,number)']
-                            String[] split = tokens[0].split(",");
-                            min = split[0].substring(1);
-                            max = split[1].substring(0, split[1].length() - 1);
-                        } else {
-                            throw new IOException("Malformed limits token: " + line);
-                        }
-
-                        parseLimits(min, max, minLimits, maxLimits);
-                    } else if (tokens[0].equals("end")) {
-                        // process accumulated limits text
-                        if (currentBone.motionDoF != null) {
-                            currentBone.minLimits = new double[currentBone.motionDoF.length];
-                            currentBone.maxLimits = new double[currentBone.motionDoF.length];
-
-                            if (minLimits != null) {
-                                if (minLimits.size() != currentBone.motionDoF.length) {
-                                    throw new IOException(
-                                            "Bone does not have same limit count as degrees of freedom");
-                                }
-
-                                for (int i = 0; i < currentBone.motionDoF.length; i++) {
-                                    if (anglesInDegrees && currentBone.motionDoF[i].isAngle()) {
-                                        currentBone.minLimits[i] = Math.toRadians(minLimits.get(i));
-                                        currentBone.maxLimits[i] = Math.toRadians(maxLimits.get(i));
-                                    } else {
-                                        currentBone.minLimits[i] = minLimits.get(i);
-                                        currentBone.maxLimits[i] = maxLimits.get(i);
-                                    }
-                                }
-                            } else {
-                                // fill in infinite limits
-                                for (int i = 0; i < currentBone.motionDoF.length; i++) {
-                                    currentBone.minLimits[i] = Double.NEGATIVE_INFINITY;
-                                    currentBone.maxLimits[i] = Double.POSITIVE_INFINITY;
-                                }
-                            }
-                        }
-
-                        boneData.add(currentBone);
-                        currentBone = null;
-                    }
-                } else {
-                    if (tokens[0].equals("begin")) {
-                        currentBone = new ASFBone();
-                        minLimits = null;
-                        maxLimits = null;
-                    }
-                }
-            } else if (section == FileSection.HIERARCHY) {
-                if (!buildingHierarchy) {
-                    if (tokens[0].equals("begin")) {
-                        hierarchy = new HashMap<Object, List<ASFBone>>();
-                        buildingHierarchy = true;
-                    }
-                } else {
-                    if (tokens[0].equals("end")) {
-                        // process the completed hierarchy to compute local
-                        // transforms for each bone
-                        computeLocalSpace();
-                        buildingHierarchy = false;
-                    } else {
-                        // assume tokens represent hierarchy
-                        if (tokens.length < 2) {
-                            throw new IOException("Bad hierarchy definition: " + line);
-                        }
-
-                        Object parent = (tokens[0].equals("root") ? root : getBone(tokens[0]));
-                        if (parent == null) {
-                            throw new IOException("Parent bone does not exist: " + tokens[0]);
-                        }
-                        List<ASFBone> children = new ArrayList<ASFBone>();
-                        for (int i = 1; i < tokens.length; i++) {
-                            ASFBone child = getBone(tokens[i]);
-                            if (child == null) {
-                                throw new IOException("Child bone does not exist: " + tokens[i]);
-                            }
-                            children.add(child);
-                        }
-
-                        hierarchy.put(parent, children);
-                    }
-                }
-            }
-        }
-    }
-
-    private ASFBone getBone(String name) {
-        for (ASFBone b : boneData) {
-            if (b.name.equals(name)) {
-                return b;
-            }
-        }
-        return null;
-    }
-
-    private void toRadians(Vector3 angles, boolean inDegrees) {
-        if (inDegrees) {
-            angles.x = Math.toRadians(angles.x);
-            angles.y = Math.toRadians(angles.y);
-            angles.z = Math.toRadians(angles.z);
-        }
-        // else already in radians
-    }
-
-    private void parseLimits(String min, String max, List<Double> minLimits, List<Double> maxLimits) {
-        if (min.equals("-inf")) {
-            minLimits.add(Double.NEGATIVE_INFINITY);
-        } else if (min.equals("inf")) {
-            minLimits.add(Double.POSITIVE_INFINITY);
-        } else {
-            minLimits.add(Double.parseDouble(min));
-        }
-
-        if (max.equals("-inf")) {
-            maxLimits.add(Double.NEGATIVE_INFINITY);
-        } else if (max.equals("inf")) {
-            maxLimits.add(Double.POSITIVE_INFINITY);
-        } else {
-            maxLimits.add(Double.parseDouble(max));
-        }
-    }
-
-    private static enum FileSection {
-        VERSION,
-        NAME,
-        UNITS,
-        DOCUMENTATION,
-        ROOT,
-        BONE_DATA,
-        HIERARCHY,
-        SKIN
-    }
-
-    private static enum DoF {
-        TX(false),
-        TY(false),
-        TZ(false),
-        RX(true),
-        RY(true),
-        RZ(true),
-        L(false);
-
-        private final boolean angle;
-
-        private DoF(boolean angle) {
-            this.angle = angle;
-        }
-
-        public static DoF byToken(String token) throws IOException {
-            for (DoF d : values()) {
-                if (d.name().equalsIgnoreCase(token)) {
-                    return d;
-                }
-            }
-            throw new IOException("Unsupported/unknown DoF token: " + token);
-        }
-
-        public boolean isAngle() {
-            return angle;
-        }
-    }
-
-    private static class ASFRoot {
-        private DoF[] order; // will not contain L
-        private DoF[] axis; // will only contain RX, RY, and RZ
-        private Vector3 presetPosition;
-        private Vector3 presetOrientation;
-    }
-
-    private static class ASFBone {
-        private String name;
-        private Vector3 direction; // FIXME I think this might be in global as well
-        private double length;
-
-        private Vector3 axisRotation; // rotations about global axis
-        private DoF[] axis; // axis order in above vector
-
-        private Matrix3 childToParentBasis;
-        private Vector3 localDirection; // direction converted to bone's coordinate frame
-
-        private DoF[] motionDoF; // DoF's present in AMC files, won't be TX, TY, or TZ
-        private double[] minLimits;
-        private double[] maxLimits;
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/Animated.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.property.BooleanProperty;
-import com.lhkbob.entreri.property.BooleanProperty.DefaultBoolean;
-import com.lhkbob.entreri.property.DoubleProperty;
-import com.lhkbob.entreri.property.DoubleProperty.DefaultDouble;
-import com.lhkbob.entreri.property.ObjectProperty;
-
-public class Animated extends ComponentData<Animated> {
-    private ObjectProperty<SkeletonAnimation> animation;
-
-    @DefaultDouble(0.0)
-    private DoubleProperty playTime;
-
-    @DefaultDouble(1.0)
-    private DoubleProperty timeScale;
-
-    @DefaultBoolean(true)
-    private BooleanProperty loop;
-
-    private Animated() {
-    }
-
-    public Animated setLoopPlayback(boolean loop) {
-        this.loop.set(loop, getIndex());
-        return this;
-    }
-
-    public boolean getLoopPlayback() {
-        return loop.get(getIndex());
-    }
-
-    public Animated setTimeScale(double scale) {
-        timeScale.set(scale, getIndex());
-        return this;
-    }
-
-    public double getTimeScale() {
-        return timeScale.get(getIndex());
-    }
-
-    public Animated setCurrentTime(double time) {
-        playTime.set(time, getIndex());
-        return this;
-    }
-
-    public double getCurrentTime() {
-        return playTime.get(getIndex());
-    }
-
-    public Animated setAnimation(SkeletonAnimation animation) {
-        this.animation.set(animation, getIndex());
-        return this;
-    }
-
-    public SkeletonAnimation getAnimation() {
-        return animation.get(getIndex());
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/Bone.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.ferox.math.Const;
-import com.ferox.math.Matrix4;
-
-public class Bone {
-    private final Matrix4 relativeBoneTransform;
-    // FIXME should this actually be global? or just relative to the entity's transform
-    // keep it global for now, we can make skinning work around that (on GPU or CPU),
-    // and it works a lot better with setting bones to be physics objects
-    private final Matrix4 globalBoneTransform;
-    private final String name;
-
-    public Bone(String name) {
-        this.name = name;
-        relativeBoneTransform = new Matrix4().setIdentity();
-        globalBoneTransform = new Matrix4();
-    }
-
-    @Const
-    public Matrix4 getRelativeBoneTransform() {
-        return relativeBoneTransform;
-    }
-
-    public void setRelativeBoneTransform(@Const Matrix4 relativeBoneTransform) {
-        this.relativeBoneTransform.set(relativeBoneTransform);
-    }
-
-    @Const
-    public Matrix4 getGlobalBoneTransform() {
-        return globalBoneTransform;
-    }
-
-    public void setGlobalBoneTransform(@Const Matrix4 globalBoneTransform) {
-        this.globalBoneTransform.set(globalBoneTransform);
-    }
-
-    public String getName() {
-        return name;
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/BoneLink.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.ferox.scene.Transform;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.Requires;
-import com.lhkbob.entreri.property.ObjectProperty;
-
-@Requires(Transform.class)
-public class BoneLink extends ComponentData<BoneLink> {
-    private ObjectProperty<Bone> linkedBone;
-
-    private BoneLink() {
-    }
-
-    public Bone getLinkedBone() {
-        return linkedBone.get(getIndex());
-    }
-
-    public BoneLink setLinkedBone(Bone bone) {
-        linkedBone.set(bone, getIndex());
-        return this;
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/BoneLinkTask.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.ferox.scene.Transform;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-public class BoneLinkTask implements Task, ParallelAware {
-    private static final Set<Class<? extends ComponentData<?>>> COMPONENTS;
-
-    static {
-        Set<Class<? extends ComponentData<?>>> set = new HashSet<Class<? extends ComponentData<?>>>();
-        set.add(BoneLink.class);
-        set.add(Transform.class);
-        COMPONENTS = Collections.unmodifiableSet(set);
-    }
-
-    private BoneLink boneLink;
-    private Transform transform;
-
-    private ComponentIterator iterator;
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        while (iterator.next()) {
-            transform.setMatrix(boneLink.getLinkedBone().getGlobalBoneTransform());
-        }
-
-        return null;
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        if (boneLink == null) {
-            boneLink = system.createDataInstance(BoneLink.class);
-            transform = system.createDataInstance(Transform.class);
-
-            iterator = new ComponentIterator(system);
-            iterator.addRequired(boneLink);
-            iterator.addRequired(transform);
-        }
-
-        iterator.reset();
-    }
-
-    @Override
-    public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-        return COMPONENTS;
-    }
-
-    @Override
-    public boolean isEntitySetModified() {
-        return false;
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/BoneTransformTask.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.ferox.math.Matrix4;
-import com.ferox.scene.Transform;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class BoneTransformTask implements Task, ParallelAware {
-    private static final Set<Class<? extends ComponentData<?>>> COMPONENTS;
-
-    static {
-        Set<Class<? extends ComponentData<?>>> set = new HashSet<Class<? extends ComponentData<?>>>();
-        set.add(Skeleton.class);
-        set.add(Transform.class);
-        COMPONENTS = Collections.unmodifiableSet(set);
-    }
-
-    private Skeleton skeleton;
-    private Transform transform;
-
-    private ComponentIterator iterator;
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        Matrix4 m = new Matrix4();
-        while (iterator.next()) {
-            Bone root = skeleton.getRootBone();
-            m.mul(transform.getMatrix(), root.getRelativeBoneTransform());
-            root.setGlobalBoneTransform(m);
-
-            updateChildren(root, m);
-        }
-
-        return null;
-    }
-
-    private void updateChildren(Bone parent, Matrix4 store) {
-        List<Bone> children = skeleton.getChildren(parent);
-
-        if (children != null) {
-            Bone child;
-            for (int i = 0; i < children.size(); i++) {
-                child = children.get(i);
-                store.mul(parent.getGlobalBoneTransform(), child.getRelativeBoneTransform());
-                child.setGlobalBoneTransform(store);
-
-                updateChildren(child, store);
-            }
-        }
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        if (skeleton == null) {
-            skeleton = system.createDataInstance(Skeleton.class);
-            transform = system.createDataInstance(Transform.class);
-
-            iterator = new ComponentIterator(system);
-            iterator.addRequired(skeleton);
-            iterator.addRequired(transform);
-        }
-
-        iterator.reset();
-    }
-
-    @Override
-    public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-        return COMPONENTS;
-    }
-
-    @Override
-    public boolean isEntitySetModified() {
-        return false;
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/KeyFrame.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.ferox.math.Const;
-import com.ferox.math.Matrix4;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-public class KeyFrame {
-    private final double keyTime;
-    private final Map<String, Matrix4> boneTransforms;
-
-    private KeyFrame(double time, Map<String, Matrix4> transforms) {
-        keyTime = time;
-        boneTransforms = Collections.unmodifiableMap(transforms);
-    }
-
-    public double getFrameTime() {
-        return keyTime;
-    }
-
-    @Const
-    public Matrix4 getBoneTransform(String name) {
-        return boneTransforms.get(name);
-    }
-
-    public Map<String, Matrix4> getBoneTransforms() {
-        return boneTransforms;
-    }
-
-    public static Builder newKeyFrame(double keyTime) {
-        return new Builder(keyTime);
-    }
-
-    public static class Builder {
-        private final double keyTime;
-        private final Map<String, Matrix4> boneTransforms;
-
-        public Builder(double keyTime) {
-            this.keyTime = keyTime;
-            boneTransforms = new HashMap<String, Matrix4>();
-        }
-
-        public Builder setBone(String name, @Const Matrix4 transform) {
-            boneTransforms.put(name, new Matrix4(transform));
-            return this;
-        }
-
-        public KeyFrame build() {
-            return new KeyFrame(keyTime, new HashMap<String, Matrix4>(boneTransforms));
-        }
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/Skeleton.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.ferox.scene.Transform;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.Requires;
-import com.lhkbob.entreri.property.ObjectProperty;
-
-import java.util.*;
-
-@Requires(Transform.class)
-public class Skeleton extends ComponentData<Skeleton> {
-    // FIXME for templating to work correctly, this will need a custom
-    // factory that clones properly
-    private ObjectProperty<Map<String, Bone>> bones;
-    private ObjectProperty<Map<Bone, List<Bone>>> parentToChildHierarchy;
-    private ObjectProperty<Map<Bone, Bone>> childToParentHierarchy;
-
-    private ObjectProperty<Bone> root;
-
-    private Skeleton() {
-    }
-
-    public Skeleton addBone(Bone bone) {
-        Bone old = getBoneMap().put(bone.getName(), bone);
-        if (old != null) {
-            removeBone(old);
-        }
-        return this;
-    }
-
-    public Collection<Bone> getBones() {
-        return getBoneMap().values();
-    }
-
-    public Bone getRootBone() {
-        return root.get(getIndex());
-    }
-
-    public Skeleton setRootBone(Bone bone) {
-        if (getBoneMap().get(bone.getName()) != bone) {
-            throw new IllegalArgumentException("Bone is not in skeleton");
-        }
-        root.set(bone, getIndex());
-        return this;
-    }
-
-    public List<Bone> getChildren(Bone bone) {
-        // FIXME read-only
-        return getParentToChildMap().get(bone);
-    }
-
-    public Skeleton removeBone(Bone bone) {
-        Map<String, Bone> boneMap = getBoneMap();
-        Map<Bone, Bone> childToParent = getChildToParentMap();
-        Map<Bone, List<Bone>> parentToChild = getParentToChildMap();
-
-        // perform check to make sure we're not removing a bone
-        // instance with the same name that is replacing this bone
-        if (boneMap.get(bone.getName()) == bone) {
-            boneMap.remove(bone.getName());
-        }
-
-        // remove bone from child-to-parent map
-
-        Bone parent = childToParent.remove(bone);
-        if (parent != null) {
-            // remove child from its parent's list of child bones
-            List<Bone> neighbors = parentToChild.get(parent);
-            neighbors.remove(bone);
-        }
-
-        // remove all of bone's children from the child-to-parent map,
-        // since they no longer have a parent
-        List<Bone> children = parentToChild.remove(bone);
-        if (children != null) {
-            for (int i = 0; i < children.size(); i++) {
-                childToParent.remove(children.get(i));
-            }
-        }
-
-        return this;
-    }
-
-    public Skeleton connect(Bone parent, Bone child) {
-        Map<String, Bone> boneMap = getBoneMap();
-        Map<Bone, Bone> childToParent = getChildToParentMap();
-        Map<Bone, List<Bone>> parentToChild = getParentToChildMap();
-
-        if (boneMap.get(parent.getName()) != parent) {
-            throw new IllegalArgumentException("Parent bone is not in this skeleton");
-        }
-        if (boneMap.get(child.getName()) != child) {
-            throw new IllegalArgumentException("Child bone is not in this skeleton");
-        }
-
-        // disconnect child from its current parent
-        Bone oldParent = childToParent.remove(child);
-        if (oldParent != null) {
-            parentToChild.get(oldParent).remove(child);
-        }
-
-        // attach child to the new parent bone
-        childToParent.put(child, parent);
-        List<Bone> children = parentToChild.get(parent);
-        if (children == null) {
-            children = new ArrayList<Bone>();
-            parentToChild.put(parent, children);
-        }
-        children.add(child);
-
-        return this;
-    }
-
-    public Bone getBone(String name) {
-        return getBoneMap().get(name);
-    }
-
-    private Map<String, Bone> getBoneMap() {
-        Map<String, Bone> boneMap = bones.get(getIndex());
-        if (boneMap == null) {
-            boneMap = new HashMap<String, Bone>();
-            bones.set(boneMap, getIndex());
-        }
-
-        return boneMap;
-    }
-
-    private Map<Bone, Bone> getChildToParentMap() {
-        Map<Bone, Bone> boneMap = childToParentHierarchy.get(getIndex());
-        if (boneMap == null) {
-            boneMap = new HashMap<Bone, Bone>();
-            childToParentHierarchy.set(boneMap, getIndex());
-        }
-
-        return boneMap;
-    }
-
-    private Map<Bone, List<Bone>> getParentToChildMap() {
-        Map<Bone, List<Bone>> boneMap = parentToChildHierarchy.get(getIndex());
-        if (boneMap == null) {
-            boneMap = new HashMap<Bone, List<Bone>>();
-            parentToChildHierarchy.set(boneMap, getIndex());
-        }
-
-        return boneMap;
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/SkeletonAnimation.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.ferox.math.Matrix3;
-import com.ferox.math.Matrix4;
-import com.ferox.math.Quat4;
-import com.ferox.math.Vector3;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-public class SkeletonAnimation {
-    private final List<KeyFrame> frames;
-    private boolean sortDirty;
-
-    public SkeletonAnimation() {
-        frames = new ArrayList<KeyFrame>();
-    }
-
-    public double getAnimationLength() {
-        return (frames.isEmpty() ? 0 : frames.get(frames.size() - 1).getFrameTime());
-    }
-
-    public void addKeyFrame(KeyFrame frame) {
-        frames.add(frame);
-        sortDirty = true;
-    }
-
-    public void removeKeyFrame(KeyFrame frame) {
-        frames.remove(frame);
-    }
-
-    public List<KeyFrame> getKeyFrames() {
-        return Collections.unmodifiableList(frames);
-    }
-
-    public void updateSkeleton(Skeleton skeleton, double animationTime) {
-        if (sortDirty) {
-            Collections.sort(frames, new Comparator<KeyFrame>() {
-                @Override
-                public int compare(KeyFrame o1, KeyFrame o2) {
-                    return Double.compare(o1.getFrameTime(), o2.getFrameTime());
-                }
-            });
-            sortDirty = false;
-        }
-
-        int startFrame = getStartFrame(animationTime);
-
-        Matrix3 upper = new Matrix3();
-        Quat4 aRot = new Quat4();
-        Quat4 bRot = new Quat4();
-        Vector3 aPos = new Vector3();
-        Vector3 bPos = new Vector3();
-
-        for (Bone b : skeleton.getBones()) {
-            int boneStartTarget = getStartFrame(startFrame, b.getName());
-            int boneEndTarget = getEndFrame(startFrame, b.getName());
-
-            double alpha = 0;
-            if (boneStartTarget != boneEndTarget) {
-                alpha = (animationTime - frames.get(boneStartTarget).getFrameTime()) /
-                        (frames.get(boneEndTarget).getFrameTime() -
-                         frames.get(boneStartTarget).getFrameTime());
-            }
-
-            Matrix4 aMat = frames.get(boneStartTarget).getBoneTransform(b.getName());
-            Matrix4 bMat = frames.get(boneEndTarget).getBoneTransform(b.getName());
-
-            if (aMat == null || bMat == null) {
-                continue;
-            }
-
-            // rotation
-            aRot.set(upper.setUpper(aMat));
-            bRot.set(upper.setUpper(bMat));
-
-            aRot.slerp(aRot, bRot, alpha);
-            upper.set(aRot);
-
-            // position
-            aPos.set(aMat.m03, aMat.m13, aMat.m23);
-            bPos.set(bMat.m03, bMat.m13, bMat.m23);
-
-            aPos.scale(1 - alpha).add(bPos.scale(alpha));
-
-            Matrix4 m = b.getRelativeBoneTransform();
-            m.setUpper(upper);
-            m.m03 = aPos.x;
-            m.m13 = aPos.y;
-            m.m23 = aPos.z;
-            b.setRelativeBoneTransform(m);
-        }
-    }
-
-    private int getStartFrame(double time) {
-        // FIXME do a binary search
-        for (int i = 0; i < frames.size() - 1; i++) {
-            if (frames.get(i).getFrameTime() <= time && frames.get(i + 1).getFrameTime() > time) {
-                return i;
-            }
-        }
-        return frames.size() - 1;
-    }
-
-    private int getStartFrame(int bestStartFrame, String bone) {
-        for (int i = bestStartFrame; i > 0; i--) {
-            if (frames.get(i).getBoneTransform(bone) != null) {
-                return i;
-            }
-        }
-
-        return 0;
-    }
-
-    private int getEndFrame(int bestStartFrame, String bone) {
-        for (int i = bestStartFrame + 1; i < frames.size(); i++) {
-            if (frames.get(i).getBoneTransform(bone) != null) {
-                return i;
-            }
-        }
-
-        return frames.size() - 1;
-    }
-}

File ferox-scene/src/main/java/com/ferox/anim/SkeletonAnimationTask.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.anim;
-
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.ElapsedTimeResult;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-public class SkeletonAnimationTask implements Task, ParallelAware {
-    private static final Set<Class<? extends ComponentData<?>>> COMPONENTS;
-
-    static {
-        Set<Class<? extends ComponentData<?>>> set = new HashSet<Class<? extends ComponentData<?>>>();
-        set.add(Skeleton.class);
-        set.add(Animated.class);
-        COMPONENTS = Collections.unmodifiableSet(set);
-    }
-
-    private Skeleton skeleton;
-    private Animated animated;
-    private ComponentIterator iterator;
-
-    private double dt;
-
-    public void report(ElapsedTimeResult r) {
-        dt = r.getTimeDelta();
-    }
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        while (iterator.next()) {
-            SkeletonAnimation anim = animated.getAnimation();
-            double newTime = animated.getCurrentTime() + dt * animated.getTimeScale();
-            if (newTime > anim.getAnimationLength()) {
-                if (animated.getLoopPlayback()) {
-                    newTime = newTime - anim.getAnimationLength();
-                } else {
-                    newTime = anim.getAnimationLength();
-                }
-            }
-
-            animated.setCurrentTime(newTime);
-            anim.updateSkeleton(skeleton, newTime);
-        }
-
-        return null;
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        if (skeleton == null) {
-            skeleton = system.createDataInstance(Skeleton.class);
-            animated = system.createDataInstance(Animated.class);
-
-            iterator = new ComponentIterator(system);
-            iterator.addRequired(skeleton);
-            iterator.addRequired(animated);
-        }
-
-        iterator.reset();
-    }
-
-    @Override
-    public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-        return COMPONENTS;
-    }
-
-    @Override
-    public boolean isEntitySetModified() {
-        return false;
-    }
-}

File ferox-scene/src/main/java/com/ferox/scene/AbstractPlacedLight.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.scene;
-
-import com.lhkbob.entreri.Requires;
-import com.lhkbob.entreri.property.DoubleProperty;
-import com.lhkbob.entreri.property.DoubleProperty.DefaultDouble;
-
-/**
- * <p/>
- * An intermediate light type that is shared by {@link PointLight} and {@link SpotLight}. Although it is
- * common to represent a point and spot light as the same object (such as by using a cutoff angle of 180
- * degrees to represent a point light), it was decided to have them be separate components because they are
- * often treated fundamentally different in rendering engines.
- * <p/>
- * Placed lights should be combined with a {@link Transform} component to control the position (and possibly
- * direction). A placed light without a transform defaults to the default transform.
- *
- * @param <T> The component light type
- *
- * @author Michael Ludwig
- */
-@Requires(Transform.class)
-public abstract class AbstractPlacedLight<T extends AbstractPlacedLight<T>> extends Light<T> {
-    @DefaultDouble(-1.0)
-    private DoubleProperty falloffDistance;
-
-    protected AbstractPlacedLight() {
-    }
-
-    /**
-     * Set the distance to where the light's energy has fallen to zero and no longer contributes to the
-     * lighting of a scene. If this is negative, the light has no energy falloff and all lit objects will be
-     * lit with the same energy. When enabled, a light's energy falls off acoording to an inverse square law
-     * based on the distance to an object.
-     *
-     * @param distance The new falloff distance
-     *
-     * @return This light for chaining purposes
-     */
-    @SuppressWarnings("unchecked")
-    public final T setFalloffDistance(double distance) {
-        // No argument checking, a negative distance disables
-        // light falloff so every value is supported
-        falloffDistance.set(distance, getIndex());
-        updateVersion();
-        return (T) this;
-    }
-
-    /**
-     * Return the distance to where the light's energy has fallen off to zero and no longer contributes to
-     * lighting. If this is negative, then the light has no energy falloff. When enabled, a light's energy
-     * falls off according to an inverse square law based on the distance to an object
-     *
-     * @return The falloff distance
-     */
-    public final double getFalloffDistance() {
-        return falloffDistance.get(getIndex());
-    }
-}

File ferox-scene/src/main/java/com/ferox/scene/BlinnPhongMaterial.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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 com.ferox.scene;
-
-import com.lhkbob.entreri.property.DoubleProperty;
-import com.lhkbob.entreri.property.DoubleProperty.DefaultDouble;
-
-/**
- * <p/>
- * The BlinnPhongMaterial is a Component that specifies that the renderable Entity should be rendered using a
- * Blinn-Phong lighting model. This is the lighting model used by OpenGL's fixed-function pipeline, although
- * that is per-vertex lighting. If possible, renderers should use per-pixel Phong shading to achieve better
- * rendering quality.
- * <p/>
- * <p/>
- * The Blinn-Phong model supports diffuse, specular and emitted light colors. This Component does not specify
- * values for these, but is expected to be combined with {@link DiffuseColor}, {@link DiffuseColorMap}, {@link
- * SpecularColor}, {@link SpecularColorMap} and the like. It does provide a shininess exponent that describes
- * the shape of specular highlights on objects. This separation was done so that other lighting models can be
- * added but still enable the use of the color providing components.
- *
- * @author Michael Ludwig
- */
-public final class BlinnPhongMaterial extends Material<BlinnPhongMaterial> {
-    @DefaultDouble(1.0)
-    private DoubleProperty shininess;
-
-    private BlinnPhongMaterial() {
-    }
-
-    /**
-     * Set the shininess exponent to use with this material. The shininess exponent controls how sharp or
-     * shiny the specular highlights of an object are. A high value implies a shinier surface with brighter,
-     * sharper highlights. The minimum value is 0, and although there is no explicit maximum, the OpenGL
-     * fixed-function pipeline imposes a maximum of 128 that this might get clamped to when rendering.
-     *
-     * @param shiny The new shininess exponent
-     *
-     * @return This component
-     *
-     * @throws IllegalArgumentException if shiny is less than 0
-     */
-    public BlinnPhongMaterial setShininess(double shiny) {
-        if (shiny < 0f) {
-            throw new IllegalArgumentException("Shininess must be positive, not: " + shiny);
-        }
-        shininess.set(shiny, getIndex());
-        updateVersion();
-        return this;
-    }
-
-    /**
-     * Return the shininess exponent of the Blinn-Phong material. A higher value means a very shiny surface
-     * with bright, small specular highlights. Lower values have fainter and more diffuse specular
-     * highlights.
-     *
-     * @return The shininess exponent, will be at least 0
-     */
-    public double getShininess() {
-        return shininess.get(getIndex());
-    }
-}

File ferox-scene/src/main/java/com/ferox/scene/ColorComponent.java

-/*
- * Ferox, a graphics and game library in Java
- *
- * Copyright (c) 2012, Michael Ludwig