package mocap.figure; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.media.j3d.Node; import javax.media.j3d.SceneGraphPath; import javax.media.j3d.Switch; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import j3d.Util; /** * Bone representation tailored to Acclaim format. Transform groups in this order (first is base): * * 1) base translation 2) translation (only used for root) 2) base rotation (joint geom attached * here) 3) current rotation 4) inverse base rotation (bone geom attached here) 3) final * translation, using direction + length from ASF definition (the translation could also be * performed at the root of the child node -> then you could omit the first base translation) * * Note: Euler rotations performed with fixed axes. * * @author Michael Kipp */ public class Bone implements java.io.Serializable { public static final int FIXED_AXES = 0, MOVING_AXES = 1; public static final int RX = 0, RY = 1, RZ = 2; // for specifying rotation order public static final int TX = 3, TY = 4, TZ = 5; public static final int NO_NAME = -1, SMALL_NAME = 0, BIG_NAME = 1; private String _name; private int _index; private Bone _parent; private Bone[] _children = new Bone[0]; transient BoneGeom _boneGeom; // the displayed geometry of the bone/joint transient JointGeom _jointGeom; transient private Switch _textSwitch; transient TransformGroup _baseTransTG ,_transTG ,_baseRotTG ,_rotTG ,_invBaseRotTG ; transient private Transform3D _t1 = new Transform3D(); transient private Transform3D _t2 = new Transform3D(); transient private Transform3D _transTF = new Transform3D(); transient private Transform3D _tworld = new Transform3D(); transient private Vector3d _trans = new Vector3d(); public int[] _dof = new int[0]; // rotation order public int _numDOF = 0; private boolean _translationEnabled = true; // translation can be switched off private boolean _rotationEnabled = true; // rotation can be switched off public int _rotationType = FIXED_AXES; // Euler rotation type public double _scale = 1d; // scale is applied to all translational components private Vector3d _geomDirection; private double _geomRadius; public Bone() { //Init(); } void Init() { if (_baseTransTG != null) return; // _baseTransTG = new TransformGroup(); // _baseTransTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); // _baseTransTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); _baseRotTG = new TransformGroup(); _baseRotTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); _baseRotTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); // _baseRotTG.setCapability(TransformGroup.ALLOW_CHILDREN_READ); _baseRotTG.setCapability(TransformGroup.ALLOW_LOCAL_TO_VWORLD_READ); _rotTG = new TransformGroup(); _rotTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); _invBaseRotTG = new TransformGroup(); _invBaseRotTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); // wire up TG's if (false) // _parent == null) { _transTG = new TransformGroup(); _transTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); _baseTransTG = _transTG; // .addChild(_transTG); _transTG.addChild(_baseRotTG); } else _baseTransTG = _baseRotTG; // .addChild(_baseRotTG); _baseRotTG.addChild(_rotTG); _rotTG.addChild(_invBaseRotTG); _baseTransTG.setUserData(_name); } public Bone(String name, int index, int[] dof) { this(); _name = name; _index = index; _dof = dof; _numDOF = _dof.length; } public void scale(double factor) { _scale = factor; _baseTransTG.getTransform(_t1); _t1.get(_trans); _trans.scale(factor); _t1.set(_trans); _baseTransTG.setTransform(_t1); for (Bone b : _children) { b.scale(factor); } // _boneGeom.detach(); // _jointGeom.detach(); } public double getScale() { return _scale; } public void getWorldPosition(Point3d p) { p.set(0, 0, 0); Util.getFullTransform(_baseRotTG, _tworld); // getWorldTransform(_tworld); _tworld.transform(p); } /** * Ignores rotations and assumes that coord system is only translated from joint to joint. * * @param ref * @param p */ public void getRelativeTransPosition(Bone ref, Point3d p) { getWorldPosition(p); Point3d p2 = new Point3d(); ref.getWorldPosition(p2); p.sub(p2); } /** * Returns position of this bone in frame of reference of given bone. * * Note that rotations in the kinematic chain can render this information useless. * * @param ref * @param p */ public void getRelativePosition(Bone ref, Point3d p) { p.set(0, 0, 0); Util.getRelativeTransform(_baseRotTG, ref.getBaseRotTG(), _t1); _t1.transform(p); // Transform3D t1 = new Transform3D(); // Transform3D t2 = new Transform3D(); // Transform3D t3 = new Transform3D(); // getWorldTransform(t1); // ref.getWorldTransform(t2); // t2.invert(); // t3.mul(t2, t1); //// t1.mul(t1, t2); // t3.transform(p); } // public void getWorldTransform(Transform3D tf) { // _rotTG.getLocalToVworld(tf); // } public void getPosition(SceneGraphPath path, Point3d p) { p.set(0, 0, 0); _baseRotTG.getLocalToVworld(path, _tworld); _tworld.transform(p); } /** * Determines whether to rotate in fixed or moving axes manner (Euler rotation). * * @param type * Either FIXED_AXES or MOVING_AXES. */ public void setRotationType(int type) { _rotationType = type; } public void setSelected(boolean val) { _boneGeom.setSelected(val); } public String getDOFString() { String res = ""; for (int i = 0; i < _dof.length; i++) { switch (_dof[i]) { case RX: res += "RX "; break; case RY: res += "RY "; break; case RZ: res += "RZ "; break; case TX: res += "TX "; break; case TY: res += "TY "; break; case TZ: res += "TZ "; break; } } return res; } /** * Sets bone and children to zero rotation. */ public void reset() { _t1.setIdentity(); _rotTG.setTransform(_t1); for (Bone b : _children) { b.reset(); } } /** * Switches on/off translation. */ public void setTranslationEnabled(boolean val) { _translationEnabled = val; } /** * Switches on/off orientation/rotation. */ public void setRotationEnabled(boolean val) { _rotationEnabled = val; } public double getLength() { return _boneGeom != null ? _boneGeom.getLength() : 0d; } public void selectGeom(int style) { if (_boneGeom != null) { _boneGeom.select(style); } for (int i = 0; i < _children.length; i++) { _children[i].selectGeom(style); } } public void selectJointGeom(int style) { selectJointGeom(style, true); } public void selectJointGeom(int style, boolean recursiv) { if (_jointGeom != null) { _jointGeom.select(style); } if (recursiv) { for (int i = 0; i < _children.length; i++) { _children[i].selectJointGeom(style); } } } public double getMaxDistance() { // System.out.println(">>>>>> Branch >>>>>"); double dDistance = 0; if (_children.length > 0) { Point3d p3dCurrentBone = new Point3d(); getWorldPosition(p3dCurrentBone); double[] arrayDistances = new double[_children.length]; int i = 0; for (Bone bone : _children) { double dTmp = 0; if (bone.getChildren().length == 0) { // System.out.println(">>>>>> leaf >>>>>\n\n"); Point3d p3dChild = new Point3d(); bone.getWorldPosition(p3dChild); dTmp = p3dChild.distanceSquared(p3dCurrentBone); // System.out.println("Distance to leaf (" + _name + ", " // + bone.getName() + ") : " + dTmp); // } else { Point3d p3dChild = new Point3d(); bone.getWorldPosition(p3dChild); dTmp = p3dChild.distanceSquared(p3dCurrentBone); // System.out.println("Distance to node (" + _name + ", " // + bone.getName() + ") : " + dTmp); // double dRest = bone.getMaxDistance(); // System.out.println("Distance of rest (" + _name + ", " // + bone.getName() + ") :: " + dRest); dTmp += bone.getMaxDistance(); ; // System.out.println("Distance of node (" + _name + ", " // + bone.getName() + ") + rest:: " + dTmp +"\n\n"); } arrayDistances[i++] = dTmp; } for (double distance : arrayDistances) { dDistance = Math.max(distance, dDistance); } } return dDistance; } public Bone getParent() { return _parent; } public void setParent(Bone parent) { _parent = parent; } public Bone[] getChildren() { return _children; } public String getName() { return _name; } public void setName(String name) { _name = name; } private List collectScenegraphPath(Bone initialBone, boolean all) { // System.out.println(">> collect " + getName() + " initial=" + initialBone); List result = new ArrayList(); if (!this.equals(initialBone)) { result.addAll(_parent.collectScenegraphPath(initialBone, true)); result.add(_baseTransTG); result.add(_transTG); } if (all) { result.add(_baseRotTG); result.add(_rotTG); result.add(_invBaseRotTG); } return result; } public SceneGraphPath getSceneGraphPath(Bone initialBone) { List path = collectScenegraphPath(initialBone, false); Node[] p = new Node[path.size()]; p = (Node[]) path.toArray(p); SceneGraphPath sgp = new SceneGraphPath(); sgp.setNodes(p); return sgp; } public void setChildren(Bone[] children) { _children = children; for (int i = 0; i < children.length; i++) { Bone bone = children[i]; getEndTG().addChild(bone.getBaseTG()); } } /** * This does not work for some reason. */ public void setRot(float x, float y, float z) { System.out.println("setRot " + x + " " + " " + y + " " + z); _t1.setIdentity(); _t2.setIdentity(); for (int i = 0; i < _numDOF; i++) { switch (_dof[i]) { case RX: _t2.rotX(x); break; case RY: _t2.rotY(y); break; case RZ: _t2.rotZ(z); break; } // multiply rotations // fixed axes: multiply from left if (_rotationType == FIXED_AXES) { if (i > 0) { _t2.mul(_t1); } _t1 = new Transform3D(_t2); } else { // moving axes: multiply from right _t1.mul(_t2); _t2.setIdentity(); } // set rotation if (_rotationEnabled) { _rotTG.setTransform(_t1); } } } public void setFrame(int frame, float[] data) { setPose(frame, data, null); } /** * Sets this bone/joint to the translation/rotation of the given frame. * Called in the animation loop. * * @param frame * Frame number */ public void setPose(int frame, float[] data, Point3d offsetTrans) { // System.out.println("setPose " + frame); // _lastFrame = frame; // do something if joint has at least one DOF if (_numDOF > 0) { int offset = frame * _numDOF; if (offset + _numDOF - 1 < data.length) { boolean hasTranslation = false; if (_parent == null) { _trans.set(0, 0, 0); } _t1.setIdentity(); // go through DOF in the specified order for (int i = 0; i < _numDOF; i++) { boolean isRotation = false; switch (_dof[i]) { case RX: _t2.rotX(data[offset + i]); isRotation = true; break; case RY: _t2.rotY(data[offset + i]); isRotation = true; break; case RZ: _t2.rotZ(data[offset + i]); isRotation = true; break; case TX: _trans.x += _scale * data[offset + i]; hasTranslation = true; isRotation = false; break; case TY: _trans.y += _scale * data[offset + i]; hasTranslation = true; isRotation = false; break; case TZ: _trans.z += _scale * data[offset + i]; hasTranslation = true; isRotation = false; break; } // multiply rotations if (isRotation) { // fixed axes: multiply from left if (_rotationType == FIXED_AXES) { if (i > 0) { _t2.mul(_t1); } _t1 = new Transform3D(_t2); } else { // moving axes: multiply from right _t1.mul(_t2); _t2.setIdentity(); } } } // set rotation if (_rotationEnabled) { _rotTG.setTransform(_t1); } // add offset to translation of root if (offsetTrans != null && _parent == null) { _trans.add(offsetTrans); hasTranslation = true; } // set translation // (currently only possible for root bone! see mocapdata.com data) if (_parent == null) { if (hasTranslation && _translationEnabled) { _transTF.setIdentity(); _transTF.setTranslation(_trans); _transTG.setTransform(_transTF); } } } } } public int getIndex() { return _index; } public int[] getDOF() { return _dof; } /** * Attaches bone geometry (e.g. line) which has orientation and length of the given vector, also * attaches joint geometry to the beginning of this bone. */ public void attachGeom(Vector3d direction, double maxRadius, boolean joint) { _geomDirection = new Vector3d(direction); _geomRadius = maxRadius; _boneGeom = new BoneGeom(_invBaseRotTG, direction); // double maxlength = _parent == null ? getLength() : Math.min( // getLength(), _parent.getLength()); // _jointGeom = new JointGeom(_baseRotTG, (float) Math.min(maxRadius, // maxlength / 4)); if (joint) _jointGeom = new JointGeom(_baseRotTG, (float)(BoneGeom.CYLINDER_RADIUS*direction.length())); } public Bone findBone(String name) { if (_name.equals(name)) { return this; } for (int i = 0; i < _children.length; i++) { Bone bone = _children[i].findBone(name); if (bone != null) { return bone; } } return null; } public void collectBones(HashMap map) { map.put(_name, this); for (int i = 0; i < _children.length; i++) { Bone bone = _children[i]; bone.collectBones(map); } } public void collectBones(List list) { list.add(this); for (int i = 0; i < _children.length; i++) { Bone bone = _children[i]; bone.collectBones(list); } } public void setBaseTranslation(Vector3d vec) { Transform3D tf = new Transform3D(); tf.setTranslation(vec); getBaseTG(). //_baseTransTG. setTransform(tf); } public TransformGroup getBaseTransTG() { Init(); return _baseTransTG; } public TransformGroup getBaseTG() { Init(); return _baseTransTG; // ? } public TransformGroup getBaseRotTG() { Init(); return _baseRotTG; } public TransformGroup getEndTG() { Init(); return _rotTG; // _invBaseRotTG; } public Vector3d getUpVector() { Transform3D xform = new Transform3D(); _baseTransTG.getLocalToVworld(xform); /// transform the northward vector from world to local space Vector3d v3dUpVector = new Vector3d(0, 1, 0); // xform.invert(); xform.transform(v3dUpVector); System.out.println("UpVector: " + v3dUpVector); Util.getFullTransform(_baseRotTG, _tworld); // getWorldTransform(_tworld); return v3dUpVector; } /** * Sets the base rotation of the joint (specified as ":axis" in ASF). Note that this operation * is reversed in _invBaseRotTG so that the position of the child limb is not affected by the * base rotation. */ public void setBaseRotDeg(Vector3d eulerDegrees) { _t1.rotX(Math.toRadians(eulerDegrees.x)); _t2.rotY(Math.toRadians(eulerDegrees.y)); _t2.mul(_t1); Transform3D baseRot = new Transform3D(); baseRot.rotZ(Math.toRadians(eulerDegrees.z)); baseRot.mul(_t2); _baseRotTG.setTransform(baseRot); Transform3D invBaseRot = new Transform3D(baseRot); invBaseRot.invert(); _invBaseRotTG.setTransform(invBaseRot); } /** * Select display style of bone's name. */ public void displayName(int type) { if (_textSwitch != null) { _textSwitch.setWhichChild(type == NO_NAME ? Switch.CHILD_NONE : type); } for (int i = 0; i < _children.length; i++) { _children[i].displayName(type); } } /** * Adds geometry to the joints. */ // public void init() { // _jointGeom = new JointGeom(_baseRotTG); // for (Bone bone : _children) { // bone.init(); // } // } public String toString() { return ""; } public void print() { System.out.println(" bone: " + _name + " dof=" + _numDOF + " -> " + (_parent == null ? "VOID" : _parent.getName())); for (int i = 0; i < _children.length; i++) { Bone bone = _children[i]; bone.print(); } } }