You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ryzom-core/nel/src/pacs/surface_quad.cpp

494 lines
10 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdpacs.h"
#include "nel/pacs/surface_quad.h"
#include "nel/misc/file.h"
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
using namespace NLMISC;
using namespace std;
NLPACS::CSurfaceQuadTree::CSurfaceQuadTree()
{
_Root = NULL;
_MaxThickness = FLT_MAX;
_MaxLevel = 1;
_BBox.setCenter(CVector::Null);
_BBox.setSize(CVector::Null);
}
NLPACS::CSurfaceQuadTree::CSurfaceQuadTree(const NLPACS::CSurfaceQuadTree &quad)
{
*this = quad;
}
NLPACS::CSurfaceQuadTree &NLPACS::CSurfaceQuadTree::operator = (const NLPACS::CSurfaceQuadTree &quad)
{
if (&quad == this)
return *this;
_MaxThickness = quad._MaxThickness;
_MaxLevel = quad._MaxLevel;
_BBox = quad._BBox;
_Root = NULL;
if (quad._Root != NULL)
{
if (quad._Root->isLeaf())
{
CQuadLeaf *newLeaf = new CQuadLeaf();
*newLeaf = *((CQuadLeaf *)(quad._Root));
_Root = newLeaf;
}
else
{
CQuadBranch *newBranch = new CQuadBranch();
*newBranch = *((CQuadBranch *)(quad._Root));
_Root = newBranch;
}
}
return *this;
}
void NLPACS::CSurfaceQuadTree::clear()
{
delete _Root;
_Root = NULL;
}
void NLPACS::CSurfaceQuadTree::init(float maxThickness, uint maxLevel, const CVector &center, float halfSize)
{
nlassert(maxLevel > 0);
clear();
_MaxThickness = maxThickness;
_MaxLevel = uint8(maxLevel);
_BBox.setCenter(center);
_BBox.setHalfSize(CVector(halfSize, halfSize, 10000.0f));
}
void NLPACS::CSurfaceQuadTree::addVertex(const CVector &v)
{
if (!_BBox.include(v))
return;
if (_Root == NULL)
{
if (_MaxLevel == 1)
{
_Root = new CQuadLeaf(_MaxLevel);
}
else
{
_Root = new CQuadBranch(_MaxLevel);
}
_Root->_MaxThickness = _MaxThickness;
_Root->_HalfSize = _BBox.getHalfSize().x;
_Root->_MinHeight = FLT_MAX;
_Root->_MaxHeight = -FLT_MAX;
_Root->_XCenter = _BBox.getCenter().x;
_Root->_YCenter = _BBox.getCenter().y;
}
_Root->addVertex(v);
}
void NLPACS::CSurfaceQuadTree::compile()
{
if (_Root != NULL &&
!_Root->isLeaf() &&
_Root->getMaxHeight()-_Root->getMinHeight() <= _MaxThickness)
{
CQuadLeaf *leaf = new CQuadLeaf();
*((IQuadNode *)leaf) = *_Root;
delete _Root;
_Root = leaf;
}
else if (_Root != NULL &&
!_Root->isLeaf())
{
((CQuadBranch *)_Root)->reduceChildren();
}
}
NLPACS::CQuadBranch::CQuadBranch(const NLPACS::CQuadBranch &branch) : NLPACS::IQuadNode(branch)
{
*this = branch;
}
NLPACS::CQuadBranch &NLPACS::CQuadBranch::operator = (const NLPACS::CQuadBranch &branch)
{
IQuadNode::operator=(branch);
uint child;
for (child=0; child<4; ++child)
{
_Children[child] = NULL;
if (branch._Children[child] != NULL)
{
if (branch._Children[child]->isLeaf())
{
CQuadLeaf *newLeaf = new CQuadLeaf();
*newLeaf = *((CQuadLeaf *)(branch._Children[child]));
_Children[child] = newLeaf;
}
else
{
CQuadBranch *newBranch = new CQuadBranch();
*newBranch = *((CQuadBranch *)(branch._Children[child]));
_Children[child] = newBranch;
}
}
}
return *this;
}
void NLPACS::CQuadBranch::reduceChildren()
{
uint i;
for (i=0; i<4; ++i)
{
if (_Children[i] != NULL &&
!_Children[i]->isLeaf() &&
_Children[i]->getMaxHeight()-_Children[i]->getMinHeight() <= _MaxThickness)
{
CQuadLeaf *leaf = new CQuadLeaf();
*((IQuadNode *)leaf) = *_Children[i];
delete _Children[i];
_Children[i] = leaf;
}
else if (_Children[i] != NULL &&
!_Children[i]->isLeaf())
{
((CQuadBranch *)_Children[i])->reduceChildren();
}
}
}
void NLPACS::CQuadBranch::addVertex(const CVector &v)
{
IQuadNode::addVertex(v);
uint child;
if (v.x > _XCenter)
child = (v.y > _YCenter) ? 2 : 1;
else
child = (v.y > _YCenter) ? 3 : 0;
if (_Children[child] == NULL)
{
if (_Level == 2)
{
_Children[child] = new CQuadLeaf(_Level-1);
}
else
{
_Children[child] = new CQuadBranch(_Level-1);
}
_Children[child]->_MaxThickness = _MaxThickness;
_Children[child]->_HalfSize = _HalfSize/2.0f;
_Children[child]->_MinHeight = FLT_MAX;
_Children[child]->_MaxHeight = -FLT_MAX;
_Children[child]->_XCenter = _XCenter+_Children[child]->_HalfSize*((child == 1 || child == 2) ? 1.0f : -1.0f);
_Children[child]->_YCenter = _YCenter+_Children[child]->_HalfSize*((child == 2 || child == 3) ? 1.0f : -1.0f);
}
_Children[child]->addVertex(v);
}
bool NLPACS::CQuadBranch::check() const
{
if (!IQuadNode::check())
return false;
uint child;
for (child=0; child<4; ++child)
if (_Children[child] != NULL && !_Children[child]->check())
return false;
return true;
}
/*
* Serialization methods...
*/
void NLPACS::CQuadBranch::serial(NLMISC::IStream &f)
{
IQuadNode::serial(f);
uint child;
for (child=0; child<4; ++child)
{
uint8 childType = 0;
if (f.isReading())
{
CQuadLeaf *leaf;
CQuadBranch *branch;
f.serial(childType);
switch (childType)
{
case NoChild:
_Children[child] = NULL;
break;
case LeafChild:
leaf = new CQuadLeaf();
_Children[child] = leaf;
leaf->serial(f);
break;
case BranchChild:
branch = new CQuadBranch();;
_Children[child] = branch;
branch->serial(f);
break;
default:
nlerror("While serializing (read) CQuadBranch: unknown child type");
break;
}
}
else
{
if (_Children[child] == NULL)
{
childType = NoChild;
f.serial(childType);
}
else
{
childType = uint8(_Children[child]->isLeaf() ? LeafChild : BranchChild);
f.serial(childType);
_Children[child]->serial(f);
}
}
}
}
bool NLPACS::CSurfaceQuadTree::check() const
{
if (_Root != NULL)
return _Root->check();
return true;
}
const NLPACS::CQuadLeaf *NLPACS::CSurfaceQuadTree::getLeaf(const CVector &v) const
{
CVector pos = CVector(v.x, v.y, 0.0f);
if (_Root == NULL || !_BBox.include(pos))
return NULL;
const IQuadNode *node = _Root;
while (node != NULL && !node->isLeaf())
{
nlassert(node->getBBox().include(pos));
uint child;
if (pos.x > node->_XCenter)
child = ((pos.y > node->_YCenter) ? 2 : 1);
else
child = ((pos.y > node->_YCenter) ? 3 : 0);
node = node->getChild(child);
}
return (const CQuadLeaf *)node;
}
void NLPACS::CSurfaceQuadTree::serial(NLMISC::IStream &f)
{
/*
Version 0:
- base version.
*/
(void)f.serialVersion(0);
uint8 childType = 0;
f.serial(_MaxThickness);
f.serial(_MaxLevel);
f.serial(_BBox);
if (f.isReading())
{
CQuadLeaf *leaf;
CQuadBranch *branch;
f.serial(childType);
switch (childType)
{
case CQuadBranch::NoChild:
_Root = NULL;
break;
case CQuadBranch::LeafChild:
leaf = new CQuadLeaf();
_Root = leaf;
leaf->serial(f);
break;
case CQuadBranch::BranchChild:
branch = new CQuadBranch();
_Root = branch;
branch->serial(f);
break;
default:
nlerror("While serializing (read) CQuadBranch: unknown child type");
break;
}
}
else
{
if (_Root == NULL)
{
childType = CQuadBranch::NoChild;
f.serial(childType);
}
else
{
childType = uint8(_Root->isLeaf() ? CQuadBranch::LeafChild : CQuadBranch::BranchChild);
f.serial(childType);
_Root->serial(f);
}
}
}
//
float NLPACS::CSurfaceQuadTree::getInterpZ(const CVector &v) const
{
// first get final leaf for position
CVector pos = CVector(v.x, v.y, 0.0f);
if (_Root == NULL || !_BBox.include(pos))
return v.z; // return unmodified z
const IQuadNode *node = _Root;
vector<uint> children;
vector<const IQuadNode*> nodes;
while (node != NULL && !node->isLeaf())
{
nodes.push_back(node);
nlassert(node->getBBox().include(pos));
uint child;
if (pos.x > node->_XCenter)
child = ((pos.y > node->_YCenter) ? 2 : 1);
else
child = ((pos.y > node->_YCenter) ? 3 : 0);
children.push_back(child);
node = node->getChild(child);
}
if (node == NULL)
return v.z; // return unmodified z
nodes.push_back(node);
vector<const CQuadLeaf*> leaves;
vector<const IQuadNode*> explore;
leaves.push_back((const CQuadLeaf*)node);
// for each side of the leaf, find neighbor leaves
uint side;
for (side=0; side<4; ++side)
{
static const sint ct[4][4] = { {-1, 1, 3,-1}, {-1,-1, 2, 0}, { 1,-1,-1, 3}, { 0, 2,-1,-1} }; // child table
static const sint nt[4][4] = { { 3, 1, 3, 1}, { 2, 0, 2, 0}, { 1, 3, 1, 3}, { 0, 2, 0, 2} }; // neighbor table
sint nlev = (sint)nodes.size()-1;
sint child = -1;
while (nlev > 0)
{
child = ct[children[nlev-1]][side];
if (child >= 0)
break;
--nlev;
}
// neighbor not found in root, leave
if (nlev == 0)
continue;
// get father
node = nodes[nlev-1];
while (nlev < (sint)nodes.size() && node!=NULL && !node->isLeaf())
{
child = nt[children[nlev-1]][side];
node = node->getChild(child);
++nlev;
}
if (node == NULL)
continue;
if (node->isLeaf())
{
leaves.push_back((const CQuadLeaf*)node);
}
else
{
explore.push_back(node);
while (!explore.empty())
{
node = explore.back();
explore.pop_back();
if (node == NULL)
continue;
if (node->isLeaf())
leaves.push_back((const CQuadLeaf*)node);
else
{
explore.push_back(node->getChild((side+2)&3));
explore.push_back(node->getChild((side+3)&3));
}
}
}
}
uint i;
float di, wi;
float sum = 0.0;
float interpZ = 0.0;
for (i=0; i<leaves.size(); ++i)
{
di = (float)sqrt(sqr(v.x-leaves[i]->_XCenter)+sqr(v.y-leaves[i]->_YCenter))*(float)pow(1.5, leaves[i]->_Level);
wi = 1.0f/di;
sum += wi;
interpZ += (leaves[i]->getMinHeight()+leaves[i]->getMaxHeight())*0.5f*wi;
}
return interpZ / sum;
}