Hi Niello.
Thanks again for your feedback on the approach.
I actually complete rewrote my renderqueue when I introduced 'indices'/ scene graph also for each effect and with opaque/blended split up.
All working great now, really happy with it. Profiling with PIX tells me that I eliminated ALL unnecessary setting of materials, states, buffers etc.
Next step; lighting system
Probably combined with introducing spatial culling, to prevent limiting myself to 8 point lights in the 'world'.
Here's the latest version of my demo, if you'd like to take a peek: www.sierracosworth.nl/gamedev/2013-02-10-demo.zip
(controls: W/S/A/D, PG UP/PG DWN)
I've posted just the 3d class rendering code, to give you an impression. Input and feedback always welcome :)
(I didn't post all renderqueue creation code, would be a waste)
/**************************************************************************************/
/*** RENDERFRAME ***/
/*** ==> usage: in main loop, for each frame; RenderQueue added 24-1-2013 ***/
/*** ==> render a frame with 3d scene; 1. update cam and scene 2. render fx ***/
/**************************************************************************************/
bool CD3d::RenderFrame(CD3dscene *pD3dscene, CD3dcam *pCam)
{
if(!CheckDevice()) { mDeviceLost = true; return true; }
mDrawCallsPerFrame = 0; mDrawTriPerFrame = 0;
pCam->Update();
/** CULLING AND SORTING **/
if(!UpdateScene(pD3dscene, pCam)) return false;
mD3ddev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
mD3ddev->BeginScene();
/** SET SHARED FX/ SHADER PARAMETERS **/
if(D3DERR_INVALIDCALL == pD3dscene->mEffect[0]->SetMatrix("ViewProj", &pCam->mMatViewProjection)) return false; // SHARED PAREMETER IN POOL
/** RENDER SCENE USING FX/ SHADER WITH SPECIFIC TECHNIQUE **/
if(!RenderSceneOpaque(pD3dscene, pCam, "OpaqueShader")) return false;
if(!RenderSceneBlended(pD3dscene, pCam, "BlendingShader")) return false;
if(pD3dscene->mSkyBoxInScene) if(!pD3dscene->mSkyBox.Render(pCam->mPosition, pCam, mD3ddev)) return false;
/** FFP RENDERING, I.E. SCENE STATISTICS **/ /** not used: SetDefaultRenderStates() doesn't do anything now **/
PrintSceneInfo(pCam, pD3dscene->mNrMaterials);
/** PRESENT THE FINAL RENDERED SCENE FROM BACKBUFFER **/
mD3ddev->EndScene();
HRESULT hr = mD3ddev->Present(NULL, NULL, NULL, NULL);
OutputDebugRenderInfo(hr);
return true;
}
/**************************************************************************************/
/*** UPDATE SCENE ***/
/*** ==> usage: within renderframe, before draw calls etc. ***/
/*** ==> checks all meshes against space positioning, frustum etc. ***/
/**************************************************************************************/
bool CD3d::UpdateScene(CD3dscene *pD3dscene, CD3dcam *pCam)
{
/** UPDATE DISTANCE TO CAM FOR BLENDED MESH INSTANCES **/ // TODO
for(fx=0;fx<mRenderQueue.mNrEffects;++fx)
{
for(m=0;m<mRenderQueue.mEffect[fx].nrMeshesBlended;++m)
{
for(mi=0;mi<mRenderQueue.mEffect[fx].meshInstBlended[m].nrInstances;++mi)
pD3dscene->mMeshInstances[mRenderQueue.mEffect[fx].meshInstBlended[m].instances[mi]].UpdateDistToCam(pCam->mPosition);
}
}
/** SORT BLENDED MESH INSTANCES, BACK TO FRONT **/
if(!mRenderQueue.SortBlendedMeshes(pD3dscene)) return false;
/** UPDATE WORLD MATRIX, FOR DYNAMIC MESH INSTANCES ONLY **/
for(mi=0;mi<mRenderQueue.mNrMeshInstDynamic;++mi)
pD3dscene->mMeshInstances[mRenderQueue.mDynamicMeshInstIndex[mi]].UpdateWorldMatrix();
// TODO here; introduce tree - spatial culling
/** CULL MESH INSTANCES AGAINST FRUSTUM, VISIBLE YES/NO **/
for(mi=0;mi<mRenderQueue.mNrMeshInst;++mi)
{
if(pCam->SphereInFrustum(&pD3dscene->mMeshInstances[mi].mWorldPos, pD3dscene->mMeshInstances[mi].mBoundingRadius))
mRenderQueue.mMeshInst[mi].visible = true;
else mRenderQueue.mMeshInst[mi].visible = false;
}
return true;
}
/**************************************************************************************/
/*** RENDER SCENE - OPAQUE ***/
/*** ==> usage: within renderframe, to render specific technique of shader ***/
/*** ==> renders all opaque mesh instances through renderqueue for all effects ***/
/**************************************************************************************/
bool CD3d::RenderSceneOpaque(CD3dscene *pD3dscene, CD3dcam *pCam, char *pTechnique)
{
for(fx=0;fx<mRenderQueue.mNrEffects;++fx)
{
if(!SetShaderTechnique(pD3dscene, fx, pTechnique)) return false;
pD3dscene->mEffect[fx]->Begin(&pD3dscene->mEffectNumPasses[fx], D3DXFX_DONOTSAVESTATE);
for(_i=0;_i<pD3dscene->mEffectNumPasses[fx];++_i)
{
pD3dscene->mEffect[fx]->BeginPass(_i);
for(mat=0;mat<mRenderQueue.mEffect[fx].nrMaterialsOpaque;++mat)
{
matid = mRenderQueue.mEffect[fx].materialsOpaque[mat];
if(!pD3dscene->PreSelectMaterial(matid, fx)) return false;
for(m=0;m<mRenderQueue.mEffect[fx].nrMeshesOpaque;++m)
{
mesh = mRenderQueue.mEffect[fx].meshOpaque[m];
if(!pD3dscene->mMeshes[mesh].SetBuffers(mD3ddev)) return false;
for(mi=0;mi<mRenderQueue.mEffect[fx].meshInstOpaque[m].nrInstances;++mi)
{
instance = mRenderQueue.mEffect[fx].meshInstOpaque[m].instances[mi];
if(mRenderQueue.mMeshInst[instance].visible) // (MICRO)optimization? Sort per frame
{
if(!pD3dscene->PreSelectMeshInst(instance, mD3ddev)) return false;
pD3dscene->mEffect[fx]->CommitChanges();
RenderSubMeshes(pD3dscene, matid, mesh);
}
}
}
}
pD3dscene->mEffect[fx]->EndPass();
}
pD3dscene->mEffect[fx]->End();
}
return true;
}
/**************************************************************************************/
/*** RENDER SCENE - BLENDED ***/
/*** ==> usage: within renderframe, to render specific technique of shader ***/
/*** ==> renders all blended mesh instances through renderqueue for all effects ***/
/**************************************************************************************/
bool CD3d::RenderSceneBlended(CD3dscene *pD3dscene, CD3dcam *pCam, char *pTechnique)
{
for(fx=0;fx<mRenderQueue.mNrEffects;++fx)
{
if(!SetShaderTechnique(pD3dscene, fx, pTechnique)) return false;
pD3dscene->mEffect[fx]->Begin(&pD3dscene->mEffectNumPasses[fx], D3DXFX_DONOTSAVESTATE);
for(_i=0;_i<pD3dscene->mEffectNumPasses[fx];++_i)
{
pD3dscene->mEffect[fx]->BeginPass(_i);
for(mat=0;mat<mRenderQueue.mEffect[fx].nrMaterialsBlended;++mat)
{
matid = mRenderQueue.mEffect[fx].materialsBlended[mat];
if(!pD3dscene->PreSelectMaterial(matid, fx)) return false;
for(m=0;m<mRenderQueue.mEffect[fx].nrMeshesBlended;++m)
{
mesh = mRenderQueue.mEffect[fx].meshBlended[m];
if(!pD3dscene->mMeshes[mesh].SetBuffers(mD3ddev)) return false;
for(mi=0;mi<mRenderQueue.mEffect[fx].meshInstBlended[m].nrInstances;++mi)
{
instance = mRenderQueue.mEffect[fx].meshInstBlended[m].instances[mi];
if(mRenderQueue.mMeshInst[instance].visible) // (MICRO)optimization? Sort per frame
{
if(!pD3dscene->PreSelectMeshInst(instance, mD3ddev)) return false;
pD3dscene->mEffect[fx]->CommitChanges();
RenderSubMeshes(pD3dscene, matid, mesh);
}
}
}
}
pD3dscene->mEffect[fx]->EndPass();
}
pD3dscene->mEffect[fx]->End();
}
return true;
}
/**************************************************************************************/
/*** RENDER SUBMESHES ***/
/*** ==> usage: within renderscene, to render submeshes with specific material ***/
/*** ==> renders all submeshes of given mesh with corresponding material ***/
/**************************************************************************************/
void CD3d::RenderSubMeshes(CD3dscene *pD3dscene, int pMatId, int pMeshId)
{
for(sub=0;sub<mRenderQueue.mMaterials[matid].meshes[mesh].nrSubMeshes;++sub) // NOT OPAQUE/BLENDED SPECIFIC
{
submesh = mRenderQueue.mMaterials[matid].meshes[mesh].submeshes[sub];
pD3dscene->mMeshes[mesh].RenderSubMesh(mD3ddev, submesh, LIST);
#ifdef _DEBUG
++mDrawCallsPerFrame;
mDrawTriPerFrame += pD3dscene->mMeshes[mesh].mSubMeshTable[submesh].FaceCount;
#endif
}
}