インデックス球体の簡単作成





やあみんな。よくポリゴンメッシュをインデックスフォーマットで作成するための実験として、球体のインデックスフォーマットデータを作りたい!なんてことがあるね。
ところが球体ときたら、まあ極座標ベースで構築する話だけれども、球体の極座標ベース構築ときたら北極南極部位の処理が特殊だったり、トポロジー的に穴ができたりしないようにいろいろとささいな気遣いが必要で、テストしたりして結局半日仕事とは言わないが、どんなに速くても0.5時間仕事くらいにはなってしまうのではないだろうか。
そんな時のために本日のCreateIndexSphere()を紹介するぞ。ソースは以下のようになったよ。まずは全て三角形のポリゴンからなる、三角メッシュとしての球体から行ってみようか。

template< class Vec3 >
void CreateIndexSphere( const int nres, const float radi,
                       vector< int >* il, vector< Vec3 >* pl )
{
    // 出力の初期化
    il->clear();
    pl->clear();
    // 初期設定
    const float uni = M_PI/(float)nres;
    const int nres2 = nres*2;
    // 南極北極以外の部位の座標を生成
    for( int i=1; i<nres; ++i ){
        for( int j=0; j<nres2; ++j ){
            const float phi = j*uni;
            const float tht = i*uni;
            Vec3 pos = Vec3(
                            radi*sin(tht)*cos(phi),
                            -radi*cos(tht),
                            -radi*sin(tht)*sin(phi) );
            pl->push_back( pos );
        }// j
    }// i
    // 南極北極の座標を作成
    auto spole_id = pl->size();
    pl->push_back( Vec3( 0, -radi, 0 ) );
    auto npole_id = pl->size();
    pl->push_back( Vec3( 0, radi, 0 ) );
    // 南極北極以外の部位のトポロジーを生成
    IdCirculator ids(nres2);
    for( int i=0; i<nres-2; ++i ){
        for( int j=0; j<nres2; ++j ){
            auto ii = i+1;
            auto jj = ids(j+1);
            il->push_back( i*nres2 + j );
            il->push_back( i*nres2 + jj );
#if 01
            il->push_back( ii*nres2 + j );
            il->push_back( -1 );
            il->push_back( ii*nres2 + j );
            il->push_back( i*nres2 + jj );
            il->push_back( ii*nres2 + jj );
#else
            il->push_back( ii*nres2 + jj );
            il->push_back( ii*nres2 + j );            
#endif
            il->push_back( -1 );
        }// j
    }// i
    // 南極および北極部位のトポロジーを生成
    for( int j=0; j<nres2; ++j ){
        int jj = ids(j+1);
        il->push_back( (nres-2)*nres2 + j );
        il->push_back( (nres-2)*nres2 + jj );
        il->push_back( npole_id );
        il->push_back( -1 );
        il->push_back( jj );
        il->push_back( j );
        il->push_back( spole_id );
        il->push_back( -1 );
    }// j
    return;
}

上記ソースに出てくる「IdCirculator」についてはこちら未来日記を参照してほしい。
どうだろう?あまりスマートな感じのするコードではないが汎用性は随分と高そうな感じしないかこれ。これをコピペしていつでも好きなときにインデックス球体を作成してくれ。Vec3に要求されるインタフェースは、x, y, zのfloat値を受け取るコンストラクタだけだよ。(例えばglm::vec3などはそのまま使える。)
ちなみにだ。上述の#if 01を、#if 0 にするだけで、北極南極の部位以外はクアッドメッシュで表現したバージョンなったりもするよ。(冒頭図、右)
最後にこれを使って作成したポリゴンメッシュのオイラー数はいずれもちゃんと2になっているよということを記述しておくとしよう。うっと、今日は雨か、やれやれ最悪だな…。じゃっ!