5

私は学術目的でカスタムコリジョンエンジンを作成しようとしていますが、一般的なC ++プログラミングの問題に固執しています。適切に機能するすべてのジオメトリが既にあり、質問の範囲でこの機能があります:

template<typename lhs_geometry, typename rhs_geometry>
bool intersects( const lhs_geometry& lhs, const rhs_geometry& rhs )
{
    //returns true if does objects intersects 
    //(assume this functions works perfectly with every geometry type)
}

実装を完了する必要がある次のクラスもあります

template<typename geometry_type>
class collidable_object
{
public:
    explicit collidable_object( geometry_type& geometry ) :
        m_geometry( geometry )
    {

    }

    ~collidable_object()
    {

    }

private:
    geometry_type& m_geometry;
};

私の問題が発生するのは、リストを作成collidable_objectして交差 2 ごとにテストしたいときです。

Google でいくつかの調査を行ったところ、基本クラスをcollidable_object使用すると、オブジェクトをリストに格納できることがわかりました。しかし、その後、特定のジオメトリに応じてオブジェクトをテストするにはどうすればよいでしょうか?

私は訪問者パターンを実装しようとしましたが、私は常にintersetcs().

協力的な訪問者に関する記事も見つけましたが、これは複雑なようです。

誰もがシンプルで効率的なソリューションを持っていますか?

編集: ジオメトリのリストを持つことを避けたかった理由は、樹木でファイルを見つけなくても新しいジオメトリを比較的簡単に追加できるようにしたいからです。

EDIT2:intersetcsメソッドの詳細は次のとおりです。交差メソッドは、適切なジオメトリを見つけるためのタグ ディスパッチに基づいていますが、ほとんどすべての凸形状は、オブジェクトが特定の方向で最も遠いポイントを返すことができることのみを必要とする GJK アルゴリズムを使用します。非凸形状の場合、形状は凸サブ形状に断片化され、プロセスが再開されます。

intersectsが特定の形状を処理できるかどうかを確認するための統一基準はありfurthest_alongませんが、球上の球はそうではなく、球の集約も の使用を必要としません。furthest_along

追加情報: VS2012 と C++11 を使用しています

4

2 に答える 2

3

考えられるすべてのジオメトリのリストをどこかに保存せずに逃げることはできません。そうしないと、コンパイラは生成するテンプレートのインスタンス化を認識できません。しかし、そのリストを単一の場所、つまりGeometryTypes. 他のすべてはそれに基づいています。ここでは vistor パターンを使用していません。これには、さまざまなジオメトリ クラスの実装にボイラープレート コードを追加する必要がないという利点があります。intersectsすべての組み合わせに対して実装するだけで十分です。

最初のいくつかの内容: 後で使用しshared_ptrて印刷し、不明なジオメトリ タイプの場合は中止します。

#include <memory>
#include <iostream>
#include <cstdlib>

ここで、ポリモーフィック ポインターに使用できる共通の基本クラスを使用して、いくつかのジオメトリを定義します。後で使用できる仮想関数テーブルを取得できるように、少なくとも 1 つの仮想関数を含める必要がありますdynamic_cast。デストラクタをポリモーフィックにすると、ポリモーフィック ポインタを介して削除された場合でも、派生クラスが適切にクリーンアップされることが保証されます。

struct Geometry {
  virtual ~Geometry() { }
};
struct Circle : public Geometry { };
struct Rectangle : public Geometry { };

intersectsこれでテンプレートが表示されます。このデモ用に、すべてを網羅する実装を 1 つだけ作成します。

template<typename lhs_geometry, typename rhs_geometry>
bool intersects(const lhs_geometry& lhs, const rhs_geometry& rhs) {
  std::cout << __PRETTY_FUNCTION__ << " called\n"; // gcc-specific?
  return false;
}

これは、すべてのジオメトリのリストを宣言する場所です。相互に派生したジオメトリがある場合は、動的キャストのためにこれらが試行されるため、最も具体的なものを最初に用意してください。

template<typename... Ts> class TypeList { };
typedef TypeList<Circle, Rectangle> GeometryTypes;

ヘルパーコードの束です。基本的な考え方は、そのようなものを反復しTypeList、すべての型に対して動的キャストを試みることです。最初のヘルパーは lhs 引数を反復し、2 番目は rhs 引数を反復します。一致するものが見つからない場合は、リストが不完全であるため、アプリケーションが異常終了し、役立つと思われるエラー メッセージが表示されます。

template<typename TL1, typename TL2> struct IntersectHelper1;
template<typename T1, typename TL2> struct IntersectHelper2;

template<typename TL2, typename T1, typename... Ts>
struct IntersectHelper1<TypeList<T1, Ts...>, TL2> {
  static bool isects(Geometry* lhs, Geometry* rhs) {
    T1* t1 = dynamic_cast<T1*>(lhs);
    if (!t1)
      return IntersectHelper1<TypeList<Ts...>, TL2>::isects(lhs, rhs);
    else
      return IntersectHelper2<T1, TL2>::isects(t1, rhs);
  }
};

template<typename T1, typename T2, typename... Ts>
struct IntersectHelper2<T1, TypeList<T2, Ts...>> {
  static bool isects(T1* lhs, Geometry* rhs) {
    T2* t2 = dynamic_cast<T2*>(rhs);
    if (!t2)
      return IntersectHelper2<T1, TypeList<Ts...>>::isects(lhs, rhs);
    else
      return intersects(*lhs, *t2);
  }
};

// Catch unknown types, where all dynamic casts failed:

bool unknownIntersects(Geometry* g) {
  std::cerr << "Intersection with unknown type: "
            << typeid(*g).name() << std::endl;
  std::abort();
  return false; // should be irrelevant due to abort
}

template<typename TL2>
struct IntersectHelper1<TypeList<>, TL2> {
  static bool isects(Geometry* lhs, Geometry* rhs) {
    return unknownIntersects(lhs);
  }
};

template<typename T1>
struct IntersectHelper2<T1, TypeList<>> {
  static bool isects(T1* lhs, Geometry* rhs) {
    return unknownIntersects(rhs);
  }
};

これらすべてのヘルパーが配置されたので、ポリモーフィック交差テストを実行できるようになりました。このようなポリモーフィック ポインターを格納するために を導入しています。クラスshared_ptrでも同じことを行うことをお勧めします。collidable_objectそれ以外の場合は、衝突可能なオブジェクトが生きている限り、参照されたジオメトリが生き続けることを保証する責任を負う必要がありますが、最終的にはクリーンアップされます。そんな責任取りたいの?

typedef std::shared_ptr<Geometry> GeomPtr;

bool intersects(GeomPtr lhs, GeomPtr rhs) {
  return IntersectHelper1<GeometryTypes, GeometryTypes>::
    isects(lhs.get(), rhs.get());
}

最後に main をいくつか追加して、上記のコードをすべて小さな例で実際に実行できるようにします。

int main() {
  GeomPtr g1(new Rectangle), g2(new Circle);
  std::cout << intersects(g1, g2) << std::endl;
  return 0;
}
于 2013-06-19T07:31:09.243 に答える