OpenGL State Sets

These are fewer OpenGL questions and more C ++ related questions.

I will have a simple scene graph (n-tree) with nodes that (for the sake of this question) will display some geometry. To be more specific, each of them has some OpenGL drawing commands inside the draw() method.

For optimization reasons, I would like to combine similar objects together and draw them all at once. For this reason, I would like to express what I call "state sets" for OpenGL. A set of states is just a bunch of OpenGL bundles, or the commands that are installed before drawing are called on X objects and after that they become uninstalled.

Thus, a set of states has at least set() and unset() and will be called by the rendering system before and after commands to draw nodes that use this set of states.

My question is how to express these states of sets? Of course, there can be many functions, but I would prefer to name the set and recall it. For example, node A has a set of states LIGHTING_AND_SHADOW and node B has CEL_SHADING .

For this reason, creating an abstract class called stateSet , which is essentially an interface to the set() and unset() methods and inheriting each set of states, seems like a good idea. But this requires the creation of many objects in order to get the name in essence. It seems that there may be a better way.

Ideally, I would like to have a list of all stateSets that can be easily called. For example, before starting the rendering, it would be nice to sort all the nodes in the scene graph using stateSet .

Any ideas?

+6
source share
2 answers

You can implement your states using the singleton pattern. Then another oneton state tracking class manages the instances of these state classes and sets only the state when it is not already installed. The following is a rough implementation. This should give you an idea of โ€‹โ€‹how to do this:

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

Please note that the above design is very simple. You can have several state classes for inheriting a common interface, as you noted in your question. The key must have the Tracker class (aka Manager), through which the states of the objects are set. The task of the Tracker class is to eliminate unnecessary state settings if it is already installed, and to disable other states if they are currently installed.

+2
source

For this reason, creating an abstract class called stateSet , which is essentially an interface for the set() and unset() methods and having each set of states inherited from it, seems like a good idea. But this requires the creation of many objects in order to get the name in essence.

Your hypothetical abstract class implementation works through a table of virtual functions, which is usually implemented as an array of function pointers. Apparently, the meaningless objects that you would have to create in this case should have a meaningful state - these are function pointers.

As an alternative to creating multiple arrays of two function pointers, perhaps you should create two arrays of function pointers in the array, indexes of names into arrays, save the last used index and check stateSet(uint state_name) to see if state_name is different, before touching through arrays. I would recommend hiding all this global state from other compilation units, although the keyword is static .

This approach offers less automatic security - semantically nothing prevents you from passing any integer to stateSet(uint) , and there is no range checking on an raw array if you don't place it yourself.

Both options are basically similar in principle; so give your own idea with due attention.

+1
source

All Articles