XSIのシーンからデータを抽出する3

今回はマトリクスとウェイトデータに関してです。

まずはマトリクスを取得します。

void Func( const X3DObject& obj )
{
    MATH::CMatrix4 global = obj.GetKinematics().GetGlobal().GetTransform().GetMatrix4();
    MATH::CMatrix4 local  = obj.GetKinematics().GetLocal().GetTransform().GetMatrix4();
    CString        name   = obj.GetName();

    CRefArray children = obj.GetChildren();
    for( LONG i = 0 ; i < children.GetCount() ; ++i ){
        X3DObject child( children[ i ] );
        Func( child );
    }

}

{
    Project project = app.GetActiveProject();
    Scene   scene   = project.GetActiveScene();
    Model   root    = scene.GetRoot();

    Func( root );
}

こんな感じでルートから再帰的に末端までのマトリクスを取得していきます。


ちなみにDirectXは左手系、XSIは右手系の座標系なので、
マトリクスも変換しなくてはいけません。

void ConvertMatrix( D3DXMATRIX& dst, const MATH::CMatrix4& src )
{
    double d00, d01, d02, d03;
    double d10, d11, d12, d13;
    double d20, d21, d22, d23;
    double d30, d31, d32, d33;

    src.Get(
        d00, d01, d02, d03,
        d10, d11, d12, d13,
        d20, d21, d22, d23,
        d30, d31, d32, d33
    );
    dst._11 = static_cast< float >(  d00 );
    dst._12 = static_cast< float >(  d01 );
    dst._13 = static_cast< float >( -d02 );
    dst._14 = static_cast< float >(  d03 );
    dst._21 = static_cast< float >(  d10 );
    dst._22 = static_cast< float >(  d11 );
    dst._23 = static_cast< float >( -d12 );
    dst._24 = static_cast< float >(  d13 );
    dst._31 = static_cast< float >( -d20 );
    dst._32 = static_cast< float >( -d21 );
    dst._33 = static_cast< float >(  d22 );
    dst._34 = static_cast< float >(  d23 );
    dst._41 = static_cast< float >(  d30 );
    dst._42 = static_cast< float >(  d31 );
    dst._43 = static_cast< float >( -d32 );
    dst._44 = static_cast< float >(  d33 );
}

なぜこのようになるかは面倒なので説明は省きます。
きちんと幾何学を解説しているページを探してみてください。
ちなみにベクトルもZ要素(厳密にはZとは限りませんが)の符号を反転する必要があります。

次にウェイトデータの取得方法です。
ウェイトデータもマテリアルの時のようにClusterクラスからアクセスします。

void Func( const PolygonMesh& mesh )
{
    CRefArray    clusters = mesh.GetClusters();
    CStringArray deformer_names;

    for( LONG i = 0 ; i < clusters.GetCount() ; ++i ){
        Cluster   cluster   = clusters[ i ];
        CRefArray envelopes = cluster.GetEnvelopes();

        for( LONG j = 0 ; j < envelopes.GetCount() ; ++j ){
            Envelope  envelope  = envelopes[ j ];
            CRefArray deformers = envelope.GetDeformers();

            for( LONG k = 0 ; k < deformers.GetCount() ; ++k ){
                X3DObject deformer = deformers[ k ];
                deformer_names.Add( deformer.GetName() );
            }

            CClusterPropertyElementArray weights = envelope.GetWeights( 0 );
            if( weights.GetCount() == 0 ){
                continue;
            }

            for( LONG k = 0 ; k < weights.GetCount() ; ++k ){
                CDoubleArray items = weights.GetItem( k );
                unsigned int num = 0;
                unsigned int deformer_indices = 0;
                float weight[ 4 ] = 0.0f;
                for( int index = 0 ; index < items.GetCount() ; ++index ){
                    // 1頂点につきウェイト付けできるマトリクスは4つまでにしておく
                    if( ( items[ index ] > 0.0f ) && ( num < 4 ) ){
                        deformer_indices |= ( ( 0x000000ff & index ) << ( num * 8 ) );  // 1byte毎にマトリクスのインデックスを格納する
                        weight[ num ] = static_cast< float >( items[ index ] * 0.01 );  // DirectXでは0.0 ~ 1.0 なので 100 で割る
                        ++num;
                    }
                }
            }
        }
    }
}

このようにdeformer(ウェイト付けされるオブジェクト)の名前と
どのdeformaerにウェイト付けされるかのインデックスを4byteの変数に格納したものと、
どのdeformaerに何%づつウェイト付けされているかの数値を取っておきます。
DirectXの固定機能パイプラインでは変換マトリクスは256個まで、
ウェイト付けできるマトリクスは1頂点につき4つまでなので、
上記のコードのようになります。


大分説明が足りていない気がしますが、これで一応必要なデータは揃いました。
これをプロジェクトに組み込んで見たのがこれです。
exporter02
見た目では分かりませんが、ちゃんとスキニングされています…
DirectXでスキンメッシュを表示させる方法はきちんと説明が必要かと思いますが、
とりあえず今回はプラグインの話なので割愛します。
次にアニメーションを実装してプラグインの話はひと段落して、
その時点でソースをまとめて公開したいと思います。

コメントする

post date*

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

トラックバックする

トラックバック用URL:

アニメーションが親切に解説されております

レンダリング、ライティングの基本が分かります

図版が見やすい美術解剖書です