• Advertisement
Sign in to follow this  

"Mesh" should be an interface or an abstract class?

This topic is 1939 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

[code]
class IMesh
{
public:
virtual void update(float dt) = 0;
virtual void render() = 0;
};

or

class AMesh
{
public:
virtual void update(float dt) = 0;
virtual void render() = 0;
public:
D3DXFRAME *m_Root;
};
[/code]
Any advices please?
Thanks
Jack

Share this post


Link to post
Share on other sites
Advertisement
rnlf: Yes, all classes derived from it need a hierarchy of frames/meshcontainers
Hodgman: CCBMesh, CVNAMesh, COperatorMesh etc
swiftcoder: I think I should use aggregation here, comprises of a MeshRenderer and a MeshAnimator?

Represntation == mesh

I've got a diagram here

[attachment=11117:PerfectSim_ClassDiagram3.jpg]


BTW, should I add a class called RigidRepresentation, under it, there are Warehouse, VNA, CB and handtrolley? Edited by lucky6969b

Share this post


Link to post
Share on other sites
Hello Aressera,
Could you please recommend a good book on game design techniques based on OOP concepts?
What is a data-driven design anyway? Could you please give an example?

[url="http://www.amazon.com/s?ie=UTF8&page=1&rh=n%3A283155%2Ck%3ACOMPUTERS%20%2F%20Computer%20Graphics%20%2F%20Game%20Programming%20%26%20Design"]http://www.amazon.co...amming & Design[/url]

Yes, I also notice different meshes exhibit similar or common behaviours, they should be in the same "mesh" class.


Thanks
Jack Edited by lucky6969b

Share this post


Link to post
Share on other sites
Data-oriented design is basically the process of describing the function of a program in data - be it an XML material specification, runtime scripting, etc. Rather than making large complex functions that operate on dumb data, the goal is to move the intelligence to the data: The application becomes analogous to an interpreter, doing what the data says, albeit at a high level.

When this strategy is applied to software design, I have noticed that certain patterns of classes emerge. First, I tend to have pure-data classes that just store and manage some data (i.e. the Mesh class). These pure data classes can then be aggregated into larger, more complex data models, or operated on by functions or classes that act as simple operations with some object state (MeshRenderer, MeshAnimator).

If I had to pick the most important 'commandments' of software design I've developed, they'd be:[list]
[*]Classes should in general only perform only one task or hold one type of data. Same goes for functions. This is the single responsibility principle. It keeps you from creating monolithic functions/classes that do 1001 things that produce hard to debug and unorganized code.
[*]Avoid dependencies between classes and modules wherever possible. This keeps the individual parts small and simple, producing easier to debug and more robust code. Changes in one module rarely effect another.
[*]Keep your code simple while providing maximum flexibility. This tends to result in a data-oriented design without even trying.
[*]Make your code as clearly legible as possible. This means good use of whitespace and indentation as well as using class and function names that read like nouns and phrases and are very clear about what they are doing, even without comments. Keep actual comments concise and precise, noting any special behavior, rather than generic Doxygen-style @param comments.
[/list] Edited by Aressera

Share this post


Link to post
Share on other sites
[quote name='Aressera' timestamp='1346921866' post='4977121']
When this strategy is applied to software design, I have noticed that certain patterns of classes emerge. First, I tend to have pure-data classes that just store and manage some data (i.e. the Mesh class). These pure data classes can then be aggregated into larger, more complex data models, or operated on by functions or classes that act as simple operations with some object state (MeshRenderer, MeshAnimator).
[/quote]
This also implies that Mesh is going to be a single, concrete class (every mesh, regardless of what it represents, where it is used, how it is loaded, how it is rendered etc. is a bundle of various vertex and index buffers as Swiftcoder suggests, or more generally a geometry representation that is cheap to render) while MeshRenderer, MeshAnimator and the like might have interfaces, multiple genuinely different implementations and [i]possibly [/i]inheritance hierarchies: unlike meshes, such classes are going to have very few instances and, if they process whole Mesh collections, very few virtual function calls.

Share this post


