#include "StdAfx.h" #include "CullingProcess.h" #include "Map/WaterShader.h" #include "Map/Water.h" #include "Map/Map.h" CCullingProcess::CCullingProcess() :NiCullingProcess(NULL, true) ,m_pfDepths(NULL) ,m_uiAllocatedDepths(0) ,m_bIsCulled(0) ,m_kVisible(1024, 1024) ,m_pkNormalVisible(0) ,m_uiCurCullLayer(CullMax) ,m_kShadowGeometrys(4, 4) { m_pkVisibleSet = &m_kVisible; m_spReflectCamera = NiNew NiCamera; } CCullingProcess::~CCullingProcess() { if(m_pfDepths) NiFree(m_pfDepths); } void CCullingProcess::Reset(NiCamera* pkCamera) { NIASSERT(pkCamera); m_pkCamera = pkCamera; SetFrustum(m_pkCamera->GetViewFrustum()); m_kVisible.RemoveAll(); RemoveAll(); m_kWaterArray.RemoveAll(); m_bHasRRWater = false; m_kReflectModels.RemoveAll(); m_kReflectChunks.RemoveAll(); m_kShadowGeometrys.RemoveAll(); } void CCullingProcess::AppendVirtual(NiGeometry& kRenderMesh) { m_bIsCulled = TRUE; if ( kRenderMesh.GetFlag() == 100 ) { m_arrVisible[CullOtherObj].Add( kRenderMesh ); return; } if ( m_uiCurCullLayer == CullGameObj || m_uiCurCullLayer == CullTerObject ) { NiGeometryData* pData = kRenderMesh.GetModelData(); NiVisibleArray* kVisibleArray = pData->GetVisibleArray(); if ( !pData->IsRendering() ) //不在渲染队列里 { pData->SetRendering(true); m_arrVisible[m_uiCurCullLayer].Add(kRenderMesh); } kVisibleArray->Add(kRenderMesh); } else { m_arrVisible[m_uiCurCullLayer].Add(kRenderMesh); } if ( m_pkNormalVisible ) m_pkNormalVisible->Add( kRenderMesh ); } void BatchRenderArray( NiVisibleArray* pVisibleArray , NiRenderer* pkRender) { NiGeometry& kMesh = pVisibleArray->GetAt(0); NiPropertyStatePtr spPropState = kMesh.GetPropertyState(); NiDynamicEffectStatePtr spEffectState = kMesh.GetEffectState(); const NiAlphaProperty *pkAlpha = spPropState->GetAlpha(); if ( pkAlpha ) { if (pkAlpha->GetAlphaBlending() && !pkAlpha->GetNoSorter() && kMesh.GetSortObject()) { NiDrawVisibleArrayAppend( *pVisibleArray ); return; } } const unsigned int uiQuantity = pVisibleArray->GetCount(); unsigned int i; pkRender->BeginBatch(spPropState, spEffectState); for (i = 0; i < uiQuantity; i++) { NiGeometry& pkGeom = pVisibleArray->GetAt( i ); if (NiIsKindOf(NiTriStrips, &pkGeom)) { pkRender->BatchRenderStrips((NiTriStrips*)&pkGeom); } else if (NiIsKindOf(NiTriShape, &pkGeom)) { pkRender->BatchRenderShape((NiTriShape*)&pkGeom); } } pkRender->EndBatch(); } unsigned int CCullingProcess::DrawCullObj(NiRenderer* pkRender, ui32 eLayer) { if ( eLayer == CullGameObj || eLayer == CullTerObject ) { NiVisibleArray& kCurVis = m_arrVisible[eLayer]; const unsigned int uiQuantity = kCurVis.GetCount(); unsigned int i; for (i = 0; i < uiQuantity; i++) { NiGeometryData* pData = kCurVis.GetAt(i).GetModelData(); if ( pData ) { NiVisibleArray* pVisibleArry = pData->GetVisibleArray(); //if ( pVisibleArry->GetCount() > 1 ) //{ // // BatchRenderArray(pVisibleArry, pkRender); //} //else NiDrawVisibleArrayAppend( *pVisibleArry ); } } } else //地形,水 { NiDrawVisibleArrayAppend( m_arrVisible[eLayer] ); } return 0; } bool CCullingProcess::ProcessObject(NiAVObject* pkObject, ui32 uiLayer, NiVisibleArray* pVisible ) { m_bIsCulled = FALSE; m_pkNormalVisible = pVisible; m_uiCurCullLayer = uiLayer; if ( !pkObject->GetAppCulled() ) NiCullingProcess::Process(pkObject); return m_bIsCulled; } void CCullingProcess::Remove(ui32 eLayer) { NiVisibleArray& kVisible = m_arrVisible[eLayer]; if ( eLayer == CullGameObj || eLayer == CullTerObject ) { const unsigned int uiQuantity = kVisible.GetCount(); unsigned int i; for (i = 0; i < uiQuantity; i++) { NiGeometryData* pGeoData = kVisible.GetAt(i).GetModelData(); if ( pGeoData ) { pGeoData->SetRendering(false); NiVisibleArray* pVisibleArry = pGeoData->GetVisibleArray(); pVisibleArry->RemoveAll(); } } } m_arrVisible[eLayer].RemoveAll(); } void CCullingProcess::RemoveAll() { for (ui32 i=0; iGetShader(); if(!pkShader->GetRR()) { //如果没有反射折射效果 m_arrVisible[CullTerMesh].Add(*(pkWater->GetGeometry())); return; } m_kWaterArray.Add(*(pkWater->GetGeometry())); if(!m_bHasRRWater) { m_bHasRRWater = true; m_fRRWaterHeight = pkWater->GetAVGHeight(); m_kRRWaterBase = pkWater->GetBasePoint(); m_kRRWaterBase.z = m_fRRWaterHeight; return; } //寻找更近的反射水面 NiPoint3 kPos = m_pkCamera->GetWorldLocation(); NiPoint3 kWaterBase = pkWater->GetBasePoint(); kWaterBase.z = pkWater->GetAVGHeight(); if( (kPos - kWaterBase).SqrLength() < (kPos - m_kRRWaterBase).SqrLength() ) { m_kRRWaterBase = kWaterBase; m_fRRWaterHeight = kWaterBase.z; } } void CCullingProcess::SortWater() { const unsigned int uiCount = m_kWaterArray.GetCount(); // Initialize size of object depths array. if (m_uiAllocatedDepths < uiCount) { NiFree(m_pfDepths); m_pfDepths = NiAlloc(float, uiCount); m_uiAllocatedDepths = uiCount; } // Iterate over geometry array. NiPoint3 kWorldDir = m_pkCamera->GetWorldDirection(); for (unsigned int ui = 0; ui < uiCount; ui++) { NiGeometry& kGeometry = m_kWaterArray.GetAt(ui); const NiBound& kWorldBound = kGeometry.GetWorldBound(); m_pfDepths[ui] = kWorldBound.GetCenter() * kWorldDir; } // Sort output array by depth. SortObjectsByDepth(m_kWaterArray, 0, m_kWaterArray.GetCount() - 1); } //--------------------------------------------------------------------------- void CCullingProcess::SortObjectsByDepth(NiVisibleArray& kArrayToSort, int l, int r) { // This recursive function implements a quick sort of kArrayToSort. It is // taken directly from the function of the same name in // NiBackToFrontAccumulator except that it sorts back to front instead of // front to back. if (r > l) { int i = l - 1; int j = r + 1; float fPivot = ChoosePivot(l, r); while (true) { do { j--; } while (fPivot > m_pfDepths[j]); do { i++; } while (m_pfDepths[i] > fPivot); if (i < j) { // Swap array elements. NiGeometry* pkTempObj = &kArrayToSort.GetAt(i); kArrayToSort.SetAt(i, kArrayToSort.GetAt(j)); kArrayToSort.SetAt(j, *pkTempObj); float fTempDepth = m_pfDepths[i]; m_pfDepths[i] = m_pfDepths[j]; m_pfDepths[j] = fTempDepth; } else { break; } } if (j == r) { SortObjectsByDepth(kArrayToSort, l, j - 1); } else { SortObjectsByDepth(kArrayToSort, l, j); SortObjectsByDepth(kArrayToSort, j + 1, r); } } } //--------------------------------------------------------------------------- float CCullingProcess::ChoosePivot(int l, int r) const { // Check the first, middle, and last element. Choose the one which falls // between the other two. This has a good chance of discouraging // quadratic behavior from qsort. // In the case when all three are equal, this code chooses the middle // element, which will prevent quadratic behavior for a list with // all elements equal. int m = (l + r) >> 1; const float fDepth_l = m_pfDepths[l]; const float fDepth_r = m_pfDepths[r]; const float fDepth_m = m_pfDepths[m]; if (fDepth_l > fDepth_m) { if (fDepth_m > fDepth_r) { return fDepth_m; } else { if (fDepth_l > fDepth_r) { return fDepth_r; } else { return fDepth_l; } } } else { if (fDepth_l > fDepth_r) { return fDepth_l; } else { if (fDepth_m > fDepth_r) { return fDepth_r; } else { return fDepth_m; } } } } void CCullingProcess::OnReflectChunkVisible(CChunk* pkChunk) { m_kReflectChunks.Add(*pkChunk->GetGeometry()); } void CCullingProcess::CullReflectModel(NiAVObject* pkSceneRoot) { if(pkSceneRoot == NULL || pkSceneRoot->GetAppCulled()) return; unsigned int i; int iSide; unsigned int uiSaveActive = m_kReflectPlanes.GetActivePlaneState(); for (i = 0; i < NiFrustumPlanes::MAX_PLANES; i++) { if (m_kReflectPlanes.IsPlaneActive(i)) { iSide = pkSceneRoot->GetWorldBound().WhichSide(m_kReflectPlanes.GetPlane(i)); if (iSide == NiPlane::NEGATIVE_SIDE) { // The object is not visible since it is on the negative // side of the plane. break; } if (iSide == NiPlane::POSITIVE_SIDE) { // The object is fully on the positive side of the plane, // so there is no need to compare child objects to this // plane. m_kReflectPlanes.DisablePlane(i); } } } if(i == NiFrustumPlanes::MAX_PLANES) { if(NiIsKindOf(NiNode, pkSceneRoot)) { for(unsigned int ui = 0; ui < ((NiNode*)pkSceneRoot)->GetArrayCount(); ++ui) { CullReflectModel(((NiNode*)pkSceneRoot)->GetAt(ui)); } } else if(NiIsKindOf(NiGeometry, pkSceneRoot)) { m_kReflectModels.Add(*(NiGeometry*)pkSceneRoot); } } m_kReflectPlanes.SetActivePlaneState(uiSaveActive); } void CCullingProcess::UpdateReflectCamera() { const NiCamera* pkCamera = m_pkCamera; NiPoint3 kWorldLoc = pkCamera->GetWorldLocation(); NiPoint3 kWorldDir = pkCamera->GetWorldDirection(); NiPoint3 kWorldUp = pkCamera->GetWorldUpVector(); kWorldLoc.z = GetRRWaterHeight()*2 - kWorldLoc.z; kWorldDir.z = -kWorldDir.z; kWorldUp.z = -kWorldUp.z; m_spReflectCamera->SetTranslate(kWorldLoc); m_spReflectCamera->Update(0.f, false); m_spReflectCamera->LookAtWorldPoint(kWorldLoc + kWorldDir, kWorldUp); m_spReflectCamera->SetViewFrustum(pkCamera->GetViewFrustum()); m_spReflectCamera->SetViewPort(pkCamera->GetViewPort()); m_spReflectCamera->Update(0.f, false); m_kReflectPlanes.Set(pkCamera->GetViewFrustum(), m_spReflectCamera->GetWorldTransform()); m_kReflectPlanes.EnableAllPlanes(); } void CCullingProcess::BuildReflectVisibleSet() { if(!HasRRWater()) return; UpdateReflectCamera(); //水面以下 if(m_pkCamera->GetWorldLocation().z < m_fRRWaterHeight) return; CMap::Get()->BuildReflectVisibleSet(this); } void CCullingProcess::OnDynmicShadowVisible(CShadowGeometry* pkShadowGeometry) { NIASSERT(pkShadowGeometry); m_kShadowGeometrys.Add(pkShadowGeometry); }