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.
237 lines
6.3 KiB
237 lines
6.3 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/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
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==*/ |
|
|
|
|
|
void plOccTree::AddPoly(plPolygon* poly) |
|
{ |
|
fBasePolys.Append(*poly); |
|
} |
|
|
|
void plOccTree::MakeOccTree() |
|
{ |
|
if( !fBasePolys.GetCount() ) |
|
return; |
|
|
|
ISortBasePolys(); |
|
|
|
int i; |
|
for( i = 0; i < fBasePolys.GetCount(); i++ ) |
|
fRoot = IAddPolyRecur(fRoot, fBasePolys[i], false); |
|
|
|
fBasePolys.SetCount(0); |
|
} |
|
|
|
plOccNode* poOccTree::IMakeSubTree(plOccPoly* poly) |
|
{ |
|
plOccNode* nextNode = nil; |
|
plOccNode* lastNode = nil; |
|
|
|
int i; |
|
for( i = 0; i < poly->fVerts.GetCount(); i++ ) |
|
{ |
|
if( poly->fEdgeFlags[i] & plOccPoly::kEdgeClipped ) |
|
continue; |
|
|
|
nextNode = fNodePool.Append(); |
|
nextNode->fFlags = 0; |
|
nextNode->fOutChild = nil; |
|
|
|
int j = i+1 < poly->fVerts.GetCount() ? i+1 : 0; |
|
|
|
// Need to set the viewplane here. Calc once per base poly and use |
|
// that for fragments? |
|
nextNode->Init(poly->fVerts[i], poly->fVerts[j], fViewPos); |
|
|
|
if( nextNode->fInChild = lastChild ) |
|
nextNode->fFlags = plOccNode::kHasInChild; |
|
else |
|
{ |
|
nextNode->fInChild = fNodePool.Append(); |
|
nextNode->fInChild->Init(poly, false); |
|
} |
|
|
|
lastNode = nextNode; |
|
} |
|
|
|
// If we have no nextNode, all our edges were clipped. In |
|
// that case, we'll just return an "out" leaf. |
|
if( !nextNode ) |
|
{ |
|
nextNode = fNodePool.Append(); |
|
nextNode->fFlags = 0; |
|
nextNode->fInChild = nextNode->fOutChild = nil; |
|
nextNode->Init(poly, true); |
|
} |
|
|
|
return nextNode; |
|
} |
|
|
|
void plOccNode::Init(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& pv) |
|
{ |
|
hsVector3 v0, v1; |
|
v0.Set(&p0, &pv); |
|
v1.Set(&p1, &pv); |
|
|
|
fPlane.fNormal = v0 % v1; |
|
fPlane.fDist = fPlane.fNormal.InnerProduct(v0); |
|
} |
|
|
|
void plOccNode::Init(plOccPoly* poly) |
|
{ |
|
fPlane = poly->fPlane; |
|
// set the viewplane |
|
fFlags = kIsLeaf; |
|
} |
|
|
|
// Adding a poly to a node |
|
// if the node is nil |
|
// IMakeSubTree(poly) replaces the node |
|
// else |
|
// if the node is a leaf |
|
// pitch the poly |
|
// return node (no replacement) |
|
// else |
|
// if poly is inside the node |
|
// recur on node's inner child |
|
// return node (no replacement) |
|
// else |
|
// if poly is ouside the node |
|
// recur on node's outer child |
|
// return node (no replacement) |
|
// else (node splits poly) |
|
// recur on node's inner child |
|
// recur on node's outer child |
|
// return node (no replacement) |
|
// end |
|
// |
|
// Special case - Degenarate poly's can come |
|
// from ITestPoly if an edge of the input poly |
|
// is on the plane. In that case, the function |
|
// will return kSplit, but either inPoly or outPoly |
|
// will have no vertices. That degenerate poly, |
|
// when added to a node, should just be pitched. |
|
// |
|
|
|
|
|
|
|
|
|
// Returns new root, in case it changed. |
|
// This assumes polys are being added front to back. |
|
// This function will break the poly into fragments that fit in the |
|
// current planes within the tree. Planes are added when a final fragment |
|
// is added (in IMakeSubTree). |
|
// We count on ITestPoly to properly mark edges which were created by |
|
// clipping, as those won't generate leaf nodes. |
|
plOccNode* plOccTree::IAddPolyRecur(plOccNode* node, plOccPoly* poly) |
|
{ |
|
if( !poly->fVerts.GetCount() ) |
|
return node; |
|
|
|
if( !node ) |
|
{ |
|
return IMakeSubTree(poly); |
|
} |
|
|
|
plOccPoly* inPoly = nil; |
|
plOccPoly* outPoly = nil; |
|
|
|
|
|
UInt32 test = ITestPoly(node->fPlane, poly, inPoly, outPoly); |
|
|
|
switch( test ) |
|
{ |
|
case kAllIn: |
|
node->fInChild = IAddPolyRecur(node->fInChild, poly); |
|
break; |
|
case kAllOut: |
|
node->fOutChild = IAddPolyRecur(node->fOutChild, poly); |
|
break; |
|
case kSplit: |
|
node->fInChild = IAddPolyRecur(node->fInChild, inPoly); |
|
node->fOutChild = IAddPolyRecur(node->fOutChild, outPoly); |
|
break; |
|
}; |
|
|
|
return node; |
|
} |
|
|
|
hsBool plOccTree::BoundsVisible(const hsBounds3Ext& bnd) const |
|
{ |
|
if( !fRoot ) |
|
return true; |
|
|
|
return fRoot->IBoundsVisible(bnd); |
|
} |
|
|
|
|
|
hsBool plOccNode::IInChildBoundsVisible(const hsBounds3Ext& bnd) const |
|
{ |
|
return fInChild |
|
? fInChild->IBoundsVisible(bnd) |
|
: false; |
|
} |
|
|
|
hsBool plOccNode::IOutChildBoundsVisible(const hsBounds3Ext& bnd) const |
|
{ |
|
return fOutChild |
|
? fOutChild->IBoundsVisible(bnd) |
|
: true; |
|
} |
|
|
|
hsBool plOccNode::IBoundsVisible(const hsBounds3Ext& bnd) const |
|
{ |
|
hsPoint2 depth; |
|
bnd.TestPlane(fPlane.fNormal, depth); |
|
if( depth.fX > fPlane.fDist ) |
|
{ |
|
return IOutChildVisible(bnd); |
|
} |
|
else if( depth.fY < fPlane.fDist ) |
|
{ |
|
return IInChildVisible(bnd); |
|
} |
|
|
|
// here's where it gets wierd. we pass the bounds in |
|
// both directions. if either says it's visible, it's visible. |
|
// doesn't seem like it would work, but you never know. |
|
return IOutChildVisible(bnd) || IInChildVisible(bnd); |
|
} |