/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. ******************************************************************************/ package com.badlogic.gdx.graphics.g3d.model.skeleton; import java.util.HashMap; import java.util.Map; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.ObjectMap; public class Skeleton { /** each joint is a root joint in the hierachy **/ public final Array<SkeletonJoint> hierarchy = new Array<SkeletonJoint>(); /** the names of each joint in breadth first order **/ public final Array<String> jointNames = new Array<String>(); /** names to indices **/ public final Map<String, Integer> namesToIndices = new HashMap<String, Integer>(); /** the bind pose joints in breadth first order **/ public final Array<SkeletonKeyframe> bindPoseJoints = new Array<SkeletonKeyframe>(); /** the joints in breadth first order for the last calculates animation pose **/ public final Array<SkeletonKeyframe> animPoseJoints = new Array<SkeletonKeyframe>(); /** the offset matrices for each joint in the same order as the bindPoseJoints **/ public final Array<Matrix4> offsetMatrices = new Array<Matrix4>(); /** the scene matrices for each joint in the same order as bindPoseJoints **/ public final Array<Matrix4> sceneMatrices = new Array<Matrix4>(); /** combined scene and offset matrices **/ public final Array<Matrix4> combinedMatrices = new Array<Matrix4>(); /** map of animations, indexed by name **/ public final ObjectMap<String, SkeletonAnimation> animations = new ObjectMap<String, SkeletonAnimation>(); private static final Matrix4 IDENTITY = new Matrix4(); private final Matrix4 rotMatrix = new Matrix4(); /** * Fills the baseJoints, offsetMatrices and sceneMatrices Array instances with joints and Matrix4 instances in an * breadth first order. This allows one to iterate over the joint arrays instead of recursing over the hierarchy * when calculating the scene matrices. */ public void buildFromHierarchy() { jointNames.clear(); namesToIndices.clear(); bindPoseJoints.clear(); animPoseJoints.clear(); offsetMatrices.clear(); sceneMatrices.clear(); for (int i = 0; i < hierarchy.size; i++) { recursiveFill(hierarchy.get(i)); } calculateMatrices(bindPoseJoints); calculateOffsetMatrices(); } private void recursiveFill(SkeletonJoint joint) { joint.index = bindPoseJoints.size; joint.parentIndex = joint.parent != null ? joint.parent.index : -1; SkeletonKeyframe keyFrame = new SkeletonKeyframe(); keyFrame.position.set(joint.position); keyFrame.scale.set(joint.scale); keyFrame.rotation.set(joint.rotation); keyFrame.parentIndex = joint.parentIndex; jointNames.add(joint.name); namesToIndices.put(joint.name, joint.index); bindPoseJoints.add(keyFrame); SkeletonKeyframe animKeyframe = new SkeletonKeyframe(); animKeyframe.parentIndex = joint.parentIndex; animPoseJoints.add(animKeyframe); offsetMatrices.add(new Matrix4()); sceneMatrices.add(new Matrix4()); combinedMatrices.add(new Matrix4()); int len = joint.children.size; for (int i = 0; i < len; i++) { recursiveFill(joint.children.get(i)); } } protected void calculateOffsetMatrices() { for (int i = 0; i < offsetMatrices.size; i++) { offsetMatrices.get(i).set(sceneMatrices.get(i)).inv(); } } protected void calculateMatrices(Array<SkeletonKeyframe> joints) { for (int i = 0; i < joints.size; i++) { SkeletonKeyframe joint = joints.get(i); Matrix4 sceneMatrix = sceneMatrices.get(i); Matrix4 parentMatrix = joint.parentIndex != -1 ? sceneMatrices.get(joint.parentIndex) : IDENTITY; Matrix4 combinedMatrix = combinedMatrices.get(i); joint.rotation.toMatrix(rotMatrix.val); rotMatrix.trn(joint.position); rotMatrix.scl(joint.scale); sceneMatrix.set(parentMatrix); sceneMatrix.mul(rotMatrix); combinedMatrix.set(sceneMatrix); combinedMatrix.mul(offsetMatrices.get(i)); } } public void setAnimation(String name, float time) { SkeletonAnimation anim = animations.get(name); if (anim == null) throw new IllegalArgumentException("Animation with name '" + name + "' does not exist"); if (time < 0 || time > anim.totalDuration) throw new IllegalArgumentException("time must be 0 <= time <= animation duration"); int len = anim.perJointkeyFrames.length; for (int i = 0; i < len; i++) { SkeletonKeyframe[] jointTrack = anim.perJointkeyFrames[i]; int idx = 0; int len2 = jointTrack.length; for (int j = 0; j < len2; j++) { SkeletonKeyframe jointFrame = jointTrack[j]; if (jointFrame.timeStamp >= time) { idx = Math.max(0, j - 1); break; } } SkeletonKeyframe startFrame = jointTrack[idx]; SkeletonKeyframe endFrame = idx + 1 == len2 ? startFrame : jointTrack[idx + 1]; float alpha = 0; if (startFrame != endFrame) { alpha = Math.min(1, (time - startFrame.timeStamp) / (endFrame.timeStamp - startFrame.timeStamp)); } SkeletonKeyframe animFrame = animPoseJoints.get(i); animFrame.position.set(startFrame.position).lerp(endFrame.position, alpha); animFrame.scale.set(startFrame.scale).lerp(endFrame.scale, alpha); animFrame.rotation.set(startFrame.rotation).slerp(endFrame.rotation, alpha); } calculateMatrices(animPoseJoints); } public void setBindPose() { calculateMatrices(bindPoseJoints); } }