Link to post
Share on other sites
[quote name='Aressera' timestamp='1346921866' post='4977121']
Data-oriented design is basically the process of describing the function of a program in data
[/quote]N.B. this is "[i]data-driven design[/i]", not "[i]data-oriented design[/i]". The former is about making data more in control of behaviour rather than code, and the latter is a methodology for writing code that has optimal memory access patterns. Edited by Hodgman

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1346924791' post='4977129']
[quote name='Aressera' timestamp='1346921866' post='4977121']
Data-oriented design is basically the process of describing the function of a program in data
[/quote]N.B. this is "[i]data-driven design[/i]", not "[i]data-oriented design[/i]". The former is about making data more in control of behaviour rather than code, and the latter is a methodology for writing code that has optimal memory access patterns.
[/quote]

Oops, my mistake. I knew there was a distinction in there but I couldn't put my finger on it.

Share this post


Link to post
Share on other sites
Lucky, your hierachy is far too deep and complicated imho. I've tried designs like that before over the years, and they always turn into an unsustainable mess. Swiftcoder finally helped me understand how to implement a good data-driven Renderer design, and I haven't looked back since. Listen to his wisdom and it will help you greatly indeed. ;-)

Share this post


Link to post
Share on other sites
Thanks ATC, I listen to the experts too. I'd like to see some diagrams on how a data-driven system is designed.
:) Yes, I am trying to conform to the standards.
Thanks
Jack

Share this post


Link to post
Share on other sites
In my engine I first made "Mesh" an abstract class. However, I changed it to an interface, "IMesh", a couple weeks ago and it works out better. I just implement new Mesh classes for DirectX10, 11 and OpenGL and let the engine pass around IMesh and IMeshIndexed references... the underlying rendering API and its bindings don't matter, keeping the code weakly/loosely (or not at all) coupled and engine components tidy, clean and easily maintainable. So I say go with an interface-type design. Interfaces are most often badly underused or terribly abused. But find a happy medium and your code will be excellent and almost pain-free to work with!

Share this post


Link to post
Share on other sites
[quote name='lucky6969b' timestamp='1347954159' post='4981169']
Is this you guys talking about?
[url="http://dundee.cs.queensu.ca/wiki/index.php/CAX_Game_Architecture"]http://dundee.cs.que...me_Architecture[/url][/quote]
A word to the wise: your life doesn't have to be ruled by UML diagrams and class hierarchies. Formal design process has a place, but it's generally not necessary to be so formal.

A lot of the beauty of data-driven design is in how it simplifies designs:
[code]struct Mesh
{
VertexBuffer *vertices;
IndexBuffer *indices;
};

struct Camera
{
Matrix4 viewMatrix;
Matrix4 projectionMatrix;
};

class IMeshAnimator
{
IMeshAnimator(const Mesh &mesh);
virtual void update(float deltaTime) = 0;
};

class IRenderer // Has OpenGLRenderer and D3DRenderer as subclasses
{
virtual void render(const Mesh &mesh, Matrix4 modelMatrix, const Camera &camera) = 0;
};
[/code]

Take a look at how conceptually clean this is:
- The Mesh and Camera classes are just pure data containers.
- The MeshAnimator uses composition, instead of inheritance.
- The Renderer just deals with individual render operations.

Share this post


Link to post
Share on other sites
[quote name='lucky6969b' timestamp='1348026747' post='4981528']
That's beauty. Thanks
[/quote]

Indeed it is. I hadn't ever been properly exposed to those ideas until I ran into swiftcoder and had a very interesting conversation. The data-driven design really [i]is[/i] amazing. Since then I've started reading technical articles on the subject from nVidia, and my questions are becoming less and less frequent/necessary. Have a look at [URL="http://http.developer.nvidia.com/GPUGems/gpugems_ch36.html"]this article[/URL] when you begin working shaders and materials into your engine/app.

Share this post


Link to post
Share on other sites
Swiftcoder's example is almost exactly how I started out with my first graphics engine. Only I wasn't using interfaces for rendering, since the engine was targeted only for DirectX 9.

Some other challenges you may plan for in the future: After you get your basic renderer working, figure out how to cull objects. Maybe sort your objects in different containers and pool for the ones you need to render.

