tge/engine/terrain/bvQuadTree.cc
2017-04-17 06:17:10 -06:00

167 lines
5.2 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "terrain/bvQuadTree.h"
#include "console/console.h"
BVQuadTree::BVQuadTree(BitVector *bv)
{
if (bv != NULL)
init(*bv);
else
{
BitVector localBV;
localBV.setSize(4);
localBV.set();
init(localBV);
}
}
BVQuadTree::~BVQuadTree()
{
while (mQTHierarchy.size() > 0)
{
delete mQTHierarchy.last();
mQTHierarchy.pop_back();
}
}
bool BVQuadTree::isSet(const Point2F &pos, S32 level) const
{
AssertFatal(pos.x >= 0. && pos.x <= 1., "BVQuadTree::isSet: x must be in range [0,1]");
AssertFatal(pos.y >= 0. && pos.y <= 1., "BVQuadTree::isSet: y must be in range [0,1]");
AssertFatal(level >= 0, "BVQuadTree:isSet: level must be greater than or equal to zero");
if (level >= mQTHierarchy.size())
// force to be within resolution of QT
level = mQTHierarchy.size() - 1;
if (level < 0)
level = 0;
F32 dimension = F32(1 << level);
U32 offset = U32((F32(U32(pos.y * dimension)) + pos.x) * dimension);
return(mQTHierarchy[level]->test(offset));
}
bool BVQuadTree::isClear(const Point2F &pos, S32 level) const
{
AssertFatal(pos.x >= 0. && pos.x <= 1., "BVQuadTree::isClear: x must be in range [0,1]");
AssertFatal(pos.y >= 0. && pos.y <= 1., "BVQuadTree::isClear: y must be in range [0,1]");
AssertFatal(level >= 0, "BVQuadTree:isClear: level must be greater than or equal to zero");
if (level >= mQTHierarchy.size())
// force to be within resolution of QT
level = mQTHierarchy.size() - 1;
if (level < 0)
level = 0;
F32 dimension = F32(1 << level);
U32 offset = U32((F32(U32(pos.y * dimension)) + pos.x) * dimension);
return(mQTHierarchy[level]->test(offset) == false);
}
/* Initialize the quadtree with the provided bit vector. Note that the bit vector
* denotes data at each corner of the quadtree cell. Hence the dimension for the
* deepest quadtree level must be 1 less than that of the bit vector.
*/
void BVQuadTree::init(const BitVector &bv)
{
while (mQTHierarchy.size() > 0)
{
delete mQTHierarchy.last();
mQTHierarchy.pop_back();
}
// get the width/height of the square bit vector
U32 bvDim = (U32)mSqrt((F32)bv.getSize());
U32 qtDim = bvDim - 1; // here's where we correct dimension...
AssertFatal(((mSqrt((F32)bv.getSize()) - 1) == (F32)qtDim) && (isPow2(qtDim) == true), "BVQuadTree::init: bit vector size must be power of 4");
// find the power of two we're starting at
mResolution = qtDim;
U32 level = 0;
while ((1 << (level + 1)) <= qtDim)
level++;
BitVector *initBV = new BitVector;
AssertFatal(initBV != NULL, "BVQuadTree::init: failed to allocate highest detail bit vector");
initBV->setSize(qtDim * qtDim);
initBV->clear();
for (S32 i = 0; i < qtDim; i++)
for (S32 j = 0; j < qtDim; j++)
{
S32 k = i * bvDim + j;
if (bv.test(k) || bv.test(k + 1) || bv.test(k + bvDim) || bv.test(k + bvDim + 1))
initBV->set(i * qtDim + j);
}
mQTHierarchy.push_back(initBV);
if (level > 0)
buildHierarchy(level - 1);
}
#ifdef BV_QUADTREE_DEBUG
void BVQuadTree::dump() const
{
char str[256];
U32 strlen;
for (U32 i = 0; i < mQTHierarchy.size(); i++)
{
U32 dimension = 1 << i;
Con::printf("level %d:", i);
for (U32 y = 0; y < dimension; y++)
{
U32 yOffset = y * dimension;
str[0] = '\0';
for (U32 x = 0; x < dimension; x++)
{
U32 offset = yOffset + x;
strlen = dStrlen(str);
if (strlen < 252)
dSprintf(str + strlen, 256 - strlen, mQTHierarchy[i]->isSet(offset) ? "1 " : "0 ");
else
{
dSprintf(str + strlen, 256 - strlen, "...");
break;
}
}
Con::printf("%s", str);
}
}
}
#endif
void BVQuadTree::buildHierarchy(U32 level)
{
BitVector *priorBV = mQTHierarchy.first();
BitVector *levelBV = new BitVector;
AssertFatal(levelBV != NULL, "BVQuadTree::buildHierarchy: failed to allocate bit vector");
U32 levelDim = (1 << level);
levelBV->setSize(levelDim * levelDim);
levelBV->clear();
U32 yOffset, offset, yPriorOffset, priorOffset;
// COULD THIS BE DONE WITH A SINGLE LOOP?
for (U32 y = 0; y < levelDim; y++)
{
yOffset = y * levelDim;
yPriorOffset = yOffset << 2;
for (U32 x = 0; x < levelDim; x++)
{
offset = yOffset + x;
priorOffset = yPriorOffset + (x << 1);
if (priorBV->test(priorOffset) || priorBV->test(priorOffset + 1) ||
priorBV->test(priorOffset + (levelDim << 1)) ||
priorBV->test(priorOffset + (levelDim << 1) + 1))
levelBV->set(offset);
}
}
mQTHierarchy.push_front(levelBV);
if (level > 0)
buildHierarchy(level - 1);
}