PDA

View Full Version : Using OpenGL with C++



Anis Benyelloul
09-03-2004, 01:38 AM
Let's take a look at a tipical OpenGL code :

glBegin(GL_TRIANGLES);
glColor3f(0.0,0.0,0.1);
glVertex3fv(vertex);

glColor3fv(green);
glVertex3f(0,3,2.2);

... // etc

Well, this might seem obvious to you but show this to any C++ expert
and you're likely to hear :

" Nah !! you're encoding the type of the argument in the function name ?
why don't you let your compiler do this job ? "

Yes, it is right, C++ compilers are able to give the same name for different
functions, taking different number argument with different types .....

What I need is a library that would allow me to say:

vertex(x,y,z);

and Automatically understands that:
- I'm giving three arguments
- I count on it to deduce the types of the argument and
do the right thing.
and to say :

GLfloat v []={...};
vertex(v);

and Automatically understands that:
- I'm giving one argument and thus must be an array.
- I count on it to deduce the types of the argument which is GLfloat.
GL function taking arguments that specify the type of others (such as glDrawElements)
could also greatly benifit fomr the C++ auto type deduction ...

Also there is another problem ...
if you look at some implementation of GL/gl.h, you'll see something like :

#define GL_TRIANGLES //...
#define GL_POINTS // ... etc

Again if you show this the above C++ expert be prepared to hear:
" What ?!! you're #defining constants ? what std::sort of game you're playing ?"
(If you're not a C++ fan ignore the 'std::' above ;) )

#defining constants has well known problems, for example :
glBegin(GL_LINE);
glVertex ...
// ...
glEnd();

This won't do the right thing because the writer really meant GL_LINES,
and your compiler can't help you.
in 'the C++ way of doing things' we would have :

enum DrawMode { GL_LINES, GL_POLYGON ,...};
enum PolygonMode{ GL_FILL, GL_LINE};
void glBegin(DrawMode);
void glPolygonMode(PolygonMode);

And the above error would have been detected at compile time ....

But wait there is more !! aren't you tired of writing all those GL and gl prefixes ?
of course they are important to avoid name clashes but if there is nothing else named
Vertex3fv why do I need to type the gl ? C++ can help here also with the use of namespaces

....

In fact what would be really halpful is to have a C++ wraper around standard
GL calls that would make good use of the C++ compiler capabilities seen above.

Does such a library exists ? (or must I write one myself)

PS: below is a C++ example code that shows how to write the vertex()(and begin()) function
described earlier :

namespace cgl
{
template<class Coord>
void vertex(Coord, Coord, Coord);

template<class Coord>
void vertex(const Coord*);

template<>
inline void vertex<GLfloat>(GLfloat x, GLfloat y, GLfloat z)
{ glVertex3f(x,y,z);}

template<>
inline void vertex<GLdouble>(GLdouble x, GLdouble y, GLdouble z)
{ glVertex3f(x,y,z);}
// etc for vertex ...

enum DrawMode { POINTS, LINES, LINE_STRIP, ... };
inline void begin(DrawMode m)
{ glBegin(static_cast<GLenum>(m));}
// etc ..
}

Thanks in advence .... :)

Bob
09-03-2004, 02:08 AM
I don't know of any library like this. Basically, it's just a thin layer helping you save a few keystrokes and gives you a little bit of type safety.

If you absolutely need it, then just make the functions you want. No need for templates, just use overloading. Variable number of parameters is solved with default parameters, OpenGL automatically adds default values to z and/or w if you use the 2 or 3-parameter version of glVertex anyways.

vertex(GLfloat x, GLfloat y, GLfloat z = 0, GLfloat w = 1)
{ glVertex4f(x, y, z, w); }
vertex(GLint x, GLint y, GLint z = 0, GLint w = 1)
{ glVertex4i(x, y, z, w); }This doesn't really help much, other than the compiler being able to select the proper OpenGL function automatically. If you use the original openGL functions and passes the wrong number of parameters, you get a compile error. If you use the wrong types, you get a type case to the correct type, something that will happen anyway, either in your layer or in OpenGL (as the driver is likely to internally handle a single data type only). Do you're not really gaining anything.

