📄 sceneobject.cc
字号:
#endif
Container::Container()
{
mEnd.next = mEnd.prev = &mStart;
mStart.next = mStart.prev = &mEnd;
if (!sBoxPolyhedron.edgeList.size())
{
Box3F box;
box.min.set(-1,-1,-1);
box.max.set(+1,+1,+1);
MatrixF imat(1);
sBoxPolyhedron.buildBox(imat,box);
}
mBinArray = new SceneObjectRef[csmNumBins * csmNumBins];
for (U32 i = 0; i < csmNumBins; i++)
{
U32 base = i * csmNumBins;
for (U32 j = 0; j < csmNumBins; j++)
{
mBinArray[base + j].object = NULL;
mBinArray[base + j].nextInBin = NULL;
mBinArray[base + j].prevInBin = NULL;
mBinArray[base + j].nextInObj = NULL;
}
}
mOverflowBin.object = NULL;
mOverflowBin.nextInBin = NULL;
mOverflowBin.prevInBin = NULL;
mOverflowBin.nextInObj = NULL;
VECTOR_SET_ASSOCIATION(mRefPoolBlocks);
VECTOR_SET_ASSOCIATION(mSearchList);
mFreeRefPool = NULL;
addRefPoolBlock();
cleanupSearchVectors();
}
Container::~Container()
{
for (U32 i = 0; i < mRefPoolBlocks.size(); i++)
{
SceneObjectRef* pool = mRefPoolBlocks[i];
for (U32 j = 0; j < csmRefPoolBlockSize; j++)
{
// Depressingly, this can give weird results if its pointing at bad memory...
if(pool[j].object != NULL)
Con::warnf("Error, a %s (%x) isn't properly out of the bins!", pool[j].object->getClassName(), pool[j].object);
// If you're getting this it means that an object created didn't
// remove itself from its container before we destroyed the
// container. Typically you get this behavior from particle
// emitters, as they try to hang around until all their particles
// die. In general it's benign, though if you get it for things
// that aren't particle emitters it can be a bad sign!
}
delete [] pool;
}
mFreeRefPool = NULL;
cleanupSearchVectors();
}
bool Container::addObject(SceneObject* obj)
{
AssertFatal(obj->mContainer == NULL, "Adding already added object.");
obj->mContainer = this;
obj->linkAfter(&mStart);
insertIntoBins(obj);
return true;
}
bool Container::removeObject(SceneObject* obj)
{
AssertFatal(obj->mContainer == this, "Trying to remove from wrong container.");
removeFromBins(obj);
obj->mContainer = 0;
obj->unlink();
return true;
}
void Container::addRefPoolBlock()
{
mRefPoolBlocks.push_back(new SceneObjectRef[csmRefPoolBlockSize]);
for (U32 i = 0; i < csmRefPoolBlockSize-1; i++)
{
mRefPoolBlocks.last()[i].object = NULL;
mRefPoolBlocks.last()[i].prevInBin = NULL;
mRefPoolBlocks.last()[i].nextInBin = NULL;
mRefPoolBlocks.last()[i].nextInObj = &(mRefPoolBlocks.last()[i+1]);
}
mRefPoolBlocks.last()[csmRefPoolBlockSize-1].object = NULL;
mRefPoolBlocks.last()[csmRefPoolBlockSize-1].prevInBin = NULL;
mRefPoolBlocks.last()[csmRefPoolBlockSize-1].nextInBin = NULL;
mRefPoolBlocks.last()[csmRefPoolBlockSize-1].nextInObj = mFreeRefPool;
mFreeRefPool = &(mRefPoolBlocks.last()[0]);
}
void Container::insertIntoBins(SceneObject* obj)
{
AssertFatal(obj != NULL, "No object?");
AssertFatal(obj->mBinRefHead == NULL, "Error, already have a bin chain!");
// The first thing we do is find which bins are covered in x and y...
const Box3F* pWBox = &obj->getWorldBox();
U32 minX, maxX, minY, maxY;
getBinRange(pWBox->min.x, pWBox->max.x, minX, maxX);
getBinRange(pWBox->min.y, pWBox->max.y, minY, maxY);
// Store the current regions for later queries
obj->mBinMinX = minX;
obj->mBinMaxX = maxX;
obj->mBinMinY = minY;
obj->mBinMaxY = maxY;
// For huge objects, dump them into the overflow bin. Otherwise, everything
// goes into the grid...
if ((maxX - minX + 1) < csmNumBins || (maxY - minY + 1) < csmNumBins && !obj->isGlobalBounds())
{
SceneObjectRef** pCurrInsert = &obj->mBinRefHead;
for (U32 i = minY; i <= maxY; i++)
{
U32 insertY = i % csmNumBins;
U32 base = insertY * csmNumBins;
for (U32 j = minX; j <= maxX; j++)
{
U32 insertX = j % csmNumBins;
SceneObjectRef* ref = allocateObjectRef();
ref->object = obj;
ref->nextInBin = mBinArray[base + insertX].nextInBin;
ref->prevInBin = &mBinArray[base + insertX];
ref->nextInObj = NULL;
if (mBinArray[base + insertX].nextInBin)
mBinArray[base + insertX].nextInBin->prevInBin = ref;
mBinArray[base + insertX].nextInBin = ref;
*pCurrInsert = ref;
pCurrInsert = &ref->nextInObj;
}
}
}
else
{
SceneObjectRef* ref = allocateObjectRef();
ref->object = obj;
ref->nextInBin = mOverflowBin.nextInBin;
ref->prevInBin = &mOverflowBin;
ref->nextInObj = NULL;
if (mOverflowBin.nextInBin)
mOverflowBin.nextInBin->prevInBin = ref;
mOverflowBin.nextInBin = ref;
obj->mBinRefHead = ref;
}
}
void Container::insertIntoBins(SceneObject* obj,
U32 minX, U32 maxX,
U32 minY, U32 maxY)
{
AssertFatal(obj != NULL, "No object?");
AssertFatal(obj->mBinRefHead == NULL, "Error, already have a bin chain!");
// Store the current regions for later queries
obj->mBinMinX = minX;
obj->mBinMaxX = maxX;
obj->mBinMinY = minY;
obj->mBinMaxY = maxY;
// For huge objects, dump them into the overflow bin. Otherwise, everything
// goes into the grid...
//
if ((maxX - minX + 1) < csmNumBins || (maxY - minY + 1) < csmNumBins && !obj->isGlobalBounds())
{
SceneObjectRef** pCurrInsert = &obj->mBinRefHead;
for (U32 i = minY; i <= maxY; i++)
{
U32 insertY = i % csmNumBins;
U32 base = insertY * csmNumBins;
for (U32 j = minX; j <= maxX; j++)
{
U32 insertX = j % csmNumBins;
SceneObjectRef* ref = allocateObjectRef();
ref->object = obj;
ref->nextInBin = mBinArray[base + insertX].nextInBin;
ref->prevInBin = &mBinArray[base + insertX];
ref->nextInObj = NULL;
if (mBinArray[base + insertX].nextInBin)
mBinArray[base + insertX].nextInBin->prevInBin = ref;
mBinArray[base + insertX].nextInBin = ref;
*pCurrInsert = ref;
pCurrInsert = &ref->nextInObj;
}
}
}
else
{
SceneObjectRef* ref = allocateObjectRef();
ref->object = obj;
ref->nextInBin = mOverflowBin.nextInBin;
ref->prevInBin = &mOverflowBin;
ref->nextInObj = NULL;
if (mOverflowBin.nextInBin)
mOverflowBin.nextInBin->prevInBin = ref;
mOverflowBin.nextInBin = ref;
obj->mBinRefHead = ref;
}
}
void Container::removeFromBins(SceneObject* obj)
{
AssertFatal(obj != NULL, "No object?");
SceneObjectRef* chain = obj->mBinRefHead;
obj->mBinRefHead = NULL;
while (chain)
{
SceneObjectRef* trash = chain;
chain = chain->nextInObj;
AssertFatal(trash->prevInBin != NULL, "Error, must have a previous entry in the bin!");
if (trash->nextInBin)
trash->nextInBin->prevInBin = trash->prevInBin;
trash->prevInBin->nextInBin = trash->nextInBin;
freeObjectRef(trash);
}
}
void Container::checkBins(SceneObject* obj)
{
AssertFatal(obj != NULL, "No object?");
if (obj->mBinRefHead == NULL)
{
insertIntoBins(obj);
return;
}
// Otherwise, the object is already in the bins. Let's see if it has strayed out of
// the bins that it's currently in...
const Box3F* pWBox = &obj->getWorldBox();
U32 minX, maxX, minY, maxY;
getBinRange(pWBox->min.x, pWBox->max.x, minX, maxX);
getBinRange(pWBox->min.y, pWBox->max.y, minY, maxY);
if (obj->mBinMinX != minX || obj->mBinMaxX != maxX ||
obj->mBinMinY != minY || obj->mBinMaxY != maxY)
{
// We have to rebin the object
removeFromBins(obj);
insertIntoBins(obj, minX, maxX, minY, maxY);
}
}
void Container::findObjects(const Box3F& box, U32 mask, FindCallback callback, void *key)
{
U32 minX, maxX, minY, maxY;
getBinRange(box.min.x, box.max.x, minX, maxX);
getBinRange(box.min.y, box.max.y, minY, maxY);
smCurrSeqKey++;
for (U32 i = minY; i <= maxY; i++)
{
U32 insertY = i % csmNumBins;
U32 base = insertY * csmNumBins;
for (U32 j = minX; j <= maxX; j++)
{
U32 insertX = j % csmNumBins;
SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
while (chain)
{
if (chain->object->getContainerSeqKey() != smCurrSeqKey)
{
chain->object->setContainerSeqKey(smCurrSeqKey);
if ((chain->object->getType() & mask) != 0 &&
chain->object->isCollisionEnabled())
{
if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
{
(*callback)(chain->object,key);
}
}
}
chain = chain->nextInBin;
}
}
}
SceneObjectRef* chain = mOverflowBin.nextInBin;
while (chain)
{
if (chain->object->getContainerSeqKey() != smCurrSeqKey)
{
chain->object->setContainerSeqKey(smCurrSeqKey);
if ((chain->object->getType() & mask) != 0 &&
chain->object->isCollisionEnabled())
{
if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
{
(*callback)(chain->object,key);
}
}
}
chain = chain->nextInBin;
}
}
void Container::polyhedronFindObjects(const Polyhedron& polyhedron, U32 mask, FindCallback callback, void *key)
{
U32 i;
Box3F box;
box.min.set(1e9, 1e9, 1e9);
box.max.set(-1e9, -1e9, -1e9);
for (i = 0; i < polyhedron.pointList.size(); i++)
{
box.min.setMin(polyhedron.pointList[i]);
box.max.setMax(polyhedron.pointList[i]);
}
U32 minX, maxX, minY, maxY;
getBinRange(box.min.x, box.max.x, minX, maxX);
getBinRange(box.min.y, box.max.y, minY, maxY);
smCurrSeqKey++;
for (i = minY; i <= maxY; i++)
{
U32 insertY = i % csmNumBins;
U32 base = insertY * csmNumBins;
for (U32 j = minX; j <= maxX; j++)
{
U32 insertX = j % csmNumBins;
SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
while (chain)
{
if (chain->object->getContainerSeqKey() != smCurrSeqKey)
{
chain->object->setContainerSeqKey(smCurrSeqKey);
if ((chain->object->getType() & mask) != 0 &&
chain->object->isCollisionEnabled())
{
if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
{
(*callback)(chain->object,key);
}
}
}
chain = chain->nextInBin;
}
}
}
SceneObjectRef* chain = mOverflowBin.nextInBin;
while (chain)
{
if (chain->object->getContainerSeqKey() != smCurrSeqKey)
{
chain->object->setContainerSeqKey(smCurrSeqKey);
if ((chain->object->getType() & mask) != 0 &&
chain->object->isCollisionEnabled())
{
if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
{
(*callback)(chain->object,key);
}
}
}
chain = chain->nextInBin;
}
}
//----------------------------------------------------------------------------
// DMMNOTE: There are still some optimizations to be done here. In particular:
// - After checking the overflow bin, we can potentially shorten the line
// that we rasterize against the grid if there is a collision with say,
// the terrain.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -