2012-12-30 12 views
6

To jest mniej pytanie OpenGL i więcej o organizacyjne pytanie C++.Zestawy stanu OpenGL

Będę miał prosty wykres sceny (n-drzewa) z węzłami, które (ze względu na to pytanie) będą renderować pewną geometrię. Mówiąc dokładniej, każdy z nich posiada pewne polecenia do rysowania OpenGL w metodzie draw().

Ze względów optymalizacyjnych chciałbym grupować podobne obiekty razem i narysować je wszystkie naraz. Z tego powodu chciałbym wyrazić to, co nazywam "zestawami stanów" dla OpenGL. Ustawiony stan to tylko kilka wiązań OpenGL lub polecenia ustawione przed losowaniem są wywoływane na obiektach X, a następnie zostają rozbrojone później.

Tak więc zestaw stanów ma co najmniej set() i unset() i będzie wywoływany przez system renderowania przed i po komendach rysowania węzłów, które używają tego zestawu stanów.

Moje pytanie brzmi: jak wyrazić wspomniane zbiory stanów? Jasne, że wiele funkcji może zrobić, ale wolałbym móc nazwać zestaw i go przywołać. Podobnie jak węzeł A ma zestaw stanów LIGHTING_AND_SHADOW, a węzeł B ma CEL_SHADING.

Z tego powodu podejmowania abstrakcyjną klasę o nazwie stateSet która jest zasadniczo interfejs do metod set() i unset() i posiadające każdego zestawu stanu dziedziczą wydaje się dobrym pomysłem. Wymaga to jednak stworzenia kilku obiektów, aby w zasadzie uzyskać nazwę. wygląda na to, że może być lepszy sposób.

Również idealnie chciałbym mieć listę wszystkich stateSets, które można łatwo przywołać. Na przykład przed rozpoczęciem renderingu dobrze byłoby móc sortować wszystkie węzły na wykresie sceny za pomocą ich stateSet.

Wszelkie pomysły?

+0

Wygląda na to, że prosisz nas o nadanie nazw twoim zestawom stanów. Czy możesz trochę wyjaśnić, jakiego rodzaju pomocy szukasz. – Mads

+0

Jeśli aktywny program GLSL jest częścią twojego "zestawu stanów", możesz uczynić te "zestawy stanów" częścią swojej klasy programu GLSL: każdy wie, jakie stany musi ustawić i program, który musi aktywować. Następnie zgrupujesz węzły na program przed renderowaniem. – Gigi

Odpowiedz

2

Możesz zaimplementować swoje stany używając wzoru singleton. Następnie inna klasa trackera stanu pojedynczego zarządza instancjami tych klas stanów i ustawia tylko stan, gdy nie jest jeszcze ustawiony. Poniżej przedstawiono prostą implementację. To powinno dać ci pojęcie, jak się do tego zabrać:

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 ... 
}; 

Pamiętaj, że powyższy projekt jest bardzo prosty. Możesz mieć wiele klas stanów, aby dziedziczyć wspólny interfejs, jak zauważyłeś w swoim pytaniu. Kluczem jest posiadanie klasy Tracker (a.k.a Manager), przez którą obiekty ustawiają stany. Zadaniem klasy Tracker jest wyeliminowanie niepotrzebnego ustawienia stanu, jeśli jest już ustawione, i anulowanie pozostałych stanów, jeśli są ustawione.

+0

To jest doskonałe, zdecydowanie mogę z tym skorzystać. Jak zasugerowałeś, dodam interfejs StateSet z set() unset() i getInstance(), a menedżer może mieć wiele różnych gadżetów. Szczególnie podoba mi się zdolność menedżerów do wywoływania unset() za każdym razem, gdy ustawiany jest inny StateSet, eliminując w ten sposób potrzebę wywoływania przez użytkownika nieustawionych ustawień. Dzięki jeszcze raz! -Cody –

+0

Dalsze przemyślenia: z bezpiecznej perspektywy projektowania można zawrzeć definicję klas stanów prywatnie w klasie menedżerskiej. W ten sposób klasy te nie muszą nawet być pojedyncze, ponieważ menedżer upewnia się, że istnieje tylko jedna statyczna instancja każdej z tych klas stanów i nie ma publicznego dostępu do ich konstruktorów. Kierownikiem jest jednak Singleton. To trochę klasa loggera, gdzie Singleton jest zasugerowany i szeroko stosowany. – meyumer

1

Z tego powodu podejmowania abstrakcyjną klasę o nazwie stateSet który jest zasadniczo interfejs do metod set() i unset() i posiadające każde państwo ustawić dziedziczą wydaje się dobrym pomysłem. Ale, wymaga stworzenia kilku obiektów tylko po to, aby uzyskać nazwę.

Twoja hipotetyczna implementacja klas abstrakcyjnych działa poprzez wirtualną tablicę funkcji, która jest zwykle implementowana jako tablica wskaźników funkcji. Pozornie bezcelowe obiekty, które tworzysz w tym przypadku, mają znaczący stan - te wskaźniki funkcji.

Jako alternatywę do tworzenia wielu tablic dwóch wskaźników funkcji, być może trzeba utworzyć dwie tablice wskaźników funkcji, indeksów nazwę na macierzach, zachowują ostatniego indeksu używane i mają swój czek stateSet(uint state_name) aby sprawdzić, czy state_name różni przed pośredniem przez tablice. Polecam ukrywanie całego tego stanu globalnego przed innymi jednostkami kompilacji za pomocą słowa kluczowego static.

Takie podejście oferuje mniej automatyczne bezpieczeństwo - semantycznie nic nie powstrzyma przed przekazaniem dowolnej liczby całkowitej do stateSet(uint) i nie ma żadnego sprawdzenia zakresu na tablicy raw, chyba że umieścisz ją samodzielnie.

Te dwie opcje są zasadniczo całkowicie podobne; więc weź swój własny pomysł z należytą uwagą.