Share this post


Link to post
Share on other sites
[quote name='CC Ricers' timestamp='1348068607' post='4981715']
Some other challenges you may plan for in the future: After you get your basic renderer working, figure out how to cull objects. Maybe sort your objects in different containers and pool for the ones you need to render.
[/quote]

I'd also suggest culling in a "chain", of sorts:

Scene Node >> Object >> Model >> Mesh

For instance, a large model made up of many individual meshes or an "object" made up of several models might only be partially on camera. You would not want to cull that entire object/model, nor would you want to draw the entire thing and waste "GPU juice" (lol). So you test in sequence... first make sure the scene node is visible at all, then checks its object groups, then the models of each object group and the meshes of each model.

If your game is very small and simple then this broad testing can be a bottleneck. But in large game worlds populated with tons of geometry it can indeed save a lot of time; especially when individual models and their meshes are required to use multiple textures with complex shaders and dynamic lighting parameters.

Share this post


Link to post
Share on other sites
Can you point me to that conversation you had with swiftcoder perhaps please?

I've done about half the DX11 rastertek tutes, and I'd like to start building something more sensible, and what you've suggested here makes a lot of sense.

I've got a vague idea of what I want, but I've only three years C++ / UML. I've bought two books from Amazon about C++/DX11, they look like the most recommended but they haven't been delivered yet. That conversation would make beautiful reading!

Share this post


Link to post
Share on other sites
[quote name='swiftcoder' timestamp='1347998187' post='4981380']
[quote name='lucky6969b' timestamp='1347954159' post='4981169']
Is this you guys talking about?
[url="http://dundee.cs.queensu.ca/wiki/index.php/CAX_Game_Architecture"]http://dundee.cs.que...me_Architecture[/url][/quote]
A word to the wise: your life doesn't have to be ruled by UML diagrams and class hierarchies. Formal design process has a place, but it's generally not necessary to be so formal.

A lot of the beauty of data-driven design is in how it simplifies designs:
[code]struct Mesh
{
VertexBuffer *vertices;
IndexBuffer *indices;
};

struct Camera
{
Matrix4 viewMatrix;
Matrix4 projectionMatrix;
};

class IMeshAnimator
{
IMeshAnimator(const Mesh &mesh);
virtual void update(float deltaTime) = 0;
};

class IRenderer // Has OpenGLRenderer and D3DRenderer as subclasses
{
virtual void render(const Mesh &mesh, Matrix4 modelMatrix, const Camera &camera) = 0;
};
[/code]

Take a look at how conceptually clean this is:
- The Mesh and Camera classes are just pure data containers.
- The MeshAnimator uses composition, instead of inheritance.
- The Renderer just deals with individual render operations.
[/quote]

I can't understand this, if the Renderer is responsible for updating its children, I expect to use a pointer like this
[code]
class IRenderer
{
virtual void render(const Mesh &mesh, Matrix4 modelMatrix, const Camera &camera) = 0;
};

class CD3DRenderer : public IRenderer
{
void render(const Mesh &mesh,....);
bool UpdateFrame (Mesh *mesh, Matrix4 world);
};

bool CD3DRenderer::UpdateFrame(Mesh *mesh, Matrix4 world)
{

UpdateAllChildren((D3DXFRAME_DERIVED*)m_mesh.m_Root, world);
}

void CD3DRenderer::UpdateAllChildren(D3DXFRAME_DERIVED* pFrame, D3DXMATRIX* pMat)
{
if (pFrame == NULL) return;

D3DXMatrixMultiply(&pFrame->CombinedTransformationMatrix,
&pFrame->TransformationMatrix,
pMat);
if(pFrame->pFrameSibling)UpdateAllChildren((D3DXFRAME_DERIVED*)pFrame->pFrameSibling, pMat);
if(pFrame->pFrameFirstChild)UpdateAllChildren((D3DXFRAME_DERIVED*)pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix);
}

[/code]
The problem is when I pass in the mesh as const&, the mesh cannot be modified, I have integrated the knowledge of swiftcoder and Aressera.
Thanks
Jack Edited by lucky6969b

Share this post


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

  • Advertisement