Archived

This topic is now archived and is closed to further replies.

Moe

Best code structure for a 3d engine?

Recommended Posts

Lately I have been trying to hack together a real rough 3d engine. By this I mean something that gets input, loads model(s), and renders things (models, particle effects). I have been having some problems with the overall structure of the program. Here is how I have it set up right now: I have several major classes, which are global. I have a big D3D class, DI (Direct Input) class, a timer class, and so on. Now the problem is, is that I need access to the input in parts of the 3d class (for things like a mouse look, moving the camera, etc). I made the classes global so that I could access thier parts anywhere in my program. Now I realize that this is probably a bad thing, because my seperate classes (D3D, DI, etc) are not independant of eachother. What if I want to reuse my D3D class in a later project? Well, if I did, I would have to include all the other classes that got tied into it (DI, timer class, etc). I want to be able to reuse my code without having to re-write it. I remember hearing something about a pure virtual base class, and having something derived of it, but I am pretty confused on this. I have looked for a few articles on the subject, and haven''t found any. How is a professional 3D engine structured (codewise) so that it doesn''t collapse under its own weight of code after 10,000 lines? Moe''s site

Share this post


Link to post
Share on other sites
The problem I''m seeing with this thread is that most of the people are trying to prove that OOP somehow helps a single programmer write a program.

Nothing could be further from the truth. The main benefit of OOP is the benefit to other programmers, in that when another programmer is maintaining your code, OOP cuts down on the number of things they have to remember. Check Djikstra''s "The Humble Programmer" for the classic treatment of this subject.

Consider: In a C program, almost any data is valid for a function to use. This leads to thousands of functions and variables that are at one''s disposal to use. Now, perhaps the person who originally programmed it could remember all of them and know the interactions between them, but it will certainly not be obvious to a later maintainer.

By using objects, you DRASTICALLY restrict the number of functions and variables available to any one function. When I go in and try to maintain a C program, I find myself bewildered. The vast majority of my time is spent learning about the program. In a C++ program, all I have to learn about is the object I am working on (theoretically). Moreover, the interactions between one object and another can be much more sharply and clearly defined.

OOP, in general, works to mandate abstraction and encapsulation. These two concepts are powerful indeed when it comes to maintaining code. By abstracting similar functionality into its own object, I can design a program at a higher level than the nuts and bolts without getting confused. By encapsulating functionality into its own object, I can utilize the object without worrying about the underlying implementation.

Again, if I programmed the whole thing and still remembered it all, OOP is almost worthless, although I''d still use it to make it easier to return to the code. But in a 50 programmer, million-line project with an expected lifespan of ten years, I GUARANTEE you OOP will reduce the headache and increase productivity by probably almost an order of magnitude, and there are numerous studies that will show you the same.

In short, OOP is in many ways comparable to commenting. Commenting doesn''t make you code faster, and it doesn''t make your code run faster. In fact, commenting provides almost no benefit to the programmer, especially in smaller programs. Where it provides benefit is when people need to understand and maintain the program who have not been involved with the program before.

Share this post


Link to post
Share on other sites
I definitly want to go with OOP. I know how messy things can get if you don''t use OOP. I want to be able to reuse my code later, and have objects that don''t rely on eachother. Does anyone have any idea how to structure a program like this?

Moe''s site

Share this post


Link to post
Share on other sites
I will have to see if I can get it from a library somewhere, as I don''t have money to buy books. Thanks for the suggestion!

Moe''s site

Share this post


Link to post
Share on other sites
Here, try this:

Each object should probably contain pointers to pointers of their data. For example, all the graphics classes will contain an LPDIRECT3DDEVICEX* where X is the version you are using. Then some code should put put somewhere to set that. Your calls will have to be modified somewhat, to look something like this
  
//Let''s call LPDIRECT3DDEVICE8''s GetAdapterDisplayMode

//In the class definition under protected:

LPDIRECT3DDEVICE8* m_ppDevice;

//And your call:

(*m_ppDevice)->GetAdapterDisplayMode( Put Params Here );


