Original Post
Comments for the article Object-Oriented Scene Management
quote:
Original post by Chris Hargrove
In other words, you are using an inappropriate data structure for your tree. A properly designed scene graph would allow node insertions and removals in constant time with all references cleaned up automatically. This is easy to do with a simple quad-linked tree structure, and it avoids all these problems.
quote:
Chris, would you care to elaborate a bit on this quad linked tree data structure.
class CMyTreeNode
: public TTreeNode<CMyTreeNode>
{
// regular node contents go here
};
CMyTreeNode* parent; // passed in from somewhere else
for (TTreeIterator<CMyTreeNode> iter(parent); iter; ++iter)
{
// overloads operator * and -> to use node pointer
CMyTreeNode* child = *iter;
iter->DoSomething(); // same as child->DoSomething()
}
quote:
Is there a reason why the parent and child links need to be seperate from the node? Can we not just create a node class that has previous, next, parent and child pointers, and then do without the parentlinka and child link objects?
quote:
Original post by Chris Hargrove
All of this can be wrapped with relative ease into a few templates along the lines of Coplien's "Curiously Recurring Template" pattern, where the tree node template class is inherited from and the name of the subclass is passed into the template.
template <class _TDerived>
class TNode
{
protected:
template <class _TNode>
struct TLink
{ // link for circular node linked list
typedef TLink<_TNode> _MyType;
TLink() : m_pParent(NULL), m_pChild(NULL)
, m_pNext(NULL), m_pPrev(NULL)
{}
void Init(_TNode *pParent, _TNode *pChild, _MyType *pNext, _MyType *pPrev)
{ // initialize node and link pointers
m_pParent = pParent; m_pChild = pChild;
m_pNext = pNext; m_pPrev = pPrev;
}
_TNode *m_pParent;
_TNode *m_pChild;
_MyType *m_pNext;
_MyType *m_pPrev;
};
typedef TNode<_TDerived> _MyType;
typedef TLink<_MyType>* _Linkptr;
typedef TLink<_MyType>*& _Linkpref;
typedef _TDerived& _Vref;
typedef _TDerived* _Vptr;
protected:
static _Linkpref _NextSibling(_Linkptr pLink)
{ // return reference to successor pointer in node
return ((_Linkpref)(*pLink).m_pNext);
}
static _Linkpref _PrevSibling(_Linkptr pLink)
{ // return reference to predecessor pointer in node
return ((_Linkpref)(*pLink).m_pPrev);
}
static _Vref _Myval(_Linkptr pLink)
{ // return links owner node
return (_Vref)(*(*pLink).m_pChild);
}
public:
class iterator;
friend class iterator;
class iterator
{ // iterator for sibling nodes
public:
iterator()
: m_pLink(0)
{ // construct with null node pointer
}
iterator(_Linkptr pLink)
: m_pLink(pLink)
{ // construct with node pointer _Pnode
}
_Vref operator*() const
{ // return designated value
return _Myval(m_pLink);
}
_Vptr operator->() const
{ // return pointer to class object
return (&**this);
}
iterator& operator++()
{ // preincrement
m_pLink = _NextSibling(m_pLink);
return (*this);
}
iterator operator++(int)
{ // postincrement
iterator iTmp = *this;
++*this;
return (iTmp);
}
iterator& operator--()
{ // predecrement
m_pLink = _PrevSibling(m_pLink);
return (*this);
}
iterator operator--(int)
{ // postdecrement
iterator iTmp = *this;
--*this;
return (iTmp);
}
bool operator==(const iterator& iRight) const
{ // test for iterator equality
return (m_pLink == iRight.m_pLink);
}
bool operator!=(const iterator& iRight) const
{ // test for iterator inequality
return (!(*this == iRight));
}
_Linkptr _Mynode() const
{ // return node pointer
return (m_pLink);
}
protected:
_Linkptr m_pLink; // pointer to node
};
public:
TNode()
{ // initialize links
m_lnkParent.Init(NULL, this, &m_lnkParent, &m_lnkParent);
m_lnkChild.Init(this, NULL, &m_lnkChild, &m_lnkChild);
}
virtual ~TNode()
{ }
void attach_parent(_MyType *pParent)
{ // link to parent
m_lnkParent.m_pParent = pParent;
m_lnkParent.m_pPrev = pParent->m_lnkChild.m_pPrev;
m_lnkParent.m_pNext = &pParent->m_lnkChild;
pParent->m_lnkChild.m_pPrev->m_pNext = &m_lnkParent;
pParent->m_lnkChild.m_pPrev = &m_lnkParent;
}
void detach_parent()
{ // un-link from parent
m_lnkParent.m_pParent = NULL;
m_lnkParent.m_pPrev->m_pNext = m_lnkParent.m_pNext;
m_lnkParent.m_pNext->m_pPrev = m_lnkParent.m_pPrev;
m_lnkParent.m_pPrev = m_lnkParent.m_pNext = &m_lnkParent;
}
_MyType* parent()
{ // return parent node
return m_lnkParent.m_pParent;
}
iterator child_begin()
{ // return begin iterator for children
return iterator(m_lnkChild.m_pNext);
}
iterator child_end()
{ // return end iterator for children
return iterator(&m_lnkChild);
}
iterator begin()
{ // return begin iterator for siblings
return (NULL == m_lnkParent.m_pParent) ? iterator(m_lnkChild.m_pNext) :
iterator(m_lnkParent.m_pParent->m_lnkChild.m_pNext);
}
iterator end()
{ // return end iterator for siblings
return (NULL == m_lnkParent.m_pParent) ? iterator(&m_lnkChild) :
iterator(&m_lnkParent.m_pParent->m_lnkChild);
}
protected:
TLink<_MyType> m_lnkParent; // link to parent node and sibling list
TLink<_MyType> m_lnkChild; // link to child node list
};
This topic has been locked by a moderator. New replies are not allowed.
GameDev.net uses cookies to ensure you have the best experience on our platform. Learn more