OT (ish): Quake2 Source Code

VikingCoder you seem to misunderstood my post. Of course designing should be done before the actual writing of the code but in order to use the advantages of OOP you need to spend more time in that. All I am saying is that no matter how well you design it is probable that you will want to implement a feature that you didn’t thought off during the designing and may need to take a “shortcut” through the hierarchy chart you have so well designed. This wil result in ugly come that will need even more time to make it look nice.

And don’t tell me that every game programmer(s) know exactly what features their engine will finally have. A typical game developement spans for almost 2 years and 2 years are a lot in gaphic hardware terms. Many games were delayed in order to add new features that has been just introduced in hardware and other even changed half of their engine code midway. Take for example quake1. when released it had a software renderer an I think only after some months came the Glide version for the new (at that time) Voodoos. So should carmack have spent more months in the beggining to make a nice OO design for the code when he couldn’t even know that there would be a decent 3D graphic card an a new API?

Also about code reusability, many applications need to be very well designed because they will probably survive many years and will be several versions released. Well, usually games don’t belong to this group of applications. It is true that some engines will be used in more than one project. but if a company wants a engine that will be used in different games then they should expect that this engine will take more time to develop.

sorry about the long post

Nice coding isn’t just a gimmic for applications that’ll last more than a year or so.
A proper coding style and a good knowledge of your tools (e.g. C++) does cut the developement time.

I mean, most common bugs (e.g. memory leak, buffer overrun, heap corrumption, null pointers, etc…) can be avoided with a good coding style. If more people were correctly using the C++ features and its standard library (the ex-STL) there wouldn’t be such bugs (well… almost).
Just think how many bugs are related to C’strings and memory allocation; and most of the time those can be avoided by simply using std::string and std::vector.

Most parts of Quake 2 can be rewritten in C++ in a short amount of time; and they’ll be shorter (in lines of code), easier to understand, probably faster, and certainly less buggy. It’s a win win situation.

Most people think that a good coding style is useless in practical apps. I was often thinking like that too, but it’s just an ignorant attitude.
There is nothing to lose by trying new ways, especially for small projects.

A good coding style isn’t just for other people looking at your code, it’s firstly for yourself!

[This message has been edited by GPSnoopy (edited 05-06-2003).]

GPSnoopy, you are right that most bugs in C come from the things you mentioned. But I don’t think that you can always say that C++ code is more readable (for the author) than C. It depends. Also I think it is true that whether C or C++ is faster it depends on the compiler and how the coder uses the language.

I personally use a mix between C/C++. The only reason I posted in the first place was because I believe that there is no black and white. The best language to use is the one you are more confortable with, and it will do the job whether it includes ugly or nice code.

GPSnoopy, you sure make interesting points, but this thread was about Quake (and other id) engines.

Some people are able to put up well-designed code straight. Some others are quicker at writting “dirty code”, with tricks and hacks everywhere.

Assuming that someone writting poorly-designed code (from an aesthetic point of view) is ignorant seems wrong to me.

And it’s always easier to design a neat engine when working on things that have already been done. When pioneering in challenging fields such as computer graphics, hard coding some parts can make it easier to experiment and get the whole thing working. If you have time later to improve, rewrite some functionnalities differently, with better software design, that’s fine. But from a company’s point of view, when the engine is written, tested and running, you won’t be allowed to spend extra months to make it look nicer. I hear you : “why not write it with good design in the first place”? I don’t know. Maybe that’s what is called “iterative developement”. The whole cycle can span over several shipped products.

The idea that id throws together engines in a hacky manner because they are disposable is kind of naive. The current code base that id is using for the new DooM started in the code base for quake1, which was begun at the very end of 1994 / start of 1995 if memory serves. A lot has changed, but a lot is the same.

From looking at the difference between the Q1 and Q3 code bases you can tell that id’s software is maturing. Jim Dose has had a big influence on the code there, too, from listening to Carmack’s talks. In particular, he is the sole reason why id now uses C++. Dose used to work at Ritual; I worked with a lot of his code on my previous project, and it was entirely C++. Annoyingly so, in fact; lots of things were hard to understand and impossible to debug because of it. To be fair the debugging problems were more due to the system design than to the C++ implementation, but it was a system you probably wouldn’t think of designing in straight C.

