///////////////////////////////////// // Purpose: set the relative translation // of the given bone // Output: bone moved // Return: none ///////////////////////////////////// void IgfxObject::BoneSetTrans(s32 boneID, const Vec3D & loc) { if(m_pCalModel) { Vec3D trans; BoneGetTrans(boneID, &trans); CalSkeleton *pSkel = m_pCalModel->getSkeleton(); CalBone *pBone = pSkel->getBone(boneID); if(pBone) { pBone->setTranslation(CalVector(trans.x+loc.x, trans.y+loc.y, trans.z+loc.z)); pBone->calculateState(); } } }
///////////////////////////////////// // Purpose: set the relative rotation // of the given bone // Output: bone rotated // Return: none ///////////////////////////////////// void IgfxObject::BoneSetRotate(s32 boneID, const Quaternion & q) { if(m_pCalModel) { Quaternion quat; BoneGetRotate(boneID, &quat); quat *= q; CalSkeleton *pSkel = m_pCalModel->getSkeleton(); CalBone *pBone = pSkel->getBone(boneID); if(pBone) { pBone->setRotation(CalQuaternion(quat.x, quat.y, quat.z, quat.w)); pBone->calculateState(); } } }
void CalBone::calculateState() { // check if the bone was not touched by any active animation if(m_accumulatedWeight == 0.0f) { // set the bone to the initial skeleton state m_translation = m_pCoreBone->getTranslation(); m_rotation = m_pCoreBone->getRotation(); } // get parent bone id int parentId; parentId = m_pCoreBone->getParentId(); if(parentId == -1) { // no parent, this means absolute state == relative state m_translationAbsolute = m_translation; m_rotationAbsolute = m_rotation; } else { // get the parent bone const CalBone *pParent; pParent = m_pSkeleton->getBone(parentId); // transform relative state with the absolute state of the parent m_translationAbsolute = m_translation; m_translationAbsolute *= pParent->getRotationAbsolute(); m_translationAbsolute += pParent->getTranslationAbsolute(); m_rotationAbsolute = m_rotation; m_rotationAbsolute *= pParent->getRotationAbsolute(); } // calculate the bone space transformation m_translationBoneSpace = m_pCoreBone->getTranslationBoneSpace(); // Must go before the *= m_rotationAbsolute. bool meshScalingOn; if( m_meshScaleAbsolute.x != 1 || m_meshScaleAbsolute.y != 1 || m_meshScaleAbsolute.z != 1 ) { meshScalingOn = true; CalVector scalevec; // The mesh transformation is intended to apply to the vector from the // bone node to the vert, relative to the model's global coordinate system. // For example, even though the head node's X axis aims up, the model's // global coordinate system has X to stage right, Z up, and Y stage back. // // The standard vert transformation is: // v1 = vmesh - boneAbsPosInJpose // v2 = v1 * boneAbsRotInAnimPose // v3 = v2 + boneAbsPosInAnimPose // // Cal3d does the calculation by: // u1 = umesh * transformMatrix // u2 = u1 + translationBoneSpace // // where translationBoneSpace = // "coreBoneTranslationBoneSpace" // * boneAbsRotInAnimPose // + boneAbsPosInAnimPose // // and where transformMatrix = // "coreBoneRotBoneSpace" // * boneAbsRotInAnimPose // // I don't know what "coreBoneRotBoneSpace" and "coreBoneTranslationBoneSpace" actually are, // but to add scale to the scandard vert transformation, I simply do: // // v3' = vmesh * scalevec * boneAbsRotInAnimPose // - boneAbsPosInJpose * scalevec * boneAbsRotInAnimPose // + boneAbsPosInAnimPose // // Essentially, the boneAbsPosInJpose is just an extra vector added to // each vertex that we want to subtract out. We must transform the extra // vector in exactly the same way we transform the vmesh. Therefore if we scale the mesh, we // must also scale the boneAbsPosInJpose. // // Expanding out the u2 equation, we have: // // u2 = umesh * "coreBoneRotBoneSpace" * boneAbsRotInAnimPose // + "coreBoneTranslationBoneSpace" * boneAbsRotInAnimPose // + boneAbsPosInAnimPose // // We assume that "coreBoneTranslationBoneSpace" = vectorThatMustBeSubtractedFromUmesh * "coreBoneRotBoneSpace": // // u2 = umesh * "coreBoneRotBoneSpace" * boneAbsRotInAnimPose // + vectorThatMustBeSubtractedFromUmesh * "coreBoneRotBoneSpace" * boneAbsRotInAnimPose // + boneAbsPosInAnimPose // // We assume that scale should be applied to umesh, not umesh * "coreBoneRotBoneSpace": // // u2 = umesh * scaleVec * "coreBoneRotBoneSpace" * boneAbsRotInAnimPose // + "coreBoneTranslationBoneSpace" * "coreBoneRotBoneSpaceInverse" * scaleVec * "coreBoneRotBoneSpace" * boneAbsRotInAnimPose // + boneAbsPosInAnimPose // // which yields, // // transformMatrix' = scaleVec * "coreBoneRotBoneSpace" * boneAbsRotInAnimPose // // and, // // translationBoneSpace' = // coreBoneTranslationBoneSpace * "coreBoneRotBoneSpaceInverse" * scaleVec * "coreBoneRotBoneSpace" // * boneAbsRotInAnimPose // + boneAbsPosInAnimPose CalQuaternion coreBoneRotBoneSpaceInverse = m_pCoreBone->getRotationBoneSpace(); coreBoneRotBoneSpaceInverse.invert(); m_translationBoneSpace *= coreBoneRotBoneSpaceInverse; m_translationBoneSpace.x *= m_meshScaleAbsolute.x; m_translationBoneSpace.y *= m_meshScaleAbsolute.y; m_translationBoneSpace.z *= m_meshScaleAbsolute.z; m_translationBoneSpace *= m_pCoreBone->getRotationBoneSpace(); } else { meshScalingOn = false; } m_translationBoneSpace *= m_rotationAbsolute; m_translationBoneSpace += m_translationAbsolute; m_rotationBoneSpace = m_pCoreBone->getRotationBoneSpace(); m_rotationBoneSpace *= m_rotationAbsolute; m_transformMatrix = m_pCoreBone->getRotationBoneSpace(); if( meshScalingOn ) { // By applying each scale component to the row, instead of the column, we // are effectively making the scale apply prior to the rotationBoneSpace. m_transformMatrix.dxdx *= m_meshScaleAbsolute.x; m_transformMatrix.dydx *= m_meshScaleAbsolute.x; m_transformMatrix.dzdx *= m_meshScaleAbsolute.x; m_transformMatrix.dxdy *= m_meshScaleAbsolute.y; m_transformMatrix.dydy *= m_meshScaleAbsolute.y; m_transformMatrix.dzdy *= m_meshScaleAbsolute.y; m_transformMatrix.dxdz *= m_meshScaleAbsolute.z; m_transformMatrix.dydz *= m_meshScaleAbsolute.z; m_transformMatrix.dzdz *= m_meshScaleAbsolute.z; } m_transformMatrix *= m_rotationAbsolute; // calculate all child bones std::list<int>::iterator iteratorChildId; int i = 0; for(iteratorChildId = m_pCoreBone->getListChildId().begin(); iteratorChildId != m_pCoreBone->getListChildId().end(); ++iteratorChildId, i++ ) { CalBone * bo = m_pSkeleton->getBone(*iteratorChildId); bo->calculateState(); } }