Jump to content
  • Advertisement
Sign in to follow this  
BBB

Various engine design questions

This topic is 5137 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Im writing my own 3D-engine (or actually, learning to do that) and was wondering about a couple of things. I use plugins (.so under Unix and .dll in win32) and don't want to re-write the entire OpenGL renderer plugin when i start working on the AI or something and realize that i have worked myself into a corner (as a beginner it is very easy to do just that). For example when i started to work on my 3D-engine i only knew about glVertex3f(...) and such wich means that you have to send the rendering data to the gfx every frame. Then i learned about GL display lists and vertex buffer objects wich puts the data on the gfx so that you don't have to send it every frame. So i had to throw away and re-write alot of code to supports VBOs. I really want to avoid such problems or atleast minimize them. Right now im at a cross road in the design of my renderer, should i:
class Renderer
 {
  public:
  Resource poly_vect_lock; // mutex locking and stuff
  vector<Polygon> poly_vect;
 }; // end class Renderer



and let the AI, physics, etc. plugins directly mess with the stuff the renderer cares about or should i go through an abstractation:
class Renderer
 {
  public:
  VFRV add_vertex(Polygon *poly, ID &new_poly_id, THREAD_ID thread_id, UINT mutex_timeout) { } // add a poly
  VFRV request_polygon_read(Polygon *poly,ID poly_id THREAD_ID thread_id, UINT mutex_timeout) { } // we want to find out something about a poly
  VFRV request_polygon_write(Polygon *poly, ID poly_id THREAD_ID thread_id, UINT mutex_timeout) { } // we want to mess with a poly
  VRFV done_with_poly(ID poly_id THREAD_ID thread_id, UINT mutex_timeout) { } // we are done with the poly so the renderer can put it back to the VBA or whatever
 }; // end class Renderer

class Renderer_stl : public Renderer
 {
  public:
  VFRV add_vertex(Polygon *poly, ID &new_poly_id, THREAD_ID thread_id, UINT mutex_timeout);
  ...
  protected:
  Resource poly_vect_lock; // mutex locking and stuff
  vector<Polygon> poly_vect;
 }; // end class Renderer_stl : public Renderer



i think the last solution is the best, but what about performance? Unfortunatly im not a low-level zealot so i don't know that much about assembler and the inner workings of C++. Futhermore i try to support threading (as you can see), is that i could idea? Should i just ignore it for now and then paste in on later or should i try to atleast make room for it so that i can implement it later? For example the main rendering is linear but perhaps i want to software optimize a texture over a number of frames, so i start a new thread, then call
texture_vect_lock->lock_mutex(UINT timeout, THREAD_ID thread_id);
and then call the
optimize_texture(Texture *texture, what_ever);
from the new thread. With VBOs and the ability to lock single textures in my texture vector this becomes a real headache. And as you can see i use pointers instead of handles since if i use handles functions like
FRV optimize_texture(ID texture_id, ...)
has to mess with the texture containers directly which means that if i swith from STL to something else it means that i have to re-write some stuff in them, that's why i use pointers. If you use pointers the functions don't care if the texture is stored in a STL vector, a simple array, VBOs or what-ever. Again this is easier to mess around with but what about performance? Anything else i should know? And one last question, what is this ASSERT(...) macro stuff? I have seen it in my book "Game programming gems 4" but have no idea what it does (the assert macro that is :) ). Greatfull for any help // BBB

Share this post


Link to post
Share on other sites
Advertisement
Writing a 3D engine is a very big project for someone just starting to program. I think a good idea would be to start small and add features to it as you go. As you implement new features in the engine, write applications that use those features.

Don't worry about making it perfect the first time. Plan on completely rewriting your engine many many times.

Share this post


Link to post
Share on other sites
It's important that you design a good interface and stick with that.

If you look at the Bridge pattern you will see that it's possible to seperate your interface completely from your implementation. This would be perfect for your renderer. Your other classes only see the interface and they call function X to do something, if you change the implementation of function X they won't notice and your code still works.

Create a good interface and don't change it if other code depends on it. It is really important to make sure that other code is not depending on the inner workings of a class. They should never expect that the polygon list is a std::vector or that an algorithm will first do X and then Y.. if you don't do that you will have to rewrite a lot of code if you change the implementation.

About the ASSERT macro check this link:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/html/_core_the_assert_macro.asp

You will use the ASSERT macro when you are developing your game. You can use it to check things that should always be true if the program is running correctly.