Now, how would you implement the vector-version of these functions?

vector(GLfloat *v)
{ ??? }Should you call the 2, 3 or 4 component version? Pointers don't know their size, so you won't know how many ocmponents are actually passed to the function.

You can solve that by an array of referenecs, like this.

void vertex(GLfloat(&amp;v)[2])
{glVertex2fv(v);}

void vertex(GLfloat(&amp;v)[3])
{glVertex3fv(v);}But then you can only pass an array, not pointers. Well, you can, but not without very ugly casting, in which case you're better off integrating the number of aprameters in the function name... just like the original functions.

Making glBegin take a specific type sure isn't bad, as it helps you catch errors during compile time. But, in my oppinion, the gain isn't that big, as the error is likely to be noticed pretty quick as nothing is drawn.

In my oppinion, a layer like this is pretty useless, as it really doesn't add anything major, just saving minor amount of keystrokes. And of course, immediate mode rendering is not something I would use anyways. Vertex arrays is the way to go.

09-03-2004, 02:12 AM
And the point of your post is what?

OpenGL has bindings for many different programming languages and plattforms which may use different operand sizes and endianess, therefore the argument encoding into the functionname makes perfect sense and improves portability and readability.

Anis Benyelloul
09-03-2004, 02:42 AM
(I'm just discovering those QUOTE commands, so please excuse any misuse)


Basically, it's just a thin layer helping you save a few keystrokes and gives you a little bit of type safety.
Yes of course ! I didn't meant adding new features just using existing ones in a more C++ manner ...



Variable number of parameters is solved with default parameters, OpenGL automatically adds default values to z and/or w if you use the 2 or 3-parameter version of glVertex anyways.
Really ? but why passing three(or even four) parameters when we really need two ? can't a particual OpenGL implementation provide something less trivial then:


This doesn't really help much, other than the compiler being able to select the proper OpenGL function automatically.
But that was the goal !



If you use the original openGL functions and passes the wrong number of parameters, you get a compile error. If you use the wrong types, you get a type case to the correct type, something that will happen anyway, either in your layer or in OpenGL (as the driver is likely to internally handle a single data type only). Do you're not really gaining anything.
Well, A single type cast is better then two, also
the template technique I used can be used to save you a useless type cast, because templates do not allow you to write :


Now, how would you implement the vector-version of these functions?
Aha !! I expected that !!!, well the best sollution (IMHO) (as you said) is to encode the number of arguments in the name:


You can solve that by an array of referenecs, like this.

void vertex(GLfloat(&amp;v)[2])
{glVertex2fv(v);}

void vertex(GLfloat(&amp;v)[3])
{glVertex3fv(v);}Mmmm sounds bad ....



Making glBegin take a specific type sure isn't bad, as it helps you catch errors during compile time. But, in my oppinion, the gain isn't that big, as the error is likely to be noticed pretty quick as nothing is drawn.
Well, I (by far) prefere it to be resolved at compile time ...



In my oppinion, a layer like this is pretty useless, as it really doesn't add anything major, just saving minor amount of keystrokes.
And type checks, and enum values checks.



And of course, immediate mode rendering is not something I would use anyways. Vertex arrays is the way to go.
Well, wouldn't vertex array benefit from type checks ? aren't you obliged to pass GL_FLOAT, GL_UNSIGNED_BYTES and other redoundant information, that could be deduced from the type of the array ?

Thanks a lot for answering !!!! :)

Anis Benyelloul
09-03-2004, 02:47 AM
Originally posted by <Somebodyelse>:
And the point of your post is what?