The notion that just hacking things together being acceptible in a game is also naive. The only time this is acceptible is when you have absolutely no other way to meet a milestone, and even then it is sometimes better to slip. Sloppy code almost always ends up costing more development time in the long run, and invariably leads to systems that are more bug-prone and unmodifiable.

You sounds like you believe that bugs,immature code , etc are a feature only in C programs. Did the commercial applications got generally better since they started using C++? Or before the introduction of C++ there was not any good code in the applications?

Anyway because the post got really away from the purpose of the forum I will attempt to bring it back:

“Do you think that OpenGL should evolve and become an object oriented API like Direct3D? Maybe OGL2 is a good opportunity to do the switch?”

(my personal opinion is that the comparison between D3D and OGL reflects all the advantages and disadvantages of C/C++ programming everyone of us descibed above)

“Do you think that OpenGL should evolve and become an object oriented API like Direct3D? Maybe OGL2 is a good opportunity to do the switch?”

First of all, the very question itself is loaded. You presume the notion that D3D is more “evolved” than GL. Secondly, you assume this more “evolved” state comes from it’s object oriented-ness.

Personally, I don’t care too much for the state-based approach of OpenGL. It would be nice for GL2 to take a more object-based approach (and it might even make implementing it easier), but that doesn’t require C++. You can write object-oriented code in straight C. Indeed, the first C++ compilers first compiled into C.

And this isn’t because I see OOP as something more “evolved” than a state-based approach. It is merely because it more closely mirrors the graphics hardware. You know that changing the state of some object, unless that object is bound to the hardware currently, will not cause undue stalls in the graphics pipe.

Also, with so many types of objects in GL already (program objects, texture objects, buffer objects, display list objects, etc), taking the plunge into an object-based approach doesn’t cost anything. Indeed, it’s all pretty trivial to have all the functions that set texture parameters take a texture object, rather than having to rely on the currently bound one. It makes the API cleaner.

However, I don’t consider it more “evolved”. It is simply more… appropriate for the task of graphics these days. The state-based appropach made sense back when GL was originally written. Nowadays, however, it does not.

The difference is that the state-based approach could be faster, because it can more easily read the differences between the previous state and the next one. That way, it only has to change certain state. However, typically, once you change state in hardware, it doesn’t matter how much you change; the damage is done. So changing a parameter passed to a vertex program is no worse than swapping out the entire vp (as long as it is resident in video memory).

Extensions would become a nightmare in a C++ model. You can have a dozen people add global functions that take an opaque handle with no problems so long as they behave themselves with their naming conventions. If you get two people to add methods to a class, you’re screwed.

Also, tying an API too closely to current hardware implementations rather than to abstract concepts makes evolution of the hardware much more difficult.

Lastly, the state-based approach of OpenGL makes some coding tasks simpler, since you don’t have to pass a bunch of handles around or to keep a bunch of globals internally. It also promotes the good practice of doing a bunch of stuff on the current object before switching, since that is more convenient than doing one thing on each object in turn.

OpenGL’s approach is simply better suited to the problem.

Funny thread.

OpenGL is the ultimate cross-breed of OOP and efficiency. If you don’t see that, you don’t want to see it.

Select an object:
glBindTexture(GL_TEXTURE_2D.yipiieh);
Change its state:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

