Hi,
I've decided to fully redesign my scenegraph/ renderqueue, meaning completely starting over from scratch.
Adding stuff on and on even makes it difficult for myself to understand the current approach and code.
New approach:
- make 2 'buckets' of renderables, opaque and blended
- simply loop through the buckets in my 2 main drawing functions
- the scenegraph will use the d3dscene class and it's objects and only saves ID's and other parameters dependent on actual rendering
First (pseudo) code:
typedef struct Q_Renderable
{
unsigned int Effect;
unsigned int Material;
unsigned int Mesh;
unsigned int Instance;
unsigned int Id; // subset/ renderable of the mesh
// bool Visible;
}
class CSceneGraph
{
public:
void Update(const Crealysm_d3drenderer::CD3dscene *pD3dscene);
std::vector<Q_Renderable> mOpaqueBucket;
std::vector<Q_Renderable> mBlendedBucket;
private:
std::vector<Q_Mesh> mMeshes;
std::vector<Q_Material> mMaterials;
std::vector<Q_Instance> mInstances;
std::vector<Q_Light> mLights;
// etc., saves ID's, visibility state after culling, distances etc.
// functions to update the renderables buckets, based on visibility, sorting, culling etc.
// etc.
}
Then in the drawing function my approach is something like this:
bool CD3d::RenderSceneOpaque(const char *pTechnique, const CD3dcam &pCam)
{
/** retrieve the 1st element from bucket and set all buffers/ shader constants **
Q_Renderable last = mSceneGraph.mOpaqueBucket[0];
ShaderSetTechnique(last.Effect, pTechnique);
mD3dscene->mShaders[last.Effect].Begin();
mD3dscene->ShaderSelectMaterial(last.Material, lastEffect);
mD3dscene->mMeshes[last.Mesh].SetBuffers();
mD3dscene->ShaderSetWorld(last.Instance, lastId);
bool bucketDone = false;
bool nextEffect = false;
int renderable = 0;
while(!bucketDone)
{
auto &next = mSceneGraph.mOpaqueBucket[renderable];
if(next.Effect != last.Effect)
{
mD3dscene->mShaders[last.Effect].End();
ShaderSetTechnique(next.Effect, pTechnique);
mD3dscene->mShaders[next.Effect].Begin();
nextEffect = false;
}
for(unsigned int p=0;p<shader.GetNumPasses();++p)
{
mD3dscene->mShaders[next.Effect].BeginPass(p);
if(next.Material != last.Material) mD3dscene->ShaderSelectMaterial(next.Material, lastEffect);
if(next.Mesh != last.Mesh) mD3dscene->mMeshes[next.Mesh].SetBuffers();
mD3dscene->ShaderSetWorld(next.Instance, next.Id);
mD3dscene->mShaders[next.Effect].Commit();
mD3dscene->mMeshes[next.Mesh].RenderSubMesh(next.Id, LIST); // does the draw call
mD3dscene->mShaders[next.Effect].EndPass(p);
}
last = mSceneGraph.mOpaqueBucket[renderable];
++renderable;
if(renderable = (int)mSceneGraph.mOpaqueBucket.size()) bucketDone = true;
}
}
(Nb. 1: I'm aware that this assumes all my effect files have a shared technique name)
(Nb. 2: I've left out error checking/ returning false to keep it readable for now)
My questions/ I'm curious about your opinion on:
1 - the approach itself
2 - although it might sound like 'pre optimization'/evil, would you store only visible renderables in the bucket, or do a 'if(visible)' check in the drawing loop? (more cpu activity per frame or maybe not). Only visibles gives a cleaner code draw loop for sure.
3 - what are your suggestions to 'clean' the somewhat ugly main drawing loop?
(it feels like it could be done easier and/or more efficiently)
Any input is appreciated.