You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

301 lines
7.6 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsTypes.h"
#include "Max.h"
#include "plDistTree.h"
plDistTree::plDistTree()
: fRoot(-1)
{
}
plDistTree::~plDistTree()
{
}
void plDistTree::Reset()
{
fRoot = -1;
fNodes.Reset();
}
void plDistTree::AddBoxIData(const Box3& box, const Box3& fade, UInt32 iData)
{
fRoot = IAddNodeRecur(fRoot, box, fade, iData);
}
BOOL plDistTree::BoxClear(const Box3& box, const Box3& fade) const
{
return IBoxClearRecur(fRoot, box, fade);
}
BOOL plDistTree::PointClear(const Point3& pt, const Box3& fade) const
{
return IPointClearRecur(fRoot, pt, fade);
}
BOOL plDistTree::IFadesClear(const Box3& fade0, const Box3& fade1) const
{
// Only two ways fade can come out non-overlapping.
// Either fade0 fades out before fade1 fades in, or v.v.
// First case, does fade0 fade out?
if( fade0.Max()[2] > 0 )
{
// does fade1 fade in?
if( fade1.Min()[2] < 0 )
{
// Okay they do, does fade0 fade out before fade1 fades in?
if( fade0.Max()[0] <= fade1.Min()[0] )
return true;
}
}
// Second case, same thing but reversed order
if( fade1.Max()[2] > 0 )
{
// does fade0 fade in?
if( fade0.Min()[2] < 0 )
{
// Okay they do, does fade1 fade out before fade0 fades in?
if( fade1.Max()[0] <= fade0.Min()[0] )
return true;
}
}
return false;
}
BOOL plDistTree::IBox0ContainsBox1(const Box3& box0, const Box3& box1, const Box3& fade0, const Box3& fade1) const
{
#ifdef MAX_CONTAINS_WORKS
if( !box0.Contains(box1) )
return false;
#else MAX_CONTAINS_WORKS
if( (box0.Min()[0] > box1.Min()[0])
||(box0.Min()[1] > box1.Min()[1])
||(box0.Min()[2] > box1.Min()[2])
||(box0.Max()[0] < box1.Max()[0])
||(box0.Max()[1] < box1.Max()[1])
||(box0.Max()[2] < box1.Max()[2]) )
return false;
#endif // MAX_CONTAINS_WORKS
if( IFadesClear(fade0, fade1) )
return false;
return true;
}
BOOL plDistTree::IBoxesClear(const Box3& box0, const Box3& box1) const
{
return (box0.Min()[0] > box1.Max()[0])
||(box0.Max()[0] < box1.Min()[0])
||(box0.Min()[1] > box1.Max()[1])
||(box0.Max()[1] < box1.Min()[1])
||(box0.Min()[2] > box1.Max()[2])
||(box0.Max()[2] < box1.Min()[2]);
}
BOOL plDistTree::IBoxClearRecur(Int32 iNode, const Box3& box, const Box3& fade) const
{
if( iNode < 0 )
return true;
if( IBoxesClear(fNodes[iNode].fBox, box) )
return true;
if( IFadesClear(fNodes[iNode].fFade, fade) )
return true;
if( fNodes[iNode].IsLeaf() )
return false;
int i;
for( i = 0; i < 8; i++ )
{
if( !IBoxClearRecur(fNodes[iNode].fChildren[i], box, fade) )
return false;
}
return true;
}
BOOL plDistTree::IPointClearRecur(Int32 iNode, const Point3& pt, const Box3& fade) const
{
if( iNode < 0 )
return true;
if( !fNodes[iNode].fBox.Contains(pt) )
return true;
if( IFadesClear(fNodes[iNode].fFade, fade) )
return true;
if( fNodes[iNode].IsLeaf() )
return false;
int i;
for( i = 0; i < 8; i++ )
{
if( !IPointClearRecur(fNodes[iNode].fChildren[i], pt, fade) )
return false;
}
return true;
}
Int32 plDistTree::IAddNodeRecur(Int32 iNode, const Box3& box, const Box3& fade, UInt32 iData)
{
// if iNode < 0, make a node for box and return that.
if( iNode < 0 )
{
return INextNode(box, fade, iData);
}
// if the box is contained
// if this node is a leaf, pitch the box
//
// else
// recur on one of 8 children, based on
// box's center relative to node center.
// if the box doesn't intercect this node,
// replace this node with a node of combined boxes.
// this node becomes sibling of box.
// if the box does intercect, but isn't contained
// same thing.
#if 0
if( IBox0ContainsBox1(fNodes[iNode].fBox, box, fNodes[iNode].fFade, fade) )
{
if( !fNodes[iNode].IsLeaf() )
#else
if( !fNodes[iNode].IsLeaf() && IBox0ContainsBox1(fNodes[iNode].fBox, box, fNodes[iNode].fFade, fade) )
{
#endif
{
Int32 iChild = IGetChild(fNodes[iNode].fBox, box);
Int32 iChildNode = IAddNodeRecur(fNodes[iNode].fChildren[iChild], box, fade, iData);
fNodes[iNode].fChildren[iChild] = iChildNode;
fNodes[iNode].fBox += fNodes[fNodes[iNode].fChildren[iChild]].fBox;
}
return iNode;
}
else
{
return IMergeNodes(iNode, box, fade, iData);
}
}
Int32 plDistTree::IMergeNodes(Int32 iNode, const Box3& box, const Box3& fade, UInt32 iData)
{
Box3 parBox = box;
parBox += fNodes[iNode].fBox;
Int32 pNode = INextNode(parBox, NonFade(), UInt32(-1));
Int32 iChild = IGetChild(parBox, box);
Int32 cNode = INextNode(box, fade, iData);
fNodes[pNode].fChildren[iChild] = cNode;
// Put the original node in the opposite quadrant from the child.
// This handles the case where one of the bounds completely contains
// the other. The octant structure of the tree isn't relied on, it
// only helps balance the tree. So being wrong here won't hurt anything.
iChild = iChild ^ 0x7;
fNodes[pNode].fChildren[iChild] = iNode;
fNodes[pNode].SetIsLeaf(false);
return pNode;
}
Int32 plDistTree::IGetChild(const Box3& parent, const Box3& child) const
{
Point3 parCenter = parent.Center();
Point3 chiCenter = child.Center();
Int32 idx = ((parCenter[0] < chiCenter[0]) << 0)
| ((parCenter[1] < chiCenter[1]) << 1)
| ((parCenter[2] < chiCenter[2]) << 2);
return idx;
}
Int32 plDistTree::INextNode(const Box3& box, const Box3& fade, UInt32 iData)
{
Int32 iNode = fNodes.GetCount();
fNodes.Push();
fNodes[iNode].fFlags = plDistNode::kIsLeaf;
fNodes[iNode].fBox = box;
fNodes[iNode].fFade = fade;
fNodes[iNode].fIData = iData;
fNodes[iNode].fChildren[0]
= fNodes[iNode].fChildren[1]
= fNodes[iNode].fChildren[2]
= fNodes[iNode].fChildren[3]
= fNodes[iNode].fChildren[4]
= fNodes[iNode].fChildren[5]
= fNodes[iNode].fChildren[6]
= fNodes[iNode].fChildren[7] = -1;
return iNode;
}
void plDistTree::HarvestBox(const Box3& box, Tab<Int32>& out) const
{
IHarvestBoxRecur(fRoot, box, out);
}
void plDistTree::IHarvestBoxRecur(Int32 iNode, const Box3& box, Tab<Int32>& out) const
{
if( iNode < 0 )
return;
if( IBoxesClear(fNodes[iNode].fBox, box) )
return;
if( fNodes[iNode].IsLeaf() )
{
out.Append(1, &iNode);
}
else
{
int i;
for( i = 0; i < 8; i++ )
IHarvestBoxRecur(fNodes[iNode].fChildren[i], box, out);
}
}