Commit 54ed9e49 authored by Isaak Lim's avatar Isaak Lim

When a joint is translated via the SkeletonEditing Plugin, its parent joint...

When a joint is translated via the SkeletonEditing Plugin, its parent joint and itself are rotated according to the translation, such that their coordinate systems stay constant w.r.t the bone axis.

This rotation is done on the reference pose as well as the animation pose. 

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@13086 383ad7c9-94d9-4d36-a494-682f7c89f535
parent e6145320
......@@ -2,75 +2,105 @@
#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <ObjectTypes/Skeleton/Helper/SkeletonTransform.hh>
#include <ACG/Geometry/Algorithms.hh>
//------------------------------------------------------------------------------
/** \brief Set Descriptions for Scripting Slots
*
*/
void SkeletonEditingPlugin::setDescriptions(){
emit setSlotDescription("splitBone(int,int)",tr("insert a joint in the middle of a bone."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of tail joint")).split(","));
emit setSlotDescription("addJoint(int,int,Vector)",tr("add a joint to the skeleton."),
QString(tr("objectId,jointId,Vector")).split(","),
QString(tr("ID of an object,ID of parent joint,Position for the new joint")).split(","));
emit setSlotDescription("deleteJoint(int,int)",tr("delete a joint from the skeleton."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("transformJoint(int,int,Matrix4x4)",tr("transform a joint with a matrix."),
QString(tr("objectId,jointId,Matrix")).split(","),
QString(tr("ID of an object,ID of a joint,transformation matrix")).split(","));
emit setSlotDescription("globalMatrix(int,int)",tr("get the global matrix of a joint in the active pose."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("localMatrix(int,int)",tr("get the local matrix of a joint in the active pose."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("globalTranslation(int,int)",tr("get the global translation of a joint in the active pose."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("localTranslation(int,int)",tr("get the local translation of a joint in the active pose."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("animationCount(int)",tr("get the number of animations the skeleton has."),
QString(tr("objectId")).split(","),
QString(tr("ID of an object")).split(","));
emit setSlotDescription("frameCount(int,int)",tr("get the number of frames a given animation has."),
QString(tr("objectId,animationIndex")).split(","),
QString(tr("ID of an object,Index of an animation")).split(","));
emit setSlotDescription("activeAnimation(int)",tr("get the animation which is currently active."),
QString(tr("objectId")).split(","),
QString(tr("ID of an object")).split(","));
emit setSlotDescription("activeFrame(int)",tr("get the frame which is currently active"),
QString(tr("objectId")).split(","),
QString(tr("ID of an object")).split(","));
emit setSlotDescription("setActivePose(int,int,int)",tr("set the active pose of the skeleton."),
QString(tr("objectId,animationIndex,frame")).split(","),
QString(tr("ID of an object,Index of an animation,Index of a frame")).split(","));
emit setSlotDescription("addAnimation(int,QString,int)",tr("add an animation to the skeleton."),
QString(tr("objectId,AnimationName,frameCount")).split(","),
QString(tr("ID of an object,name for the animation,number of frames the animation should have")).split(","));
void SkeletonEditingPlugin::setDescriptions() {
emit setSlotDescription("splitBone(int,int)",
tr("insert a joint in the middle of a bone."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of tail joint")).split(","));
emit
setSlotDescription(
"addJoint(int,int,Vector)",
tr("add a joint to the skeleton."),
QString(tr("objectId,jointId,Vector")).split(","),
QString(
tr(
"ID of an object,ID of parent joint,Position for the new joint")).split(
","));
emit setSlotDescription("deleteJoint(int,int)",
tr("delete a joint from the skeleton."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription(
"transformJoint(int,int,Matrix4x4)",
tr("transform a joint with a matrix."),
QString(tr("objectId,jointId,Matrix")).split(","),
QString(tr("ID of an object,ID of a joint,transformation matrix")).split(
","));
emit setSlotDescription("globalMatrix(int,int)",
tr("get the global matrix of a joint in the active pose."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("localMatrix(int,int)",
tr("get the local matrix of a joint in the active pose."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("globalTranslation(int,int)",
tr("get the global translation of a joint in the active pose."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("localTranslation(int,int)",
tr("get the local translation of a joint in the active pose."),
QString(tr("objectId,jointId")).split(","),
QString(tr("ID of an object,ID of a joint")).split(","));
emit setSlotDescription("animationCount(int)",
tr("get the number of animations the skeleton has."),
QString(tr("objectId")).split(","),
QString(tr("ID of an object")).split(","));
emit setSlotDescription("frameCount(int,int)",
tr("get the number of frames a given animation has."),
QString(tr("objectId,animationIndex")).split(","),
QString(tr("ID of an object,Index of an animation")).split(","));
emit setSlotDescription("activeAnimation(int)",
tr("get the animation which is currently active."),
QString(tr("objectId")).split(","),
QString(tr("ID of an object")).split(","));
emit setSlotDescription("activeFrame(int)",
tr("get the frame which is currently active"),
QString(tr("objectId")).split(","),
QString(tr("ID of an object")).split(","));
emit
setSlotDescription(
"setActivePose(int,int,int)",
tr("set the active pose of the skeleton."),
QString(tr("objectId,animationIndex,frame")).split(","),
QString(tr("ID of an object,Index of an animation,Index of a frame")).split(
","));
emit
setSlotDescription(
"addAnimation(int,QString,int)",
tr("add an animation to the skeleton."),
QString(tr("objectId,AnimationName,frameCount")).split(","),
QString(
tr(
"ID of an object,name for the animation,number of frames the animation should have")).split(
","));
}
//------------------------------------------------------------------------------
/// insert a joint in the middle of a bone given by its (unique) tailJoint
void SkeletonEditingPlugin::splitBone( int _objectId, int _tailJoint){
void SkeletonEditingPlugin::splitBone(int _objectId, int _tailJoint) {
BaseObjectData* baseObject = 0;
PluginFunctions::getObject(_objectId, baseObject);
......@@ -78,51 +108,62 @@ void SkeletonEditingPlugin::splitBone( int _objectId, int _tailJoint){
if (baseObject == 0)
return;
Skeleton* skeleton = PluginFunctions::skeleton( baseObject );
Skeleton* skeleton = PluginFunctions::skeleton(baseObject);
if (skeleton == 0)
return;
Skeleton::Joint* tailJoint = skeleton->joint( _tailJoint );
Skeleton::Joint* tailJoint = skeleton->joint(_tailJoint);
if (tailJoint == 0){
emit log(LOGERR, tr("Cannot split bone. Unable to find joint with id ") + QString::number(_tailJoint) );
if (tailJoint == 0) {
emit log(
LOGERR,
tr("Cannot split bone. Unable to find joint with id ")
+ QString::number(_tailJoint));
return;
}
Skeleton::Joint* headJoint = tailJoint->parent();
//add the new joint
Skeleton::Joint* jointNew = new Skeleton::Joint( headJoint );
Skeleton::Joint* jointNew = new Skeleton::Joint(headJoint);
skeleton->addJoint(headJoint, jointNew);
tailJoint->setParent( jointNew, *skeleton );
tailJoint->setParent(jointNew, *skeleton);
//set position in refPose
Skeleton::Pose* refPose = skeleton->referencePose();
refPose->setGlobalTranslation(jointNew->id(), 0.5 * refPose->globalTranslation(headJoint->id()) + 0.5 * refPose->globalTranslation(tailJoint->id()) );
refPose->setGlobalTranslation(
jointNew->id(),
0.5 * refPose->globalTranslation(headJoint->id()) + 0.5
* refPose->globalTranslation(tailJoint->id()));
//set position in animations
for (unsigned int a=0; a < skeleton->animationCount(); a++)
if ( AnimationHandle(a, 0 ).isValid() ){
for (unsigned int a = 0; a < skeleton->animationCount(); a++)
if (AnimationHandle(a, 0).isValid()) {
AnimationT<ACG::Vec3d> *animation = skeleton->animation( AnimationHandle(a, 0 ) );
AnimationT<ACG::Vec3d> *animation = skeleton->animation(
AnimationHandle(a, 0));
if ( animation != 0){
if (animation != 0) {
//set initial joint translation
for (int iFrame=0; iFrame < (int)animation->frameCount(); iFrame++) {
for (int iFrame = 0; iFrame < (int) animation->frameCount(); iFrame++) {
PoseT<ACG::Vec3d>* pose = skeleton->pose( AnimationHandle(a, iFrame ) );
PoseT<ACG::Vec3d>* pose = skeleton->pose(AnimationHandle(a, iFrame));
pose->setGlobalMatrix(jointNew->id(), pose->globalMatrix(headJoint->id()) );
pose->setGlobalTranslation(jointNew->id(), 0.5 * pose->globalTranslation(headJoint->id()) + 0.5 * pose->globalTranslation(tailJoint->id()) );
pose->setGlobalMatrix(jointNew->id(),
pose->globalMatrix(headJoint->id()));
pose->setGlobalTranslation(
jointNew->id(),
0.5 * pose->globalTranslation(headJoint->id()) + 0.5
* pose->globalTranslation(tailJoint->id()));
}
}
}
emit updatedObject(_objectId, UPDATE_GEOMETRY);
emit scriptInfo("splitBone( ObjectId, " + QString::number(_tailJoint) + " )" );
emit scriptInfo("splitBone( ObjectId, " + QString::number(_tailJoint) + " )");
// Create backup
emit createBackup(_objectId, "Split Bone", UPDATE_TOPOLOGY);
......@@ -131,7 +172,8 @@ void SkeletonEditingPlugin::splitBone( int _objectId, int _tailJoint){
//------------------------------------------------------------------------------
/// add joint to the skeleton
void SkeletonEditingPlugin::addJoint( int _objectId , int _parent, Vector _position ){
void SkeletonEditingPlugin::addJoint(int _objectId, int _parent,
Vector _position) {
BaseObjectData* baseObject = 0;
PluginFunctions::getObject(_objectId, baseObject);
......@@ -139,30 +181,35 @@ void SkeletonEditingPlugin::addJoint( int _objectId , int _parent, Vector _posit
if (baseObject == 0)
return;
Skeleton* skeleton = PluginFunctions::skeleton( baseObject );
Skeleton* skeleton = PluginFunctions::skeleton(baseObject);
if (skeleton == 0)
return;
Skeleton::Joint* parent = skeleton->joint( _parent );
Skeleton::Joint* parent = skeleton->joint(_parent);
if (parent == 0){
emit log(LOGERR, tr("Cannot add joint. Unable to find joint with id ") + QString::number(_parent) );
if (parent == 0) {
emit log(
LOGERR,
tr("Cannot add joint. Unable to find joint with id ")
+ QString::number(_parent));
return;
}
//add the new joint
Skeleton::Joint* jointNew = new Skeleton::Joint( parent );
Skeleton::Joint* jointNew = new Skeleton::Joint(parent);
skeleton->addJoint(parent, jointNew);
//set the position
setJointPosition(PluginFunctions::skeletonObject(baseObject), jointNew, _position);
setJointPosition(PluginFunctions::skeletonObject(baseObject), jointNew,
_position);
emit updatedObject(_objectId, UPDATE_ALL);
emit scriptInfo("addJoint( ObjectId, " + QString::number(_parent) + ", Vector(" + QString::number(_position[0]) + ","
+ QString::number(_position[1]) + ","
+ QString::number(_position[2]) + ") )" );
emit scriptInfo(
"addJoint( ObjectId, " + QString::number(_parent) + ", Vector("
+ QString::number(_position[0]) + "," + QString::number(_position[1])
+ "," + QString::number(_position[2]) + ") )");
// Create backup
emit createBackup(_objectId, "Add Joint", UPDATE_TOPOLOGY);
......@@ -171,7 +218,7 @@ void SkeletonEditingPlugin::addJoint( int _objectId , int _parent, Vector _posit
//------------------------------------------------------------------------------
/// delete joint from the skeleton
void SkeletonEditingPlugin::deleteJoint( int _objectId , int _jointId ){
void SkeletonEditingPlugin::deleteJoint(int _objectId, int _jointId) {
BaseObjectData* baseObject = 0;
PluginFunctions::getObject(_objectId, baseObject);
......@@ -179,59 +226,63 @@ void SkeletonEditingPlugin::deleteJoint( int _objectId , int _jointId ){
if (baseObject == 0)
return;
Skeleton* skeleton = PluginFunctions::skeleton( baseObject );
Skeleton* skeleton = PluginFunctions::skeleton(baseObject);
if (skeleton == 0)
return;
Skeleton::Joint* joint = skeleton->joint( _jointId );
Skeleton::Joint* joint = skeleton->joint(_jointId);
if (joint == 0){
emit log(LOGERR, tr("Cannot Remove joint. Unable to find joint with id ") + QString::number(_jointId) );
if (joint == 0) {
emit log(
LOGERR,
tr("Cannot Remove joint. Unable to find joint with id ")
+ QString::number(_jointId));
return;
}
skeleton->removeJoint( joint );
skeleton->removeJoint(joint);
emit scriptInfo("deleteJoint( ObjectId, " + QString::number(_jointId) + " )" );
emit scriptInfo("deleteJoint( ObjectId, " + QString::number(_jointId) + " )");
// Create backup
emit createBackup(_objectId, "Delete Joint", UPDATE_TOPOLOGY);
emit updatedObject(_objectId, UPDATE_ALL);
}
//------------------------------------------------------------------------------
/// transform selected joint with given matrix
void SkeletonEditingPlugin::transformJoint( int _objectId , int _jointId, Matrix4x4 _matrix ){
void SkeletonEditingPlugin::transformJoint(int _objectId, int _jointId,
Matrix4x4 _matrix) {
BaseObjectData* obj = 0;
PluginFunctions::getObject(_objectId, obj);
if (obj == 0){
if (obj == 0) {
emit log(LOGERR, tr("Unable to get object"));
return;
}
SkeletonObject* skeletonObj = PluginFunctions::skeletonObject(obj);
if (skeletonObj == 0){
if (skeletonObj == 0) {
emit log(LOGERR, tr("Unable to get skeletonObject"));
return;
}
Skeleton* skeleton = PluginFunctions::skeleton(obj);
if (skeleton == 0){
if (skeleton == 0) {
emit log(LOGERR, tr("Unable to get skeleton"));
return;
}
Skeleton::Joint* joint = skeleton->joint( _jointId );
Skeleton::Joint* joint = skeleton->joint(_jointId);
if (joint == 0){
if (joint == 0) {
emit log(LOGERR, tr("Unable to get joint"));
return;
}
......@@ -242,11 +293,12 @@ void SkeletonEditingPlugin::transformJoint( int _objectId , int _jointId, Matrix
Skeleton::Pose* activePose;
if ( !handle.isValid() ){
if (!handle.isValid()) { // reference pose
activePose = skeleton->referencePose();
}else{
} else { // animation pose
activePose = skeleton->animation( handle.animationIndex() )->pose( handle.frame() );
activePose = skeleton->animation(handle.animationIndex())->pose(
handle.frame());
//always transform children otherwise only the local coordsys is rotated
recursiveJointTransformation = true;
......@@ -254,19 +306,20 @@ void SkeletonEditingPlugin::transformJoint( int _objectId , int _jointId, Matrix
// translation changes the skeleton structure
// this is only allowed in refPose therefore delete translation
Matrix4x4 mat = _matrix;
mat(0,3) = 0.0;
mat(1,3) = 0.0;
mat(2,3) = 0.0;
if ( mat.is_identity() )
mat(0, 3) = 0.0;
mat(1, 3) = 0.0;
mat(2, 3) = 0.0;
if (mat.is_identity())
_matrix = mat;
}
SkeletonTransform transformer(*skeleton);
if ( handle.isValid() ){
if (handle.isValid()) { // animation pose
//we are in an animation pose -> only rotation allowed
transformer.rotateJoint(joint, activePose, _matrix, transformAllFrames_);
//update the skin
bool exists = false;
......@@ -275,52 +328,214 @@ void SkeletonEditingPlugin::transformJoint( int _objectId , int _jointId, Matrix
if (exists)
RPC::callFunction("skeletalanimation", "updateSkin");
}else
//we are in the refPose apply full transformation
transformer.transformJoint(joint, _matrix, !recursiveJointTransformation);
} else { // reference pose
// full transformation
if (_matrix(0, 0) != 1 || _matrix(1, 1) != 1 || _matrix(2, 2) != 1
|| _matrix(0, 1) != 0 || _matrix(0, 2) != 0 || _matrix(1, 0) != 0
|| _matrix(1, 2) != 0 || _matrix(2, 0) != 0 || _matrix(2, 1) != 0) {
// apply full transformation
transformer.transformJoint(joint, _matrix, !recursiveJointTransformation);
} else { // translation only
if (_matrix(0, 3) == 0 && _matrix(1, 3) == 0 && _matrix(2, 3) == 0) // no translation
return;
if (!joint->parent())
return;
// init params
bool parentIsNotBranch = (joint->parent()->size() == 1);
bool hasOneChild = (joint->size() == 1);
ACG::Vec3d oldParentAxis(0.0, 0.0, 0.0);
ACG::Vec3d oldJointAxis(0.0, 0.0, 0.0);
ACG::Vec3d transParentAxis(0.0, 0.0, 0.0);
ACG::Vec3d transJointAxis(0.0, 0.0, 0.0);
ACG::Vec3d parentRotAxis(0.0, 0.0, 0.0);
ACG::Vec3d jointRotAxis(0.0, 0.0, 0.0);
double parentRotAngle = 0.0;
double jointRotAngle = 0.0;
// get the original parent axis: parent joint -----> current joint
oldParentAxis = activePose->localTranslation(_jointId);
// get the original joint axis: current joint -----> child joint
if (hasOneChild)
oldJointAxis = activePose->localTranslation(joint->child(0)->id());
// store the joint axes of all animations before the translation of the current joint
// so that the rotations can be calculated for the animation poses
std::vector<ACG::Vec3d> oldAnimJoint, oldAnimParent;
for (unsigned int a = 0; a < skeleton->animationCount(); ++a) {
for (unsigned int iFrame = 0; iFrame
< skeleton->animation(a)->frameCount(); iFrame++) {
Skeleton::Pose* pose = skeleton->animation(a)->pose(iFrame);
if (hasOneChild)
oldAnimJoint.push_back(
pose->localTranslation(joint->child(0)->id()));
oldAnimParent.push_back(pose->localTranslation(_jointId));
}
}
// translate the joint
transformer.translateJoint(joint,
ACG::Vec3d(_matrix(0, 3), _matrix(1, 3), _matrix(2, 3)), true);
// get translated parent axis
transParentAxis = activePose->localTranslation(_jointId);
if (hasOneChild) {
// get translated joint axis
transJointAxis = activePose->localTranslation(joint->child(0)->id());
}
// now calculate the rotation that has to occur for a translation of the joint:
// get the rotation axis and angle between the old and new parent axis
if (!ACG::Geometry::rotationOfTwoVectors<double>(oldParentAxis,
transParentAxis, parentRotAxis, parentRotAngle))
return;
// get rotation axis and angle between old and new joint axis
if (hasOneChild) {
if (!ACG::Geometry::rotationOfTwoVectors<double>(oldJointAxis,
transJointAxis, jointRotAxis, jointRotAngle))
return;
}
if (parentIsNotBranch) {
// parent rotation matrix
ACG::GLMatrixT<double> parentRotMatrix;
parentRotMatrix.identity();
parentRotMatrix.rotate(parentRotAngle, parentRotAxis);
// apply rotation to parent joint
ACG::Matrix4x4d localParent = activePose->localMatrix(
joint->parent()->id());
localParent *= parentRotMatrix;
activePose->setLocalMatrix(joint->parent()->id(), localParent, false);
}
if (hasOneChild) {
// joint rotation matrix
ACG::GLMatrixT<double> jointRotMatrix;
jointRotMatrix.identity();
jointRotMatrix.rotate(jointRotAngle, jointRotAxis);
// apply current joint
ACG::Matrix4x4d localJoint = activePose->localMatrix(joint->id());
localJoint *= jointRotMatrix;
activePose->setLocalMatrix(joint->id(), localJoint, false);
}
// apply rotations to animation
std::vector<ACG::Vec3d>::iterator jointIt, parentIt;
jointIt = oldAnimJoint.begin();
parentIt = oldAnimParent.begin();
for (unsigned int a = 0; a < skeleton->animationCount(); ++a) {
for (unsigned int iFrame = 0; iFrame
< skeleton->animation(a)->frameCount(); iFrame++) {
Skeleton::Pose* pose = skeleton->animation(a)->pose(iFrame);
// only apply rotation to parent joint if it is not a branch
if (parentIsNotBranch) {
// get the rotation axis and angle between the old and new parent axis
ACG::Vec3d translatedParent = pose->localTranslation(_jointId);
ACG::Vec3d parentRotAxis(0.0, 0.0, 0.0);
double parentRotAngle = 0.0;
if (!ACG::Geometry::rotationOfTwoVectors<double>(*parentIt,
translatedParent, parentRotAxis, parentRotAngle))
return;
// parent rotation matrix
ACG::GLMatrixT<double> parentRotMatrix;
parentRotMatrix.identity();
parentRotMatrix.rotate(parentRotAngle, parentRotAxis);
// apply rotation to parent joint
ACG::Matrix4x4d parentMat =
pose->localMatrix(joint->parent()->id());
parentMat *= parentRotMatrix;
pose->setLocalMatrix(joint->parent()->id(), parentMat, false);
++parentIt;
}
if (hasOneChild) {
// get rotation axis and angle between old and new joint axis
ACG::Vec3d translatedAxis = pose->localTranslation(
joint->child(0)->id());
ACG::Vec3d jointRotAxis(0.0, 0.0, 0.0);
double jointRotAngle = 0.0;
if (!ACG::Geometry::rotationOfTwoVectors<double>(*jointIt,
translatedAxis, jointRotAxis, jointRotAngle))
return;
// joint rotation matrix
ACG::GLMatrixT<double> jointRotMatrix;
jointRotMatrix.identity();
jointRotMatrix.rotate(jointRotAngle, jointRotAxis);
// apply rotation to current joint
ACG::Matrix4x4d localMat = pose->localMatrix(joint->id());
localMat *= jointRotMatrix;
pose->setLocalMatrix(joint->id(), localMat, false);
++jointIt;
}
}
}
}
}
emit updatedObject(_objectId, UPDATE_GEOMETRY);
QString matString;
for (int i=0; i < 4; i++)
for (int j=0; j < 4; j++)
matString += " , " + QString::number( _matrix(i,j) );
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
matString += " , " + QString::number(_matrix(i, j));
matString = matString.right(matString.length() - 3);
matString = matString.right( matString.length()-3 );
emit scriptInfo(
"transformJoint( ObjectId, " + QString::number(_jointId) + ", Matrix4x4("
+ matString + " ) )");
emit scriptInfo( "transformJoint( ObjectId, " + QString::number(_jointId) + ", Matrix4x4(" + matString + " ) )" );
// Create backup if there was a change
// the backup is only created when the slot is called via scripting (sender == 0)
if ( !_matrix.is_identity() && (sender() == 0) )
if (!_matrix.is_identity() && (sender() == 0))
emit createBackup(_objectId, "Joint Transformation", UPDATE_GEOMETRY);
}