6

これは OpenGL に関する質問ではなく、C++ の組織に関する質問です。

(この質問のために) すべてのジオメトリをレンダリングするノードを持つ単純なシーン グラフ (n ツリー) を作成します。より具体的には、draw()メソッド内にいくつかの OpenGL 描画コマンドがあります。

最適化の理由から、同様のオブジェクトをまとめてバッチ処理し、一度に描画したいと考えています。このため、OpenGL の「状態セット」と呼んでいるものを表現する方法が必要です。状態セットは、OpenGL バインドの集まり、または X オブジェクトで draw が呼び出される前に設定され、後で設定解除されるコマンドです。

そのため、ステート セットには、このステート セットを使用するノードの描画コマンドの前後に、少なくともset()andがレンダリング システムによって呼び出されます。unset()

私の質問は、上記の状態セットを表現する方法ですか? 確かに多くの関数で実行できますが、セットに名前を付けて呼び出すことができるようにしたいと考えています。同様に、ノード A には の状態セットがLIGHTING_AND_SHADOWあり、ノード B には がありますCEL_SHADING

このため、基本的にとメソッドstateSetのインターフェイスである抽象クラスを呼び出し、各状態セットをそれから継承させることは良い考えのように思えます。ただし、本質的に名前を取得するには、一連のオブジェクトを作成する必要があります。もっと良い方法があるようです。set()unset()

stateSetsまた、理想的には、簡単に思い出せるすべてのリストが欲しいです。たとえば、レンダリングを開始する前に、シーン グラフ内のすべてのノードをstateSet.

何か案は?

4

2 に答える 2

2

シングルトンパターンを使用して状態を実装できます。次に、別のシングルトン状態トラッカークラスがこれらの状態クラスのインスタンスを管理し、まだ設定されていない場合にのみ状態を設定します。大まかな実装については、以下を参照してください。これにより、その方法についてのアイデアが得られるはずです。

class SingletonStateClass1 {
public:
    static SingletonStateClass1* getInstance() {
        if(! instanceFlag) {
            single = new SingletonStateClass1();
            instanceFlag = true;
            return single;
        }
        else {
            return single;
        }
    }
    void set() {
        // Do setting stuff
    }
    void unset() {
        // Do unsetting stuff
    }
    ~SingletonStateClass1() {
        instanceFlag = false;
    }

private:
    static bool instanceFlag;
    static SingletonStateClass1 *single;
    SingletonStateClass1() {} //private constructor
};
bool SingletonStateClass1::instanceFlag = false; //needs to be set so that the first call to getInstance() works ok.


//ASSUME THERE IS ANOTHER CLASS WITH SIMILAR DESIGN, NAMED: SingletonStateClass2  

class SingletonStateTracker {
public:
    static SingletonStateTracker* getInstance() {
        if(! instanceFlag) {
            single = new SingletonStateTracker();
            state1 = SingletonStateClass1::getInstance();
            state2 = SingletonStateClass2::getInstance();
            instanceFlag = true;
            isSetState1 = false;
            isSetState2 = false;
            return single;
        }
        else {
            return single;
        }
    }

    // Only setting a state unsets the other states
    void set1() {
        if (!isSetState1) {
            if (isSetState2) {
                state2->unset();
                isSetState2 = false;
            }
            state1->set();
            isSetState1 = true;
        }
    }
    void set2() {
        if (!isSetState2) {
            if (isSetState1) {
                state1->unset();
                isSetState1 = false;
            }
            state2->set();
            isSetState2 = true;
        }
    }

private:
    static bool instanceFlag;
    static bool isSetState1;
    static bool isSetState2;
    static SingletonStateTracker *single;
    static SingletonStateClass1 *state1; 
    static SingletonStateClass2 *state2; 
    SingletonStateTracker() {} //private constructor
};
bool SingletonStateTracker::instanceFlag = false; //needs to be set so that the first call to getInstance() works ok.




class DrawableObject1 {
public:
    DrawableObject1() {
        tracker = SingletonStateTracker::getInstance();
    }
    void draw() const
    {
        tracker->set1();
        //DO YOUR OBJECT SPECIFIC OPENGL DRAW STUFF HERE
    }
private:
    SingletonStateTracker* tracker;
};

class DrawableObject2 {
public:
    DrawableObject2() {
        tracker = SingletonStateTracker::getInstance();
    }
    void draw() const
    {
        tracker->set2();
        //DO YOUR OBJECT SPECIFIC OPENGL DRAW STUFF HERE
    }
private:
    SingletonStateTracker* tracker;
};


/* Below two classes show a crude usage of the above design */
class Scene {
    // ... other stuff ...

public:
    DrawableObject1 obj1a;
    DrawableObject1 obj1b;
    DrawableObject2 obj2;
    // ... other stuff ...
};

class Viewer {
    // ... other stuff ...
public:
    void draw() {
        scene->obj1a.draw(); //This call unsets state2, sets state1
        scene->obj1b.draw(); //This call does not set any state since state1 is already set
        scene->obj2.draw();  //This call unsets state1, sets state2
    }
private:
    Scene* scene;
    // ... other stuff ...
};

上記の設計は非常に単純であることに注意してください。質問で述べたように、共通のインターフェースを継承するために複数の状態クラスを持つことができます。重要なのは、オブジェクトが状態を設定するためのTracker(別名Manager)クラスを持つことです。Trackerクラスの役割は、状態がすでに設定されている場合は不要な設定を排除し、現在設定されている場合は他の状態の設定を解除することです。

于 2012-12-30T20:41:36.317 に答える
1

このため、基本的におよびメソッドstateSetのインターフェイスである抽象クラスを呼び出し、各状態セットをそれから継承させることは良い考えのように思えます。ただし、本質的に名前を取得するには、一連のオブジェクトを作成する必要があります。set()unset()

仮想的な抽象クラスの実装は、通常、関数ポインターの配列として実装される仮想関数テーブルを通じて機能します。この場合にインスタンス化する明らかに無意味なオブジェクトは、意味のある状態を保持します-それらの関数ポインター。

2 つの関数ポインターの多数の配列を作成する代わりに、関数ポインターの 2 つの配列を作成し、配列にインデックスを付け、最後に使用したインデックスを保持し、配列を間接的に参照する前に が異なるstateSet(uint state_name)かどうかを確認する必要があります。キーワードstate_nameを使用して、そのすべてのグローバル状態を他のコンパイル単位から隠すことをお勧めします。static

このアプローチでは、自動安全性が低くなります。意味的には、整数を渡すのを止めるものは何もstateSet(uint)なく、生の配列を自分で配置しない限り、範囲チェックはありません。

それ以外の点では、2 つのオプションは基本的に非常に似ています。自分の考えを十分に考慮してください。

于 2012-12-30T21:03:33.733 に答える