// 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 "edge_quad.h"
#include "global_retriever.h"
using namespace std;
using namespace NLMISC;
namespace NLPACS
{
// ***************************************************************************
const float CEdgeQuad::_QuadElementSize= 4; // = 4 meters.
// ***************************************************************************
CEdgeQuad::CEdgeQuad()
{
_QuadData= NULL;
_QuadDataLen= 0;
}
// ***************************************************************************
CEdgeQuad::~CEdgeQuad()
{
clear();
}
// ***************************************************************************
CEdgeQuad::CEdgeQuad(const CEdgeQuad &o)
{
_QuadData= NULL;
_QuadDataLen= 0;
*this= o;
}
// ***************************************************************************
CEdgeQuad &CEdgeQuad::operator=(const CEdgeQuad &o)
{
// Alloc good quaddata.
_QuadDataLen= o._QuadDataLen;
delete [] _QuadData;
if(_QuadDataLen>0)
{
_QuadData= (uint8*)new uint8[_QuadDataLen];
// copy contents.
memcpy(_QuadData, o._QuadData, _QuadDataLen);
}
else
_QuadData= NULL;
// copy infos.
_Width= o._Width;
_Height= o._Height;
_X= o._X;
_Y= o._Y;
_EdgeEntries = o._EdgeEntries;
// copy good pointers.
_Quad.clear();
_Quad.resize(o._Quad.size(), NULL);
for(sint i=0; i<(sint)_Quad.size(); i++)
{
if(o._Quad[i])
{
uint32 off= (uint32)(o._Quad[i]-o._QuadData);
_Quad[i]= _QuadData+off;
}
}
return *this;
}
// ***************************************************************************
void CEdgeQuad::clear()
{
delete [] _QuadData;
_QuadData= NULL;
_QuadDataLen= 0;
_Quad.clear();
_EdgeEntries.clear();
_Width = 0;
_Height = 0;
_X = 0;
_Y = 0;
}
// ***************************************************************************
void CEdgeQuad::getGridBounds(sint32 &x0, sint32 &y0, sint32 &x1, sint32 &y1, const CVector &minP, const CVector &maxP) const
{
x0= (sint32)floor(minP.x / _QuadElementSize) - _X;
y0= (sint32)floor(minP.y / _QuadElementSize) - _Y;
x1= (sint32) ceil(maxP.x / _QuadElementSize) - _X;
y1= (sint32) ceil(maxP.y / _QuadElementSize) - _Y;
x0= max(x0, (sint32)0);
y0= max(y0, (sint32)0);
x1= min(x1, (sint32)_Width);
y1= min(y1, (sint32)_Height);
}
// ***************************************************************************
void CEdgeQuad::build(const CExteriorMesh &em,
const CGlobalRetriever &global,
CCollisionSurfaceTemp &cst,
uint32 thisInstance)
{
const std::vector &edges = em.getEdges();
vector< list > tempQuad;
sint i, j;
// first, clear any pr-build.
contReset(_Quad);
delete [] _QuadData;
_QuadData= NULL;
_QuadDataLen= 0;
// don't care about the origin of the instance
CVector origin = global.getInstance(thisInstance).getOrigin();
// 0. Find BBox of the grid. Allocate grid.
//=========================================
bool first=true;
CAABBox chainquadBBox;
// run all chains.
for (i=0; i<(sint)edges.size()-1; i++)
{
// enlarge bbox.
if (first)
first= false, chainquadBBox.setCenter(edges[i].Start);
else
chainquadBBox.extend(edges[i].Start);
}
// compute X,Y,Width, Height.
_X= (sint32)floor(chainquadBBox.getMin().x / _QuadElementSize);
_Y= (sint32)floor(chainquadBBox.getMin().y / _QuadElementSize);
_Width= (sint32)ceil(chainquadBBox.getMax().x / _QuadElementSize) - _X;
_Height= (sint32)ceil(chainquadBBox.getMax().y / _QuadElementSize) - _Y;
tempQuad.resize(_Width*_Height);
_Quad.resize(_Width*_Height, NULL);
// 1. For each edge, add them to the quadgrid.
//=========================================
// run all chains.
for (i=0; i<(sint)edges.size()-1; i++)
{
if (edges[i].Link == -2)
continue;
float dnorm = (edges[i+1].Start-edges[i].Start).norm();
uint numStep = (uint)(dnorm/0.1f)+1;
uint step;
CVector pbegin = edges[i].Start+origin,
pend = edges[i+1].Start+origin;
CVector opbegin = edges[i].Start,
opend = edges[i+1].Start;
for (step=0; step 0)
{
nlwarning ("In NLPACS::CEdgeQuad::build()");
nlwarning ("ERROR: exterior edge %d with interior link crosses some surfaces", i);
cd.clear ();
}
// add start surface to the collision description
CCollisionSurfaceDesc stcd;
stcd.ContactTime = 0.0f;
stcd.ContactSurface.RetrieverInstanceId = gp0.InstanceId;
stcd.ContactSurface.SurfaceId = gp0.LocalPosition.Surface;
cd.insert(cd.begin(), stcd);
// get the surface, chain ...
sint edgeId = i;
uint16 chainId;
CSurfaceIdent interior;
if (edges[i].Link == -1)
{
interior.RetrieverInstanceId = -1;
interior.SurfaceId = -1;
chainId = 0xFFFF;
}
else
{
interior.RetrieverInstanceId = thisInstance;
interior.SurfaceId = em.getLink(edges[i].Link).SurfaceId;
chainId = em.getLink(edges[i].Link).ChainId;
}
// add end point to the collision description
stcd = cd.back();
stcd.ContactTime = 1.0f;
cd.push_back(stcd);
for (j=0; j<(sint)cd.size()-1; ++j)
{
s0 = op0*(float)(1.0-cd[j].ContactTime) + op1*(float)(cd[j].ContactTime);
s1 = op0*(float)(1.0-cd[j+1].ContactTime) + op1*(float)(cd[j+1].ContactTime);
mins.minof(s0, s1);
maxs.maxof(s0, s1);
// PrecisionPb: extend a little this edge. This is important for special case like borders on zones.
if(mins.x-maxs.x==0)
mins.x-=0.001f, maxs.x+=0.001f;
if(mins.y-maxs.y==0)
mins.y-=0.001f, maxs.y+=0.001f;
// get bounding coordinate of this edge in the quadgrid.
sint32 x0, y0, x1, y1;
sint x, y;
getGridBounds(x0, y0, x1, y1, mins, maxs);
CSurfaceIdent exterior = cd[j].ContactSurface;
uint entry;
for (entry=0; entry<_EdgeEntries.size(); ++entry)
{
if (_EdgeEntries[entry].EdgeId == edgeId &&
_EdgeEntries[entry].Exterior == exterior)
{
if (_EdgeEntries[entry].ChainId != chainId ||
_EdgeEntries[entry].Interior != interior)
{
nlwarning("In NLPACS::CEdgeQuad::build()");
nlerror("exterior edge %d has different interior linkage", edgeId);
}
break;
}
}
// if this entry didn't exist before create a new one...
if (entry == _EdgeEntries.size())
{
_EdgeEntries.push_back(CExteriorEdgeEntry());
_EdgeEntries.back().EdgeId = uint16(edgeId);
_EdgeEntries.back().ChainId = chainId;
_EdgeEntries.back().Interior = interior;
_EdgeEntries.back().Exterior = exterior;
}
// add this edge to all the quadnode it touches.
for(y=y0; y::iterator it;
for (it=tempQuad[y*_Width+x].begin(); it!=tempQuad[y*_Width+x].end(); ++it)
if (entry == *it)
break;
if (it == tempQuad[y*_Width+x].end())
tempQuad[y*_Width+x].push_back(uint16(entry));
}
}
}
}
}
nlinfo("Built ExteriorEdgeQuad, linked following doors:");
for (i=0; i<(sint)_EdgeEntries.size(); ++i)
{
if (edges[_EdgeEntries[i].EdgeId].Link != -1 &&
(_EdgeEntries[i].Interior.RetrieverInstanceId == -1 || _EdgeEntries[i].Interior.SurfaceId == -1 ||
_EdgeEntries[i].Exterior.RetrieverInstanceId == -1 || _EdgeEntries[i].Exterior.SurfaceId == -1))
{
nlwarning("In NLPACS::CEdgeQuad::build(): exterior door %d has corrupted link", i);
}
else if (edges[_EdgeEntries[i].EdgeId].Link != -1)
{
nlinfo("Inst=%d ExtEdge=%d IntInst=%d IntSurf=%d IntChain=%d ExtInst=%d ExtSurf=%d", thisInstance, _EdgeEntries[i].EdgeId,
_EdgeEntries[i].Interior.RetrieverInstanceId, _EdgeEntries[i].Interior.SurfaceId, _EdgeEntries[i].ChainId,
_EdgeEntries[i].Exterior.RetrieverInstanceId, _EdgeEntries[i].Exterior.SurfaceId);
}
}
// 2. Mem optimisation: Use only 1 block for ALL quads of the grid.
//=========================================
sint memSize= 0;
// run all quads.
for(i=0;i<(sint)tempQuad.size();i++)
{
list &quadNode= tempQuad[i];
if(!quadNode.empty())
{
// add an entry for Len.
memSize+= sizeof(uint16);
// add N entry of CEdgeChainEntry.
memSize+= quadNode.size()*sizeof(uint16);
}
}
// allocate.
_QuadData= (uint8*)new uint8[memSize];
_QuadDataLen= memSize;
// 3. Fill _QuadData with lists.
//=========================================
uint8 *ptr= _QuadData;
for(i=0;i<(sint)tempQuad.size();i++)
{
list &srcQuadNode= tempQuad[i];
list::iterator it;
if(!srcQuadNode.empty())
{
_Quad[i]= ptr;
// write len.
uint16 len= uint16(srcQuadNode.size());
*((uint16*)ptr)= len;
ptr+= sizeof(uint16);
// add entries.
it= srcQuadNode.begin();
for(j=0; j maxx || end.x < minx || start.y > maxy || end.y < miny)
return nRes;
if (start.x < minx)
{
start.y = start.y+(end.y-start.y)*(minx-start.x)/(end.x-start.x);
start.x = minx;
}
if (start.y < miny)
{
start.x = start.x+(end.x-start.x)*(miny-start.y)/(end.y-start.y);
start.y = miny;
}
if (end.x > maxx)
{
end.y = start.y+(end.y-start.y)*(minx-start.x)/(end.x-start.x);
end.x = maxx;
}
if (end.y > maxy)
{
end.x = start.x+(end.x-start.x)*(miny-start.y)/(end.y-start.y);
end.y = maxy;
}
sint32 x0, x1, ya, yb;
sint x, y;
float fx, fxa, fxb, fya, fyb;
x0 = (sint32)floor(start.x / _QuadElementSize) - _X;
x1 = (sint32)ceil(end.x / _QuadElementSize) - _X;
fx = (x0+_X)*_QuadElementSize;
for (x=x0; x end.x) ? end.x : fx+_QuadElementSize;
fya = start.y+(end.y-start.y)*(fxa-start.x)/(end.x-start.x);
fyb = start.y+(end.y-start.y)*(fxb-start.x)/(end.x-start.x);
if (fya > fyb)
swap (fya, fyb);
ya = (sint32)floor(fya / _QuadElementSize) - _Y;
yb = (sint32)ceil(fyb / _QuadElementSize) - _Y;
fx += _QuadElementSize;
for (y=ya; y0)
_QuadData= (uint8*)new uint8[_QuadDataLen];
else
_QuadData= NULL;
}
// Since we have only uint16 (see CEdgeChainEntry), serial them in a single block.
uint16 *ptrQData= (uint16*)_QuadData;
for(i=0;i<_QuadDataLen/2; i++, ptrQData++)
{
f.serial(*ptrQData);
}
// serial _Quad.
std::vector offsets;
uint32 len;
uint32 val;
if(f.isReading())
{
// len/resize.
f.serial(len);
offsets.resize(len);
contReset(_Quad);
_Quad.resize(len);
// read offsets -> ptrs.
for(i=0; i