a bit of a pain, but it works better.

-----------------------------
The sad thing about artificial intelligence is that it lacks artifice and therefore intelligence.

Share this post


Link to post
Share on other sites
quote:
Original post by Moe
Now the problem is, is that I need access to the input in parts of the 3d class (for things like a mouse look, moving the camera, etc). I made the classes global so that I could access thier parts anywhere in my program.



Yeah, you don''t want your graphics classses to even know about the input class, much less use it. The Design Patterns book Magmai suggested is a beauty to solve these kinds of problems.

Anyways, I''ll try to explain one basic approach to solve this problem:

You don''t want your modules to talk directly to other modules, yet you need them to communicate to each other. One way to do this is to create a kind of messaging class. All modules talk to this one class instead of specific modules.

Basically, if you want your graphics class to receive input messages, then your graphics class must "register" itself with the messaging service, telling the messaging service that it wants to receive input messages.

When your input class receives input, it sends that information in the form of a message to the messaging class, which in turn sends that message to all classes who "registered" to receive input messages.

This is explained in much greater detail in the Design Patterns book. I''d highly recommend picking it up.


- Houdini

Share this post


Link to post
Share on other sites
I suggest that you try to communicate less between objects. If your 3d module needs a timer, input and other things you are doing too much with just one class. Design your 3d class to just draw things and show them. Then design a separate class which coordinated the 3d part and the input and timer. If your main graphics engine needs to do any communication, then already you are limitting it to use solely in your one project.

Bad Example
3dengine::Run()
{
// iterate through entire scenegraph and update then draw each
// object, bad idea, this will need a timer and knowledge of
// the scenegraph
}

Better Example
FrameCounter::Run()
{
// UpdatePhysics
// updatesound
// scenegraph.draw()
}

This way the interlocking code is all in one class, and the 3d engine needs no knowledge of anything it is drawing.

Share this post


Link to post
Share on other sites
Houdini, thanks for the suggestion.

I would consider creating a few extra classes, but I want to keep this as simple and elegant as possible.

EDIT - I just had an interesting idea. Instead of using the globals to let parts access members of other parts, why not pass them as a paremeter? I mean, if I need to access to the keyboard input to update the camera position, just call a function with a paremeter like:
  
UpdateCameraPos(CAMERA camera);

Would that be feasible?

Moe's site

Edited by - Moe on January 5, 2002 1:23:55 PM

Share this post


Link to post
Share on other sites
quote:
Original post by Moe
EDIT - I just had an interesting idea. Instead of using the globals to let parts access members of other parts, why not pass them as a paremeter? I mean, if I need to access to the keyboard input to update the camera position, just call a function with a paremeter like:

    
UpdateCameraPos(CAMERA camera);

Would that be feasible?


Yes, it would be feasible, and would probably be preferred to using globals for reusability sake. Then again, if you are going to let modules "know" about each other then that''s also breaking reusability, so passing variables the doesn''t really buy you much.

I guess it''s all about how you want to approach it. There is no right or wrong way to create an engine. The "simple" ways are more direct but don''t bode well for code reuse. If that doesn''t bother you then there''s no problem. If it does, then you need to look at a slightly more indirect approach, perhaps similiar to the one I suggested.


- Houdini

Share this post


Link to post
Share on other sites
quote:
Original post by Moe
I would consider creating a few extra classes, but I want to keep this as simple and elegant as possible.


This is something you must overcome, in order to use OOP effectively you must be willing to create new classes at the drop of a hat.

Try
bool MoveCamera(vector Displacement_m);
bool RotateCamera(quaternion Rotation);

Share this post


Link to post
Share on other sites
Yeah, I think I have it all figured out now. I decided to go with the idea that Dog_Food suggested. It seems to be working for what I need so far, and it hasn''t been painful to implement. Thanks for all the suggestions that you guys have given me!

Oh, and as for the camera code, that was just something that I thought up for an example. I don''t think I will have much trouble writing it.

Moe''s site

Share this post


Link to post
Share on other sites