Good news, the basics are set up and working nicely..
Created a renderqueue class taking care of things and split up updating the scene from rendering (might be usefull if I need multi threading in the future).
What I don't get yet, is how to implement a check for redundant setting of a vertexbuffer or indices.
Next steps are;
- add an index to sort per shader
- add a form a culling 'areas' (quadtree or something, rather think of something myself :))
- after that no more optimizing, just add lots of new goodies
Here are the results (code), please shoot :) really like to hear your suggestions
// RenderFrame function
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(!RenderScene(pD3dscene, pCam, "OpaqueShader", _OPAQUE)) return false;
if(!RenderScene(pD3dscene, pCam, "BlendingShader", _BLENDED)) return false;
if(pD3dscene->mSkyBoxInScene) if(!pD3dscene->mSkyBox.Render(pCam->mPosition, pCam, mD3ddev)) return false;
/** FFP RENDERING, I.E. SCENE STATISTICS **/
if(!SetDefaultRenderStates()) return false;
PrintSceneInfo(pCam, pD3dscene->mNrMaterials);
/** PRESENT THE FINAL RENDERED SCENE FROM BACKBUFFER **/
mD3ddev->EndScene();
HRESULT hr = mD3ddev->Present(NULL, NULL, NULL, NULL);
return true;
}
// Render a scene with specific technique
bool CD3d::RenderScene(CD3dscene *pD3dscene, CD3dcam *pCam, char *pTechnique, int mattype)
{
for(fx=0;fx<mRenderQueue.mNrEffects;++fx)
{
if(!SetShaderTechnique(pD3dscene, fx, pTechnique)) return false; // 1x SetTechnique, 1x SetPixelShader/ SetVertexShader?
pD3dscene->mEffect[fx]->Begin(&pD3dscene->mEffectNumPasses[fx], D3DXFX_DONOTSAVESTATE); // 'x' RenderStates, based on FX/shader content
for(_i=0;_i<pD3dscene->mEffectNumPasses[fx];++_i)
{
pD3dscene->mEffect[fx]->BeginPass(_i);
for(mat=0;mat<mRenderQueue.mNrMaterials;++mat)
{
if(!pD3dscene->PreSelectMaterial(mat, fx)) return false; // 2x SetFloatArray, 1x SetTexture
for(m=0;m<mRenderQueue.mMaterialData[mat].nrMeshes;++m)
{
mesh = mRenderQueue.mMaterialData[mat].meshIds[m];
if(!pD3dscene->mMeshes[mesh].SetBuffers(mD3ddev)) return false; // SetStreamSource, SetIndices
for(mi=0;mi<mRenderQueue.GetNrInstances(mesh, mattype);++mi)
{
instance = mRenderQueue.GetInstance(mesh, mi, mattype);
if(mRenderQueue.mMeshInstData[instance].effectId == fx) // INDEX NEEDED TO?
{
if(mRenderQueue.mMeshInstData[instance].visible) // (MICRO-OPT) optimization? Sort index per frame
{
if(!pD3dscene->PreSelectMeshInst(instance, mD3ddev)) return false; // 2x SetMatrix (World/WorldInvTransp)
pD3dscene->mEffect[fx]->CommitChanges();
for(subm=0;subm<mRenderQueue.mMaterialData[mat].meshSubMeshes[m].nrSubMeshes;++subm)
{
submesh = mRenderQueue.mMaterialData[mat].meshSubMeshes[m].subMeshes[subm];
pD3dscene->mMeshes[mesh].RenderSubMesh(mD3ddev, submesh, LIST);
}
}
}
}
}
}
pD3dscene->mEffect[fx]->EndPass();
}
pD3dscene->mEffect[fx]->End();
}
return true;
}
// Update scene function
bool CD3d::UpdateScene(CD3dscene *pD3dscene, CD3dcam *pCam)
{
// TODO here; introduce tree - spatial culling
/** UPDATE DISTANCE TO CAM FOR BLENDED MESH INSTANCES **/
for(m=0;m<mRenderQueue.mNrMeshes;++m)
for(mi=0;mi<mRenderQueue.mMeshData[m].nrInstancesBlended;++mi)
pD3dscene->mMeshInstances[mRenderQueue.mMeshData[m].instancesBlended[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();
/** 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.mMeshInstData[mi].visible = true;
else mRenderQueue.mMeshInstData[mi].visible = false;
}
return true;
}
// the small functions which do the actual parameter changes
bool CD3d::SetShaderTechnique(CD3dscene *pD3dscene, int pEffectIndex, char *pTechnique)
{
if(D3DERR_INVALIDCALL == pD3dscene->mEffect[pEffectIndex]->SetTechnique(pTechnique)) return false;
return true;
}
bool CD3dscene::PreSelectMeshInst(int pMeshInstId, LPDIRECT3DDEVICE9 pD3ddev)
{
if(D3DERR_INVALIDCALL == mEffect[mMeshInstances[pMeshInstId].mEffectIndex]->SetMatrix("World", &mMeshInstances[pMeshInstId].mMatWorld)) return false;
if(D3DERR_INVALIDCALL == mEffect[mMeshInstances[pMeshInstId].mEffectIndex]->SetMatrix("WorldInvTransp", &mMeshInstances[pMeshInstId].mMatWorldInvTransp)) return false;
// OR normalize in Shader for lighting
return true;
}
bool CD3dscene::PreSelectMaterial(DWORD pMatId, int pEffectIndex)
{
if(D3DERR_INVALIDCALL == mEffect[pEffectIndex]->SetFloatArray("MatAmb", mMaterials[pMatId].Ambient, 4)) return false;
if(D3DERR_INVALIDCALL == mEffect[pEffectIndex]->SetFloatArray("MatDiff", mMaterials[pMatId].Diffuse, 4)) return false;
if(mTextures[pMatId] != NULL)
if(D3DERR_INVALIDCALL == mEffect[pEffectIndex]->SetTexture("Tex0", mTextures[pMatId])) return false;
return true;
}