//----------------------------------------------------------------------------- // 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); }