Would you rather have this?
glChangeState(GL_TEXTURE_2D,yippieh,GL_PARAMETERS,GL_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

No. If you batch changes to the state of one object together, the first few parameters are redundant. You would want to leave out superfluous parameters on your precious entry points. Which, in consequence, leads you to what OpenGL currently is.

The API couldn’t be made any better in its fundamental design concepts.

Bonus flame: there is no design capability in Redmond. Random quotes from the MSDN:
If this bit is set on an active control, it means that the control changes the value of the property whose name is the value of the named property. Otherwise the named property is changed.

If the function succeeds, the return value is ERROR_SUCCESS.

This parameter is not used. Set to zero.

These people know nothing about software design. Or at least they try hard to make the appearance.

My favorite issue in the MSDN is when a function returning an unsigned integer has the comment that “a return value of -1 indicates success”. I forget what function I saw that on, though.

Originally posted by Coriolis:
Extensions would become a nightmare in a C++ model. You can have a dozen people add global functions that take an opaque handle with no problems so long as they behave themselves with their naming conventions. If you get two people to add methods to a class, you’re screwed.

I said nothing about C++. As I pointed out, you can implement an object-based methodology in C.

As it stands now, OpenGL has lots of object-based things, but interacting with them requires doing a glBind* and then changing their parameters. This glBind* operation is not guarenteed to be light-weight.

Also, it is changing hardware state to do something that is, for all intents and purposes, client-side. If you happen to be rendering at the time, and you just want to do some setup work for some shaders you will be rendering with later in the frame, you’ve just inserted a stall in the pipeline for no real reason. The only reason this stall happens is because these objects have to be accessed in a state-based fashion.

Also, tying an API too closely to current hardware implementations rather than to abstract concepts makes evolution of the hardware much more difficult.

On the plus side, it makes current applications faster. That can’t be discounted either.

Lastly, the state-based approach of OpenGL makes some coding tasks simpler, since you don’t have to pass a bunch of handles around or to keep a bunch of globals internally.

Just because GL won’t have a C++ interface doesn’t mean that you can’t use C++ to get around these, rather trivial, issues.

Yes, you don’t have to pass someone an object to allow them to change the texture’s state, but, as I pointed out, doing a glBindTexture actually forces an immediate upload of the texture to the card, regardless of whether or not you intend to render with it. If you’re only binding it to change a parameter or two… you’ve murdered your performance for no real reason.

It also promotes the good practice of doing a bunch of stuff on the current object before switching, since that is more convenient than doing one thing on each object in turn.

First of all, why is that “good practice”? Because it happens to be perhaps a bit more cache friendly, depending on the internals of the object in question? As I pointed out, a glBind* can easily induce a stall bubble into your pipeline. As such, doing glBind*'s (which is the only way to change an object’s state) is about the best way to kill your performance.

Originally posted by zeckensack:
Funny thread.

OpenGL is the ultimate cross-breed of OOP and efficiency. If you don’t see that, you don’t want to see it.

Select an object:
glBindTexture(GL_TEXTURE_2D.yipiieh);
Change its state:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

[quote]Would you rather have this?
glChangeState(GL_TEXTURE_2D,yippieh,GL_PARAMETERS,GL_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

Oh, sure, create a really stupid example, rather than making a reasonable one that someone would actually propose. It makes your fallicious argument seem much more rational.

A reasonable way to do an object-based mechanism would be:

glTexParameteri(TexObjName, );

No need to call the horribly costly glBindTexture call. It just changes some internal state that is local to the texture object. There is no need to change the currently bound texture, and thus induce a stall in both the CPU and GPU, to perform this basic function.

No. If you batch changes to the state of one object together, the first few parameters are redundant.

Yes. Did you know that, every time you call a member function of a class, the compiler silently tacks on a “this” pointer?

Besides the blatent, and intensional, idiocy of your “example” function, you need the texture object if glTexParameter is to function.The way OpenGL works currently is that the functions only act on the currently bound texture object. Because glBindTexture is also bound to uploading the texture, it means that you can’t change texture parameters on an unbound object. Thus, you have no choice but to wait until you’re actually going to use that object before making any changes to it. This is hardly optimal.

Let’s say, I want to iterate through all my textures and turn on anisotropic filtering (because the user just selected it from a menu, perhaps). The GL method forces me to upload each and every texture object I have into video memory, because that’s what glBindTexture does. My method, because it only involves changing a few parameters in the texture object (which doesn’t change the texture’s data), is much faster. Indeed, you can do it without any performance hit at all. You can do it without inducing a stall in the GPU or waiting for the texture to finish uploading.

Lastly, you don’t damage the caching mechanism used by drivers. By binding a texture, you announce your intension to use it. As such, the driver, after uploading it, now must say that this texture was recently used. Very recently. You have to run through the vast majority of your textures to apply this change, which means all of them have been recently used. If their caching mechanism is not terribly good, you can page out textures you actually intend to use, in favor of ones that you don’t.

Which, in consequence, leads you to what OpenGL currently is.

Alternatively, you stop dreaming up rediculous examples and actually give the other side a fair shake. You might be surprised that you like the alternatives better.

The API couldn’t be made any better in its fundamental design concepts.

Bonus flame: there is no design capability in Redmond.

Oh, of course. If you say it, it must be true. I’m sure you’ve fully explored the other posibilities outside the OpenGL API and, after careful and fair-minded consideration of the alternatives, have settled on this design as optimal

Had you actually posted a reasonable alternative, as I did, to state-based code, I wouldn’t feel nearly the need for sarcasm.

…err…guys…speaking of high level API: could you also check the topic i’ve posted in ‘High Level APIs’, maybe you could help? Thank you!

Korval:

You’re entire page of arguments hinges on the assumption that OpenGL has to upload a texture the instant that a glBindTexture call happens. I don’t think that is a valid assumption. Every single case you’ve mentioned would work just as well if the driver was clever enough to realize that not all cases of binding a texture require it to be uploaded to the card. In fact, there’s no reason to assume that the driver does any uploading of the texture to the card until the next vertex is given to OpenGL.

I will grant you that a naive driver implementation in your proposed model would be better than a naive driver implementation in the current OpenGL model, but that’s all you’ve managed to show.

Originally posted by KuriousOrange:
Renderware is much better - even though it has it’s own problems, at least it has an architecture open enough to allow optimisations for specific platforms.

Ok, Renderware can be a nice toy if you want a simple rendering engine (one texture, 2 at max, per object; basic per vertex lighting model; very simple shadow map rendering), but as soon as you want some spetial fx, you’ll need to code platform specific code (which we’re doing where I’m working, for multitexturing, nice shadows that are casted properly, and the like).

Originally posted by zeckensack:
Bonus flame: there is no design…

Don’t forget “To shut down your PC, click the START button”.

Originally posted by tfpsly:
but as soon as you want some spetial fx, you’ll need to code platform specific code

Well, yes - that was my point - you are free to do that without hacking around in bsp traversal source code.
Bear in mind that renderware is taking care of the rendering/collision for some of the best games around on the consoles (gta3 and the burnout series to name a couple).

Korval, let’s make a habit out of this “I throw a blatant statement on the floor. You tell me I’m wrong” thing. Makes these forums much more spicey

glTexParameteri(TexObjName, );


Whis is pretty much the OpenGL design. Or is it not? OpenGL gives you 'object selection' in place of the this pointer. There is no difference between these two concepts, really. A naive implementation of object selection might even look like this:
class
Object {...};

Object* current_object=NULL;

APIENTRY void
BindObject(int selector)
{
  current_object=(Object*)selector;
}

APIENTRY void
ChangeObjectState(int value)
{
  current_object->change_state(value);
}

What was it, that you are arguing about?

No need to call the horribly costly glBindTexture call. It just changes some internal state that is local to the texture object. There is no need to change the currently bound texture, and thus induce a stall in both the CPU and GPU, to perform this basic function.
You would change state of a texture you’re not going to render? Okay, during level load time and similar occasions this is a good thing to get everything prepared.
But I digress, changing texture state will not cause a quality driver to instantly stall. Nor will just binding a texture, flipping a bit, and then rebinding the texture that was last used for actual rendering, if you’re into that sort of thing.

Trust me, I’ve written something that might be called a “graphics driver”. There are ways around this that are simple enough that I really cannot imagine the ‘big guys’ haven’t found them.

Yes. Did you know that, every time you call a member function of a class, the compiler silently tacks on a “this” pointer?
I knew that, thanks. I don’t know exactly how this is meant to relate here. But it makes a good introduction for my next point:
You don’t funnel a this pointer through an API entry. Every time this pointer is used, it must be checked for validity if you don’t want to risk your driver going down due to an app error. Object selection allows this check to be performed during binds, and can guarantee that the ‘internal’ this pointer is always valid. Saves a fair bit of overhead from the state modifying functions. Nice, huh? I’m glad to see that your proposal handles this right.

Besides the blatent, and intensional, idiocy of your “example” function, you need the texture object if glTexParameter is to function.The way OpenGL works currently is that the functions only act on the currently bound texture object. Because glBindTexture is also bound to uploading the texture, it means that you can’t change texture parameters on an unbound object. Thus, you have no choice but to wait until you’re actually going to use that object before making any changes to it. This is hardly optimal.
You state that binding causes uploading. That’s just as blatant a claim as my initial post.
I’ll give you the benefit of doubt, you probably meant a flush. Would be nice if you could actually say whether you’ve benchmarked that. This is hardly a design flaw of OpenGL, if it’s really the case. Implementations don’t need to do this and indeed they shouldn’t.
I’m with Coriolis on this one, and if I may say so, I’ve done it myself and it performs nicely.

Let’s say, I want to iterate through all my textures and turn on anisotropic filtering (because the user just selected it from a menu, perhaps). The GL method forces me to upload each and every texture object I have into video memory, because that’s what glBindTexture does. My method, because it only involves changing a few parameters in the texture object (which doesn’t change the texture’s data), is much faster. Indeed, you can do it without any performance hit at all. You can do it without inducing a stall in the GPU or waiting for the texture to finish uploading.
I believe everything’s been said above. There’s no fundamental difference between ‘your’ design and OpenGL design. If your design can avoid this problem, an OpenGL implementation can as well.

Lastly, you don’t damage the caching mechanism used by drivers. By binding a texture, you announce your intension to use it. As such, the driver, after uploading it, now must say that this texture was recently used. Very recently. You have to run through the vast majority of your textures to apply this change, which means all of them have been recently used. If their caching mechanism is not terribly good, you can page out textures you actually intend to use, in favor of ones that you don’t.
No. See above.

Alternatively, you stop dreaming up rediculous examples and actually give the other side a fair shake. You might be surprised that you like the alternatives better.
Oh, of course. If you say it, it must be true. I’m sure you’ve fully explored the other posibilities outside the OpenGL API and, after careful and fair-minded consideration of the alternatives, have settled on this design as optimal
I’ve seen my fair share of DirectX (I’m using the version 5 Interfaces for sound and input; I’ve looked at D3D6; and I’ve used D3D7 and 8). D3D7 is actually a nice target for my more outlandish work. I also know the strengths (and weaknesses) of D3D8’s vendor neutral shading. But wasn’t this about OOP? If anything Direct3D is OOP, OpenGL is, too, you bet. Filling a structure with function pointers to be used with the ‘->’ operator has nothing to with OOP.
If you’re so inclined, get a recent DDK and look at what Direct3D really is (look for the ‘thunk layer’).

Had you actually posted a reasonable alternative, as I did, to state-based code, I wouldn’t feel nearly the need for sarcasm.
Korval, I know you’re a clever chap and I have nothing against you personally. I’ve applied brevity and harshness as I did in the other thread (you sure know which one I mean), because I had hoped that I’m understood anyway and might get away with it. Now I get the feeling you ‘hunt’ me here, in part because you’ve hunted me there.
I must admit that my willingness for giving thorough explanation of (what are IMO) obvious things isn’t what it used to be right now, and you make me feel sorry for it. But really, there’s no need for getting generally mad at me.

[This message has been edited by zeckensack (edited 05-07-2003).]

!uhhumm!
Sorry, I was just clearing my throat.

I knew it was easy to shift this post to a more OpenGL-related subject :slight_smile:

Just another remark about OpenGL. The current iplementation (state machine) is much-much better in terms of teaching for begginers. I think it is easier to teach the basics of opengl than messing with direct3d. I was an assistant to a opengl course and it wasn’t that bad even though most students weren’t good on C (let alone C++). I guess it would be a nightmare to do the same for D3D.

I am using DX for sound and input and I just hate the fact that in order to call the simplest function I have to fill a stupid stuct with parameters that won’t be eventually used.