Hello,
I've already implemented a basic rendering system for my entities. Its working, but its not very effective. Take a look:
class DeferredRenderSystem : public EntitySystem<DeferredRenderSystem>
{
public:
DeferredRenderSystem(Gfx3D& gfx3D): m_pGfx3D(&gfx3D) {};
void Update(EntityManager& entityManager)
{
//todo: HUGE cleanup
EntityManager::entityVector* vEntities = entityManager.EntitiesWithComponents<ModelComponent, Position>();
if(vEntities)
{
const Camera* pCamera = m_pGfx3D->GetCamera();
D3DXMATRIX mWorld, mTrans, mRotX, mRotY, mRotZ, mScale;
Effect* effect;
Model* model;
Material* material;
// set g-buffer
m_pGfx3D->Set3DRenderTarget(0, RenderTargets::DEFERRED_MATERIAL);
m_pGfx3D->Set3DRenderTarget(1, RenderTargets::DEFERRED_POSITION);
m_pGfx3D->Set3DRenderTarget(2, RenderTargets::DEFERRED_NORMAL);
m_pGfx3D->ClearAllBound();
for(auto entity : *vEntities)
{
D3DXMatrixIdentity(&mWorld);
Position* pos = entity->GetComponent<Position>();
ModelComponent* mod = entity->GetComponent<ModelComponent>();
//render components
effect = m_pGfx3D->GetEffect(mod->m_effect);
model = m_pGfx3D->GetModel(mod->m_model);
material = m_pGfx3D->GetMaterial(mod->m_material);
//set effect constants
effect->SetTexture("Material", m_pGfx3D->GetTexture(material->GetTexture()));
effect->SetTexture("Bump", m_pGfx3D->GetTexture(material->GetBumpMap()));
effect->SetMatrix("ViewProj", pCamera->GetViewProjectionMatrix());
//rotation
if(Rotation* pRotation = entity->GetComponent<Rotation>())
{
D3DXMatrixRotationX(&mRotX, DEGTORAD(pRotation->m_x));
D3DXMatrixRotationY(&mRotY, DEGTORAD(pRotation->m_y));
D3DXMatrixRotationZ(&mRotZ, DEGTORAD(pRotation->m_z));
mWorld *= mRotX;
mWorld *= mRotY;
mWorld *= mRotZ;
}
//scaling
if(Scalation* pScalation = entity->GetComponent<Scalation>())
{
D3DXMatrixScaling(&mScale, pScalation->m_x, pScalation->m_y, pScalation->m_z);
mWorld *= mScale;
}
//translation
D3DXMatrixTranslation(&mTrans, pos->m_x, pos->m_y, pos->m_z);
mWorld *= mTrans;
effect->SetMatrix("World", mWorld);
effect->Begin();
model->Render();
effect->End();
}
delete vEntities;
//unbind g-buffer
m_pGfx3D->Set3DRenderTarget(0, RenderTargets::BACKBUFFER);
m_pGfx3D->Set3DRenderTarget(1, RenderTargets::BACKBUFFER);
m_pGfx3D->Set3DRenderTarget(2, RenderTargets::BACKBUFFER);
}
}
private:
Gfx3D* m_pGfx3D;
};
So way aside from me not using a renderqueue (going to be resolved at another time), there are two main Problems:
- I have to reconstruct and multiply all matrices each frame, even if the object did not change at all.
- I have to access the model, effect, and material from my graphics module every frame, even if they did not change.
This is due to my attempt on following the approach that each entities component shall only have data, and even as little data as possible, so the involved components look like this:
struct Position : Component<Position>
{
Position(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};
float m_x, m_y, m_z;
};
struct ModelComponent : Component<ModelComponent>
{
ModelComponent(LPCWSTR model, unsigned int material, LPCWSTR effect): m_model(model), m_material(material), m_effect(effect) {};
std::wstring m_model, m_effect;
unsigned int m_material;
};
The obvious advantage is my components being completely independent of the actul render implementation, and being easily editable e.g. in my editor. So I don't think I should change this entirely (unless someone can give me a good reason to do so), but to introduce some sort of caching. My question is now: Which one of those ways would you consider the best (most flexible, ...) and if so, why?
- Give each component additional members like a translation-matrix for my position component and a dirty flag. The rendering system would then use this components and, if the dirty flag is set, update it based on the float position members of the component (same applies to the model component etc...).
- Same as above, but instead of having a dirty flag and the render system being responsible for updating the matrix, accessing the position through getters and setters thus always updating the matrix when changing the position.
- Having a CacheComponent on each entity that is responsibly for storing things like the actual translation matrix for the position component etc... . Like above, I would have a dirty flag and have the render system update the certain cached matrices etc.. if necessary.
- Same as above but instead of a CacheComponent have each system implement a caching functionality themselfs, working the way as above.
- Something else?
What do you think would be the "best" approach here?