OpenGL Engine in C++

So far I’ve always programmed with plain C but now I want to change to C++. My question is a bit unrelated, trust me. Well, how far should one abstract all the stuff. Should one create a Vector class with overloaded operators (that is REALLY low level abstraction), should one have every vertex of a model in a vertex object? I don’t think so. Might it be that C++ should only serve as a high level abstraction layer as I think?

Any comments are highly appreciated!
Michael

Hi!
Difficult question if you´re asking about the general case…But I´ll try to answer your specific ones:

1.>Should one create a Vector class with overloaded operators (that is REALLY low level abstraction)<

I would definately say yes. It is so comfortable to write 3d-code with a good vector class and nice overloaded operators you simply don´t want to miss it if you used it once. The only thing here is to profile carefully to prevent nasty little details in class implementations which slow down things unexpectedly. Make alot of use of references and “inline”. But there are alot of good vector classes out there to look at.

2.>should one have every vertex of a model in a vertex object?<
Depends on how many info your engine needs to be stored with one vertex…

P.s.: Perhaps I told you only stuff that you already know…If so - sorry!

HTH, XBTC!

[This message has been edited by XBCT (edited 01-29-2001).]

Hi Michael!

I stood in front of the same problem last month.
I created a Vertex-class with all the vertex-math stuff in it (with overloaded operators).
but faces and texture-vertices are only a typedef struct
every 3d-object (better 3d-thing ) is a own class with a Loader in it, a faces-array, a vertex-class array (of pointers) and a texture-vertice-array. you also find a Display-function here, which displays the obj. I use this for effects (p.ex. the water-sphere effect from 3dmark… ), cause every effect has it’s own display-function.
If you’re interested in demo-code, don’t hesistate to contact me If i finish my engine I will put it onto my site, but this is far away

greetings,

Sebastian

Thanks guys!
I don’t hesitate…

Well, some time ago I created a vector class, maybe someone can look at that code an tell me wether it is a) correct and b) fast. I know it is not fast nor is it correct, but you could give me a lot of help and wouldn’t have to give me your own code.
But then remember, that is the first thing I programmed with c++ and I don’t have a c++ book…

Michael, you can send it to me for checking if you wish !

Regards.

Eric

P.S.: you see, you finally make the move to C++ !

Oh BTW is it really good to give every object it’s own rendering function.

You can’t

  • globally sort after textures
  • do some fency stuff that incorporates multiple objects

I think it would be better to have a renderer object where you can register all the objects after what they are. It could make global sorting of it all etc. The object get called when they move or change. Well, I’m currently thinking about how to make a cool 3d pipeline in c++.
Maybe its also cool to just let every object draw itself. I’m gonna try it out all.

Thanks!!!

Yes, Eric! Thanks!
Well, indeed I make the change now if at all possible, since I came to the conclusion that abstract nice code is good code. It might make problems, but if I look at the gtk window system on linux I feel good…
Hope to be able to implement a similar window system which doesn’t need its own precompiler.
Gonna send it to you within the next hour!

I still have problems understanding that & thing in for example:

void function( float &parameter );

What does that do? I only know that I don’t seem to have to use the -> with structures in this case. I have never seen that construction before. Can anyone help?

Thanks!!!

¶meter is
&[]parameter

Sigh. Well you might know what I mean…

[This message has been edited by Michael Steinberg (edited 01-29-2001).]

For your pipeline/sort problem, I have an abstract class which is called C_Rendered and contains vertices, faces, normals, texcoords and materials (could be a shader). The Build() function (call it Render if you want: it is called Build here because it builds the display list for the object) is virtual. All my objects are derived from this class.

So you can still sort per shader even with each object having it own rendering/building function…

Regards.

Eric

[This message has been edited by Eric (edited 01-29-2001).]

void function (float &blah)

is a “pass by reference” function. Basically what it does is that when you call that function…
float blah2;
function(blah2);

The address of blah is physically mapped to the address of blah2. Therefore, any changes you make to blah, will be seen in blah2 after the function exits.

It’s similar to passing by pointer except that passing by pointer you actually pass the address.

functionByPointer(&blah2);

And then since it is a pointer, you have to use dereferencing to assign a value to it in the function.

*blah = 10.0;

You don’t have to dereference it like that if you pass by reference since it sets the address of blah to be the same as the address of blah2. (&blah == &blah2)

Hope that makes sense.

Thanks Deissum!
I actually thought about that way, but why should one pass pointers to the functions then. It seems to be better to use these and’s. Any idea?

Thanks Eric!
If I understand you well, you have sort of basic object class. The object classes are derived from them. Then you also have a renderer (which is sort of container of all these objects (more correctly of the basic objects)). The basic object class contains only render specific data.
I simply can’t believe that all the different kind of objects (weapons, space ships, space stations etc.) can be made abstract to such a low layer. I mean, they all have polies, but consider special object such as an explosion or volumetric fog. The renderer would have to handle all these small effects. So why then bother deriving some special object class from the base class, when the renderer would have to do all the stuff. Also, a bone system. Bones have to be handled by the renderer because it will have to load the matrices up or pretransform the vertices. The object oriented model isn’t too clear to me.

