// ----------------------------------------------------------------------------------------- bool CPepeEngineVector3::directionEquals(const CPepeEngineVector3& rhs, const Radian& tolerance) const { float dot = dotProduct(rhs); Radian angle = CPepeEngineMath::ACos(dot); return CPepeEngineMath::Abs(angle.valueRadians()) <= tolerance.valueRadians(); }
//----------------------------------------------------------------------- Quaternion Quaternion::Exp () const { // If q = A*(x*i+y*j+z*k) where (x,y,z) is unit length, then // exp(q) = e^w(cos(A)+sin(A)*(x*i+y*j+z*k)). If sin(A) is near zero, // use exp(q) = e^w(cos(A)+(x*i+y*j+z*k)) since sin(A)/A has limit 1. Radian fAngle ( Math::Sqrt(x*x+y*y+z*z) ); Real fSin = Math::Sin(fAngle); Real fExpW = Math::Exp(w); Quaternion kResult; kResult.w = fExpW*Math::Cos(fAngle); if ( Math::Abs(fAngle.valueRadians()) >= msEpsilon ) { Real fCoeff = fExpW*(fSin/(fAngle.valueRadians())); kResult.x = fCoeff*x; kResult.y = fCoeff*y; kResult.z = fCoeff*z; } else { kResult.x = fExpW*x; kResult.y = fExpW*y; kResult.z = fExpW*z; } return kResult; }
//----------------------------------------------------------------------- bool Quaternion::equals(const Quaternion& rhs, const Radian& tolerance) const { Real d = Dot(rhs); Radian angle = Math::ACos(2.0f * d*d - 1.0f); return Math::Abs(angle.valueRadians()) <= tolerance.valueRadians(); }
bool Quaternion::equals(const Quaternion& rhs, const Radian& tolerance) const { Real fCos = Dot(rhs); Radian angle = Math::ACos(fCos); return (Math::Abs(angle.valueRadians()) <= tolerance.valueRadians()) || Math::RealEqual(angle.valueRadians(), Math::PI, tolerance.valueRadians()); }
// ----------------------------------------------------------------------------------------- bool CPepeEngineQuaternion::equals(const CPepeEngineQuaternion& rhs, const Radian& tolerance) const { float fCos = dot(rhs); Radian angle = CPepeEngineMath::ACos(fCos); return (CPepeEngineMath::Abs(angle.valueRadians()) <= tolerance.valueRadians()) || CPepeEngineMath::floatEqual(angle.valueRadians(), CPepeEngineMath::PI, tolerance.valueRadians()); }
//----------------------------------------------------------------------- Quaternion Quaternion::Slerp (Real fT, const Quaternion& rkP, const Quaternion& rkQ, bool shortestPath) { Real fCos = rkP.Dot(rkQ); Radian fAngle ( Math::ACos(fCos) ); if ( Math::Abs(fAngle.valueRadians()) < ms_fEpsilon ) return rkP; Real fSin = Math::Sin(fAngle); Real fInvSin = 1.0/fSin; Real fCoeff0 = Math::Sin((1.0-fT)*fAngle)*fInvSin; Real fCoeff1 = Math::Sin(fT*fAngle)*fInvSin; // Do we need to invert rotation? if (fCos < 0.0f && shortestPath) { fCoeff0 = -fCoeff0; // taking the complement requires renormalisation Quaternion t(fCoeff0*rkP + fCoeff1*rkQ); t.normalise(); return t; } else { return fCoeff0*rkP + fCoeff1*rkQ; } }
//--------------------------------------------------------------------- bool NodeAnimationTrack::hasNonZeroKeyFrames(void) const { KeyFrameList::const_iterator i = mKeyFrames.begin(); for (; i != mKeyFrames.end(); ++i) { // look for keyframes which have any component which is non-zero // Since exporters can be a little inaccurate sometimes we use a // tolerance value rather than looking for nothing TransformKeyFrame* kf = static_cast<TransformKeyFrame*>(*i); Vector3 trans = kf->getTranslate(); Vector3 scale = kf->getScale(); Vector3 axis; Radian angle; kf->getRotation().ToAngleAxis(angle, axis); Real tolerance = 1e-3f; if (!trans.positionEquals(Vector3::ZERO, tolerance) || !scale.positionEquals(Vector3::UNIT_SCALE, tolerance) || !Math::RealEqual(angle.valueRadians(), 0.0f, tolerance)) { return true; } } return false; }
void DemoApplication::SmoothCamera::Move (Real deltaTranslation, Real deltaStrafe, Radian pitchAngleStep, Radian yawAngleStep) { // here we update the camera movement at simulation rate m_cameraYawAngle = fmodf (m_cameraYawAngle.valueRadians() + yawAngleStep.valueRadians(), 3.141592f * 2.0f); m_cameraPitchAngle = Math::Clamp (m_cameraPitchAngle.valueRadians() + pitchAngleStep.valueRadians(), - 80.0f * 3.141592f / 180.0f, 80.0f * 3.141592f / 180.0f); Matrix3 rot; rot.FromEulerAnglesZYX (Radian (0.0f), m_cameraYawAngle, m_cameraPitchAngle); Matrix4 matrix (rot); m_cameraTranslation += Vector3 (matrix[0][2], matrix[1][2], matrix[2][2]) * deltaTranslation; m_cameraTranslation += Vector3 (matrix[0][0], matrix[1][0], matrix[2][0]) * deltaStrafe; matrix.setTrans(m_cameraTranslation); matrix = matrix.transpose(); Update (matrix[0]); }
//----------------------------------------------------------------------- Quaternion Quaternion::Exp () const { // If q = A*(x*i+y*j+z*k) where (x,y,z) is unit length, then // exp(q) = cos(A)+sin(A)*(x*i+y*j+z*k). If sin(A) is near zero, // use exp(q) = cos(A)+A*(x*i+y*j+z*k) since A/sin(A) has limit 1. Radian fAngle ( Math::Sqrt(x*x+y*y+z*z) ); scalar fSin = Math::Sin(fAngle); Quaternion kResult; kResult.w = Math::Cos(fAngle); if ( fabs(fSin) >= ms_fEpsilon ) { scalar fCoeff = fSin/(fAngle.valueRadians()); kResult.x = fCoeff*x; kResult.y = fCoeff*y; kResult.z = fCoeff*z; } else { kResult.x = x; kResult.y = y; kResult.z = z; } return kResult; }
//----------------------------------------------------------------------- Quaternion Quaternion::Log () const { // If q = cos(A)+sin(A)*(x*i+y*j+z*k) where (x,y,z) is unit length, then // log(q) = (A/sin(A))*(x*i+y*j+z*k). If sin(A) is near zero, use // log(q) = (x*i+y*j+z*k) since A/sin(A) has limit 1. Quaternion kResult; kResult.w = 0.0; if ( Math::Abs(w) < 1.0 ) { // According to Neil Dantam, atan2 has the best stability. // http://www.neil.dantam.name/note/dantam-quaternion.pdf Real fNormV = Math::Sqrt(x*x + y*y + z*z); Radian fAngle ( Math::ATan2(fNormV, w) ); Real fSin = Math::Sin(fAngle); if ( Math::Abs(fSin) >= msEpsilon ) { Real fCoeff = fAngle.valueRadians()/fSin; kResult.x = fCoeff*x; kResult.y = fCoeff*y; kResult.z = fCoeff*z; return kResult; } } kResult.x = x; kResult.y = y; kResult.z = z; return kResult; }
//----------------------------------------------------------------------- Quaternion Quaternion::Log () const { // If q = cos(A)+sin(A)*(x*i+y*j+z*k) where (x,y,z) is unit length, then // log(q) = A*(x*i+y*j+z*k). If sin(A) is near zero, use log(q) = // sin(A)*(x*i+y*j+z*k) since sin(A)/A has limit 1. Quaternion kResult; kResult.w = 0.0; if ( Math::Abs(w) < 1.0 ) { Radian fAngle ( Math::ACos(w) ); Real fSin = Math::Sin(fAngle); if ( Math::Abs(fSin) >= ms_fEpsilon ) { Real fCoeff = fAngle.valueRadians()/fSin; kResult.x = fCoeff*x; kResult.y = fCoeff*y; kResult.z = fCoeff*z; return kResult; } } kResult.x = x; kResult.y = y; kResult.z = z; return kResult; }
void LightRenderingParams::setParameters(const LightCore* light) { // Note: I could just copy the data directly to the parameter buffer if I ensured the parameter // layout matches Vector4 positionAndType = (Vector4)light->getPosition(); switch (light->getType()) { case LightType::Directional: positionAndType.w = 0; break; case LightType::Point: positionAndType.w = 0.3f; break; case LightType::Spot: positionAndType.w = 0.8f; break; } mBuffer.gLightPositionAndType.set(positionAndType); Vector4 colorAndIntensity; colorAndIntensity.x = light->getColor().r; colorAndIntensity.y = light->getColor().g; colorAndIntensity.z = light->getColor().b; colorAndIntensity.w = light->getIntensity(); mBuffer.gLightColorAndIntensity.set(colorAndIntensity); Radian spotAngle = Math::clamp(light->getSpotAngle() * 0.5f, Degree(1), Degree(90)); Radian spotFalloffAngle = Math::clamp(light->getSpotFalloffAngle() * 0.5f, Degree(1), (Degree)spotAngle); Vector4 spotAnglesAndInvSqrdRadius; spotAnglesAndInvSqrdRadius.x = spotAngle.valueRadians(); spotAnglesAndInvSqrdRadius.y = Math::cos(spotAnglesAndInvSqrdRadius.x); spotAnglesAndInvSqrdRadius.z = 1.0f / (Math::cos(spotFalloffAngle) - spotAnglesAndInvSqrdRadius.y); spotAnglesAndInvSqrdRadius.w = 1.0f / (light->getBounds().getRadius() * light->getBounds().getRadius()); mBuffer.gLightSpotAnglesAndSqrdInvRadius.set(spotAnglesAndInvSqrdRadius); mBuffer.gLightDirection.set(-light->getRotation().zAxis()); Vector4 lightGeometry; lightGeometry.x = light->getType() == LightType::Spot ? (float)LightCore::LIGHT_CONE_NUM_SIDES : 0; lightGeometry.y = (float)LightCore::LIGHT_CONE_NUM_SLICES; lightGeometry.z = light->getBounds().getRadius(); float coneRadius = Math::sin(spotAngle) * light->getRange(); lightGeometry.w = coneRadius; mBuffer.gLightGeometry.set(lightGeometry); Matrix4 transform = Matrix4::TRS(light->getPosition(), light->getRotation(), Vector3::ONE); mBuffer.gMatConeTransform.set(transform); mBuffer.flushToGPU(); }
void CameraBehaviorVehicleSpline::update(const CameraManager::CameraContext &ctx) { if ( ctx.mCurrTruck->free_camerarail <= 0 ) { gEnv->cameraManager->switchToNextBehavior(); return; } Vector3 dir = (ctx.mCurrTruck->nodes[ctx.mCurrTruck->cameranodepos[0]].smoothpos - ctx.mCurrTruck->nodes[ctx.mCurrTruck->cameranodedir[0]].smoothpos).normalisedCopy(); targetPitch = 0.0f; if ( camPitching ) { targetPitch = -asin(dir.dotProduct(Vector3::UNIT_Y)); } if ( ctx.mCurrTruck->getAllLinkedBeams().size() != numLinkedBeams ) { createSpline(ctx); } updateSpline(); updateSplineDisplay(); camLookAt = spline->interpolate(splinePos); if ( RoR::Application::GetInputEngine()->isKeyDown(OIS::KC_LSHIFT) && RoR::Application::GetInputEngine()->isKeyDownValueBounce(OIS::KC_SPACE) ) { autoTracking = !autoTracking; #ifdef USE_MYGUI if ( autoTracking ) { RoR::Application::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE, _L("auto tracking enabled"), "camera_go.png", 3000); } else { RoR::Application::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE, _L("auto tracking disabled"), "camera_go.png", 3000); } #endif // USE_MYGUI } if ( autoTracking ) { Vector3 centerDir = ctx.mCurrTruck->getPosition() - camLookAt; if ( centerDir.length() > 1.0f ) { centerDir.normalise(); Radian oldTargetDirection = targetDirection; targetDirection = -atan2(centerDir.dotProduct(Vector3::UNIT_X), centerDir.dotProduct(-Vector3::UNIT_Z)); if ( targetDirection.valueRadians() * oldTargetDirection.valueRadians() < 0.0f && centerDir.length() < camDistMin) { camRotX = -camRotX; } } } CameraBehaviorOrbit::update(ctx); }
//----------------------------------------------------------------------- Quaternion Quaternion::Slerp(Real fT, const Quaternion& rkP, const Quaternion& rkQ, bool shortestPath) { Real fCos = rkP.Dot(rkQ); Quaternion rkT; // 需要翻转旋转? if (fCos < 0.0f && shortestPath) { fCos = -fCos; rkT = -rkQ; } else { rkT = rkQ; } if (Math::Abs(fCos) < 1 - msEpsilon) { // 正常情况 (slerp) Real fSin = Math::Sqrt(1 - Math::Sqr(fCos)); Radian fAngle = Math::ATan2(fSin, fCos); Real fInvSin = 1.0f / fSin; Real fCoeff0 = Math::Sin((1.0f - fT) * fAngle.valueRadians()) * fInvSin; Real fCoeff1 = Math::Sin(fT * fAngle.valueRadians()) * fInvSin; return fCoeff0 * rkP + fCoeff1 * rkT; } else { // 这有两种情况 // 1. "rkP" 和 "rkQ" 是非常接近(fCos ~= +1), 所以我们能做安全的做线性插值 // 2. "rkP" 和 "rkQ" 几乎是每一个 (fCos ~= -1)的反转,这就有无数种插值的可能性。但是我们不可能有修正这个问题的方法, // 所有就在这里用线性插值 Quaternion t = (1.0f - fT) * rkP + fT * rkT; // 使这个分量重新标准化 t.normalise(); return t; } }
//----------------------------------------------------------------------- void Quaternion::FromAngleAxis(const Radian& rfAngle, const Vector3& rkAxis) { // 断言: axis[] 是单位长度 // // 这个四元数表示的旋转是 // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) Radian fHalfAngle(0.5*rfAngle.valueRadians()); Real fSin = Math::Sin(fHalfAngle); w = Math::Cos(fHalfAngle); x = fSin*rkAxis.x; y = fSin*rkAxis.y; z = fSin*rkAxis.z; }
//----------------------------------------------------------------------- Quaternion Quaternion::SlerpExtraSpins (Real fT, const Quaternion& rkP, const Quaternion& rkQ, int iExtraSpins) { Real fCos = rkP.Dot(rkQ); Radian fAngle ( Math::ACos(fCos) ); if ( Math::Abs(fAngle.valueRadians()) < msEpsilon ) return rkP; Real fSin = Math::Sin(fAngle); Radian fPhase ( Math::PI*iExtraSpins*fT ); Real fInvSin = 1.0f/fSin; Real fCoeff0 = Math::Sin((1.0f-fT)*fAngle - fPhase)*fInvSin; Real fCoeff1 = Math::Sin(fT*fAngle + fPhase)*fInvSin; return fCoeff0*rkP + fCoeff1*rkQ; }
//--------------------------------------------------------------------- void XMLSkeletonSerializer::writeBone(TiXmlElement* bonesElement, const Bone* pBone) { TiXmlElement* boneElem = bonesElement->InsertEndChild(TiXmlElement("bone"))->ToElement(); // Bone name & handle boneElem->SetAttribute("id", StringConverter::toString(pBone->getHandle())); boneElem->SetAttribute("name", pBone->getName()); // Position TiXmlElement* subNode = boneElem->InsertEndChild(TiXmlElement("position"))->ToElement(); Vector3 pos = pBone->getPosition(); subNode->SetAttribute("x", StringConverter::toString(pos.x)); subNode->SetAttribute("y", StringConverter::toString(pos.y)); subNode->SetAttribute("z", StringConverter::toString(pos.z)); // Orientation subNode = boneElem->InsertEndChild(TiXmlElement("rotation"))->ToElement(); // Show Quaternion as angle / axis Radian angle; Vector3 axis; pBone->getOrientation().ToAngleAxis(angle, axis); TiXmlElement* axisNode = subNode->InsertEndChild(TiXmlElement("axis"))->ToElement(); subNode->SetAttribute("angle", StringConverter::toString(angle.valueRadians())); axisNode->SetAttribute("x", StringConverter::toString(axis.x)); axisNode->SetAttribute("y", StringConverter::toString(axis.y)); axisNode->SetAttribute("z", StringConverter::toString(axis.z)); // Scale optional Vector3 scale = pBone->getScale(); if (scale != Vector3::UNIT_SCALE) { TiXmlElement* scaleNode = boneElem->InsertEndChild(TiXmlElement("scale"))->ToElement(); scaleNode->SetAttribute("x", StringConverter::toString(scale.x)); scaleNode->SetAttribute("y", StringConverter::toString(scale.y)); scaleNode->SetAttribute("z", StringConverter::toString(scale.z)); } }
inline void wrapAngle(Radian& angle) { Real rangle = angle.valueRadians(); if (rangle < -Math::PI) { rangle = fmod(rangle, -Math::TWO_PI); if (rangle < -Math::PI) { rangle += Math::TWO_PI; } angle = rangle; mChanged = true; } else if (rangle > Math::PI) { rangle = fmod(rangle, Math::TWO_PI); if (rangle > Math::PI) { rangle -= Math::TWO_PI; } angle = rangle; mChanged = true; } }
//--------------------------------------------------------------------- void XMLSkeletonSerializer::writeKeyFrame(TiXmlElement* keysNode, const TransformKeyFrame* key) { TiXmlElement* keyNode = keysNode->InsertEndChild(TiXmlElement("keyframe"))->ToElement(); keyNode->SetAttribute("time", StringConverter::toString(key->getTime())); TiXmlElement* transNode = keyNode->InsertEndChild(TiXmlElement("translate"))->ToElement(); Vector3 trans = key->getTranslate(); transNode->SetAttribute("x", StringConverter::toString(trans.x)); transNode->SetAttribute("y", StringConverter::toString(trans.y)); transNode->SetAttribute("z", StringConverter::toString(trans.z)); TiXmlElement* rotNode = keyNode->InsertEndChild(TiXmlElement("rotate"))->ToElement(); // Show Quaternion as angle / axis Radian angle; Vector3 axis; key->getRotation().ToAngleAxis(angle, axis); TiXmlElement* axisNode = rotNode->InsertEndChild(TiXmlElement("axis"))->ToElement(); rotNode->SetAttribute("angle", StringConverter::toString(angle.valueRadians())); axisNode->SetAttribute("x", StringConverter::toString(axis.x)); axisNode->SetAttribute("y", StringConverter::toString(axis.y)); axisNode->SetAttribute("z", StringConverter::toString(axis.z)); // Scale optional if (key->getScale() != Vector3::UNIT_SCALE) { TiXmlElement* scaleNode = keyNode->InsertEndChild(TiXmlElement("scale"))->ToElement(); scaleNode->SetAttribute("x", StringConverter::toString(key->getScale().x)); scaleNode->SetAttribute("y", StringConverter::toString(key->getScale().y)); scaleNode->SetAttribute("z", StringConverter::toString(key->getScale().z)); } }
/** @brief turn around, forces anim if standing in place */ void Animation::turn(Radian a_turning_speed) { if (CurrentAnim == animation::stand) { // equivalent rate of animation for turning Real turning_rate = -20 * a_turning_speed.valueRadians(); // use turning rate or moving rate for anim - whichever is faster if (Rate > 0) { // when moving forward if (turning_rate > Rate || turning_rate < -Rate) { Rate = turning_rate; Stopped = false; } } else { // when moving backward if (-turning_rate < Rate || -turning_rate > -Rate) { Rate = -turning_rate; Stopped = false; } } } }
String toString(Radian val, unsigned short precision, unsigned short width, char fill, std::ios::fmtflags flags) { return toString(val.valueRadians(), precision, width, fill, flags); }
void PhysXCharacterController::setSlopeLimit(Radian value) { mController->setSlopeLimit(value.valueRadians()); }
void SurveyMapEntity::setRotation(Radian r) { setRotation(r.valueRadians()); }
//--------------------------------------------------------------------- void Skeleton::_dumpContents(const String& filename) { std::ofstream of; Quaternion q; Radian angle; Vector3 axis; of.open(filename.c_str()); of << "-= Debug output of skeleton " << mName << " =-" << std::endl << std::endl; of << "== Bones ==" << std::endl; of << "Number of bones: " << (unsigned int)mBoneList.size() << std::endl; BoneList::iterator bi; for (bi = mBoneList.begin(); bi != mBoneList.end(); ++bi) { Bone* bone = *bi; of << "-- Bone " << bone->getHandle() << " --" << std::endl; of << "Position: " << bone->getPosition(); q = bone->getOrientation(); of << "Rotation: " << q; q.ToAngleAxis(angle, axis); of << " = " << angle.valueRadians() << " radians around axis " << axis << std::endl << std::endl; } of << "== Animations ==" << std::endl; of << "Number of animations: " << (unsigned int)mAnimationsList.size() << std::endl; AnimationList::iterator ai; for (ai = mAnimationsList.begin(); ai != mAnimationsList.end(); ++ai) { Animation* anim = ai->second; of << "-- Animation '" << anim->getName() << "' (length " << anim->getLength() << ") --" << std::endl; of << "Number of tracks: " << anim->getNumNodeTracks() << std::endl; for (unsigned short ti = 0; ti < anim->getNumNodeTracks(); ++ti) { NodeAnimationTrack* track = anim->getNodeTrack(ti); of << " -- AnimationTrack " << ti << " --" << std::endl; of << " Affects bone: " << ((Bone*)track->getAssociatedNode())->getHandle() << std::endl; of << " Number of keyframes: " << track->getNumKeyFrames() << std::endl; for (unsigned short ki = 0; ki < track->getNumKeyFrames(); ++ki) { TransformKeyFrame* key = track->getNodeKeyFrame(ki); of << " -- KeyFrame " << ki << " --" << std::endl; of << " Time index: " << key->getTime(); of << " Translation: " << key->getTranslate() << std::endl; q = key->getRotation(); of << " Rotation: " << q; q.ToAngleAxis(angle, axis); of << " = " << angle.valueRadians() << " radians around axis " << axis << std::endl; } } } }
void Skeleton::_mergeSkeletonAnimations(const Skeleton* src, const BoneHandleMap& boneHandleMap, const StringVector& animations) { ushort handle; ushort numSrcBones = src->getNumBones(); ushort numDstBones = this->getNumBones(); if (boneHandleMap.size() != numSrcBones) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Number of bones in the bone handle map must equal to " "number of bones in the source skeleton.", "Skeleton::_mergeSkeletonAnimations"); } bool existsMissingBone = false; // Check source skeleton structures compatible with ourself (that means // identically bones with identical handles, and with same hierarchy, but // not necessary to have same number of bones and bone names). for (handle = 0; handle < numSrcBones; ++handle) { const Bone* srcBone = src->getBone(handle); ushort dstHandle = boneHandleMap[handle]; // Does it exists in target skeleton? if (dstHandle < numDstBones) { Bone* destBone = this->getBone(dstHandle); // Check both bones have identical parent, or both are root bone. const Bone* srcParent = static_cast<Bone*>(srcBone->getParent()); Bone* destParent = static_cast<Bone*>(destBone->getParent()); if ((srcParent || destParent) && (!srcParent || !destParent || boneHandleMap[srcParent->getHandle()] != destParent->getHandle())) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Source skeleton incompatible with this skeleton: " "difference hierarchy between bone '" + srcBone->getName() + "' and '" + destBone->getName() + "'.", "Skeleton::_mergeSkeletonAnimations"); } } else { existsMissingBone = true; } } // Clone bones if need if (existsMissingBone) { // Create missing bones for (handle = 0; handle < numSrcBones; ++handle) { const Bone* srcBone = src->getBone(handle); ushort dstHandle = boneHandleMap[handle]; // The bone is missing in target skeleton? if (dstHandle >= numDstBones) { Bone* dstBone = this->createBone(srcBone->getName(), dstHandle); // Sets initial transform dstBone->setPosition(srcBone->getInitialPosition()); dstBone->setOrientation(srcBone->getInitialOrientation()); dstBone->setScale(srcBone->getInitialScale()); dstBone->setInitialState(); } } // Link new bones to parent for (handle = 0; handle < numSrcBones; ++handle) { const Bone* srcBone = src->getBone(handle); ushort dstHandle = boneHandleMap[handle]; // Is new bone? if (dstHandle >= numDstBones) { const Bone* srcParent = static_cast<Bone*>(srcBone->getParent()); if (srcParent) { Bone* destParent = this->getBone(boneHandleMap[srcParent->getHandle()]); Bone* dstBone = this->getBone(dstHandle); destParent->addChild(dstBone); } } } // Derive root bones in case it was changed this->deriveRootBone(); // Reset binding pose for new bones this->reset(true); this->setBindingPose(); } // // We need to adapt animations from source to target skeleton, but since source // and target skeleton bones bind transform might difference, so we need to alter // keyframes in source to suit to target skeleton. // // For any given animation time, formula: // // LocalTransform = BindTransform * KeyFrame; // DerivedTransform = ParentDerivedTransform * LocalTransform // // And all derived transforms should be keep identically after adapt to // target skeleton, Then: // // DestDerivedTransform == SrcDerivedTransform // DestParentDerivedTransform == SrcParentDerivedTransform // ==> // DestLocalTransform = SrcLocalTransform // ==> // DestBindTransform * DestKeyFrame = SrcBindTransform * SrcKeyFrame // ==> // DestKeyFrame = inverse(DestBindTransform) * SrcBindTransform * SrcKeyFrame // // We define (inverse(DestBindTransform) * SrcBindTransform) as 'delta-transform' here. // // Calculate delta-transforms for all source bones. vector<DeltaTransform>::type deltaTransforms(numSrcBones); for (handle = 0; handle < numSrcBones; ++handle) { const Bone* srcBone = src->getBone(handle); DeltaTransform& deltaTransform = deltaTransforms[handle]; ushort dstHandle = boneHandleMap[handle]; if (dstHandle < numDstBones) { // Common bone, calculate delta-transform Bone* dstBone = this->getBone(dstHandle); deltaTransform.translate = srcBone->getInitialPosition() - dstBone->getInitialPosition(); deltaTransform.rotate = dstBone->getInitialOrientation().Inverse() * srcBone->getInitialOrientation(); deltaTransform.scale = srcBone->getInitialScale() / dstBone->getInitialScale(); // Check whether or not delta-transform is identity const Real tolerance = 1e-3f; Vector3 axis; Radian angle; deltaTransform.rotate.ToAngleAxis(angle, axis); deltaTransform.isIdentity = deltaTransform.translate.positionEquals(Vector3::ZERO, tolerance) && deltaTransform.scale.positionEquals(Vector3::UNIT_SCALE, tolerance) && Math::RealEqual(angle.valueRadians(), 0.0f, tolerance); } else { // New bone, the delta-transform is identity deltaTransform.translate = Vector3::ZERO; deltaTransform.rotate = Quaternion::IDENTITY; deltaTransform.scale = Vector3::UNIT_SCALE; deltaTransform.isIdentity = true; } } // Now copy animations ushort numAnimations; if (animations.empty()) numAnimations = src->getNumAnimations(); else numAnimations = static_cast<ushort>(animations.size()); for (ushort i = 0; i < numAnimations; ++i) { const Animation* srcAnimation; if (animations.empty()) { // Get animation of source skeleton by the given index srcAnimation = src->getAnimation(i); } else { // Get animation of source skeleton by the given name const LinkedSkeletonAnimationSource* linker; srcAnimation = src->_getAnimationImpl(animations[i], &linker); if (!srcAnimation || linker) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No animation entry found named " + animations[i], "Skeleton::_mergeSkeletonAnimations"); } } // Create target animation Animation* dstAnimation = this->createAnimation(srcAnimation->getName(), srcAnimation->getLength()); // Copy interpolation modes dstAnimation->setInterpolationMode(srcAnimation->getInterpolationMode()); dstAnimation->setRotationInterpolationMode(srcAnimation->getRotationInterpolationMode()); // Copy track for each bone for (handle = 0; handle < numSrcBones; ++handle) { const DeltaTransform& deltaTransform = deltaTransforms[handle]; ushort dstHandle = boneHandleMap[handle]; if (srcAnimation->hasNodeTrack(handle)) { // Clone track from source animation const NodeAnimationTrack* srcTrack = srcAnimation->getNodeTrack(handle); NodeAnimationTrack* dstTrack = dstAnimation->createNodeTrack(dstHandle, this->getBone(dstHandle)); dstTrack->setUseShortestRotationPath(srcTrack->getUseShortestRotationPath()); ushort numKeyFrames = srcTrack->getNumKeyFrames(); for (ushort k = 0; k < numKeyFrames; ++k) { const TransformKeyFrame* srcKeyFrame = srcTrack->getNodeKeyFrame(k); TransformKeyFrame* dstKeyFrame = dstTrack->createNodeKeyFrame(srcKeyFrame->getTime()); // Adjust keyframes to match target binding pose if (deltaTransform.isIdentity) { dstKeyFrame->setTranslate(srcKeyFrame->getTranslate()); dstKeyFrame->setRotation(srcKeyFrame->getRotation()); dstKeyFrame->setScale(srcKeyFrame->getScale()); } else { dstKeyFrame->setTranslate(deltaTransform.translate + srcKeyFrame->getTranslate()); dstKeyFrame->setRotation(deltaTransform.rotate * srcKeyFrame->getRotation()); dstKeyFrame->setScale(deltaTransform.scale * srcKeyFrame->getScale()); } } } else if (!deltaTransform.isIdentity) { // Create 'static' track for this bone NodeAnimationTrack* dstTrack = dstAnimation->createNodeTrack(dstHandle, this->getBone(dstHandle)); TransformKeyFrame* dstKeyFrame; dstKeyFrame = dstTrack->createNodeKeyFrame(0); dstKeyFrame->setTranslate(deltaTransform.translate); dstKeyFrame->setRotation(deltaTransform.rotate); dstKeyFrame->setScale(deltaTransform.scale); dstKeyFrame = dstTrack->createNodeKeyFrame(dstAnimation->getLength()); dstKeyFrame->setTranslate(deltaTransform.translate); dstKeyFrame->setRotation(deltaTransform.rotate); dstKeyFrame->setScale(deltaTransform.scale); } } } }