diff options
Diffstat (limited to 'indra/llmath/lloctree.h')
-rw-r--r-- | indra/llmath/lloctree.h | 1716 |
1 files changed, 858 insertions, 858 deletions
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index 6e43646b60..475ce20ae6 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -1,858 +1,858 @@ -/**
- * @file lloctree.h
- * @brief Octree declaration.
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLOCTREE_H
-#define LL_LLOCTREE_H
-
-#include "lltreenode.h"
-#include "v3math.h"
-#include "llvector4a.h"
-#include <vector>
-
-#define OCT_ERRS LL_WARNS("OctreeErrors")
-
-#define OCTREE_DEBUG_COLOR_REMOVE 0x0000FF // r
-#define OCTREE_DEBUG_COLOR_INSERT 0x00FF00 // g
-#define OCTREE_DEBUG_COLOR_BALANCE 0xFF0000 // b
-
-extern U32 gOctreeMaxCapacity;
-extern float gOctreeMinSize;
-
-/*#define LL_OCTREE_PARANOIA_CHECK 0
-#if LL_DARWIN
-#define LL_OCTREE_MAX_CAPACITY 32
-#else
-#define LL_OCTREE_MAX_CAPACITY 128
-#endif*/
-
-// T is the type of the element referenced by the octree node.
-// T_PTR determines how pointers to elements are stored internally.
-// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and
-// deletes elements removed from the tree.
-// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API
-// user is responsible for managing the storage lifecycle of elements added to
-// the tree.
-template <class T, typename T_PTR> class LLOctreeNode;
-
-template <class T, typename T_PTR>
-class LLOctreeListener: public LLTreeListener<T>
-{
-public:
- typedef LLTreeListener<T> BaseType;
- typedef LLOctreeNode<T, T_PTR> oct_node;
-
- virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0;
- virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0;
-};
-
-template <class T, typename T_PTR>
-class LLOctreeTraveler
-{
-public:
- virtual void traverse(const LLOctreeNode<T, T_PTR>* node);
- virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0;
-};
-
-template <class T, typename T_PTR>
-class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR>
-{
-public:
- virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override;
-};
-
-template <class T, typename T_PTR>
-class alignas(16) LLOctreeNode : public LLTreeNode<T>
-{
- LL_ALIGN_NEW
-public:
-
- typedef LLOctreeTraveler<T, T_PTR> oct_traveler;
- typedef LLTreeTraveler<T> tree_traveler;
- typedef std::vector<T_PTR> element_list;
- typedef typename element_list::iterator element_iter;
- typedef typename element_list::const_iterator const_element_iter;
- typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
- typedef LLOctreeNode<T, T_PTR>** child_list;
- typedef LLOctreeNode<T, T_PTR>** child_iter;
-
- typedef LLTreeNode<T> BaseType;
- typedef LLOctreeNode<T, T_PTR> oct_node;
- typedef LLOctreeListener<T, T_PTR> oct_listener;
-
- enum
- {
- NO_CHILD_NODES = 255 // Note: This is an U8 to match the max value in mChildMap[]
- };
-
- LLOctreeNode( const LLVector4a& center,
- const LLVector4a& size,
- BaseType* parent,
- U8 octant = NO_CHILD_NODES)
- : mParent((oct_node*)parent),
- mOctant(octant)
- {
- llassert(size[0] >= gOctreeMinSize*0.5f);
-
- mCenter = center;
- mSize = size;
-
- updateMinMax();
- if ((mOctant == NO_CHILD_NODES) && mParent)
- {
- mOctant = ((oct_node*) mParent)->getOctant(mCenter);
- }
-
- clearChildren();
- }
-
- virtual ~LLOctreeNode()
- {
- BaseType::destroyListeners();
-
- const U32 element_count = getElementCount();
- for (U32 i = 0; i < element_count; ++i)
- {
- mData[i]->setBinIndex(-1);
- mData[i] = NULL;
- }
-
- mData.clear();
-
- for (U32 i = 0; i < getChildCount(); i++)
- {
- delete getChild(i);
- }
- }
-
- inline const BaseType* getParent() const { return mParent; }
- inline void setParent(BaseType* parent) { mParent = (oct_node*) parent; }
- inline const LLVector4a& getCenter() const { return mCenter; }
- inline const LLVector4a& getSize() const { return mSize; }
- inline void setCenter(const LLVector4a& center) { mCenter = center; }
- inline void setSize(const LLVector4a& size) { mSize = size; }
- inline oct_node* getNodeAt(T* data) { return getNodeAt(data->getPositionGroup(), data->getBinRadius()); }
- inline U8 getOctant() const { return mOctant; }
- inline const oct_node* getOctParent() const { return (const oct_node*) getParent(); }
- inline oct_node* getOctParent() { return (oct_node*) getParent(); }
-
- U8 getOctant(const LLVector4a& pos) const //get the octant pos is in
- {
- return (U8) (pos.greaterThan(mCenter).getGatheredBits() & 0x7);
- }
-
- inline bool isInside(const LLVector4a& pos, const F32& rad) const
- {
- return rad <= mSize[0]*2.f && isInside(pos);
- }
-
- inline bool isInside(T* data) const
- {
- return isInside(data->getPositionGroup(), data->getBinRadius());
- }
-
- bool isInside(const LLVector4a& pos) const
- {
- S32 gt = pos.greaterThan(mMax).getGatheredBits() & 0x7;
- if (gt)
- {
- return false;
- }
-
- S32 lt = pos.lessEqual(mMin).getGatheredBits() & 0x7;
- if (lt)
- {
- return false;
- }
-
- return true;
- }
-
- void updateMinMax()
- {
- mMax.setAdd(mCenter, mSize);
- mMin.setSub(mCenter, mSize);
- }
-
- inline oct_listener* getOctListener(U32 index)
- {
- return (oct_listener*) BaseType::getListener(index);
- }
-
- inline bool contains(T* xform)
- {
- return contains(xform->getBinRadius());
- }
-
- bool contains(F32 radius)
- {
- if (mParent == NULL)
- { //root node contains nothing
- return false;
- }
-
- F32 size = mSize[0];
- F32 p_size = size * 2.f;
-
- return (radius <= gOctreeMinSize && size <= gOctreeMinSize) ||
- (radius <= p_size && radius > size);
- }
-
- static void pushCenter(LLVector4a ¢er, const LLVector4a &size, const T* data)
- {
- const LLVector4a& pos = data->getPositionGroup();
-
- LLVector4Logical gt = pos.greaterThan(center);
-
- LLVector4a up;
- up = _mm_and_ps(size, gt);
-
- LLVector4a down;
- down = _mm_andnot_ps(gt, size);
-
- center.add(up);
- center.sub(down);
- }
-
- void accept(oct_traveler* visitor) { visitor->visit(this); }
- virtual bool isLeaf() const { return mChildCount == 0; }
-
- U32 getElementCount() const { return (U32)mData.size(); }
- bool isEmpty() const { return mData.empty(); }
- element_iter getDataBegin() { return mData.begin(); }
- element_iter getDataEnd() { return mData.end(); }
- const_element_iter getDataBegin() const { return mData.cbegin(); }
- const_element_iter getDataEnd() const { return mData.cend(); }
-
- U32 getChildCount() const { return mChildCount; }
- oct_node* getChild(U32 index) { return mChild[index]; }
- const oct_node* getChild(U32 index) const { return mChild[index]; }
- child_list& getChildren() { return mChild; }
- const child_list& getChildren() const { return mChild; }
-
- void accept(tree_traveler* visitor) const { visitor->visit(this); }
- void accept(oct_traveler* visitor) const { visitor->visit(this); }
-
- void validateChildMap()
- {
- for (U32 i = 0; i < 8; i++)
- {
- U8 idx = mChildMap[i];
- if (idx != NO_CHILD_NODES)
- {
- oct_node* child = mChild[idx];
-
- if (child->getOctant() != i)
- {
- LL_ERRS() << "Invalid child map, bad octant data." << LL_ENDL;
- }
-
- if (getOctant(child->getCenter()) != child->getOctant())
- {
- LL_ERRS() << "Invalid child octant compared to position data." << LL_ENDL;
- }
- }
- }
- }
-
-
- oct_node* getNodeAt(const LLVector4a& pos, const F32& rad)
- {
- oct_node* node = this;
-
- if (node->isInside(pos, rad))
- {
- //do a quick search by octant
- U8 octant = node->getOctant(pos);
-
- //traverse the tree until we find a node that has no node
- //at the appropriate octant or is smaller than the object.
- //by definition, that node is the smallest node that contains
- // the data
- U8 next_node = node->mChildMap[octant];
-
- while (next_node != NO_CHILD_NODES && node->getSize()[0] >= rad)
- {
- node = node->getChild(next_node);
- octant = node->getOctant(pos);
- next_node = node->mChildMap[octant];
- }
- }
- else if (!node->contains(rad) && node->getParent())
- { //if we got here, data does not exist in this node
- return ((oct_node*) node->getParent())->getNodeAt(pos, rad);
- }
-
- return node;
- }
-
- virtual bool insert(T* data)
- {
- //LL_PROFILE_ZONE_NAMED_COLOR("Octree::insert()",OCTREE_DEBUG_COLOR_INSERT);
-
- if (data == NULL || data->getBinIndex() != -1)
- {
- OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL;
- return false;
- }
- oct_node* parent = getOctParent();
-
- //is it here?
- if (isInside(data->getPositionGroup()))
- {
- if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) ||
- (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity)))
- { //it belongs here
- mData.push_back(data);
- data->setBinIndex(getElementCount() - 1);
- BaseType::insert(data);
- return true;
- }
- else
- {
- //find a child to give it to
- oct_node* child = NULL;
- for (U32 i = 0; i < getChildCount(); i++)
- {
- child = getChild(i);
- if (child->isInside(data->getPositionGroup()))
- {
- child->insert(data);
- return false;
- }
- }
-
- //it's here, but no kids are in the right place, make a new kid
- LLVector4a center = getCenter();
- LLVector4a size = getSize();
- size.mul(0.5f);
-
- //push center in direction of data
- oct_node::pushCenter(center, size, data);
-
- // handle case where floating point number gets too small
- LLVector4a val;
- val.setSub(center, getCenter());
- val.setAbs(val);
- LLVector4a min_diff(gOctreeMinSize);
-
- S32 lt = val.lessThan(min_diff).getGatheredBits() & 0x7;
-
- if( lt == 0x7 )
- {
- mData.push_back(data);
- data->setBinIndex(getElementCount() - 1);
- BaseType::insert(data);
- return true;
- }
-
-#if LL_OCTREE_PARANOIA_CHECK
- if (getChildCount() == 8)
- {
- //this really isn't possible, something bad has happened
- OCT_ERRS << "Octree detected floating point error and gave up." << LL_ENDL;
- return false;
- }
-
- //make sure no existing node matches this position
- for (U32 i = 0; i < getChildCount(); i++)
- {
- if (mChild[i]->getCenter().equals3(center))
- {
- OCT_ERRS << "Octree detected duplicate child center and gave up." << LL_ENDL;
- return false;
- }
- }
-#endif
-
- llassert(size[0] >= gOctreeMinSize*0.5f);
- //make the new kid
- child = new oct_node(center, size, this);
- addChild(child);
-
- child->insert(data);
- }
- }
- else if (parent)
- {
- //it's not in here, give it to the root
- OCT_ERRS << "Octree insertion failed, starting over from root!" << LL_ENDL;
-
- oct_node* node = this;
-
- while (parent)
- {
- node = parent;
- parent = node->getOctParent();
- }
-
- node->insert(data);
- }
- else
- {
- // It's not in here, and we are root.
- // LLOctreeRoot::insert() should have expanded
- // root by now, something is wrong
- OCT_ERRS << "Octree insertion failed! Root expansion failed." << LL_ENDL;
- }
-
- return false;
- }
-
- void _remove(T* data, S32 i)
- { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount())
-
- data->setBinIndex(-1);
-
- const U32 new_element_count = getElementCount() - 1;
- if (new_element_count > 0)
- {
- if (new_element_count != i)
- {
- mData[i] = mData[new_element_count]; //might unref data, do not access data after this point
- mData[i]->setBinIndex(i);
- }
-
- mData[new_element_count] = NULL;
- mData.pop_back();
- }
- else
- {
- mData.clear();
- }
-
- this->notifyRemoval(data);
- checkAlive();
- }
-
- bool remove(T* data)
- {
- //LL_PROFILE_ZONE_NAMED_COLOR("Octree::remove()", OCTREE_DEBUG_COLOR_REMOVE);
-
- S32 i = data->getBinIndex();
-
- if (i >= 0 && i < getElementCount())
- {
- if (mData[i] == data)
- { //found it
- _remove(data, i);
- llassert(data->getBinIndex() == -1);
- return true;
- }
- }
-
- if (isInside(data))
- {
- oct_node* dest = getNodeAt(data);
-
- if (dest != this)
- {
- bool ret = dest->remove(data);
- llassert(data->getBinIndex() == -1);
- return ret;
- }
- }
-
- //SHE'S GONE MISSING...
- //none of the children have it, let's just brute force this bastard out
- //starting with the root node (UGLY CODE COMETH!)
- oct_node* parent = getOctParent();
- oct_node* node = this;
-
- while (parent != NULL)
- {
- node = parent;
- parent = node->getOctParent();
- }
-
- //node is now root
- LL_WARNS() << "!!! OCTREE REMOVING ELEMENT BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << LL_ENDL;
- node->removeByAddress(data);
- llassert(data->getBinIndex() == -1);
- return true;
- }
-
- void removeByAddress(T* data)
- {
- const U32 element_count = getElementCount();
- for (U32 i = 0; i < element_count; ++i)
- {
- if (mData[i] == data)
- { //we have data
- _remove(data, i);
- LL_WARNS() << "FOUND!" << LL_ENDL;
- return;
- }
- }
-
- for (U32 i = 0; i < getChildCount(); i++)
- { //we don't contain data, so pass this guy down
- oct_node* child = (oct_node*) getChild(i);
- child->removeByAddress(data);
- }
- }
-
- void clearChildren()
- {
- mChildCount = 0;
- memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap));
- }
-
- void validate()
- {
-#if LL_OCTREE_PARANOIA_CHECK
- for (U32 i = 0; i < getChildCount(); i++)
- {
- mChild[i]->validate();
- if (mChild[i]->getParent() != this)
- {
- LL_ERRS() << "Octree child has invalid parent." << LL_ENDL;
- }
- }
-#endif
- }
-
- virtual bool balance()
- {
- return false;
- }
-
- void destroy()
- {
- for (U32 i = 0; i < getChildCount(); i++)
- {
- mChild[i]->destroy();
- delete mChild[i];
- }
- }
-
- void addChild(oct_node* child, bool silent = false)
- {
-#if LL_OCTREE_PARANOIA_CHECK
-
- if (child->getSize().equals3(getSize()))
- {
- OCT_ERRS << "Child size is same as parent size!" << LL_ENDL;
- }
-
- for (U32 i = 0; i < getChildCount(); i++)
- {
- if(!mChild[i]->getSize().equals3(child->getSize()))
- {
- OCT_ERRS <<"Invalid octree child size." << LL_ENDL;
- }
- if (mChild[i]->getCenter().equals3(child->getCenter()))
- {
- OCT_ERRS <<"Duplicate octree child position." << LL_ENDL;
- }
- }
-
- if (mChild.size() >= 8)
- {
- OCT_ERRS <<"Octree node has too many children... why?" << LL_ENDL;
- }
-#endif
-
- mChildMap[child->getOctant()] = mChildCount;
-
- mChild[mChildCount] = child;
- ++mChildCount;
- child->setParent(this);
-
- if (!silent)
- {
- for (U32 i = 0; i < this->getListenerCount(); i++)
- {
- oct_listener* listener = getOctListener(i);
- listener->handleChildAddition(this, child);
- }
- }
- }
-
- void removeChild(S32 index, bool destroy = false)
- {
- for (U32 i = 0; i < this->getListenerCount(); i++)
- {
- oct_listener* listener = getOctListener(i);
- listener->handleChildRemoval(this, getChild(index));
- }
-
- if (destroy)
- {
- mChild[index]->destroy();
- delete mChild[index];
- }
-
- --mChildCount;
-
- mChild[index] = mChild[mChildCount];
-
- //rebuild child map
- memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap));
-
- for (U32 i = 0; i < mChildCount; ++i)
- {
- mChildMap[mChild[i]->getOctant()] = i;
- }
-
- checkAlive();
- }
-
- void checkAlive()
- {
- if (getChildCount() == 0 && getElementCount() == 0)
- {
- oct_node* parent = getOctParent();
- if (parent)
- {
- parent->deleteChild(this);
- }
- }
- }
-
- void deleteChild(oct_node* node)
- {
- for (U32 i = 0; i < getChildCount(); i++)
- {
- if (getChild(i) == node)
- {
- removeChild(i, true);
- return;
- }
- }
-
- OCT_ERRS << "Octree failed to delete requested child." << LL_ENDL;
- }
-
-protected:
- typedef enum
- {
- CENTER = 0,
- SIZE = 1,
- MAX = 2,
- MIN = 3
- } eDName;
-
- LLVector4a mCenter;
- LLVector4a mSize;
- LLVector4a mMax;
- LLVector4a mMin;
-
- oct_node* mParent;
- U8 mOctant;
-
- oct_node* mChild[8];
- U8 mChildMap[8];
- U32 mChildCount;
-
- element_list mData;
-};
-
-//just like a regular node, except it might expand on insert and compress on balance
-template <class T, typename T_PTR>
-class LLOctreeRoot : public LLOctreeNode<T, T_PTR>
-{
-public:
- typedef LLOctreeNode<T, T_PTR> BaseType;
- typedef LLOctreeNode<T, T_PTR> oct_node;
-
- LLOctreeRoot(const LLVector4a& center,
- const LLVector4a& size,
- BaseType* parent)
- : BaseType(center, size, parent)
- {
- }
-
- bool balance() override
- {
- //LL_PROFILE_ZONE_NAMED_COLOR("Octree::balance()",OCTREE_DEBUG_COLOR_BALANCE);
-
- if (this->getChildCount() == 1 &&
- !(this->mChild[0]->isLeaf()) &&
- this->mChild[0]->getElementCount() == 0)
- { //if we have only one child and that child is an empty branch, make that child the root
- oct_node* child = this->mChild[0];
-
- //make the root node look like the child
- this->setCenter(this->mChild[0]->getCenter());
- this->setSize(this->mChild[0]->getSize());
- this->updateMinMax();
-
- //reset root node child list
- this->clearChildren();
-
- //copy the child's children into the root node silently
- //(don't notify listeners of addition)
- for (U32 i = 0; i < child->getChildCount(); i++)
- {
- this->addChild(child->getChild(i), true);
- }
-
- //destroy child
- child->clearChildren();
- delete child;
-
- return false;
- }
-
- return true;
- }
-
- // LLOctreeRoot::insert
- bool insert(T* data) override
- {
- if (data == nullptr)
- {
- OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << LL_ENDL;
- return false;
- }
-
- if (data->getBinRadius() > 4096.0)
- {
- OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << LL_ENDL;
- return false;
- }
-
- LLVector4a MAX_MAG;
- MAX_MAG.splat(1024.f*1024.f);
-
- const LLVector4a& v = data->getPositionGroup();
-
- LLVector4a val;
- val.setSub(v, BaseType::mCenter);
- val.setAbs(val);
- S32 lt = val.lessThan(MAX_MAG).getGatheredBits() & 0x7;
-
- if (lt != 0x7)
- {
- //OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << LL_ENDL;
- return false;
- }
-
- if (this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))
- {
- //we got it, just act like a branch
- oct_node* node = this->getNodeAt(data);
- if (node == this)
- {
- oct_node::insert(data);
- }
- else if (node->isInside(data->getPositionGroup()))
- {
- node->insert(data);
- }
- else
- {
- // calling node->insert(data) will return us to root
- OCT_ERRS << "Failed to insert data at child node" << LL_ENDL;
- }
- }
- else if (this->getChildCount() == 0)
- {
- //first object being added, just wrap it up
- while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())))
- {
- LLVector4a center, size;
- center = this->getCenter();
- size = this->getSize();
- oct_node::pushCenter(center, size, data);
- this->setCenter(center);
- size.mul(2.f);
- this->setSize(size);
- this->updateMinMax();
- }
- oct_node::insert(data);
- }
- else
- {
- while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())))
- {
- //the data is outside the root node, we need to grow
- LLVector4a center(this->getCenter());
- LLVector4a size(this->getSize());
-
- //expand this node
- LLVector4a newcenter(center);
- oct_node::pushCenter(newcenter, size, data);
- this->setCenter(newcenter);
- LLVector4a size2 = size;
- size2.mul(2.f);
- this->setSize(size2);
- this->updateMinMax();
-
- llassert(size[0] >= gOctreeMinSize);
-
- //copy our children to a new branch
- oct_node* newnode = new oct_node(center, size, this);
-
- for (U32 i = 0; i < this->getChildCount(); i++)
- {
- oct_node* child = this->getChild(i);
- newnode->addChild(child);
- }
-
- //clear our children and add the root copy
- this->clearChildren();
- this->addChild(newnode);
- }
-
- //insert the data
- insert(data);
- }
-
- return false;
- }
-
- bool isLeaf() const override
- {
- // root can't be a leaf
- return false;
- }
-};
-
-//========================
-// LLOctreeTraveler
-//========================
-template <class T, typename T_PTR>
-void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
-{
- node->accept(this);
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- traverse(node->getChild(i));
- }
-}
-
-template <class T, typename T_PTR>
-void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
-{
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- traverse(node->getChild(i));
- }
- node->accept(this);
-}
-
-#endif
+/** + * @file lloctree.h + * @brief Octree declaration. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLOCTREE_H +#define LL_LLOCTREE_H + +#include "lltreenode.h" +#include "v3math.h" +#include "llvector4a.h" +#include <vector> + +#define OCT_ERRS LL_WARNS("OctreeErrors") + +#define OCTREE_DEBUG_COLOR_REMOVE 0x0000FF // r +#define OCTREE_DEBUG_COLOR_INSERT 0x00FF00 // g +#define OCTREE_DEBUG_COLOR_BALANCE 0xFF0000 // b + +extern U32 gOctreeMaxCapacity; +extern float gOctreeMinSize; + +/*#define LL_OCTREE_PARANOIA_CHECK 0 +#if LL_DARWIN +#define LL_OCTREE_MAX_CAPACITY 32 +#else +#define LL_OCTREE_MAX_CAPACITY 128 +#endif*/ + +// T is the type of the element referenced by the octree node. +// T_PTR determines how pointers to elements are stored internally. +// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and +// deletes elements removed from the tree. +// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API +// user is responsible for managing the storage lifecycle of elements added to +// the tree. +template <class T, typename T_PTR> class LLOctreeNode; + +template <class T, typename T_PTR> +class LLOctreeListener: public LLTreeListener<T> +{ +public: + typedef LLTreeListener<T> BaseType; + typedef LLOctreeNode<T, T_PTR> oct_node; + + virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0; + virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0; +}; + +template <class T, typename T_PTR> +class LLOctreeTraveler +{ +public: + virtual void traverse(const LLOctreeNode<T, T_PTR>* node); + virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0; +}; + +template <class T, typename T_PTR> +class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR> +{ +public: + virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override; +}; + +template <class T, typename T_PTR> +class alignas(16) LLOctreeNode : public LLTreeNode<T> +{ + LL_ALIGN_NEW +public: + + typedef LLOctreeTraveler<T, T_PTR> oct_traveler; + typedef LLTreeTraveler<T> tree_traveler; + typedef std::vector<T_PTR> element_list; + typedef typename element_list::iterator element_iter; + typedef typename element_list::const_iterator const_element_iter; + typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter; + typedef LLOctreeNode<T, T_PTR>** child_list; + typedef LLOctreeNode<T, T_PTR>** child_iter; + + typedef LLTreeNode<T> BaseType; + typedef LLOctreeNode<T, T_PTR> oct_node; + typedef LLOctreeListener<T, T_PTR> oct_listener; + + enum + { + NO_CHILD_NODES = 255 // Note: This is an U8 to match the max value in mChildMap[] + }; + + LLOctreeNode( const LLVector4a& center, + const LLVector4a& size, + BaseType* parent, + U8 octant = NO_CHILD_NODES) + : mParent((oct_node*)parent), + mOctant(octant) + { + llassert(size[0] >= gOctreeMinSize*0.5f); + + mCenter = center; + mSize = size; + + updateMinMax(); + if ((mOctant == NO_CHILD_NODES) && mParent) + { + mOctant = ((oct_node*) mParent)->getOctant(mCenter); + } + + clearChildren(); + } + + virtual ~LLOctreeNode() + { + BaseType::destroyListeners(); + + const U32 element_count = getElementCount(); + for (U32 i = 0; i < element_count; ++i) + { + mData[i]->setBinIndex(-1); + mData[i] = NULL; + } + + mData.clear(); + + for (U32 i = 0; i < getChildCount(); i++) + { + delete getChild(i); + } + } + + inline const BaseType* getParent() const { return mParent; } + inline void setParent(BaseType* parent) { mParent = (oct_node*) parent; } + inline const LLVector4a& getCenter() const { return mCenter; } + inline const LLVector4a& getSize() const { return mSize; } + inline void setCenter(const LLVector4a& center) { mCenter = center; } + inline void setSize(const LLVector4a& size) { mSize = size; } + inline oct_node* getNodeAt(T* data) { return getNodeAt(data->getPositionGroup(), data->getBinRadius()); } + inline U8 getOctant() const { return mOctant; } + inline const oct_node* getOctParent() const { return (const oct_node*) getParent(); } + inline oct_node* getOctParent() { return (oct_node*) getParent(); } + + U8 getOctant(const LLVector4a& pos) const //get the octant pos is in + { + return (U8) (pos.greaterThan(mCenter).getGatheredBits() & 0x7); + } + + inline bool isInside(const LLVector4a& pos, const F32& rad) const + { + return rad <= mSize[0]*2.f && isInside(pos); + } + + inline bool isInside(T* data) const + { + return isInside(data->getPositionGroup(), data->getBinRadius()); + } + + bool isInside(const LLVector4a& pos) const + { + S32 gt = pos.greaterThan(mMax).getGatheredBits() & 0x7; + if (gt) + { + return false; + } + + S32 lt = pos.lessEqual(mMin).getGatheredBits() & 0x7; + if (lt) + { + return false; + } + + return true; + } + + void updateMinMax() + { + mMax.setAdd(mCenter, mSize); + mMin.setSub(mCenter, mSize); + } + + inline oct_listener* getOctListener(U32 index) + { + return (oct_listener*) BaseType::getListener(index); + } + + inline bool contains(T* xform) + { + return contains(xform->getBinRadius()); + } + + bool contains(F32 radius) + { + if (mParent == NULL) + { //root node contains nothing + return false; + } + + F32 size = mSize[0]; + F32 p_size = size * 2.f; + + return (radius <= gOctreeMinSize && size <= gOctreeMinSize) || + (radius <= p_size && radius > size); + } + + static void pushCenter(LLVector4a ¢er, const LLVector4a &size, const T* data) + { + const LLVector4a& pos = data->getPositionGroup(); + + LLVector4Logical gt = pos.greaterThan(center); + + LLVector4a up; + up = _mm_and_ps(size, gt); + + LLVector4a down; + down = _mm_andnot_ps(gt, size); + + center.add(up); + center.sub(down); + } + + void accept(oct_traveler* visitor) { visitor->visit(this); } + virtual bool isLeaf() const { return mChildCount == 0; } + + U32 getElementCount() const { return (U32)mData.size(); } + bool isEmpty() const { return mData.empty(); } + element_iter getDataBegin() { return mData.begin(); } + element_iter getDataEnd() { return mData.end(); } + const_element_iter getDataBegin() const { return mData.cbegin(); } + const_element_iter getDataEnd() const { return mData.cend(); } + + U32 getChildCount() const { return mChildCount; } + oct_node* getChild(U32 index) { return mChild[index]; } + const oct_node* getChild(U32 index) const { return mChild[index]; } + child_list& getChildren() { return mChild; } + const child_list& getChildren() const { return mChild; } + + void accept(tree_traveler* visitor) const { visitor->visit(this); } + void accept(oct_traveler* visitor) const { visitor->visit(this); } + + void validateChildMap() + { + for (U32 i = 0; i < 8; i++) + { + U8 idx = mChildMap[i]; + if (idx != NO_CHILD_NODES) + { + oct_node* child = mChild[idx]; + + if (child->getOctant() != i) + { + LL_ERRS() << "Invalid child map, bad octant data." << LL_ENDL; + } + + if (getOctant(child->getCenter()) != child->getOctant()) + { + LL_ERRS() << "Invalid child octant compared to position data." << LL_ENDL; + } + } + } + } + + + oct_node* getNodeAt(const LLVector4a& pos, const F32& rad) + { + oct_node* node = this; + + if (node->isInside(pos, rad)) + { + //do a quick search by octant + U8 octant = node->getOctant(pos); + + //traverse the tree until we find a node that has no node + //at the appropriate octant or is smaller than the object. + //by definition, that node is the smallest node that contains + // the data + U8 next_node = node->mChildMap[octant]; + + while (next_node != NO_CHILD_NODES && node->getSize()[0] >= rad) + { + node = node->getChild(next_node); + octant = node->getOctant(pos); + next_node = node->mChildMap[octant]; + } + } + else if (!node->contains(rad) && node->getParent()) + { //if we got here, data does not exist in this node + return ((oct_node*) node->getParent())->getNodeAt(pos, rad); + } + + return node; + } + + virtual bool insert(T* data) + { + //LL_PROFILE_ZONE_NAMED_COLOR("Octree::insert()",OCTREE_DEBUG_COLOR_INSERT); + + if (data == NULL || data->getBinIndex() != -1) + { + OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL; + return false; + } + oct_node* parent = getOctParent(); + + //is it here? + if (isInside(data->getPositionGroup())) + { + if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) || + (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity))) + { //it belongs here + mData.push_back(data); + data->setBinIndex(getElementCount() - 1); + BaseType::insert(data); + return true; + } + else + { + //find a child to give it to + oct_node* child = NULL; + for (U32 i = 0; i < getChildCount(); i++) + { + child = getChild(i); + if (child->isInside(data->getPositionGroup())) + { + child->insert(data); + return false; + } + } + + //it's here, but no kids are in the right place, make a new kid + LLVector4a center = getCenter(); + LLVector4a size = getSize(); + size.mul(0.5f); + + //push center in direction of data + oct_node::pushCenter(center, size, data); + + // handle case where floating point number gets too small + LLVector4a val; + val.setSub(center, getCenter()); + val.setAbs(val); + LLVector4a min_diff(gOctreeMinSize); + + S32 lt = val.lessThan(min_diff).getGatheredBits() & 0x7; + + if( lt == 0x7 ) + { + mData.push_back(data); + data->setBinIndex(getElementCount() - 1); + BaseType::insert(data); + return true; + } + +#if LL_OCTREE_PARANOIA_CHECK + if (getChildCount() == 8) + { + //this really isn't possible, something bad has happened + OCT_ERRS << "Octree detected floating point error and gave up." << LL_ENDL; + return false; + } + + //make sure no existing node matches this position + for (U32 i = 0; i < getChildCount(); i++) + { + if (mChild[i]->getCenter().equals3(center)) + { + OCT_ERRS << "Octree detected duplicate child center and gave up." << LL_ENDL; + return false; + } + } +#endif + + llassert(size[0] >= gOctreeMinSize*0.5f); + //make the new kid + child = new oct_node(center, size, this); + addChild(child); + + child->insert(data); + } + } + else if (parent) + { + //it's not in here, give it to the root + OCT_ERRS << "Octree insertion failed, starting over from root!" << LL_ENDL; + + oct_node* node = this; + + while (parent) + { + node = parent; + parent = node->getOctParent(); + } + + node->insert(data); + } + else + { + // It's not in here, and we are root. + // LLOctreeRoot::insert() should have expanded + // root by now, something is wrong + OCT_ERRS << "Octree insertion failed! Root expansion failed." << LL_ENDL; + } + + return false; + } + + void _remove(T* data, S32 i) + { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount()) + + data->setBinIndex(-1); + + const U32 new_element_count = getElementCount() - 1; + if (new_element_count > 0) + { + if (new_element_count != i) + { + mData[i] = mData[new_element_count]; //might unref data, do not access data after this point + mData[i]->setBinIndex(i); + } + + mData[new_element_count] = NULL; + mData.pop_back(); + } + else + { + mData.clear(); + } + + this->notifyRemoval(data); + checkAlive(); + } + + bool remove(T* data) + { + //LL_PROFILE_ZONE_NAMED_COLOR("Octree::remove()", OCTREE_DEBUG_COLOR_REMOVE); + + S32 i = data->getBinIndex(); + + if (i >= 0 && i < getElementCount()) + { + if (mData[i] == data) + { //found it + _remove(data, i); + llassert(data->getBinIndex() == -1); + return true; + } + } + + if (isInside(data)) + { + oct_node* dest = getNodeAt(data); + + if (dest != this) + { + bool ret = dest->remove(data); + llassert(data->getBinIndex() == -1); + return ret; + } + } + + //SHE'S GONE MISSING... + //none of the children have it, let's just brute force this bastard out + //starting with the root node (UGLY CODE COMETH!) + oct_node* parent = getOctParent(); + oct_node* node = this; + + while (parent != NULL) + { + node = parent; + parent = node->getOctParent(); + } + + //node is now root + LL_WARNS() << "!!! OCTREE REMOVING ELEMENT BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << LL_ENDL; + node->removeByAddress(data); + llassert(data->getBinIndex() == -1); + return true; + } + + void removeByAddress(T* data) + { + const U32 element_count = getElementCount(); + for (U32 i = 0; i < element_count; ++i) + { + if (mData[i] == data) + { //we have data + _remove(data, i); + LL_WARNS() << "FOUND!" << LL_ENDL; + return; + } + } + + for (U32 i = 0; i < getChildCount(); i++) + { //we don't contain data, so pass this guy down + oct_node* child = (oct_node*) getChild(i); + child->removeByAddress(data); + } + } + + void clearChildren() + { + mChildCount = 0; + memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap)); + } + + void validate() + { +#if LL_OCTREE_PARANOIA_CHECK + for (U32 i = 0; i < getChildCount(); i++) + { + mChild[i]->validate(); + if (mChild[i]->getParent() != this) + { + LL_ERRS() << "Octree child has invalid parent." << LL_ENDL; + } + } +#endif + } + + virtual bool balance() + { + return false; + } + + void destroy() + { + for (U32 i = 0; i < getChildCount(); i++) + { + mChild[i]->destroy(); + delete mChild[i]; + } + } + + void addChild(oct_node* child, bool silent = false) + { +#if LL_OCTREE_PARANOIA_CHECK + + if (child->getSize().equals3(getSize())) + { + OCT_ERRS << "Child size is same as parent size!" << LL_ENDL; + } + + for (U32 i = 0; i < getChildCount(); i++) + { + if(!mChild[i]->getSize().equals3(child->getSize())) + { + OCT_ERRS <<"Invalid octree child size." << LL_ENDL; + } + if (mChild[i]->getCenter().equals3(child->getCenter())) + { + OCT_ERRS <<"Duplicate octree child position." << LL_ENDL; + } + } + + if (mChild.size() >= 8) + { + OCT_ERRS <<"Octree node has too many children... why?" << LL_ENDL; + } +#endif + + mChildMap[child->getOctant()] = mChildCount; + + mChild[mChildCount] = child; + ++mChildCount; + child->setParent(this); + + if (!silent) + { + for (U32 i = 0; i < this->getListenerCount(); i++) + { + oct_listener* listener = getOctListener(i); + listener->handleChildAddition(this, child); + } + } + } + + void removeChild(S32 index, bool destroy = false) + { + for (U32 i = 0; i < this->getListenerCount(); i++) + { + oct_listener* listener = getOctListener(i); + listener->handleChildRemoval(this, getChild(index)); + } + + if (destroy) + { + mChild[index]->destroy(); + delete mChild[index]; + } + + --mChildCount; + + mChild[index] = mChild[mChildCount]; + + //rebuild child map + memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap)); + + for (U32 i = 0; i < mChildCount; ++i) + { + mChildMap[mChild[i]->getOctant()] = i; + } + + checkAlive(); + } + + void checkAlive() + { + if (getChildCount() == 0 && getElementCount() == 0) + { + oct_node* parent = getOctParent(); + if (parent) + { + parent->deleteChild(this); + } + } + } + + void deleteChild(oct_node* node) + { + for (U32 i = 0; i < getChildCount(); i++) + { + if (getChild(i) == node) + { + removeChild(i, true); + return; + } + } + + OCT_ERRS << "Octree failed to delete requested child." << LL_ENDL; + } + +protected: + typedef enum + { + CENTER = 0, + SIZE = 1, + MAX = 2, + MIN = 3 + } eDName; + + LLVector4a mCenter; + LLVector4a mSize; + LLVector4a mMax; + LLVector4a mMin; + + oct_node* mParent; + U8 mOctant; + + oct_node* mChild[8]; + U8 mChildMap[8]; + U32 mChildCount; + + element_list mData; +}; + +//just like a regular node, except it might expand on insert and compress on balance +template <class T, typename T_PTR> +class LLOctreeRoot : public LLOctreeNode<T, T_PTR> +{ +public: + typedef LLOctreeNode<T, T_PTR> BaseType; + typedef LLOctreeNode<T, T_PTR> oct_node; + + LLOctreeRoot(const LLVector4a& center, + const LLVector4a& size, + BaseType* parent) + : BaseType(center, size, parent) + { + } + + bool balance() override + { + //LL_PROFILE_ZONE_NAMED_COLOR("Octree::balance()",OCTREE_DEBUG_COLOR_BALANCE); + + if (this->getChildCount() == 1 && + !(this->mChild[0]->isLeaf()) && + this->mChild[0]->getElementCount() == 0) + { //if we have only one child and that child is an empty branch, make that child the root + oct_node* child = this->mChild[0]; + + //make the root node look like the child + this->setCenter(this->mChild[0]->getCenter()); + this->setSize(this->mChild[0]->getSize()); + this->updateMinMax(); + + //reset root node child list + this->clearChildren(); + + //copy the child's children into the root node silently + //(don't notify listeners of addition) + for (U32 i = 0; i < child->getChildCount(); i++) + { + this->addChild(child->getChild(i), true); + } + + //destroy child + child->clearChildren(); + delete child; + + return false; + } + + return true; + } + + // LLOctreeRoot::insert + bool insert(T* data) override + { + if (data == nullptr) + { + OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << LL_ENDL; + return false; + } + + if (data->getBinRadius() > 4096.0) + { + OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << LL_ENDL; + return false; + } + + LLVector4a MAX_MAG; + MAX_MAG.splat(1024.f*1024.f); + + const LLVector4a& v = data->getPositionGroup(); + + LLVector4a val; + val.setSub(v, BaseType::mCenter); + val.setAbs(val); + S32 lt = val.lessThan(MAX_MAG).getGatheredBits() & 0x7; + + if (lt != 0x7) + { + //OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << LL_ENDL; + return false; + } + + if (this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())) + { + //we got it, just act like a branch + oct_node* node = this->getNodeAt(data); + if (node == this) + { + oct_node::insert(data); + } + else if (node->isInside(data->getPositionGroup())) + { + node->insert(data); + } + else + { + // calling node->insert(data) will return us to root + OCT_ERRS << "Failed to insert data at child node" << LL_ENDL; + } + } + else if (this->getChildCount() == 0) + { + //first object being added, just wrap it up + while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))) + { + LLVector4a center, size; + center = this->getCenter(); + size = this->getSize(); + oct_node::pushCenter(center, size, data); + this->setCenter(center); + size.mul(2.f); + this->setSize(size); + this->updateMinMax(); + } + oct_node::insert(data); + } + else + { + while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))) + { + //the data is outside the root node, we need to grow + LLVector4a center(this->getCenter()); + LLVector4a size(this->getSize()); + + //expand this node + LLVector4a newcenter(center); + oct_node::pushCenter(newcenter, size, data); + this->setCenter(newcenter); + LLVector4a size2 = size; + size2.mul(2.f); + this->setSize(size2); + this->updateMinMax(); + + llassert(size[0] >= gOctreeMinSize); + + //copy our children to a new branch + oct_node* newnode = new oct_node(center, size, this); + + for (U32 i = 0; i < this->getChildCount(); i++) + { + oct_node* child = this->getChild(i); + newnode->addChild(child); + } + + //clear our children and add the root copy + this->clearChildren(); + this->addChild(newnode); + } + + //insert the data + insert(data); + } + + return false; + } + + bool isLeaf() const override + { + // root can't be a leaf + return false; + } +}; + +//======================== +// LLOctreeTraveler +//======================== +template <class T, typename T_PTR> +void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node) +{ + node->accept(this); + for (U32 i = 0; i < node->getChildCount(); i++) + { + traverse(node->getChild(i)); + } +} + +template <class T, typename T_PTR> +void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node) +{ + for (U32 i = 0; i < node->getChildCount(); i++) + { + traverse(node->getChild(i)); + } + node->accept(this); +} + +#endif |