/* * JaamSim Discrete Event Simulation * Copyright (C) 2013 Ausenco Engineering Canada Inc. * * 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.jaamsim.render; import java.util.ArrayList; import com.jaamsim.math.Mat4d; /** * A basic data holder for armature information * @author matt.chudleigh * */ public class Armature { public static class Bone { private String name; private Mat4d mat; private Mat4d invMat; private Bone parent; private double length; private int index; private final ArrayList<Bone> children = new ArrayList<>(); public Bone(String name, Mat4d mat, Bone parent, double length, int index) { this.name = name; this.mat = new Mat4d(mat); this.parent = parent; this.length = length; this.invMat = mat.inverse(); this.index = index; } public String getName() { return name; } /** * Returns the bone space to model space matrix * @return */ public Mat4d getMatrix() { return mat; } public Mat4d getInvMatrix() { return invMat; } public Bone getParent() { return parent; } public ArrayList<Bone> getChildren() { return children; } public double getLength() { return length; } public int getIndex() { return index; } } private final ArrayList<Bone> bones = new ArrayList<>(); private final ArrayList<Action> actions = new ArrayList<>(); public void addAction(Action act) { actions.add(act); } public void addBone(String boneName, Mat4d matrix, String parentName, double length) { Bone parent = null; if (parentName != null) { parent = getBoneByName(parentName); if (parent == null) { throw new RenderException(String.format("Could not find parent bone: %s", parentName)); } } if (getBoneByName(boneName) != null) { throw new RenderException(String.format("Multiple bones of the same name: %s", boneName)); } Bone b = new Bone(boneName, matrix, parent, length, bones.size()); bones.add(b); if (parent != null) { parent.children.add(b); } } public Bone getBoneByName(String name) { for (Bone b : bones) { if (b.name.equals(name)) { return b; } } return null; } public ArrayList<Bone> getRootBones() { ArrayList<Bone> ret = new ArrayList<>(); for (Bone b : bones) { if (b.parent == null) { ret.add(b); } } return ret; } public ArrayList<Bone> getAllBones() { return bones; } public ArrayList<Action> getActions() { return actions; } private Action getActionByName(String name) { for (Action a : actions) { if (a.name.equals(name)) return a; } return null; } public int getBoneIndex(String name) { for (int i = 0; i < bones.size(); ++i) { if (bones.get(i).name.equals(name)) return i; } return -1; } /** * Returns a 'pose' (a list of matrices for known bones) based on the actions for this skeleton * @param actions * @return */ public ArrayList<Mat4d> getPose(ArrayList<Action.Queue> actions) { if (actions == null) { actions = new ArrayList<>(); } ArrayList<Mat4d> poseTransforms = new ArrayList<>(bones.size()); for (int i = 0; i < bones.size(); ++i) poseTransforms.add(new Mat4d()); for (Action.Queue aq : actions) { Action a = getActionByName(aq.name); if (a == null) { continue; } for (Action.Channel ch : a.channels) { int boneInd = getBoneIndex(ch.name); assert(boneInd != -1); Mat4d mat = Action.getChannelMatAtTime(ch, aq.time); poseTransforms.get(boneInd).mult4(mat); } } ArrayList<Mat4d> ret = new ArrayList<>(bones.size()); // We have the interpolated transform per bone, now build up a list of model space transforms per bone for (int i = 0; i < bones.size(); ++i) { Bone b = bones.get(i); Mat4d mat = new Mat4d(b.mat); mat.mult4(poseTransforms.get(i)); mat.mult4(b.invMat); if (b.parent != null) { // Need to include the parent of this bone int parentBoneInd = b.parent.getIndex(); mat.mult4(ret.get(parentBoneInd), mat); } ret.add(mat); } return ret; } }