Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I have a very big design stumbling block in my rendering code. Basically what this is, is not requiring API specific code (such as OpenGL code or DirectX). Now I've thought of numerous ways on how to solve the problem, however I'm not sure which one to use, or how I should improve upon these ideas.
To give a brief example, I will use a Texture as an example. A texture is an object which represents a texture in GPU memory, implementation wise it may be resembled in any particular way, i.e. whether implementation uses a GLuint or LPDIRECT3DTEXTURE9 to resemble the texture.
Now here are the ways I've thought of to actually implement this. I'm quite unsure if there is a better way, or which way is better than another.
Method 1: Inheritance
I could use inheritance, it seems the most obvious choice for this matter. However, this method requires virtual functions, and would require a TextureFactory class in order to create Texture objects. Which would require calls to new for each Texture object (e.g. renderer->getTextureFactory()->create()).
Here's how I'm thinking of using inheritance in this case:
class Texture
{
public:
virtual ~Texture() {}
// Override-able Methods:
virtual bool load(const Image&, const urect2& subRect);
virtual bool reload(const Image&, const urect2& subRect);
virtual Image getImage() const;
// ... other texture-related methods, such as wrappers for
// load/reload in order to load/reload the whole image
unsigned int getWidth() const;
unsigned int getHeight() const;
unsigned int getDepth() const;
bool is1D() const;
bool is2D() const;
bool is3D() const;
protected:
void setWidth(unsigned int);
void setHeight(unsigned int);
void setDepth(unsigned int);
private:
unsigned int _width, _height, _depth;
};
and then in order for OpenGL (or any other API specific) textures to be created, a sub-class would have to be made, such as OglTexture.
Method 2: Use a 'TextureLoader' or some other class
This method is as simple as it sounds, I use another class to handle loading of textures. This may or may not use virtual functions, depending on the circumstance (or whether I feel it is necessary).
e.g. A polymorphic texture loader
class TextureLoader
{
public:
virtual ~TextureLoader() {}
virtual bool load(Texture* texture, const Image&, const urect2& subRect);
virtual bool reload(Texture* texture, const Image&, const urect2& subRect);
virtual Image getImage(Texture* texture) const;
};
If I were to use this, a Texture object would only be a POD type. However, in order for this to work, a handle object/ID would have to be present within the Texture class.
For example, this is how I would more than likely implement it. Although, I may be able to generalise the whole ID thing, using a base class. Such as a Resource base class in which case holds an ID for a graphics resource.
Method 3: The Pimpl Idiom
I could use the pimpl idiom, which implements how to load/reload/etc. textures. This would more than likely require an abstract factory class for creation of textures. I am unsure how this is better than using inheritance. This pimpl idiom could be used in conjunction with Method 2, i.e. Texture objects would have a reference (pointer) to their loader.
Method 4: Using concepts/compile-time polymorphism
I could on the other hand, use compile-time polymorphism and basically use what I presented in the inheritance method, except without declaring virtual functions. This would work, but if I wanted to dynamically switch from OpenGL rendering to DirectX rendering, this would not be the best solution. I would simply put OpenGL/D3D specific code within the Texture class, where there would be multiple texture classes with some-what the same interface (load/reload/getImage/etc.), wrapped inside some namespace (resembling which API it uses, e.g. ogl, d3d, etc.).
Method 5: Using integers
I could just use integers to store handles to texture objects, this seems fairly simple, but may produce some-what "messy" code.
This problem is also present for other GPU resources such as Geometry, Shaders, and ShaderPrograms.
I've also thought of just making the Renderer class handle the creation, loading, and etc. of graphical resources. However this would violate SPR. e.g.
Texture* texture = renderer->createTexture(Image("something.png"));
Image image = renderer->getImage(texture);
Can someone please guide me, I think I'm thinking too heavily about this. I've tried observing various rendering engines, such as Irrlicht, Ogre3D, and others I have found online. Ogre and Irrlicht use inheritance, however I am unsure that this is the best route to take. As some others just use void*, integers, or just put API specific (mainly OpenGL) code within their classes (e.g. GLuint directly within the Texture class). I really cannot decide which design would be the most appropriate for me.
The platforms I am going to target are:
Windows/Linux/Mac
iOS
Possibly Android
I have considered to just use OpenGL specific code, as OpenGL works for all of those platforms. However, I feel that if I do that I will have to change my code quite a lot if I wish to port to other platforms that cannot use OpenGL, such as the PS3. Any advice on my situation will be greatly appreciated.
Think of it from a high-level point of view. How will your rendering code work with the rest of you game/application model? In other words, how do you plan to create objects in your scene and to what degree of modularity? In my previous work with engines, the end result of a well-designed engine generally has a step-by-step procedure that follows a pattern. For example:
//Components in an engine could be game objects such as sprites, meshes, lights, audio sources etc.
//These resources can be created via component factories for convenience
CRenderComponentFactory* pFactory = GET_COMPONENT_FACTORY(CRenderComponentFactory);
Once a component has been obtained there are usually a variety of overloaded methods you could use to construct the object. Using a sprite as an example, a SpriteComponent could contain everything potentially needed by a sprite in the form of sub-components; like a TextureComponent for instance.
//Create a blank sprite of size 100x100
SpriteComponentPtr pSprite = pFactory->CreateSpriteComponent(Core::CVector2(100, 100));
//Create a sprite from a sprite sheet texture page using the given frame number.
SpriteComponentPtr pSprite = pFactory->CreateSpriteComponent("SpriteSheet", TPAGE_INDEX_SPRITE_SHEET_FRAME_1);
//Create a textured sprite of size 100x50, where `pTexture` is your TextureComponent that you've set-up elsewhere.
SpriteComponentPtr pSprite = pFactory->CreateSpriteComponent(Core::CVector2(100, 50), pTexture);
Then it's simply a matter of adding the object to the scene. This could be done by making an entity, which is simply a generic collection of information that would contain everything needed for scene manipulation; position, orientation, etc. For every entity in your scene, your AddEntity method would add that new entity by default to your render factory, extracting other render-dependent information from sub-components. E.g:
//Put our sprite onto the scene to be drawn
pSprite->SetColour(CColour::YELLOW);
EntityPtr pEntity = CreateEntity(pSprite);
mpScene->AddEntity(pEntity);
What you then have is a nice way of creating objects and a modular way of coding your application without having to reference 'draw' or other render-specific code. A good graphics pipeline should be something along the lines of:
This is a nice resource for rendering engine design (also where the above image is from). Jump to page 21 and read onwards where you'll see in-depth explainations of how scenegraphs operate and general engine design theory.
I don't think there's any one right answer here, but if it were me, I would:
Plan on using only OpenGL to start with.
Keep rendering code separate from other code (that's just good design), but don't try to wrap it in an extra layer of abstraction - just do whatever is most natural for OpenGL.
Figure that if and when I was porting to PS3, I would have a much better grasp of what I need my rendering code to do, so that would be the right time to refactor and pull out a more abstract interface.
I've decided to go for a hybrid approach, with method (2), (3), (5) and possibly (4) in the future.
What I've basically done is:
Every resource has a handle attached to it. This handle describes the object. Each handle has an ID associated with it, which is a simple integer. In order to talk to the GPU with each resource, an interface for each handle is made. This interface is at the moment abstract, but could be done with templates, if I choose to do so in the future. The resource class has a pointer to an interface.
Simply put, a handle describes the actual GPU object, and a resource is just a wrapper over the handle and an interface to connect the handle and the GPU together.
This is what it basically looks like:
// base class for resource handles
struct ResourceHandle
{
typedef unsigned Id;
static const Id NULL_ID = 0;
ResourceHandle() : id(0) {}
bool isNull() const
{ return id != NULL_ID; }
Id id;
};
// base class of a resource
template <typename THandle, typename THandleInterface>
struct Resource
{
typedef THandle Handle;
typedef THandleInterface HandleInterface;
HandleInterface* getInterface() const { return _interface; }
void setInterface(HandleInterface* interface)
{
assert(getHandle().isNull()); // should not work if handle is NOT null
_interface = interface;
}
const Handle& getHandle() const
{ return _handle; }
protected:
typedef Resource<THandle, THandleInterface> Base;
Resource(HandleInterface* interface) : _interface(interface) {}
// refer to this in base classes
Handle _handle;
private:
HandleInterface* _interface;
};
This allows me to extend quite easily, and allows for syntax such as:
Renderer renderer;
// create a texture
Texture texture(renderer);
// load the texture
texture.load(Image("test.png");
Where Texture derives from Resource<TextureHandle, TextureHandleInterface>, and where renderer has the appropriate interface for loading texture handle objects.
I have a short working example of this here.
Hopefully this works, I may choose to redesign it in the future, if so I will update. Criticism would be appreciated.
EDIT:
I have actually changed the way I do this again. The solution I am using is quite similar to the one described above, but here is how it is different:
The API revolves around "backends", these are objects that have a common interface and communicate with a low-level API (e.g. Direct3D or OpenGL).
Handles are no longer integers/IDs. A backend has specific typedef's for each resource handle type (e.g. texture_handle_type, program_handle_type, shader_handle_type).
Resources do not have a base class, and only require one template parameter (a GraphicsBackend). A resource stores a handle and a reference to the graphics backend it belongs to. Then the resource has a user-friendly API and uses the handle and graphics backend common interface to interact with the "actual" resource. i.e. resource objects are basically wrappers of handles that allow for RAII.
A graphics_device object is introduced to allow construction of resources (factory pattern; e.g. device.createTexture() or device.create<my_device_type::texture>(),
For example:
#include <iostream>
#include <string>
#include <utility>
struct Image { std::string id; };
struct ogl_backend
{
typedef unsigned texture_handle_type;
void load(texture_handle_type& texture, const Image& image)
{
std::cout << "loading, " << image.id << '\n';
}
void destroy(texture_handle_type& texture)
{
std::cout << "destroying texture\n";
}
};
template <class GraphicsBackend>
struct texture_gpu_resource
{
typedef GraphicsBackend graphics_backend;
typedef typename GraphicsBackend::texture_handle_type texture_handle;
texture_gpu_resource(graphics_backend& backend)
: _backend(backend)
{
}
~texture_gpu_resource()
{
// should check if it is a valid handle first
_backend.destroy(_handle);
}
void load(const Image& image)
{
_backend.load(_handle, image);
}
const texture_handle& handle() const
{
return _handle;
}
private:
graphics_backend& _backend;
texture_handle _handle;
};
template <typename GraphicBackend>
class graphics_device
{
typedef graphics_device<GraphicBackend> this_type;
public:
typedef texture_gpu_resource<GraphicBackend> texture;
template <typename... Args>
texture createTexture(Args&&... args)
{
return texture{_backend, std::forward(args)...};
}
template <typename Resource, typename... Args>
Resource create(Args&&... args)
{
return Resource{_backend, std::forward(args)...};
}
private:
GraphicBackend _backend;
};
class ogl_graphics_device : public graphics_device<ogl_backend>
{
public:
enum class feature
{
texturing
};
void enableFeature(feature f)
{
std::cout << "enabling feature... " << (int)f << '\n';
}
};
// or...
// typedef graphics_device<ogl_backend> ogl_graphics_device
int main()
{
ogl_graphics_device device;
device.enableFeature(ogl_graphics_device::feature::texturing);
auto texture = device.create<decltype(device)::texture>();
texture.load({"hello"});
return 0;
}
/*
Expected output:
enabling feature... 0
loading, hello
destroying texture
*/
Live demo: http://ideone.com/Y2HqlY
This design is currently being put in use with my library rojo (note: this library is still under heavy development).
Related
So I'm developing my first game engine and I've hit a wall. Currently I have an Entity base class that has a vector of component pointers which will hold pointers to component objects within some system manager classes (graphisManager, PhysicsManager, etc.). Here is my current Entity header (stripped down to focus on main issue):
Entity.h
class Component;
namespace BlazeGameWorld
{
class Entity
{
public:
BlazeFramework::Math::Vector2D position;
protected:
Vector<Component*> components;
static BlazeGraphics::GraphicsManager graphicsManager;
static BlazePhysics::PhysicsManager physicsManager;
static BlazeInput::InputManager inputManager;
//....other managers
private:
///////////////////////////////////////////////////////////////////////
public:
Entity();
~Entity();
virtual bool Initialize();
virtual bool Shutdown();
virtual void Update() = 0;
void AddComponent(Component* p_component);
//Part of the broadcast messaging system for components to be able
//to talk to one another in a decoupled way.
void SendMessage(uint messageID);
protected:
private:
};
}
As you can see, the idea is to have static SystemManager classes which will manage pointers to the actual components on the heap. Here is a rough Header for the potential PhysicsManager class (and it's similar for the other Manager classes):
PhysicsManager.h
class PhysicsComponent;
namespace BlazePhysics
{
class PhysicsManager
{
public:
protected:
int numPhysicsComponents;
private:
Vector<PhysicsComponent*> physicsComponents;
/////////////////////////////////////////////////////////////
public:
PhysicsManager();
~PhysicsManager();
bool Initialize();
bool Shutdown();
void Update();
template <typename PhysicsComponentType>
PhysicsComponentType* CreatePhysicsComponent();
private:
};
//template definitions
template <typename PhysicsComponentType>
PhysicsComponentType* PhysicsManager::CreatePhysicsComponent()
{
PhysicsComponentType* physicsComponent = new PhysicsComponentType
physicsComponents.push_back(physicsComponent);
return physicsComponents.at(numPhysicsComponents++);
}
}
So I can store all different physicsComponent pointers in the PhysicsManger vector (pointers to CollisionComponents, PositionComponents, etc). The issue is if I wanted to call a method specific to a specific physics component, I can't compile. For example, if (in the update loop for PhysicsManager) I wanted to update a collisionComponent's CheckCollision() method each frame I can't just say in a for loop physicsComponents.at(i).CheckCollisionbecause the compiler doesn't know at compile time what a CollisionComponent is. Is there a way to maybe deduce the type of the component in the array first and then if it matches CollisionComponent call the CheckCollision method? Or is there a better to implement this since this seems kind of clunky?
Entities shouldn't know about your systems, they should be just a collection of components. Otherwise the process of introducing another engine system would require changing entity class as well, which defies the whole purpose of ECS.
Systems, too, should not manage components at all. One system may use several components and many systems may use, e.g. position/collision geometry components.
So, in my opinion:
Components, ideally, should be simple data-only classes, while all the processing is done within systems.
Entity must only provide a way of adding and removing components, and
telling whether it has specific component.
Some sort of entity manager must store components in a cache-friendly way for systems to access them and should be able to provide a list of entities, that have specific set of components/tags to systems.
This way, for example, if you want to add scripted behavior for some entities, all you have to do is add ScriptComponent and ScriptingSystem. All of your existing code would not require any change.
This question has a lot of very useful resources on the topic.
I'm trying to write a class (some sort of graphics engine) basically it's purpose is to render ANYTHING that I pass into it. In most tutorials I've seen, objects draw themselves. I'm not sure if that's how things are supposed to work. I've been searching the internet trying to come up with different ways to handle this problem, I've been reviewing function templates and class templates over and over again (which sounds like the solution I could be looking for) but when I try using templates, it just seems messy to me (possibly because I don't fully understand how to use them) and then I'll feel like taking the template class down, then I'll give it a second try but then I just take it down again, I'm not sure if that's the way to go but it might be. Originally it was tiled-based only (including a movable player on screen along with a camera system), but now I've trying to code up a tile map editor which has things such as tool bars, lists, text, possibly even primitives on screen in the future, etc. and I'm wondering how I will draw all those elements onto the screen with a certain procedure (the procedure isn't important right now, I'll find that out later). If any of you were going to write a graphics engine class, how would you have it distinguish different types of graphic objects from one another, such as a primitive not being drawn as a sprite or a sphere primitive not being drawn as a triangle primitive, etc.? Any help would be appreciated. :)
This is the header for it, it's not functional right now because I've been doing some editing on it, Just ignore the part where I'm using the "new" keyword, I'm still learning that, but I hope this gives an idea for what I'm trying to accomplish:
//graphicsEngine.h
#pragma once
#include<allegro5\allegro.h>
#include<allegro5\allegro_image.h>
#include<allegro5\allegro_primitives.h>
template <class graphicObjectData>
class graphicsEngine
{
public:
static graphicObjectData graphicObject[];
static int numObjects;
static void setup()
{
al_init_image_addon();
al_init_primitives_addon();
graphicObject = new graphicObjectData [1]; //ignore this line
}
template <class graphicObjectData> static void registerObject(graphicObjectData &newGraphicObject) //I'm trying to use a template function to take any type of graphic object
{
graphicObject[numObjects] = &newObject;
numObjects++;
}
static void process() //This is the main process where EVERYTHING is supposed be drawn
{
int i;
al_clear_to_color(al_map_rgb(0,0,0));
for (i=0;i<numObjects;i++) drawObject(graphicObject[i]);
al_flip_display();
}
};
I am a huge fan of templates, but you may find in this case that they are cumbersome (though not necessarily the wrong answer). Since it appears you may be wanting diverse object types in your drawing container, inheritance may actually be a stronger solution.
You will want a base type which provides an abstract interface for drawing. All this class needs is some function which provides a mechanism for the actual draw process. It does not actually care how drawing occurs, what's important is that the deriving class knows how to draw itself (if you want to separate your drawing and your objects, keep reading and I will try to explain a way to accomplish this):
class Drawable {
public:
// This is our interface for drawing. Simply, we just need
// something to instruct our base class to draw something.
// Note: this method is pure virtual so that is must be
// overriden by a deriving class.
virtual void draw() = 0;
// In addition, we need to also give this class a default virtual
// destructor in case the deriving class needs to clean itself up.
virtual ~Drawable() { /* The deriving class might want to fill this in */ }
};
From here, you would simply write new classes which inherit from the Drawable class and provide the necessary draw() override.
class Circle : public Drawable {
public:
void draw() {
// Do whatever you need to make this render a circle.
}
~Circle() { /* Do cleanup code */ }
};
class Tetrahedron : public Drawable {
public:
void draw() {
// Do whatever you need to make this render a tetrahedron.
}
~Tetrahedron() { /* Do cleanup code */ }
};
class DrawableText : public Drawable {
public:
std::string _text;
// Just to illustrate that the state of the deriving class
// could be variable and even dependent on other classes:
DrawableText(std::string text) : _text(text) {}
void draw() {
// Yet another override of the Drawable::draw function.
}
~DrawableText() {
// Cleanup here again - in this case, _text will clean itself
// up so nothing to do here. You could even omit this since
// Drawable provides a default destructor.
}
};
Now, to link all these objects together, you could simply place them in a container of your choosing which accepts references or pointers (or in C++11 and greater, unique_ptr, shared_ptr and friends). Setup whatever draw context you need and loop through all the contents of the container calling draw().
void do_drawing() {
// This works, but consider checking out unique_ptr and shared_ptr for safer
// memory management
std::vector<Drawable*> drawable_objects;
drawable_objects.push_back(new Circle);
drawable_objects.push_back(new Tetrahedron);
drawable_objects.push_back(new DrawableText("Hello, Drawing Program!"));
// Loop through and draw our circle, tetrahedron and text.
for (auto drawable_object : drawable_objects) {
drawable_object->draw();
}
// Remember to clean up the allocations in drawable_objects!
}
If you would like to provide state information to your drawing mechanism, you can require that as a parameter in the draw() routine of the Drawable base class:
class Drawable {
public:
// Now takes parameters which hold program state
virtual void draw(DrawContext& draw_context, WorldData& world_data) = 0;
virtual ~Drawable() { /* The deriving class might want to fill this in */ }
};
The deriving classes Circle, Tetrahedron and DrawableText would, of course, need their draw() signatures updated to take the new program state, but this will allow you to do all of your low-level drawing through an object which is designed for graphics drawing instead of burdening the main class with this functionality. What state you provide is solely up to you and your design. It's pretty flexible.
BIG UPDATE - Another Way to Do It Using Composition
I've been giving it careful thought, and decided to share what I've been up to. What I wrote above has worked for me in the past, but this time around I've decided to go a different route with my engine and forego a scene graph entirely. I'm not sure I can recommend this way of doing things as it can make things complicated, but it also opens the doors to a tremendous amount of flexibility. Effectively, I have written lower-level objects such as VertexBuffer, Effect, Texture etc. which allow me to compose objects in any way I want. I am using templates this time around more than inheritance (though intheritance is still necessary for providing implementations for the VertexBuffers, Textures, etc.).
The reason I bring this up is because you were talking about getting a larger degree of seperation. Using a system such as I described, I could build a world object like this:
class World {
public:
WorldGeometry geometry; // Would hold triangle data.
WorldOccluder occluder; // Runs occlusion tests against
// the geometry and flags what's visible and
// what is not.
WorldCollider collider; // Handles all routines for collision detections.
WorldDrawer drawer; // Draws the world geometry.
void process_and_draw();// Optionally calls everything in necessary
// order.
};
Here, i would have multiple objects which focus on a single aspect of my engine's processing. WorldGeometry would store all polygon details about this particular world object. WorldOccluder would do checks against the camera and geometry to see which patches of the world are actually visible. WorldCollider would process collission detection against any world objects (omitted for brevity). Finally, WorldDrawer would actually be responsible for the drawing of the world and maintain the VertexBuffer and other lower-level drawing objects as needed.
As you can see, this works a little more closely to what you originally asked as the geometry is actually not used only for rendering. It's more data on the polygons of the world but can be fed to WorldGeometry and WorldOccluder which don't do any drawing whatsoever. In fact, the World class only exists to group these similar classes together, but the WorldDrawer may not be dependent on a World object. Instead, it may need a WorldGeometry object or even a list of Triangles. Basically, your program structure becomes highly flexible and dependencies begin to disappear since objects do not inherit often or at all and only request what they absolutely require to function. Case in point:
class WorldOccluder {
public:
// I do not need anything more than a WorldGeometry reference here //
WorldOccluder(WorldGeometry& geometry) : _geometry(geometry)
// At this point, all I need to function is the position of the camera //
WorldOccluderResult check_occlusion(const Float3& camera) {
// Do all of the world occlusion checks based on the passed
// geometry and then return a WorldOccluderResult
// Which hypothetically could contain lists for visible and occluded
// geometry
}
private:
WorldGeometry& _geometry;
};
I chose the WorldOccluder as an example because I've spent the better part of the day working on something like this for my engine and have used a class hierarchy much like above. I've got boxes in 3D space changing colors based on if they should be seen or not. My classes are very succinct and easy to follow, and my entire project hierarchy is easy to follow (I think it is anyway). So this seems to work just fine! I love being on vacation!
Final note: I mentioned templates but didn't explain them. If I have an object that does processing around drawing, a template works really well for this. It avoids dependencies (such as through inheritence) while still giving a great degree of flexibility. Additionally, templates can be optimized by the compiler by inlining code and avoiding virtual-style calls (if the compiler can deduce such optimizations):
template <typename TEffect, TDrawable>
void draw(TEffect& effect, TDrawable& drawable, const Matrix& world, const Matrix& view, const Matrix& projection) {
// Setup effect matrices - our effect template
// must provide these function signatures
effect.world(world);
effect.view(view);
effect.projection(projection);
// Do some drawing!
// (NOTE: could use some RAII stuff here in case drawable throws).
effect.begin();
for (int pass = 0; pass < effect.pass_count(); pass++) {
effect.begin_pass(pass);
drawable.draw(); // Once again, TDrawable objects must provide this signature
effect.end_pass(pass);
}
effect.end();
}
My technique might really suck, but I do it like this.
class entity {
public:
virtual void render() {}
};
vector<entity> entities;
void render() {
for(auto c : entities) {
c->render();
}
}
Then I can do stuff like this:
class cubeEntity : public entity {
public:
virtual void render() override {
drawCube();
}
};
class triangleEntity : public entity {
public:
virtual void render() override {
drawTriangle();
}
};
And to use it:
entities.push_back(new cubeEntity());
entities.push_back(new triangleEntity());
People say that it's bad to use dynamic inheritance. They're a lot smarter than me, but this approach has been working fine for a while. Make sure to make all your destructors virtual!
The way the SFML graphics library draws objects (and the way I think is most manageable) is to have all drawable objects inherit from a 'Drawable' class (like the one in David Peterson's answer), which can then be passed to the graphics engine in order to be drawn.
To draw objects, I'd have:
A Base class:
class Drawable
{
int XPosition;
int YPosition;
int PixelData[100][100]; //Or whatever storage system you're using
}
This can be used to contain information common to all drawable classes (like position, and some form of data storage).
Derived Subclasses:
class Triangle : public Drawable
{
Triangle() {} //overloaded constructors, additional variables etc
int indigenous_to_triangle;
}
Because each subclass is largely unique, you can use this method to create anything from sprites to graphical-primitives.
Each of these derived classes can then be passed to the engine by reference with
A 'Draw' function referencing the Base class:
void GraphicsEngine::draw(const Drawable& _object);
Using this method, a template is no longer necessary. Unfortunately your current graphicObjectData array wouldn't work, because derived classes would be 'sliced' in order to fit in it. However, creating a list or vector of 'const Drawable*' pointers (or preferably, smart pointers) would work just as well for keeping tabs on all your objects, though the actual objects would have to be stored elsewhere.
You could use something like this to draw everything using a vector of pointers (I tried to preserve your function and variable names):
std::vector<const Drawable*> graphicObject; //Smart pointers would be better here
static void process()
{
for (int i = 0; i < graphicObject.size(); ++i)
draw(graphicObject[i]);
}
You'd just have to make sure you added each object to the list as it was created.
If you were clever about it, you could even do this in the construction and destruction:
class Drawable; //So the compiler doesn't throw an error
std::vector<const Drawable*> graphicObject;
class Drawable
{
Triangle() {} //overloaded constructors, additional variables etc
int indigenous_to_triangle;
std::vector<const Drawable*>::iterator itPos;
Drawable() {
graphicObject.push_back(this);
itPos = graphicObject.end() - 1;
}
~Drawable() {
graphicObject.erase(itPos);
}
}
Now you can just create objects and they'll be drawn automatically when process() is called! And they'll even be removed from the list once they're destroyed!
All the above ideas have served me well in the past, so I hope I've helped you out, or at least given you something to think about.
We have a rendering pipeline and our current code creates an instance for every stage of the pipeline. This means that as we update our code, we will be constantly updating the pipeline (or multiple sets of pipelines) code. This feels like a point where we should have additional abstraction, but we're not sure how to proceed.
Edit: It seems my pseudo code is not well understood. Perhaps a diagram will more easily explain the pattern.
Link to a block diagram: http://yuml.me/0650d1bf.svg
// yuml.me
[GenericRenderStage|render|Parent Class]<---[Shadow1RenderStage|render|Derived Class]
[Shadow1RenderStage|render|Derived Class]<---[_shadowRenderStage1|Singleton Object]
[GenericRenderStage|render|Parent Class]<---[Shadow2RenderStage|render|Derived Class]
[Shadow2RenderStage|render|Derived Class]<---[_shadowRenderStage2|Singleton Object]
[GenericRenderStage|render|Parent Class]<---[ShadowNRenderStage|render|Derived Class]
[ShadowNRenderStage|render|Derived Class]<---[_shadowRenderStageN|Singleton Object]
Psuedo-c++ code of parent class:
class GenericRenderStage(...) {
/// Render method
virtual void render(void) {
/// handles drawing code
}
class Shadow1RenderStage : GenericRenderStage(...) {
/// Render method
void render(void) {
/// handles custom drawing for shadow1 stage
}
class Shadow2RenderStage : GenericRenderStage(...) {
/// Render method
void render(void) {
/// handles custom drawing for shadow2 stage
}
...
class ShadowNRenderStage : GenericRenderStage(...) {
/// Render method
void render(void) {
/// handles custom drawing for shadowN stage
}
We then have a set of the same type of pattern for our pipelines...
class GenericRenderPipeLine(...) {
/// Render method
virtual void render(void) {
/// handles drawing code
}
class ShadowRenderPipeline : GenericRenderPipeLine() {
/// instantiate stages for this pipeline
ShadowRenderPipeline() {
shadow1Stage = new Shadow1RenderStage();
shadow2Stage = new Shadow2RenderStage();
...
shadowNStage = new ShadowNRenderStage();
}
/// Render method
void render(void) {
/// setup fbo
/// for each render stage, render
shadow1Stage.render()
shadow2Stage.render()
...
shadowNStage.render()
/// handle fbo
}
Something here seems really wrong with the pattern. We've got a parent class that is basically a set of virtual methods to be inherited by a customized class that only ever has one instance.
From what I understand (please correct me if I'm wrong), each render stage is unique and doesn't really follow any specific pattern. So, I think it's best to keep those the way they are; have each render stage in a self-contained file/class.
However, I think you could reduce a lot of work by eliminating your render pipeline inheritance structure. All of these appear to be the same (i.e. they have some number of stages and call render() on each of them). What if you instead had a dynamic, generic pipeline?
#include <vector>
#include <memory>
class DynamicRenderPipeline {
private:
std::vector<std::unique_ptr<GenericRenderStage>> renderStages;
public:
void add(std::unique_ptr<GenericRenderStage> renderStage) {
renderStages.push_back(std::move(renderStage));
}
void render() {
for (auto& stage : renderStages) {
stage->render();
}
}
};
int main() {
DynamicRenderPipeline pipeline;
pipeline.add(std::unique_ptr<GenericRenderStage>(new RenderStage1()));
pipeline.add(std::unique_ptr<GenericRenderStage>(new RenderStage2()));
pipeline.add(std::unique_ptr<GenericRenderStage>(new RenderStage3()));
pipeline.add(std::unique_ptr<GenericRenderStage>(new RenderStage4()));
pipeline.add(std::unique_ptr<GenericRenderStage>(new RenderStage5()));
pipeline.render();
}
You can now just create instances of DynamicRenderPipeline and add any stages you wish. When you call render() on it, it will loop through all of the added render stages in the proper order. Now your pipeline only depends on the GenericRenderStage interface. If you're not using C++11 you could do the same with raw pointers (instead of unique_ptr), but you'd have to make sure to clean up your vector in the pipeline's destructor.
The fact that you only ever instantiate one object of each one of the many classes you created is a manifestation of a common problem in class-based OOP à la Java/C++. It is somewhat easier to circumvent in C++ than in Java.
The problem is simply that all code has to reside in class definitions if you want to go the pure class-based OO way. For instance, in Java, if you wanted to create many vastly different comparators (boolean predicates of two values of the same type), you ideally would have to create just as many subclasses of the standard Java generic interface Comparator. Actually, I lied, because in many cases you could just use the
new Comparator<Type>() {
public bool compare(Type a, Type b) { /* */ }
}
syntax, which is the closest Java ever got to having first-class citizen functions prior to Java 8. However, even that syntax doesn't work when you want to allow the creation of customized comparators, and you then have to write an entire class like so in order to void repeating yourself:
class CompareIntsPlusX implements Comparator<int>
{
private int number;
public CompareIntsPlusX(int number) {
this.number = number;
}
public bool compare(int x, int y) {
return x < y + number;
}
}
Comparator<int> myComparator = new CompareIntsPlusX(3);
and we end up with very cumbersome syntax for even the simplest of objects. Notice that in any (somewhat) functional programming language like Javascript, this problem would be more easily solved.
The same phenomenon is happening here. You have been going the class-based OO way in your pipeline stage definitions, and now you realise it's not scalable because you only need one object carrying with it the code defined in its class, and you need many such objects.
Those objects are what you referred to as "singletons" even though they do not in any way follow the Singleton design pattern. They are de facto singletons, and the reason for this is that they are actually just functions in disguise. Their only goal is to "carry" with them a void render() method.
From here you have several solutions:
If the render functions are vastly different, and you cannot generate them from one class that accepts parameters like we did with the CompareIntsPlusX Java class above, that is to say if you cannot factor your code, then
If you are using a functional style of C++03 (I wouldn't recommend that) or of C++11 (I have never used what I'm about to suggest) then you can simply use one RenderStage class defined like so:
class RenderStage
{
private:
std::function<void()> renderFunction;
public:
RenderStage(std::function<void()> renderFunction)
: renderFunction(renderFunction)
{ }
void render() {
renderFunction();
}
}
/* Somewhere else in the code, create your render stages */
RenderStage r1([]() {
// blah blah
});
// You can also capture stuff!
Texture tex;
RenderStage r2([&tex]() {
// blah blah
});
In C++03, we would use function pointers (whose functions are defined in a namespace because we don't want to be polluting the global namespace). Note that in the C++11 case, we only need to use std::function if captures are needed in the lambdas. If not, then just use function pointers.
Else, if you want to keep using an OO style, then create as many classes as you need and don't feel bad about it. In fact, creating anonymous classes is what the compiler does behind your back when you create lambdas (in fact it creates classes if your lambdas have captures and simple functions if not) and the Java compiler does it too when you "instantiate an interface".
Else, if the render methods can be factored, i.e. if instead of N classes you can have one with a constructor that allows for customization, then do that instead.
It's probable that a mix of the first and second solutions is what you need. You should try the second solution of having customizable render stage objects before you go for the more complex solution.
I'm currently working on a little game engine project in C++ using DirectX for rendering. The rendering part of the engine consists of classes such as Model and Texture. Because I would like to keep it (relatively) simple to switch to another rendering library (e.g. OpenGL) (and because I suppose it's just good encapsulation), I would like to keep the public interfaces of these classes completely devoid of any references to DirectX types, i.e. I would like to avoid providing public functions such as ID3D11ShaderResourceView* GetTextureHandle();.
This becomes a problem, however, when a class such as Model requires the internal texture handle used by Texture to carry out its tasks - for instance when actually rendering the model. For simplicity's sake, let's replace DirectX with an arbitrary 3D rendering library that we'll call Lib3D. Here is an example demonstrating the issue I'm facing:
class Texture {
private:
Lib3DTexture mTexture;
public:
Texture(std::string pFileName)
: mTexture(pFileName)
{
}
};
class Model {
private:
Texture* mTexture;
Lib3DModel mModel;
public:
Model(std::string pFileName, Texture* pTexture)
: mTexture(pTexture), mModel(pFileName)
{
}
void Render()
{
mModel.RenderWithTexture( /* how do I get the Lib3DTexture member from Texture? */ );
}
};
Of course, I could provide a public GetTextureHandle function in Texture that simply returns a pointer to mTexture, but this would mean that if I change the underlying rendering library, I would also have to change the type returned by that function, thus changing the public interface of Texture. Worse yet, maybe the new library isn't even structured the same way, meaning I'd have to provide entirely new functions!
The best solution I can think of is making Model a friend of Texture so that it can access Texture's members directly. This seems slightly unwieldy, however, as I add more classes that make use of Texture. I have never used friendship much at all, so I'm not sure if this is even an acceptable usage case.
So, my questions are:
Is declaring Model a friend of Texture an acceptable use of
friendship? Would it be a good solution?
If no, what would you
recommend? Do I need to redesign my class structure
completely? In that case, any tips?
PS: I realize that the title is not very descriptive and I apologize for that, but I didn't really know how to put it.
Whether it is an acceptable use of friendship is debatable. With every feature, even good ones, that you use, you risk that anti-patterns form in your code. So just use it with moderation and be cautious for anti-patterns.
While you can use friendships you can also simply use inheritance i.e. IGLTexture : ITexture and cast to the appropriate interface wherever implementation detail needs to be accessed. For instance IGLTexture could expose everything opengl related.
And there is even another paradigm that could be used. pimpl which stands for
private implementation. In short rather than exposing implementation detail
within the class, you just supply all implementation detail in a class whose implementation is unspecified publicly. I've been using this approach myself with little second regrets.
//header
class Texture
{
int width, height, depth;
struct Impl;
char reserved[32];
*Impl impl;
Texture();
...
};
//cpp
struct Texture::Impl
{
union
{
int myopenglhandle;
void* mydirectxpointer;
};
};
Texture::Texture()
{
impl = new (reserved) Impl();
}
You need to abstract this mo-fo.
class TextureBase{
public:
virtual Pixels* getTexture() = 0;
virtual ~TextureBase(){}
};
class Lib3DTexture: public TextureBase {
private:
Lib3DTexture mTexture;
public:
Texture(std::string pFileName)
: mTexture(pFileName)
{
}
Pixels* getTexture(){ return mTexture.pixels(); }
};
class Renderable{
public:
virtual void render()const = 0;
virtual ~Renderable(){}
};
class ModelBase: public Renderable{
public:
virtual ModelData* getModelData() = 0;
virtual ~ModelBase(){}
};
class Lib3DModel : ModelBase{
private:
TextureBase* texture;
ModelBase* model;
public:
Lib3DModel(std::string pFileName, Texture* pTexture): mTexture(pTexture), mModel(pFileName){}
void render()const{
model.renderWithTexture( texture.getPixels() );
}
};
class World: public Renderable{
private:
std::vector< std::shared_ptr<Renderable> > visibleData;
public:
void render()const{
for_each(visiableData.begin(),visiableData.end(),std::mem_fun(Renderable::render));
}
};
you get the idea, not guaranteeing it compiles but just to give you an idea.Also check out user2384250 comment, good idea as well.
Make Texture a template with a default template parameter using DirectX.
So you can do this:
template<typename UnderlyingType = Lib3DTexture> class Texture {
private:
UnderlyingType mTexture;
public:
Texture(std::string pFileName)
: mTexture(pFileName)
{
}
UnderlyingType UnderlyingTexture(); //returns UnderlyingType, no matter which library you use
};
I think this could be a clean way of solving that problem, and easily allowing the switching out of underlying libraries.
Since the 2 APIs are mutually exclusive and since you probably don't need to switch between the 2 at runtime, I think you should aim at building 2 different executables, one for each of the underlying API.
By that I mean use:
#if OpenGL_implementation
...
#else // DirectX
...
#if
This may or may not be the sexy solution you were looking for. But I believe this is the cleaner and simpler solution. Going with heavy template use (resp. heavy polymorphic behaviour) will probably cause even more code bloat than an #if solution and it will also compile (resp. run) slower as well. :)
In other words, if you can afford to have the 2 behaviours you want in 2 different executables, you should not allow this to have an impact on your software architecture. Just build 2 sexy, twin software solutions instead of 1 fat one. :)
From my experience, using C++ inheritance for those sort of problems often ends a quite complex and unmaintainable project.
There are basically two solutions:
Abstract all data types, making them not depend on the rendering layer at all. You will have to copy some data structures from rendering layer, but you only need to replace rendering code.
Choose a portable render layer (OpenGL) and stick to it.
I'm now writing a Direct3D renderer for our engine.
Here's the problem:
In OpenGL, I can just easily call glClearColor() to clear.
In Direct3D, I need to use g_pd3dDevice just to call ClearRenderTargetView() to clear.
The design of our engine is like this:
class Renderer
{
// ...
}
class Direct3dWin32 : public Renderer
{
private ID3D10Device* g_pd3dDevice;
}
class OpenGLWin32 : public Renderer
{
// Nothing, I can call a function easily without relying on something
}
The problem rises when my ShaderManager class wants to compile the shader. I need to use g_pd3dDevice which is on Direct3dWin32 class.
My question is, what is the best approach on solving this problem? I'm thinking of global variables, a singleton class, or just passing the class in function.
First of all, I can't help but notice g_pd3dDevice, that's not a global. It's a class member pointer to a COM interface of the device, ID3D10Device*, and it's not a global here, nor should it be.
And to answer your question as simple as possible (since it seems like a beginner engine/framework design issue), provide accessor methods which return a pointer to a working device from which it can be passed on further, where it needs to be employed.
A simple example to conform to your little "spec" upstairs:
class Direct3DWin32 : public Renderer
{
ID3D10Device* pD3DDevice;
public:
ID3D10Device* getD3DDevice();
}
Now, whenever you need it, you can just pass it around through functions when you get it from your Direct3DWin32 instance. There's a lot more to engine design than this and I personally wouldn't recommend this as a path to take, but that's a tale for another time and perhaps a series of books.
Note!
You can define the basic stuff like this, but if you really want to take the multiple render paths design to a proper level, you're going to have to introduce polymorphism, adding a nice level of abstraction. Then you can simply define a unified rendering interface that will do the right thing, whether the DirectX or the OpenGL rendering path is currently employed, instantiate a derived class and give its address to the pointer to its abstract base class which contains the specified interface everything conforms to. Then you can render obliviously to the underlying choice of API.
Hopefully this solves your current problem. Also, again, evade globals. And happy coding.
You could possibly use a variant of double dispatch (a.k.a. the visitor pattern):
class ShaderManager
{
public:
void compileShader(Renderer* r, Shader* s) { r->compileShader(this, s); }
void compileD3DShader(ID3D10Device* device, Shader*s);
void compileGLShader(Shader* s);
};
class Renderer
{
public:
virtual void compileShader(ShaderManager* m, Shader* s) = 0;
};
class Direct3dWin32 : public Renderer
{
private:
ID3D10Device* m_device;
public:
virtual void compileShader(ShaderManager* m, Shader* s)
{
m->compileD3DShader(m_device, s);
}
}
class OpenGLWin32 : public Renderer
{
public:
virtual void compileShader(ShaderManager* m, Shader* s)
{
m->compileGLShader(s);
}
}
(I'm not a huge fan of "getters".)
You should provide accessor methods for the variables you want to pass into another class.
For instance, in Direct3dWin32, you could have :
ID3d10Device* get_gpd3Device()
{
return g_pd3Device;
}
You can then pass this into OpenGLWin32:
void useDevice (ID3d10Device* aDevice)
{
// do work
}
Your application that uses both classes would then have responsibility for bridging the gap:
OpenGLWin32 openGL;
openGL.useDevice(direct3d.get_gpd3device());