// NeL - MMORPG Framework
// 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 .
#include "stdpacs.h"
#include "chain.h"
using namespace std;
using namespace NLMISC;
// Functions for vertices comparison.
// total order relation
static inline bool isStrictlyLess(const CVector &a, const CVector &b)
{
if (a.x < b.x) return true;
if (a.x > b.x) return false;
if (a.y < b.y) return true;
if (a.y > b.y) return false;
if (a.z < b.y) return true;
return false;
}
static inline bool isStrictlyGreater(const CVector &a, const CVector &b)
{
if (a.x > b.x) return true;
if (a.x < b.x) return false;
if (a.y > b.y) return true;
if (a.y < b.y) return false;
if (a.z > b.y) return true;
return false;
}
static inline bool isEqual(const CVector &a, const CVector &b)
{
return (a == b);
}
// COrderedChain3f methods implementation
void NLPACS::COrderedChain3f::serial(NLMISC::IStream &f)
{
/*
Version 0:
- base version.
*/
(void)f.serialVersion(0);
f.serialCont(_Vertices);
f.serial(_Forward);
f.serial(_ParentId);
f.serial(_IndexInParent);
}
// end of COrderedChain3f methods implementation
// COrderedChain methods implementation
// translates the ordered chain by the vector translation
void NLPACS::COrderedChain::translate(const CVector &translation)
{
uint i;
CVector2s translat;
translat.pack(translation);
for (i=0; i<_Vertices.size(); ++i)
_Vertices[i] += translat;
}
//
void NLPACS::COrderedChain::traverse(sint from, sint to, bool forward, vector &path) const
{
sint i;
if (forward)
{
if (from < 0) from = 0;
if (to < 0) to = (sint)_Vertices.size()-1;
for (i=from+1; i<=to; ++i)
path.push_back(_Vertices[i]);
}
else
{
if (from < 0) from = (sint)_Vertices.size()-2;
if (to < 0) to = -1;
for (i=from; i>to; --i)
path.push_back(_Vertices[i]);
}
}
//
float NLPACS::COrderedChain::distance(const CVector &position) const
{
float minDist = 1.0e10f;
uint i;
CVector2f pos = CVector2f(position);
for (i=0; i+1<_Vertices.size(); ++i)
{
CVector2f a = _Vertices[i].unpack(),
b = _Vertices[i+1].unpack();
CVector2f d = (b-a);
float len = d.norm();
d /= len;
CVector2f n = CVector2f(d.y, -d.x);
float l = (pos-a)*d;
float dist;
if (l < 0.0f)
{
dist = (pos-a).norm();
}
else if (l > len)
{
dist = (pos-b).norm();
}
else
{
dist = (float)fabs((pos-a)*n);
}
if (dist < minDist)
{
minDist = dist;
}
}
return minDist;
}
// serializes the ordered chain
void NLPACS::COrderedChain::serial(NLMISC::IStream &f)
{
/*
Version 0:
- base version.
Version 1:
- added _Min and _Max vectors
*/
sint ver= f.serialVersion(1);
f.serialCont(_Vertices);
f.serial(_Forward);
f.serial(_ParentId);
f.serial(_IndexInParent);
f.serial(_Length);
if (ver >= 1)
{
f.serial(_Min, _Max);
}
else if (f.isReading() && !_Vertices.empty())
{
uint i;
_Max = _Min = _Vertices[0];
for (i=1; i<_Vertices.size(); ++i)
{
_Min.minof(_Min, _Vertices[i]);
_Max.maxof(_Max, _Vertices[i]);
}
}
}
// end of COrderedChain methods implementation
// CChain methods implementation
// builds the CChain from a list of vertices and a left and right surfaces id.
// the chains vector is the vector where to store generated ordered chains.
// thisId is the current id of the CChain, and edge is the number of the edge the CChain belongs to (-1
// if none.)
void NLPACS::CChain::make(const vector &vertices, sint32 left, sint32 right, vector &chains, uint16 thisId,
vector &fullChains, vector &useOChainId)
{
sint first = 0, last = 0, i;
_Left = left;
_Right = right;
_Length = 0.0f;
// splits the vertices list in ordered sub chains.
while (first < (sint)vertices.size()-1)
{
last = first+1;
bool forward = isStrictlyLess(vertices[first], vertices[last]);
// first checks if the subchain goes forward or backward.
if (forward)
for (; last < (sint)vertices.size() && isStrictlyLess(vertices[last-1], vertices[last]); ++last)
;
else
for (; last < (sint)vertices.size() && isStrictlyGreater(vertices[last-1], vertices[last]); ++last)
;
--last;
// inserts the new subchain id within the CChain.
uint32 subChainId;
if (useOChainId.empty())
{
subChainId = (uint32)chains.size();
if (subChainId > 65535)
nlerror("in NLPACS::CChain::make(): reached the maximum number of ordered chains");
chains.resize(chains.size()+1);
fullChains.resize(fullChains.size()+1);
}
else
{
subChainId = useOChainId.back();
useOChainId.pop_back();
}
_SubChains.push_back((uint16)subChainId);
// and creates a new COrderedChain
COrderedChain3f &subchain3f = fullChains[subChainId];
subchain3f._Vertices.reserve(last-first+1);
subchain3f._Forward = forward;
subchain3f._ParentId = thisId;
subchain3f._IndexInParent = uint16(_SubChains.size()-1);
// and then copies the vertices (sorted, btw!)
if (forward)
for (i=first; i<=last; ++i)
subchain3f._Vertices.push_back(vertices[i]);
else
for (i=last; i>=first; --i)
subchain3f._Vertices.push_back(vertices[i]);
first = last;
COrderedChain &subchain = chains[subChainId];
subchain.pack(subchain3f);
subchain.computeMinMax();
float length = 0.0f;
for (i=0; i<(sint)subchain._Vertices.size()-1; ++i)
length += (subchain._Vertices[i+1]-subchain._Vertices[i]).norm();
subchain._Length = length;
_Length += length;
}
}
// serializes the CChain
void NLPACS::CChain::serial(NLMISC::IStream &f)
{
/*
Version 0:
- base version.
*/
(void)f.serialVersion(0);
f.serialCont(_SubChains);
f.serial(_Left, _Right);
f.serial(_StartTip, _StopTip);
f.serial(_Length);
f.serial(_LeftLoop, _LeftLoopIndex);
f.serial(_RightLoop, _RightLoopIndex);
}
// unifiies the chain
void NLPACS::CChain::unify(vector &ochains)
{
CVector2s snap;
uint i;
snap = (ochains[_SubChains[0]].isForward()) ? ochains[_SubChains[0]]._Vertices.back() : ochains[_SubChains[0]]._Vertices.front();
for (i=1; i<_SubChains.size(); ++i)
{
if (ochains[_SubChains[i]].isForward())
{
if (ochains[_SubChains[i]]._Vertices.front() != snap)
nlwarning("ochain %d and %d are not stuck together", _SubChains[i-1], _SubChains[i]);
ochains[_SubChains[i]]._Vertices.front() = snap;
snap = ochains[_SubChains[i]]._Vertices.back();
}
else
{
if (ochains[_SubChains[i]]._Vertices.back() != snap)
nlwarning("ochain %d and %d are not stuck together", _SubChains[i-1], _SubChains[i]);
ochains[_SubChains[i]]._Vertices.back() = snap;
snap = ochains[_SubChains[i]]._Vertices.front();
}
}
}
//
void NLPACS::CChain::setStartVector(const NLPACS::CVector2s &v, vector &ochains)
{
if (ochains[_SubChains.front()].isForward())
ochains[_SubChains.front()]._Vertices.front() = v;
else
ochains[_SubChains.front()]._Vertices.back() = v;
}
//
void NLPACS::CChain::setStopVector(const NLPACS::CVector2s &v, vector &ochains)
{
if (ochains[_SubChains.back()].isForward())
ochains[_SubChains.back()]._Vertices.back() = v;
else
ochains[_SubChains.back()]._Vertices.front() = v;
}
//
NLPACS::CVector2s NLPACS::CChain::getStartVector(vector &ochains)
{
if (ochains[_SubChains.front()].isForward())
return ochains[_SubChains.front()]._Vertices.front();
else
return ochains[_SubChains.front()]._Vertices.back();
}
//
NLPACS::CVector2s NLPACS::CChain::getStopVector(vector &ochains)
{
if (ochains[_SubChains.back()].isForward())
return ochains[_SubChains.back()]._Vertices.back();
else
return ochains[_SubChains.back()]._Vertices.front();
}
// end of CChain methods implementation