You should actually be using (const type& varname)

because

float j = 3.0
func(j);

void func( float& f )
{
f = 4.0; //doh!!! j just changed
}

void func( const float& f )
{
f = 4.0; //error f cannot change
}

As far object oriented goes, the way I do my rendering is the following. I have a renderer object which have function of the type :

setShadingAttributes( const attr*&);
setMesh( const mesh&*);
Render()
etc.
My scene graph uses those fonctions to render the scene. So it calculates which objects are in the view, sort them by attributes. and then call setShadingAttributes()
setMesh and set whatever and then calls render which uses the mesh and the shading attributes to render the object

The nice thing with that model is that the attributes can be derived. So I can simple shading(simple material color), RenderMan shaders, simple Quake like surface shaders.

So all attributes have a set fonctions which sets them a the current rendering attributes in the renderer

With the meshes, then a meshe can be a vertex array, a display list, or a Curved surface.

So all meshes have their own draw fonctions. So if it is a displaylist, glCallList is used, if it is stored in a curved surface, the tessalation is done, if it is a vertex array then glDrawElements is called. Etc…

All that without having the scene graph or the renderer now about how the attribute or meshes are implement.

You can make that scheme so powerfull that you can just change the renderer from opengl to Direct3d and you won’t even have to restarts all your objects.

OO is powerfull.

Sorry about the bad orthograph. I am just writing too fast and no re-reading myself!

[This message has been edited by Gorg (edited 01-29-2001).]

[This message has been edited by Gorg (edited 01-29-2001).]

Thanks Gorg!

Hi!
To the sorting problem:
I think by far the best approach is to have a rendering backend which only takes faces riangles as input and stores them in temporary vertex excoord
ormal arrays which then are used as CVA´s for example. You set the current state of the backend to one shader and feed all faces with this shader into the backend. You flush(render) the backend if it´s buffer is full or if a face with another shader comes in from the front end…
You now have a pipeline which eliminates all unnecessary state exture changes and you can still do what you want in your front end…
The point I try to get accross here is:
The actual renderer should only take faces riangles and NOTHING else in order to be flexible and fast.
What you do in the front-end(Rendering functions for objects…) doesn´t matter much then and you can try alot of different things in the front end without writing different renderers everytime…

HTH, XBTC!

[This message has been edited by XBCT (edited 01-29-2001).]

I actually more thought about presorting. I don’t think it could really get fast if one tries to sort >10k polys every frame.

Thanks!

Also, if you simply push them in, how would the renderer know which polys belong to each other. He could end up loading a matrix, rendering a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly, loading a matrix, render a poly.

God that I don’t mess with too large models…
Hey, I’m a bit crazy today, don’t take it bad.

Originally posted by Gorg:
You should actually be using (const type& varname)

Only if you don’t want the value to change. For smaller things like ints and floats, if you don’t want the value to change it’s just as efficient to pass by value. (Copying a 32-bit address isn’t any more expensive than copying a 32-bit value)

You should use the format you give if you want to pass large structs to a function, but don’t want them to be editable. Then it will pass only a 32-bit address as opposed to say 256-bit struct (or more). Making it const will ensure that it then can’t be edited.

Often times you pass things by reference BECAUSE you want to get the value back. For instance…

void GetPosition(float& x, float& y, float& z);

float x,y,z;

GetPosition(x,y,z);

void GetPosition(float *x, float *y, float *z);

GetPosition( &x, &y, &z );

Would be far clearer, wouldn’t it be? Noone could see out of the function call wether the floats get changed or not. If you send pointers to them that is quite probable because you could also pass them as values if the function wouldn’t change them.

I think I should collect a c++ book somewhere. Actually, I would use classes just like structures with some functions in them to clarify that the functions belong to that type of “structure” (For example normalize into the vector class). And I would still use normal arrays and structures for classes.
How fast are objects? I mean if you have overloaded operators. Is it as fast to use objects like Vectors as it is when using structures?
Any recommended books about c++?

Thanks!

Depends on your point of view. Pass by reference was added to C++ partly so that the calling conventions could be kept the same as passing by value. (i.e. not having to pre-pend variables with &)

Passing by pointer could be considered “uglier” because
a) you have to add the & to pass the address
b) you have to dereference the pointer inside the function in order to change the value (i.e. *x=10.0)

Passing by pointer and passing by reference are essentially the same except for the calling convention and the way the variables are used inside the function.

>Also, if you simply push them in, how would the renderer know which polys belong to each other. He could end up loading a matrix, rendering a poly, loading a matrix,…<

Calm down!
Very simple it isn´t important which poly belongs to which other…I talk about the very(!) back end of the engine here.
The backend only renders triangles which need completely the same rendering state. Surely you should fill the backend only with triangles which are already sorted by texture.

>I actually more thought about presorting. I don’t think it could really get fast if one tries to sort >10k polys every frame.<

I´m talking about static scenes here…
You only need to sort them once and that ahead of time.

P.s.: The architecture I described is exactly the one Q3 and my Q3Engine use so trust me it works very well…

HTH, XBTC!