You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
712 lines
20 KiB
712 lines
20 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
|
|
#include "hsTypes.h" |
|
#include "plPageTreeMgr.h" |
|
#include "../plDrawable/plSpaceTreeMaker.h" |
|
#include "../plDrawable/plSpaceTree.h" |
|
#include "plDrawable.h" |
|
#include "../plScene/plSceneNode.h" |
|
#include "plPipeline.h" |
|
#include "../plMath/hsRadixSort.h" |
|
#include "plCullPoly.h" |
|
#include "plOccluder.h" |
|
#include "hsFastMath.h" |
|
#include "plProfile.h" |
|
#include "plVisMgr.h" |
|
|
|
#include "plTweak.h" |
|
|
|
static hsTArray<hsRadixSortElem> scratchList; |
|
|
|
hsBool plPageTreeMgr::fDisableVisMgr = 0; |
|
|
|
plProfile_CreateTimer("Object Sort", "Draw", DrawObjSort); |
|
plProfile_CreateCounter("Objects Sorted", "Draw", DrawObjSorted); |
|
plProfile_CreateTimer("Occluder Sort", "Draw", DrawOccSort); |
|
plProfile_CreateCounter("Occluders Used", "Draw", DrawOccUsed); |
|
plProfile_CreateTimer("Occluder Build", "Draw", DrawOccBuild); |
|
plProfile_CreateCounter("Occluder Polys Processed", "Draw", DrawOccPolyProc); |
|
plProfile_CreateTimer("Occluder Poly Sort", "Draw", DrawOccPolySort); |
|
|
|
plPageTreeMgr::plPageTreeMgr() |
|
: fSpaceTree(nil) |
|
{ |
|
fVisMgr = plGlobalVisMgr::Instance(); |
|
} |
|
|
|
plPageTreeMgr::~plPageTreeMgr() |
|
{ |
|
delete fSpaceTree; |
|
} |
|
|
|
void plPageTreeMgr::AddNode(plSceneNode* node) |
|
{ |
|
ITrashSpaceTree(); |
|
|
|
node->Init(); |
|
|
|
fNodes.Append(node); |
|
} |
|
|
|
void plPageTreeMgr::RemoveNode(plSceneNode* node) |
|
{ |
|
ITrashSpaceTree(); |
|
|
|
int idx = fNodes.Find(node); |
|
if( idx != fNodes.kMissingIndex ) |
|
fNodes.Remove(idx); |
|
} |
|
|
|
void plPageTreeMgr::Reset() |
|
{ |
|
fNodes.Reset(); |
|
|
|
ITrashSpaceTree(); |
|
} |
|
|
|
void plPageTreeMgr::ITrashSpaceTree() |
|
{ |
|
delete fSpaceTree; |
|
fSpaceTree = nil; |
|
} |
|
|
|
hsBool plPageTreeMgr::Harvest(plVolumeIsect* isect, hsTArray<plDrawVisList>& levList) |
|
{ |
|
levList.SetCount(0); |
|
if( !(GetSpaceTree() || IBuildSpaceTree()) ) |
|
return false; |
|
|
|
static hsTArray<Int16> list; |
|
|
|
GetSpaceTree()->HarvestLeaves(isect, list); |
|
|
|
int i; |
|
for( i = 0; i < list.GetCount(); i++ ) |
|
{ |
|
fNodes[list[i]]->Harvest(isect, levList); |
|
} |
|
|
|
return levList.GetCount() > 0; |
|
} |
|
|
|
#include "plProfile.h" |
|
plProfile_CreateTimer("DrawableTime", "Draw", DrawableTime); |
|
plProfile_Extern(RenderScene); |
|
|
|
int plPageTreeMgr::Render(plPipeline* pipe) |
|
{ |
|
// If we don't have a space tree and can't make one, just bail |
|
if( !(GetSpaceTree() || IBuildSpaceTree()) ) |
|
return 0; |
|
|
|
static hsTArray<Int16> list; |
|
list.SetCount(0); |
|
|
|
plProfile_BeginTiming(RenderScene); |
|
|
|
plVisMgr* visMgr = fDisableVisMgr ? nil : fVisMgr; |
|
|
|
if( visMgr ) |
|
{ |
|
plProfile_Extern(VisEval); |
|
plProfile_BeginTiming(VisEval); |
|
visMgr->Eval(pipe->GetViewPositionWorld()); |
|
plProfile_EndTiming(VisEval); |
|
} |
|
|
|
pipe->BeginVisMgr(visMgr); |
|
|
|
IRefreshTree(pipe); |
|
|
|
IGetOcclusion(pipe, list); |
|
pipe->HarvestVisible(GetSpaceTree(), list); |
|
|
|
static hsTArray<plDrawVisList> levList; |
|
levList.SetCount(0); |
|
int i; |
|
for( i = 0; i < list.GetCount(); i++ ) |
|
{ |
|
fNodes[list[i]]->CollectForRender(pipe, levList, visMgr); |
|
} |
|
|
|
int numDrawn = IRenderVisList(pipe, levList); |
|
|
|
IResetOcclusion(pipe); |
|
|
|
pipe->EndVisMgr(visMgr); |
|
|
|
plProfile_EndTiming(RenderScene); |
|
|
|
return numDrawn; |
|
|
|
} |
|
|
|
int plPageTreeMgr::IRenderVisList(plPipeline* pipe, hsTArray<plDrawVisList>& levList) |
|
{ |
|
// Sort levList into sortedDrawList, which is just a list |
|
// of drawable/visList pairs in ascending render priority order. |
|
// visLists are just lists of span indices, but only of the |
|
// spans which are visible (on screen and non-occluded and non-disabled). |
|
static hsTArray<plDrawVisList> sortedDrawList; |
|
if( !ISortByLevel(pipe, levList, sortedDrawList) ) |
|
{ |
|
return 0; |
|
} |
|
|
|
int numDrawn = 0; |
|
|
|
plVisMgr* visMgr = fDisableVisMgr ? nil : fVisMgr; |
|
|
|
// Going through the list in order, if we hit a drawable which doesn't need |
|
// its spans sorted, we can just draw it. |
|
// If we hit a drawable which does need its spans sorted, we could just draw |
|
// it, but that precludes sorting spans between drawables (like the player avatar |
|
// sorting with normal scene objects). So when we hit a drawable which needs |
|
// span sorting, we sort its spans with the spans of the next N drawables in |
|
// the sorted list which have the same render priority and which also want their |
|
// spans sorted. |
|
int i; |
|
for( i = 0; i < sortedDrawList.GetCount(); i++ ) |
|
{ |
|
plDrawable* p = sortedDrawList[i].fDrawable; |
|
|
|
|
|
plProfile_BeginLap(DrawableTime, p->GetKey()->GetUoid().GetObjectName()); |
|
|
|
if( sortedDrawList[i].fDrawable->GetNativeProperty(plDrawable::kPropSortSpans) ) |
|
{ |
|
// IPrepForRenderSortingSpans increments "i" to the next index to be drawn (-1 so the i++ |
|
// at the top of the loop is correct. |
|
numDrawn += IPrepForRenderSortingSpans(pipe, sortedDrawList, i); |
|
} |
|
else |
|
{ |
|
pipe->PrepForRender(sortedDrawList[i].fDrawable, sortedDrawList[i].fVisList, visMgr); |
|
|
|
pipe->Render(sortedDrawList[i].fDrawable, sortedDrawList[i].fVisList); |
|
|
|
numDrawn += sortedDrawList[i].fVisList.GetCount(); |
|
|
|
} |
|
|
|
plProfile_EndLap(DrawableTime, p->GetKey()->GetUoid().GetObjectName()); |
|
} |
|
|
|
return numDrawn; |
|
} |
|
|
|
hsBool plPageTreeMgr::ISortByLevel(plPipeline* pipe, hsTArray<plDrawVisList>& drawList, hsTArray<plDrawVisList>& sortedDrawList) |
|
{ |
|
sortedDrawList.SetCount(0); |
|
|
|
if( !drawList.GetCount() ) |
|
return false; |
|
|
|
scratchList.SetCount(drawList.GetCount()); |
|
|
|
hsRadixSort::Elem* listTrav = nil; |
|
int i; |
|
for( i = 0; i < drawList.GetCount(); i++ ) |
|
{ |
|
listTrav = &scratchList[i]; |
|
listTrav->fBody = (void*)&drawList[i]; |
|
listTrav->fNext = listTrav+1; |
|
listTrav->fKey.fULong = drawList[i].fDrawable->GetRenderLevel().Level(); |
|
} |
|
listTrav->fNext = nil; |
|
|
|
hsRadixSort rad; |
|
hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), hsRadixSort::kUnsigned); |
|
|
|
listTrav = sortedList; |
|
|
|
while( listTrav ) |
|
{ |
|
plDrawVisList& drawVis = *(plDrawVisList*)listTrav->fBody; |
|
sortedDrawList.Append(drawVis); |
|
|
|
listTrav = listTrav->fNext; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
// Render from iDrawStart in drawVis list all drawables with the sort by spans property, well, sorting |
|
// by spans. |
|
// Returns the index of the last one drawn. |
|
int plPageTreeMgr::IPrepForRenderSortingSpans(plPipeline* pipe, hsTArray<plDrawVisList>& drawVis, int& iDrawStart) |
|
{ |
|
UInt32 renderLevel = drawVis[iDrawStart].fDrawable->GetRenderLevel().Level(); |
|
|
|
int i; |
|
|
|
static hsTArray<plDrawVisList*> drawables; |
|
static hsTArray<plDrawSpanPair> pairs; |
|
|
|
// Given the input drawVisList (list of drawable/visList pairs), we make two new |
|
// lists. The list "drawables" is just the excerpted sub-list from drawVis starting |
|
// from the input index and going through all compatible drawables (drawables which |
|
// are appropriate to sort (and hence intermix) with the first drawable in the list. |
|
// The second list is the drawableIndex/spanIndex pairs convenient for sorting (where |
|
// drawIndex indexes into drawables and spanIndex indexes into drawVis[iDraw].fVisList. |
|
// So pairs[i] resolves into |
|
// drawables[pairs[i].fDrawable].fDrawable->GetSpan(pairs[i].fSpan) |
|
|
|
drawables.Append(&drawVis[iDrawStart]); |
|
for( i = 0; i < drawVis[iDrawStart].fVisList.GetCount(); i++ ) |
|
{ |
|
plDrawSpanPair* pair = pairs.Push(); |
|
pair->fDrawable = 0; |
|
pair->fSpan = drawVis[iDrawStart].fVisList[i]; |
|
} |
|
|
|
int iDraw; |
|
for( iDraw = iDrawStart+1; |
|
(iDraw < drawVis.GetCount()) |
|
&& (drawVis[iDraw].fDrawable->GetRenderLevel().Level() == renderLevel) |
|
&& drawVis[iDraw].fDrawable->GetNativeProperty(plDrawable::kPropSortSpans); |
|
iDraw++ ) |
|
{ |
|
plDrawable* drawable = drawVis[iDraw].fDrawable; |
|
hsTArray<Int16>& visList = drawVis[iDraw].fVisList; |
|
for( i = 0; i < visList.GetCount(); i++ ) |
|
{ |
|
plDrawSpanPair* pair = pairs.Push(); |
|
pair->fDrawable = drawables.GetCount(); |
|
pair->fSpan = visList[i]; |
|
} |
|
drawables.Append(&drawVis[iDraw]); |
|
} |
|
|
|
// Now that we have them in a more convenient format, sort them and render. |
|
IRenderSortingSpans(pipe, drawables, pairs); |
|
|
|
int numDrawn = pairs.GetCount(); |
|
|
|
drawables.SetCount(0); |
|
pairs.SetCount(0); |
|
|
|
iDrawStart = iDraw - 1; |
|
|
|
return numDrawn; |
|
} |
|
|
|
hsBool plPageTreeMgr::IRenderSortingSpans(plPipeline* pipe, hsTArray<plDrawVisList*>& drawList, hsTArray<plDrawSpanPair>& pairs) |
|
{ |
|
|
|
if( !pairs.GetCount() ) |
|
return false; |
|
|
|
hsPoint3 viewPos = pipe->GetViewPositionWorld(); |
|
|
|
plProfile_BeginTiming(DrawObjSort); |
|
plProfile_IncCount(DrawObjSorted, pairs.GetCount()); |
|
|
|
hsRadixSort::Elem* listTrav; |
|
scratchList.SetCount(pairs.GetCount()); |
|
|
|
// First, sort on distance to the camera (squared). |
|
listTrav = nil; |
|
int iSort = 0; |
|
int i; |
|
for( i = 0; i < pairs.GetCount(); i++ ) |
|
{ |
|
plDrawable* drawable = drawList[pairs[i].fDrawable]->fDrawable; |
|
|
|
listTrav = &scratchList[iSort++]; |
|
listTrav->fBody = (void*)*(UInt32*)&pairs[i]; |
|
listTrav->fNext = listTrav + 1; |
|
|
|
if( drawable->GetNativeProperty(plDrawable::kPropSortAsOne) ) |
|
{ |
|
const hsBounds3Ext& bnd = drawable->GetSpaceTree()->GetNode(drawable->GetSpaceTree()->GetRoot()).fWorldBounds; |
|
plConst(hsScalar) kDistFudge(1.e-1f); |
|
listTrav->fKey.fFloat = -(bnd.GetCenter() - viewPos).MagnitudeSquared() + hsScalar(pairs[i].fSpan) * kDistFudge; |
|
} |
|
else |
|
{ |
|
const hsBounds3Ext& bnd = drawable->GetSpaceTree()->GetNode(pairs[i].fSpan).fWorldBounds; |
|
listTrav->fKey.fFloat = -(bnd.GetCenter() - viewPos).MagnitudeSquared(); |
|
} |
|
} |
|
if( !listTrav ) |
|
{ |
|
plProfile_EndTiming(DrawObjSort); |
|
return false; |
|
} |
|
listTrav->fNext = nil; |
|
|
|
hsRadixSort rad; |
|
hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), 0); |
|
|
|
plProfile_EndTiming(DrawObjSort); |
|
|
|
static hsTArray<Int16> visList; |
|
visList.SetCount(0); |
|
|
|
plVisMgr* visMgr = fDisableVisMgr ? nil : fVisMgr; |
|
|
|
// Call PrepForRender on each of these bad boys. We only want to call |
|
// PrepForRender once on each drawable, no matter how many times we're |
|
// going to pass it off to be rendered (like if we render span 0 from |
|
// drawable A, span 1 from drawable A, span 0 from drawable B, span 1 from Drawable A, we |
|
// don't want to PrepForRender twice or three times on drawable A). |
|
// So we're going to convert our sorted list back into a list of drawable/visList |
|
// pairs. We could have done this with our original drawable/visList, but we've |
|
// hopefully trimmed out some spans because of the fades. This drawable/visList |
|
// isn't appropriate for rendering (because it doesn't let us switch back and forth |
|
// from a drawable, but it's right for the PrepForRenderCall (which does things like |
|
// face sorting). |
|
for( i = 0; i < drawList.GetCount(); i++ ) |
|
drawList[i]->fVisList.SetCount(0); |
|
listTrav = sortedList; |
|
while( listTrav ) |
|
{ |
|
plDrawSpanPair& curPair = *(plDrawSpanPair*)&listTrav->fBody; |
|
drawList[curPair.fDrawable]->fVisList.Append(curPair.fSpan); |
|
listTrav = listTrav->fNext; |
|
} |
|
for( i = 0; i < drawList.GetCount(); i++ ) |
|
{ |
|
pipe->PrepForRender(drawList[i]->fDrawable, drawList[i]->fVisList, visMgr); |
|
} |
|
|
|
// We'd like to call Render once on a drawable for each contiguous |
|
// set of spans (so we want to render span 0 and span 1 on a single Render |
|
// of drawable A in the above, then render drawable B, then back to A). |
|
// So we go through the sorted drawable/spanIndex pairs list, building |
|
// a visList for as long as the drawable remains the same. When it |
|
// changes, we render what we have so far, and start again with the |
|
// next drawable. Repeat until done. |
|
|
|
#if 0 |
|
listTrav = sortedList; |
|
plDrawSpanPair& curPair = *(plDrawSpanPair*)&listTrav->fBody; |
|
int curDraw = curPair.fDrawable; |
|
visList.Append(curPair.fSpan); |
|
listTrav = listTrav->fNext; |
|
|
|
while( listTrav ) |
|
{ |
|
curPair = *(plDrawSpanPair*)&listTrav->fBody; |
|
if( curPair.fDrawable != curDraw ) |
|
{ |
|
pipe->Render(drawList[curDraw]->fDrawable, visList); |
|
curDraw = curPair.fDrawable; |
|
visList.SetCount(0); |
|
visList.Append(curPair.fSpan); |
|
} |
|
else |
|
{ |
|
visList.Append(curPair.fSpan); |
|
} |
|
listTrav = listTrav->fNext; |
|
} |
|
pipe->Render(drawList[curDraw]->fDrawable, visList); |
|
#else |
|
listTrav = sortedList; |
|
plDrawSpanPair& curPair = *(plDrawSpanPair*)&listTrav->fBody; |
|
int curDraw = curPair.fDrawable; |
|
listTrav = listTrav->fNext; |
|
|
|
static hsTArray<UInt32> numDrawn; |
|
numDrawn.SetCountAndZero(drawList.GetCount()); |
|
|
|
visList.Append(drawList[curDraw]->fVisList[numDrawn[curDraw]++]); |
|
|
|
while( listTrav ) |
|
{ |
|
curPair = *(plDrawSpanPair*)&listTrav->fBody; |
|
if( curPair.fDrawable != curDraw ) |
|
{ |
|
pipe->Render(drawList[curDraw]->fDrawable, visList); |
|
curDraw = curPair.fDrawable; |
|
visList.SetCount(0); |
|
} |
|
visList.Append(drawList[curDraw]->fVisList[numDrawn[curDraw]++]); |
|
listTrav = listTrav->fNext; |
|
} |
|
pipe->Render(drawList[curDraw]->fDrawable, visList); |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
hsBool plPageTreeMgr::IBuildSpaceTree() |
|
{ |
|
if( !fNodes.GetCount() ) |
|
return false; |
|
|
|
plSpaceTreeMaker maker; |
|
maker.Reset(); |
|
int i; |
|
for( i = 0; i < fNodes.GetCount(); i++ ) |
|
{ |
|
maker.AddLeaf(fNodes[i]->GetSpaceTree()->GetWorldBounds(), fNodes[i]->GetSpaceTree()->IsEmpty()); |
|
} |
|
fSpaceTree = maker.MakeTree(); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plPageTreeMgr::IRefreshTree(plPipeline* pipe) |
|
{ |
|
int i; |
|
for( i = 0; i < fNodes.GetCount(); i++ ) |
|
{ |
|
if( fNodes[i]->GetSpaceTree()->IsDirty() ) |
|
{ |
|
fNodes[i]->GetSpaceTree()->Refresh(); |
|
|
|
GetSpaceTree()->MoveLeaf(i, fNodes[i]->GetSpaceTree()->GetWorldBounds()); |
|
|
|
if( !fNodes[i]->GetSpaceTree()->IsEmpty() && fSpaceTree->HasLeafFlag(i, plSpaceTreeNode::kDisabled) ) |
|
fSpaceTree->SetLeafFlag(i, plSpaceTreeNode::kDisabled, false); |
|
|
|
} |
|
} |
|
|
|
GetSpaceTree()->SetViewPos(pipe->GetViewPositionWorld()); |
|
|
|
GetSpaceTree()->Refresh(); |
|
|
|
return true; |
|
} |
|
|
|
void plPageTreeMgr::AddOccluderList(const hsTArray<plOccluder*> occList) |
|
{ |
|
int iStart = fOccluders.GetCount(); |
|
fOccluders.Expand(iStart + occList.GetCount()); |
|
fOccluders.SetCount(iStart + occList.GetCount()); |
|
|
|
plVisMgr* visMgr = fDisableVisMgr ? nil : fVisMgr; |
|
|
|
if( visMgr ) |
|
{ |
|
const hsBitVector& visSet = visMgr->GetVisSet(); |
|
const hsBitVector& visNot = visMgr->GetVisNot(); |
|
int i; |
|
for( i = 0; i < occList.GetCount(); i++ ) |
|
{ |
|
if( occList[i] && !occList[i]->InVisNot(visNot) && occList[i]->InVisSet(visSet) ) |
|
fOccluders[iStart++] = occList[i]; |
|
} |
|
} |
|
else |
|
{ |
|
int i; |
|
for( i = 0; i < occList.GetCount(); i++ ) |
|
{ |
|
if( occList[i] ) |
|
fOccluders[iStart++] = occList[i]; |
|
} |
|
} |
|
fOccluders.SetCount(iStart); |
|
|
|
} |
|
|
|
void plPageTreeMgr::IAddCullPolyList(const hsTArray<plCullPoly>& polyList) |
|
{ |
|
int iStart = fCullPolys.GetCount(); |
|
fCullPolys.Expand(iStart + polyList.GetCount()); |
|
fCullPolys.SetCount(iStart + polyList.GetCount()); |
|
int i; |
|
for( i = 0; i < polyList.GetCount(); i++ ) |
|
{ |
|
fCullPolys[i + iStart] = &polyList[i]; |
|
} |
|
} |
|
|
|
void plPageTreeMgr::ISortCullPolys(plPipeline* pipe) |
|
{ |
|
fSortedCullPolys.SetCount(0); |
|
if( !fCullPolys.GetCount() ) |
|
return; |
|
|
|
const int kMaxCullPolys = 300; |
|
int numSubmit = 0; |
|
|
|
hsPoint3 viewPos = pipe->GetViewPositionWorld(); |
|
|
|
hsRadixSort::Elem* listTrav; |
|
scratchList.SetCount(fCullPolys.GetCount()); |
|
int i; |
|
for( i = 0; i < fCullPolys.GetCount(); i++ ) |
|
{ |
|
hsBool backFace = fCullPolys[i]->fNorm.InnerProduct(viewPos) + fCullPolys[i]->fDist <= 0; |
|
if( backFace ) |
|
{ |
|
if( !fCullPolys[i]->IsHole() && !fCullPolys[i]->IsTwoSided() ) |
|
continue; |
|
} |
|
else |
|
{ |
|
if( fCullPolys[i]->IsHole() ) |
|
continue; |
|
} |
|
|
|
listTrav = &scratchList[numSubmit]; |
|
listTrav->fBody = (void*)fCullPolys[i]; |
|
listTrav->fNext = listTrav + 1; |
|
listTrav->fKey.fFloat = (fCullPolys[i]->GetCenter() - viewPos).MagnitudeSquared(); |
|
|
|
numSubmit++; |
|
} |
|
if( !numSubmit ) |
|
return; |
|
|
|
listTrav->fNext = nil; |
|
|
|
hsRadixSort rad; |
|
hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), 0); |
|
listTrav = sortedList; |
|
|
|
if( numSubmit > kMaxCullPolys ) |
|
numSubmit = kMaxCullPolys; |
|
|
|
fSortedCullPolys.SetCount(numSubmit); |
|
|
|
for( i = 0; i < numSubmit; i++ ) |
|
{ |
|
fSortedCullPolys[i] = (const plCullPoly*)listTrav->fBody; |
|
listTrav = listTrav->fNext; |
|
} |
|
} |
|
|
|
hsBool plPageTreeMgr::IGetCullPolys(plPipeline* pipe) |
|
{ |
|
if( !fOccluders.GetCount() ) |
|
return false; |
|
|
|
plProfile_BeginTiming(DrawOccSort); |
|
|
|
hsRadixSort::Elem* listTrav = nil; |
|
scratchList.SetCount(fOccluders.GetCount()); |
|
|
|
hsPoint3 viewPos = pipe->GetViewPositionWorld(); |
|
|
|
// cull test the occluders submitted |
|
int numSubmit = 0; |
|
int i; |
|
for( i = 0; i < fOccluders.GetCount(); i++ ) |
|
{ |
|
if( pipe->TestVisibleWorld(fOccluders[i]->GetWorldBounds()) ) |
|
{ |
|
hsScalar invDist = -hsFastMath::InvSqrtAppr((viewPos - fOccluders[i]->GetWorldBounds().GetCenter()).MagnitudeSquared()); |
|
listTrav = &scratchList[numSubmit++]; |
|
listTrav->fBody = (void*)fOccluders[i]; |
|
listTrav->fNext = listTrav+1; |
|
listTrav->fKey.fFloat = fOccluders[i]->GetPriority() * invDist; |
|
} |
|
} |
|
if( !listTrav ) |
|
{ |
|
plProfile_EndTiming(DrawOccSort); |
|
return false; |
|
} |
|
|
|
listTrav->fNext = nil; |
|
|
|
|
|
// Sort the occluders by priority |
|
hsRadixSort rad; |
|
hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), 0); |
|
listTrav = sortedList; |
|
|
|
const UInt32 kMaxOccluders = 1000; |
|
if( numSubmit > kMaxOccluders ) |
|
numSubmit = kMaxOccluders; |
|
|
|
plProfile_IncCount(DrawOccUsed, numSubmit); |
|
|
|
// Take the polys from the first N of them |
|
for( i = 0; i < numSubmit; i++ ) |
|
{ |
|
plOccluder* occ = (plOccluder*)listTrav->fBody; |
|
IAddCullPolyList(occ->GetWorldPolyList()); |
|
|
|
listTrav = listTrav->fNext; |
|
} |
|
|
|
plProfile_EndTiming(DrawOccSort); |
|
|
|
return fCullPolys.GetCount() > 0; |
|
} |
|
|
|
hsBool plPageTreeMgr::IGetOcclusion(plPipeline* pipe, hsTArray<Int16>& list) |
|
{ |
|
plProfile_BeginTiming(DrawOccBuild); |
|
|
|
fCullPolys.SetCount(0); |
|
fOccluders.SetCount(0); |
|
int i; |
|
for( i = 0; i < fNodes.GetCount(); i++ ) |
|
{ |
|
fNodes[i]->SubmitOccluders(this); |
|
} |
|
|
|
if( !IGetCullPolys(pipe) ) |
|
{ |
|
plProfile_EndTiming(DrawOccBuild); |
|
return false; |
|
} |
|
|
|
plProfile_IncCount(DrawOccPolyProc, fCullPolys.GetCount()); |
|
|
|
plProfile_BeginTiming(DrawOccPolySort); |
|
ISortCullPolys(pipe); |
|
plProfile_EndTiming(DrawOccPolySort); |
|
|
|
if( fSortedCullPolys.GetCount() ) |
|
pipe->SubmitOccluders(fSortedCullPolys); |
|
|
|
plProfile_EndTiming(DrawOccBuild); |
|
|
|
return fSortedCullPolys.GetCount() > 0; |
|
} |
|
|
|
void plPageTreeMgr::IResetOcclusion(plPipeline* pipe) |
|
{ |
|
fCullPolys.SetCount(0); |
|
fSortedCullPolys.SetCount(0); |
|
} |