PDA

View Full Version : Object Oriented OpenGL Programming



sprite
07-28-2003, 08:23 PM
Hello : )

I'm relatively new to OpenGL, and normally I am very apt to develop applications in a very object oriented manner. However, OpenGL's state machine presents a problem that I can't solve... it is a giant store of global variables and settings that every renderable object can modify... this makes development very difficult, because with each object I have to keep track of the current OpenGL state (like modelview, projection, and all the lighting, blending option settings)

So how do real developers do it ? I'm already pushing and popping modelview and projection matrices, but how do you do the same to attributes and settings ? If there is a way (I've heard of popAttrib or something like that, but what are the performance risks ?) I'd like to know and hear ppl's input http://www.opengl.org/discussion_boards/ubb/smile.gif

sprite~

john
07-28-2003, 08:49 PM
Hello,


it is a giant store of global variables and settings that every renderable object can modify...

well, in the object oriented methodology, its more lioke a giant store of private variables, insofar as you use method calls to change them, but you can't get direct hold of the variables. But, yes; the opengl state is fairly large and you modify it with methods.

I don't know what you mean by 'every renderable object can modify it'; renderables use the current state and make changes to the frame buffer (which is also part of that state), but rendering a triangle won't suddenly disable lighting, for instance. glColor*() will change the current colour, but then that's because you're directly changing the state that is used by the next glVertex.


this makes development very difficult,

yes and no. It makes development interesting :-)


So how do real developers do it ? I'm already pushing and popping modelview and projection matrices, but how do you do the same to attributes and settings ? If there is a way (I've heard of popAttrib or something like that, but what are the performance risks ?)

well, I am only a fake developer. You can push abd pop attributes, though. check out pushAttrib(). You pass flags indicatonig what subset of the state you want preserved.

Efficient opengl engines use scenegraphs to manage state. A scenegraph (possibly more correctly, a scene DAG or a scene tree) keeps track of how to change the opengl state to render some scene. THere are plenty of information out there on how to best do this, including a plethora of existing scene graph impkementations.

The general idea is you attach geometry to state nodes. Each state node describes what the current opengl state needs to be to render the geometry attached to it; edges between state nodes describe how state is changed from one node to the next. When it becomes time to render the frame, the engine traverses the graph and flattens it to a list of renderable objects. THe ideas is to traverse the graph and arrange the order of the renderable objects so that they are rendered correctly, but with as few state changes as possible. (well, "as few" should mean, "as optimially" as possible: not all state changes are as equally expensive. chanmging texture, for instance, may require paging in texture from system memory, but disabling depth test is bit flipping).

cheers,
John

sprite
07-28-2003, 09:22 PM
I don't know what you mean by 'every renderable object can modify it';


What I meant here was ... let's say you have an object - anything you need to render in your 3D world. In order for the object to render correctly, you almost always have to make changes to the openGL state.

Thanks for your very concise answer http://www.opengl.org/discussion_boards/ubb/smile.gif I think for now at the beginning of every object's rendering I will push all the attribs and pop the stack after I'm done rendering. This is probably expensive but I'm only at the very preliminary stages of development.

I will look into scenegraphs http://www.opengl.org/discussion_boards/ubb/smile.gif

sprite
07-28-2003, 10:29 PM
Btw,

How do you completely reset the openGL state ? With the modelview and projection matrices you use glLoadIdentity...

Thanks,
sprite

john
07-28-2003, 11:33 PM
pushing the attributes you are planning to modify is a conservative, fail safe approach. Keep in mind that you should only push the state you plan to change in a block/object. Don't push everything: that leads to the dark side.

the alternative (well, what I sort of do:-) is to arbitarily decide on some state space that I really want to keep and make sure code blocks preserve that space when they leave. For example, I might decide that I spend most of my time with blending on. If a particular code block wants to render with blending disabled, then I explictly disable it with glDisable, do my stuff, but then explictly enable it before i return. This is only very subtly different from pushing blending attrs, disabling it and popping the attribute stack... but it works for me.

how do you set state back to the default? Not easily... but that may not be important. the opengl redbook tells you what the default state is, and there is no way to say glLoadDefaultState. if you really REALLY must do it then you'd have to write a function that explictly set the state to the defaults (glDisableG(GL_BLEND); glDisable(GL_LIGHTING); and so on.) but... thihnk carefully about whether you really want to do this. I can't think of a situation where you'd need to.

I hope this helps
cheers,
John

sprite
07-29-2003, 02:21 AM
Hmm

I am currently in the process of cleaning up a LOT of code hacked together from tutorials into one giant 3D world. I think I'll push and pop everything until I can fixed the code (ugh, there's so many interdependencies between different parts of the 3D world because of openGL state) After that, I will start keeping a "default state" like you said and make sure I don't violate it http://www.opengl.org/discussion_boards/ubb/biggrin.gif

