3

Mesh クラスの汎用プログラムを作成しようとしていました。(メッシュを変形するには CPU 変換が必要です。質問を考えている場合に備えて、GPU でこれを実行しないのはそのためです)

Mesh<T>は、3D シェイプの頂点と、面のタイアップ (頂点を結合して三角形を作るもの) が含まれています。

頂点のタイプは次のとおりTです。一部のタイプのモデルには PNCT タイプ (位置、法線、色、texcoord) があり、他は単なる PC (位置、色) です。

struct VertexPNCT {
    Vector3 pos, normal ;
    Vector4 color ;
    Vector2 tex ;
} ;

struct VertexPC {
    Vector3 pos ;
    Vector4 color ;
} ;

もちろん、すべての頂点には位置があります。

しかし、ここに私の問題があります。

Mesh<T>メソッドを実装する必要がありtransformます。もちろん、すべての頂点形式には位置があり、名前が一貫している場合 ( .pos)、メソッドは正常に機能します。

すべての頂点には確実に位置があるため、常に構造体でそのメンバー.posを呼び出すと、次のようにテンプレート化されたクラスにVertex*メソッドを追加できます。.transformMesh<T>

void transform( Matrix4& mat )
{
    each vertex
      vertex.pos = mat * vertex.pos ; // transform it
}

ここに問題があります。 頂点フォーマットに法線が含まれている場合、その法線も変換する必要があります (「法線マトリックス」によって)。テンプレートに「if ステートメント」がありますか? 頂点の種類ごとにテンプレートの特殊.transform化メソッドを作成する必要があります (基本的に、法線のあるものと法線のないものの 2 つのカテゴリに分けられます)。

ここでテンプレートを誤用しましたか? 「プログラミングボート」を見逃しましたか?

私が本当に論理的にやりたいことは次のとおりです。

void transform( Matrix4& mat )
{
    each vertex in vertices
    {
      vertex.pos = mat * vertex.pos ; // transform it
      #ifdef vertex.normal
      vertex.normal = mat * vertex.normal ; // transform normal as well*
      #endif
    }
}

* 変換にスケールがないことを前提としています (その場合、「通常のマトリックス」を使用する必要はありません)

4

3 に答える 3

3

Vertexベースが位置 (および.posメンバー) のみを保持し、 aVertexWNormalが保持し、残りがそこから継承される階層を記述できる場合は.normal、テンプレート化されていないオーバーロードを追加して、コンパイラに処理させることができます。

void transform( Matrix4& m, VertexBase& v ) {
   // transform only pos
}
void transform( Matrix4& m, VertexWNormal& v ) {
   transform(m,static_cast<VertexBase&>(v));
   // transform normal here
}
void tranform( Matrix4& m ) {
   foreach vertex:
       transform(m,vertex);
}

もちろん、これがあなたのデザインにとって意味があるかどうかは、あなたが示していない多くの事柄に依存するため、判断するのは困難です.

于 2012-10-03T18:07:42.617 に答える
2

頂点が 2 種類しかない場合は、David が上で説明したことを実行することをお勧めします。変換を行う 2 つの関数を作成し、頂点の種類に基づいてオーバーロードを使用して呼び出すだけです。これは、より多くの頂点タイプでも機能しますが、新しい頂点タイプを追加するたびに、関数を再度オーバーロードする必要があります。これは、ここで説明したような単純な関数では問題ないかもしれませんが、関数が実際にはもっと複雑な場合は面倒になるかもしれません。

部分的な修正では、特定の頂点タイプに通常のメンバーがあるかどうかを示す特性クラスを作成します。デフォルトは、ほとんどの場合に正しく、特性に基づいて適切な関数を選択できるように設定できます。コードの 2 つのバージョンを引き続き提供しますが、追加の頂点タイプごとに、必要なのは特性を定義することだけです。

template <typename> struct VertexHasNormal { enum { value = false }; };
template <> struct VertexHasNormal<VertexPNCT> { enum { value = true }; };

template <typename V, template T, template S>
void transform(V& vertices, Matrix4& m, T S::*member) {
    for (auto& v: vertices) {
        v.*member = mat * v.*member;
    }
}

template <bool, typename T>
struct Transform {
    template <typename V>
    void call(V& vertices, Matrix4& m) {
        transform(vertices, m, &T::pos);
    }
};
template <typename T>
struct Transform<bool, T> {
    template <typename V>
    void call(V& vertices, Matrix4& m) {
        transform(vertices, m, &T::pos);
        transform(vertices, m, &T::normal);
    }
};

template <typename T>
void Mash<T>::transform(Matrix4& m) {
    Transform<VertexHasNormal::value>::call(this->vertices, m);
}

関数テンプレートtransform()は、一連の頂点に対して実際の変換を行う関数です。因数分解できるけど因数分解する必要がないのはちょっとかわいいと思うので因数分解しました。autoまた、メンバーへのポインターなどを使用する必要もありませTransformん。関数テンプレートは部分的に特殊化できないため、型は使用される単なる補助型です。normal頂点タイプがメンバーの特性を持っているかどうかに特化しています。最後にMash<T>::transform()、特殊な関数の適切なバージョンにディスパッチするだけです。

normal必要なのは、メンバーを持つ別の頂点が定義されたときに、新しい特性の特殊化を追加することだけです。それは望ましくないかもしれません。この場合normal、型特性を使用して呼び出されるアクセス可能なデータ メンバーが構造体にあるかどうかを判断できます。しかし、頭のてっぺんからこれをタイプすることはできないと思います。それを実装するための基本的な考え方は、置換の失敗はエラーではない (" SFINAE") という事実と、テストされた型に必要なメンバーがある場合、2 つの潜在的なメンバー間の曖昧さをなくすように設定できるという事実を利用することです。それを行うBoostコンポーネントがありますが、自分で作成する必要がある場合は、約10行のコードです.

于 2012-10-03T18:54:27.817 に答える
1

おそらく、構造体クラスを作成し、それらを 2 つの基底クラスから派生させたいと思うでしょう。次に、テンプレートの特殊化を使用して、(すべての頂点クラスではなく) これら 2 つの基本クラスを選択できます。

于 2012-10-03T18:04:11.803 に答える