Matrix4x4f BoneBridgeCAL3D::CalculateBoneSpaceTransform() const { Matrix4x4f boneSpaceTransform = Matrix4x4f::Identity(); // set up bone space geometry transform { // transform forward by half the box length const Vec3f& dimensions = GetDimensions(); boneSpaceTransform.SetTranslation(Vec3f(dimensions[Y] / 2.0f, 0.0f, 0.0f)); // set rotational offset (the inversion of the core bone absolute rotation) { CalCoreBone* coreBone = mpCalBone->getCoreBone(); CalQuaternion calRotAbsolute = coreBone->getRotationAbsolute(); calRotAbsolute.invert(); Quaternionf kernelRot = ConvertCAL3DtoKernel(calRotAbsolute); boneSpaceTransform.SetRotate(kernelRot); } } return boneSpaceTransform; }
void CalCoreBone::initBoundingBox() { CalQuaternion rot; rot=m_rotationBoneSpace; rot.invert(); CalVector dir = CalVector(1.0f,0.0f,0.0f); dir*=rot; m_boundingBox.plane[0].setNormal(dir); dir = CalVector(-1.0f,0.0f,0.0f); dir*=rot; m_boundingBox.plane[1].setNormal(dir); dir = CalVector(0.0f,1.0f,0.0f); dir*=rot; m_boundingBox.plane[2].setNormal(dir); dir = CalVector(0.0f,-1.0f,0.0f); dir*=rot; m_boundingBox.plane[3].setNormal(dir); dir = CalVector(0.0f,0.0f,1.0f); dir*=rot; m_boundingBox.plane[4].setNormal(dir); dir = CalVector(0.0f,0.0f,-1.0f); dir*=rot; m_boundingBox.plane[5].setNormal(dir); }
void CSkeletonCandidate::GetTranslationAndRotation(int boneCandidateId, float time, CalVector& translation, CalQuaternion& rotation) { // clear the translation and the rotation translation.clear(); rotation.clear(); // check if the bone candidate id is valid if((boneCandidateId < 0) || (boneCandidateId >= (int)m_vectorBoneCandidate.size())) return; // get the bone candidate CBoneCandidate *pBoneCandidate = m_vectorBoneCandidate[boneCandidateId]; // get the node of the bone candidate CBaseNode *pNode = pBoneCandidate->GetNode(); // get the parent id int parentId = pBoneCandidate->GetParentId(); // get the node of the parent bone candidate CBaseNode *pParentNode = 0; if(parentId != -1) { pParentNode = m_vectorBoneCandidate[parentId]->GetNode(); } // get the translation and rotation of the node theExporter.GetInterface()->GetTranslationAndRotation(pNode, pParentNode, time, translation, rotation); }
float DistanceDegrees( CalQuaternion const & p1, CalQuaternion const & p2 ) { // To determine the angular distance between the oris, multiply one by the inverse // of the other, which should leave us with an identity ori if they are equal. If // they are not equal, then the angular magnitude of the rotation in degrees is the // difference between the oris. CalQuaternion odist = p1; odist.invert(); odist *= p2; float w = odist.w; if( w > 1 ) w = 1; if( w < -1 ) w = -1; float distrads = 2 * acos( w ); // Non-negative. float distdegrees = distrads * 180.0f / 3.141592654f; // Non-negative. if( distdegrees > 180.0 ) distdegrees -= 360.0; return fabsf( distdegrees ); }
bool CalCoreTrack::getState(float time, CalVector& translation, CalQuaternion& rotation) const { std::vector<CalCoreKeyframe*>::const_iterator iteratorCoreKeyframeBefore; std::vector<CalCoreKeyframe*>::const_iterator iteratorCoreKeyframeAfter; // get the keyframe after the requested time iteratorCoreKeyframeAfter = getUpperBound(time); // check if the time is after the last keyframe if(iteratorCoreKeyframeAfter == m_keyframes.end()) { // return the last keyframe state --iteratorCoreKeyframeAfter; rotation = (*iteratorCoreKeyframeAfter)->getRotation(); translation = (*iteratorCoreKeyframeAfter)->getTranslation(); return true; } // check if the time is before the first keyframe if(iteratorCoreKeyframeAfter == m_keyframes.begin()) { // return the first keyframe state rotation = (*iteratorCoreKeyframeAfter)->getRotation(); translation = (*iteratorCoreKeyframeAfter)->getTranslation(); return true; } // get the keyframe before the requested one iteratorCoreKeyframeBefore = iteratorCoreKeyframeAfter; --iteratorCoreKeyframeBefore; // get the two keyframe pointers CalCoreKeyframe *pCoreKeyframeBefore; pCoreKeyframeBefore = *iteratorCoreKeyframeBefore; CalCoreKeyframe *pCoreKeyframeAfter; pCoreKeyframeAfter = *iteratorCoreKeyframeAfter; // calculate the blending factor between the two keyframe states float blendFactor; blendFactor = (time - pCoreKeyframeBefore->getTime()) / (pCoreKeyframeAfter->getTime() - pCoreKeyframeBefore->getTime()); // blend between the two keyframes translation = pCoreKeyframeBefore->getTranslation(); translation.blend(blendFactor, pCoreKeyframeAfter->getTranslation()); rotation = pCoreKeyframeBefore->getRotation(); rotation.blend(blendFactor, pCoreKeyframeAfter->getRotation()); return true; }
bool CalCoreTrack::getState(float time, CalVector& translation, CalQuaternion& rotation) { rde::sorted_vector<float, CalCoreKeyframe *>::iterator iteratorCoreKeyframeBefore; rde::sorted_vector<float, CalCoreKeyframe *>::iterator iteratorCoreKeyframeAfter; // get the keyframe after the requested time iteratorCoreKeyframeAfter = m_mapCoreKeyframe.upper_bound(time); // check if the time is after the last keyframe if(iteratorCoreKeyframeAfter == m_mapCoreKeyframe.end()) { // return the last keyframe state --iteratorCoreKeyframeAfter; rotation = (iteratorCoreKeyframeAfter->second)->getRotation(); translation = (iteratorCoreKeyframeAfter->second)->getTranslation(); return true; } // check if the time is before the first keyframe if(iteratorCoreKeyframeAfter == m_mapCoreKeyframe.begin()) { // return the first keyframe state rotation = (iteratorCoreKeyframeAfter->second)->getRotation(); translation = (iteratorCoreKeyframeAfter->second)->getTranslation(); return true; } // get the keyframe before the requested one iteratorCoreKeyframeBefore = iteratorCoreKeyframeAfter; --iteratorCoreKeyframeBefore; // get the two keyframe pointers CalCoreKeyframe *pCoreKeyframeBefore; pCoreKeyframeBefore = iteratorCoreKeyframeBefore->second; CalCoreKeyframe *pCoreKeyframeAfter; pCoreKeyframeAfter = iteratorCoreKeyframeAfter->second; // calculate the blending factor between the two keyframe states float blendFactor; blendFactor = (time - pCoreKeyframeBefore->getTime()) / (pCoreKeyframeAfter->getTime() - pCoreKeyframeBefore->getTime()); // blend between the two keyframes translation = pCoreKeyframeBefore->getTranslation(); translation.blend(blendFactor, pCoreKeyframeAfter->getTranslation()); rotation = pCoreKeyframeBefore->getRotation(); rotation.blend(blendFactor, pCoreKeyframeAfter->getRotation()); return true; }
bool CalCoreTrack::keyframeEliminatable( CalCoreKeyframe * prev, CalCoreKeyframe * p, CalCoreKeyframe * next, double transTolerance, double rotTolerance ) { CalVector translation; CalQuaternion rotation; assert( prev && p && next ); float time = p->getTime(); float blendFactor; blendFactor = ( time - prev->getTime() ) / ( next->getTime() - prev->getTime() ); // blend between the two keyframes translation = prev->getTranslation(); translation.blend( blendFactor, next->getTranslation() ); rotation = prev->getRotation(); rotation.blend( blendFactor, next->getRotation() ); CalVector const ppos = p->getTranslation(); CalQuaternion const pori = p->getRotation(); return Near( translation, rotation, ppos, pori, transTolerance, rotTolerance ); }
void CalCoreBone::calculateBoundingBox(CalCoreModel * pCoreModel) { int boneId = m_pCoreSkeleton->getCoreBoneId(m_strName); bool bBoundsComputed=false; int planeId; CalQuaternion rot; rot=m_rotationBoneSpace; rot.invert(); CalVector dir = CalVector(1.0f,0.0f,0.0f); dir*=rot; m_boundingBox.plane[0].setNormal(dir); dir = CalVector(-1.0f,0.0f,0.0f); dir*=rot; m_boundingBox.plane[1].setNormal(dir); dir = CalVector(0.0f,1.0f,0.0f); dir*=rot; m_boundingBox.plane[2].setNormal(dir); dir = CalVector(0.0f,-1.0f,0.0f); dir*=rot; m_boundingBox.plane[3].setNormal(dir); dir = CalVector(0.0f,0.0f,1.0f); dir*=rot; m_boundingBox.plane[4].setNormal(dir); dir = CalVector(0.0f,0.0f,-1.0f); dir*=rot; m_boundingBox.plane[5].setNormal(dir); int meshId; for(meshId=0; meshId < pCoreModel->getCoreMeshCount(); ++meshId) { CalCoreMesh * pCoreMesh = pCoreModel->getCoreMesh(meshId); int submeshId; for(submeshId=0;submeshId<pCoreMesh->getCoreSubmeshCount();submeshId++) { CalCoreSubmesh *pCoreSubmesh = pCoreMesh->getCoreSubmesh(submeshId); if(pCoreSubmesh->getSpringCount()==0) { std::vector<CalCoreSubmesh::Vertex>& vectorVertex = pCoreSubmesh->getVectorVertex(); for(size_t vertexId=0;vertexId <vectorVertex.size(); ++vertexId) { for(size_t influenceId=0;influenceId<vectorVertex[vertexId].vectorInfluence.size();++influenceId) { if(vectorVertex[vertexId].vectorInfluence[influenceId].boneId == boneId && vectorVertex[vertexId].vectorInfluence[influenceId].weight > 0.5f) { for(planeId = 0; planeId < 6; ++planeId) { if(m_boundingBox.plane[planeId].eval(vectorVertex[vertexId].position) < 0.0f) { m_boundingBox.plane[planeId].setPosition(vectorVertex[vertexId].position); m_boundingPosition[planeId]=vectorVertex[vertexId].position; bBoundsComputed=true; } } } } } } } } // To handle bones with no vertices assigned if(!bBoundsComputed) { for(planeId = 0; planeId < 6; ++planeId) { m_boundingBox.plane[planeId].setPosition(m_translation); m_boundingPosition[planeId] = m_translation; } } m_boundingBoxPrecomputed = true; }
CalQuaternion CMilkBoneNode::GetRelativeRotation(float time) { // get the initial rotation component msVec3 orientation; msBone_GetRotation(m_pIBone, orientation); // calculate the initial rotation component CalQuaternion initialRotation; initialRotation = ConvertToQuaternion(orientation); // return if the initial state is requested or if there are no keyframes if((time < 0.0f) || (msBone_GetRotationKeyCount(m_pIBone) == 0)) return initialRotation; // calculate the real frame time // REMEMBER: milkshape starts at 1.0! float frameTime; frameTime = 1.0f + time * (float)theExporter.GetInterface()->GetFps(); // find the keyframe just before the requested time msRotationKey *pKeyBefore; pKeyBefore = msBone_GetRotationKeyAt(m_pIBone, msBone_GetRotationKeyCount(m_pIBone) - 1); int keyId; for(keyId = 0; keyId < msBone_GetRotationKeyCount(m_pIBone); keyId++) { // get the keyframe msRotationKey *pKey; pKey = msBone_GetRotationKeyAt(m_pIBone, keyId); // stop if we are over the requested time if(pKey->fTime > frameTime) break; pKeyBefore = pKey; } // get the keyframe just after the requested time msRotationKey *pKeyAfter; pKeyAfter = msBone_GetRotationKeyAt(m_pIBone, 0); if(keyId < msBone_GetRotationKeyCount(m_pIBone)) { pKeyAfter = msBone_GetRotationKeyAt(m_pIBone, keyId); } // calculate the "just before" rotation component CalQuaternion rotationBefore; if(pKeyBefore != 0) { rotationBefore = ConvertToQuaternion(pKeyBefore->Rotation); // return if there is no key after this one if(pKeyAfter == 0) return initialRotation * rotationBefore; } // calculate the "just after" rotation component CalQuaternion rotationAfter; if(pKeyAfter != 0) { rotationAfter = ConvertToQuaternion(pKeyAfter->Rotation); // return if there is no key before this one if(pKeyBefore == 0) return initialRotation * rotationAfter; } // return if both keys are actually the same if(pKeyBefore == pKeyAfter) return initialRotation * rotationAfter; // calculate the blending factor float factor; factor = (frameTime - pKeyBefore->fTime) / (pKeyAfter->fTime - pKeyBefore->fTime); // blend the two rotation components rotationBefore.blend(factor, rotationAfter); return initialRotation * rotationBefore; }
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(); } }