If you compile a release version of your project ASSERT will be skipped in your code and will not work in the finale exe.

Share this post


Link to post
Share on other sites
Go for the 2nd method. By separating your graphics, AI etc you are decoupling the various sum-systems. This has many advantages:

- implementation changes only impact on the changed subsystem (unless you change the interface symantics - in which case the change will knock on to clients). For example if you wanted to support 2 different gfx libraries you can do this easily behind a standard generic gfx API and swap them in and out without changing any of your other (non gfx) code.

- you can re-use subsystems independently - for example you may want to re-use your AI code in a command line tool - you don't want to have to include loads of 3D gfx classes too.

- testing/bugfixing/maintenance are all far easier as you can treat the subsystems separately.

The performance issue is an interesting one - if you're smart you should only have the overhead of vtable lookups (which you'll have with 1st option too). You'll obvioulsy have a lot more classes - wrapper classes for your APIs etc. I think an important point to engine design (or any software design) is premature optimisation is bad..

Share this post


Link to post
Share on other sites
Renderer shouldn't need to maintain geometry in it, it should probably iterate through the main data structure of the game take a renderable object and draw it, look up the vistor pattern. Basically what i mean is this:


class tri_mesh;

struct renderer {

void draw(const tri_mesh& t) const;
};





that is really simplified but you get the idea.

No i wouldn't support threading unless you design for it wright at the start even then.

Share this post


Link to post
Share on other sites
There isn't really a need for doing all that stuff you're talking about. The renderer should ONLY draw stuff. Nothing more, nothing less.
All the data you want to visualize with your renderer should be contained separately.
For example, when i want to draw a mesh, i instance a mesh-object, and tell my renderer to draw it.

CStaticMesh gMesh;

gRenderingDevice->DrawStaticMesh(&gMesh);


Since my rendering-class is declared a friend to CStaticMesh, it can reach the vertex/index-data in the mesh and draw it.
Don't stick all sorts of data into the rendering-class itself, keep it separate, keep it simple, and keep it modular.

Edit: snk_kid and me were posting the same thing at the same time ;)

Share this post


Link to post
Share on other sites
I'd just like to bring this thread back up.

I use a base class with several virtual functions in it, including: Render() and Update(). My render simply takes a reference to an instance of a CMesh class and calls Update followed by Render. In Update the CMesh class assigns pointers and prepares anything ready for rendering.

I'm using a scene-graph so basically all my objects are linked together logically. I keep my mesh data, skeletal data, keyframes etc all separately and use a function interface to interact with the data although all I really need to render a simple object is a pointer to the start of the vertices array, I just pass this onto my API (I prefere OpenGL) and thats it.

I have no need to store muliple copies of my object, each object is stored in contiguous (whole) data and I don't have overhead in copying data (simply because theres no need to move data, only pointers to data) - theres an acceptable lag in calculating frames from keyframes, but I do a-lot of pre-calculating at the start on splines (curves) for movement etc.

I dont use theading but I'm using OOP so I could (If I wanted) add it in later without re-writing anything.

Simply, if you just keep your design and interafaces clean, un-ambiguous and definately modular you will be able to produce efficient, flexible code that can be built upon without fuss and will keep options open without backing you into a closed corner.

[Edited by - dmatter on August 18, 2004 2:57:24 PM]

Share this post


Link to post
Share on other sites
For a simple render interface, take a look at Striving for Graphics API Independence. It's all about providing a clear and interface to the rendering subsytem. Bear in mind you may not want ot get this low down (dealing with vertices and triganles), instead you may wish to take a higher level approach and ask your renderer to render a mesh/model. Of course, this method takes away a little flexibility, but can be useful if the way you push triangles to the renderer doesn't change that much between projects.

===
EDIT: Fixed link (Nurgle)

[Edited by - Nurgle on August 19, 2004 1:05:58 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by BBB

Actually, i do think that my idea is better since im going to
put the mesh data into a the VBA. But ofcourse, if the gfx
doesn't support hardware accleration and i have to use software
rendering then my way would be a
waste of memory since i would have 2 copyies of the mesh,
one inside the renderer-object and the other somewhere else.
But i have a really cool idea that would'n work if i used
your way.


Well, you can keep the meshdata in a VBA, nothing's stopping you from that. Personally i keep it in both systemmemory and videomemory so i could easily access it for collision and such, while still rendering it from vidmem.
So what wouldn't work if you did it my way?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!