OpenGL has bindings for many different programming languages and plattforms which may use different operand sizes and endianess, therefore the argument encoding into the functionname makes perfect sense and improves portability and readability.:confused: , Yes but my purpose what to use the C++ type deducing capabilities to avoid having to provide redoundant informations, not to restrain the use of OpenGL to a particual implementation (and I don't think that's what I'm doing ...), Or have I missed some thing ? :confused:

Bob
09-03-2004, 03:06 AM
Assuming you really want this, then I can't argue you shouldn't as your arguments really aren't wrong in any way, so go ahead and write a layer.

However, I find it amusing that you're using speed as an argument (as in my suggestion for default parameters). Speed and immediate mode rendering are, sort of, orthogonal concepts. Using immediate mode you are likely not to get good performance under any circumstances. My argument for using default parameters is that you don't have to write wrapper functions for each combination of number of parameters. With default parameters, you can handle all combinations with one function, cotting the number of functions down by a factor 3 for vertices and 4 for colors.

So if you really want it, go ahead and write it ;)

09-03-2004, 04:48 AM
Originally posted by Anis Benyelloul:
Or have I missed some thing ?Well, the API is standardized for a reason.

I really dont see the point in writting such a layer other then doing it because its possible.

And as Bob already pointed out, if you are aiming for speed, you dont really want to use the immediate mode anyway.

If you are doing that just for some educational practice fine, go ahead do it. Personly, I think its a silly idea because its a complete waste of time.

Jan
09-03-2004, 08:19 AM
I donīt know why you NEED it so much, but well, if you canīt live without it, write it yourselve.
You already proved, that you know how to do it.

Telling other people "how to do a thing right" (tm), is usually not very welcome, if it is done in such an arrogant way. Most of us do know C++ pretty well.

Maybe you should concentrate on more important stuff.

Jan.

Anis Benyelloul
09-03-2004, 11:34 PM
Originally posted by Bob:

..However, I find it amusing that you're using speed as an argument (as in my suggestion for
default parameters). Speed and immediate mode rendering are, sort of, orthogonal concepts.
Using immediate mode you are likely not to get good performance under any circumstances..
Mmmm... ok ! but those vertex() are just examples ! all the library could benifit from it !
Do you want anothre example ? : glDrawElements() !!!
With glDrawElements you must:
- Specify the mode of drawing :
an enum(like DrawMode) is useful to detect if you use invalid values like GL_LINE.
- Specify how much indices there are: no way to avoid this however.
- Specify the array pointer.
- Specify the Type of the indices !!!
Here, the type of the indices can be deduced from the type of the array pointer !,
Furthermore, not all type of indices(As far as I know) are valid, and C can't check this
for us, for example:


...With default parameters, you can handle all combinations with one function, cotting the number of functions down by a factor 3 for vertices and 4 for colors.
So your want to cut down the number of function to write ?, well, that's not realy a problem since we are not obliged to write such a layer ourself, we can let a program do it for us !!!( a well behaved Perl script could do it, no ?)

In conclusion, a more C++ usage of OpenGL can give you :
- Type check at compile time (that calls the right function according
to the type of arguments)
- Enum range check (when enumeration values are expected only the valid set will
be accepted by the compiller)
- Keystroke save through the use of namespaces (and from the first point).
- ... (perhaps more)
In addition, all those costs nothing at execution time, absolutly nothing, ZERO, NADA !
(due the magic of the inline keyword) !!, so why not ?

PS: please excuse any arrogance from myself. That was not my intention. and Thanks once more for answering.

09-03-2004, 11:46 PM
one argument for the use of a wrapper is an abstract interface. i have an interface for 2 graphics apis, opengl and d3d. i code up one abstract interface, and implement it using each api. keeps things nice and neat.

another agument is for debugging. with a wrapper, you can nealty trap opengl calls and optionally check for errors on return.

i don't see what all the fuss is about.

lgrosshennig
09-04-2004, 03:38 AM
It is correct that you could use enums and automatic type delegation when you use OpenGL together with C++ to make it nicer and safer, but the thing is, that is nothing people have problems with.

People have problems with blending modes, clipping planes, 3d math along with very basic problems like linker usage. Problems because people pass wrong arguments (no pun intended) are very rare.

Let alone that alot of documentation and examples would need to be rewritten in order to accommodate the changes. Nowadays you can google for "glReadPixels" and use i.e. the python documentation or even use a Delphi example and port it over to C/C++ with no real problem.

Like someone said, the OpenGL API is all about portability (at least in theory). Its an old "structured" C based API, nothing more nothing less.