したがって、接続されたジョイントのシステムがあり、あるフレームから別のフレームへのジョイントの相対的なデルタ回転を見つけたいと考えています。「相対回転」自体では、それが何に対して相対的であるかがわからないため、相対回転をローカル回転と呼びます。(それは、宇宙の中心などに対して、オブジェクトに対して相対的ですか?)
仮定
各ジョイントには親が 1 つだけあり、ルート ジョイントが 1 つだけ (親なし) になるように、ジョイントのツリー構造を想定します。ジョイントごとに複数の親がある場合、同じソリューションを使用できるはずですが、各ジョイントは親ごとに 1 つの相対回転を行うため、親ごとに計算を 1 回行う必要があります。親のないジョイントが複数ある場合、それぞれが、接続されたジョイントで構成される独自のツリーのルートと考えることができます。
次のことができる四元数数学ライブラリがあると仮定します。軸と角度から作成し、単位元、逆数、四元数の累積に設定します。そうでない場合は、ウィキペディアまたは Google からそれらを実装するために必要なすべての情報を見つける必要があります。
回転の計算
以下のコードは、最初にフレームの開始と終了のジョイントのローカル回転を計算します。2 つのベクトルを使用してこの計算を行います。親からのベクトルと祖父母から親へのベクトル。次に、デルタ回転を計算するために、反転された開始回転を使用して、逆回転を適用することによって終了回転から開始回転を「削除」します。そのため、そのフレームのローカル デルタ ローテーションで終了します。
ジョイント階層の最初の 2 つのレベルには、直接解決できる特殊なケースがあります。
疑似コード
out パラメータは、 resultという名前の多次元配列です。
注意: startPosition、endPosition、parentStartPosition、parentEndPosition、grandParentStartPosition、grandParentStartPosition はすべて、ループの反復ごとに更新する必要があります。問題の核心に焦点を当てるために、その更新は表示されません。
for each frame {
for each joint {
if no parent {
// no parent means no local rotation
result[joint,frame] = identityQuaternion
}
else {
startLink = startPosition - parentStartPosition
endLink = endPosition - parentEndPosition
if no grandParent {
// no grand parent - we can calculate the local rotation directly
result[joint,frame] = QuaternionFromVectors( startLink, endLink )
}
else {
parentStartLink = parentStartPosition - grandParentStartPosition
parentEndLink = parentEndPosition - grandParentEndPosition
// calculate the local rotations
// = the difference in rotation between parent link and child link
startRotation = QuaternionFromVectors( parentStartLink, startLink )
endRotation = QuaternionFromVectors( parentEndLink, endLink )
// calculate the delta local rotation
// = the difference between start and end local rotations
invertedStartRotation = Inverse( startRotation )
deltaRotation = invertedStartRotation.Rotate( endRotation )
result[joint,frame] = deltaRotation
}
}
}
}
QuaternionFromVectors( fromVector, toVector )
{
axis = Normalize( fromVector.Cross( toVector ) )
angle = Acos( fromVector.Dot( toVector ) )
return Quaternion( axis, angle )
}
C++ 実装
以下は、テストされていない C++ での再帰的な実装です。フレームごとに、JointDataツリーのルートから開始し、 JointData::calculateRotations()関数を再帰的に呼び出してツリーをトラバースします。
コードを読みやすくするために、ジョイント ツリー ノードJointDataから FrameData へのアクセサーがあります。おそらく、実装でそのような直接的な依存関係を持ちたくないでしょう。
// Frame data holds the individual frame data for a joint
struct FrameData
{
Vector3 m_positionStart;
Vector3 m_positionEnd;
// this is our unknown
Quaternion m_localDeltaRotation;
}
class JointData
{
public:
...
JointData *getChild( int index );
int getNumberOfChildren();
FrameData *getFrame( int frameIndex );
void calculateDeltaRotation( int frameIndex, JointData *parent = NULL,
Vector3& parentV1 = Vector3(0),
Vector3& parentV2 = Vector3(0) );
...
}
void JointData::calculateDeltaRotation( int frameIndex, JointData *parent,
Vector3& parentV1, Vector3& parentV2 )
{
FrameData *frameData = getFrame( frameIndex );
if( !parent )
{
// this is the root, it has no local rotation
frameData->m_localDeltaRotation.setIdentity();
return;
}
FrameData *parentFrameData = parent->getFrame( frameIndex );
// calculate the vector from our parent
// for the start (v1) and the end (v2) of the frame
Vector3 v1 = frameData->m_positionStart - parentFrameData->m_positionStart;
Vector3 v2 = frameData->m_positionEnd - parentFrameData->m_positionEnd;
if( !getParent()->getParent() )
{
// child of the root is a special case,
// we can calculate it's rotation directly
frameData->m_localDeltaRotation = calculateQuaternion( v1, v2 );
}
else
{
// calculate start and end rotations
// apply inverse start rotation to end rotation
Quaternion startRotation = calculateQuaternion( parentV1, v1 );
Quaternion endRotation = calculateQuaternion( parentV2, v2 );
Quaternion invStartRot = startRotation.inverse();
frameData->m_localDeltaRotation = invStartRot.rotate( endRotation );
}
for( int i = 0; i < getNumberOfChildren(); ++i )
{
getChild( i )->calculateRotations( frameIndex, this, v1, v2 );
}
}
// helper function to calulate a quaternion from two vector
Quaternion calculateQuaternion( Vector3& fromVector, Vector3& toVector )
{
float angle = acos( fromVector.dot( toVector ) );
Vector3 axis = fromVector.cross( toVector );
axis.normalize();
return Quaternion( axis, angle );
}
コードは読みやすくするために書かれており、最適ではありません。