I find it odd that openGL doesn't have a command that explicitly creates a "default state" and has something to revert to it. It seems like it would help a lot in terms of developers having to tediously keep track of the state in every single one of their objects... the lack of such a command seems to violate software engineering principles dealing with dependencies. But I am an amateur, so, hehe http://www.opengl.org/discussion_boards/ubb/smile.gif

Thanks for your help !
sprite

M/\dm/\n
07-29-2003, 02:35 AM
Forget about private variables that are used in gl calls & everything will work. EX. when you create texture class, leve id as public, so you can reuse it in fp's etc.

starman
07-29-2003, 05:12 AM
I struggle with the same issues. The design of an optimally coded OpenGL program can be at odds with OO design, where textures, vertices, GL state changes, etc. might be distributed across multiple objects.

I have downloaded a couple OO graphics APIs and was relieved to see that they are designed at roughly the same granularity/level that my small library is (Light classes, Matrix classes, Vector classes, etc.). You might try the same to get some ideas on how to lay things out. One that I haven't downloaded but that would probably be a good candidate would be OpenInventor - an OO scene graph API that I believe is built on top of OpenGL.

One thing I would advise, though, is to get your application working first and worry about the "perfect OO design" later, if at all. i.e., don't try to over-engineer or over-design it. You can always refactor your code into a better design after it's working like you want.

[This message has been edited by starman (edited 07-29-2003).]

torisutan
07-29-2003, 03:46 PM
Hi,
To keep track of all specific parts of my opengl programs (i.e. loaded textures, lights, camera position, etc) i have made additional classes for each part, and have made them singleton classes (only one instance of the class exists in the program). This, i hope, adds an extra layer of protection ontop of opengl. This is extreamly helpful with textures, as i don't need to pick hard set id's for all my textures (making adding additional ones a breeze).

one problem i had with glEnable()ing things is usually i would want to enable or disable specific elements (like lighting etc) and then return to the original state afterwards. This was done simply by changing the state of whatever i enabled/disabled back to what it was before i started
e.g.



glDisable(GL_LIGHTING);
... do stuff ...
glEnable(GL_LIGHTING);

however, sometimes (especially during debuging) the state would not be changed at the first step, thus when i got to the end, the state would be changed to something that it shouldn't be (e.g. if lighting is disabled at the start of the code above, it gets enabled at the end, which is wrong)
i got around this by using "isEnabled(GL_*)" which returns the current state of the specific GL_*. this way, i could check if GL_LIGHTING is enabled before i disable it, and store that state in a local variable, then check that local variable before i re-enable it.

If you're really abmitios, u could write a class that keeps the state of opengl and use that to do all your gl-En/dis-able()s for u. but i found it was too much messing around.

anyways, thats enough ranting for me
hope this helps some
--Tristan

starman
07-30-2003, 06:50 AM
This is somewhat related as I think it would help debug/log OpenGL calls that are spread across multiple objects, but what do you think of the idea of "wrapping" OpenGL calls and conditionally compiling in logging and error handling per call? You could create a mondo header file that wrapped all the functions in a namespace:

#include <opengl headers>

namespace Graphics {
void glTranslatef(float x, float y, float z)
{
::glTranslatef(x, y, z);
#ifdef DEBUG_OPENGL
// error checking code
#endif

#ifdef LOG_OPENGL
// logging code
#endif
}
}

Your application would then include just your header file. Of course, you would have to fully qualify your calls so that they wouldn't collide with the OpenGL calls:

Graphics::glTranslatef(1.0, 1.0, 1.0);

If I get ambitious, I may code this up...at least with the few OpenGL calls that my small application makes.

[This message has been edited by starman (edited 07-30-2003).]

john
07-30-2003, 01:45 PM
that's a good idea, starman. so good, in fact, that it has already been done :-)
look on google for gltrace. It links against a logged version of opengl so you can see what's going on.

cheers,
John

starman
07-30-2003, 03:04 PM
Day late, dollar short once again! That's OK, though...I'm working on my next big idea: a UNIX-like operating system that's open source and freely available. I'll call it "starnix". :-)

starman
07-31-2003, 07:22 AM
Oh fiddlesticks, I did it anyway. I hacked together a Perl script to parse gl.h and generate a header file of wrapper functions that call the global GL function, and then (if NDEBUG is not defined) logs the call to a file and checks the GL error flag. If a GL error occurs, it throws up a Windows message box with the GL error and GL function in which it occured, which should make it easier to track down GL errors than randomly placing these checks.

Now I can't wait to get home and try it out!

[This message has been edited by starman (edited 07-31-2003).]