2012-05-29 13:31:11 +00:00
// 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 <map>
# include <vector>
2012-10-28 10:55:21 +00:00
# include "nel/pacs/collision_mesh_build.h"
# include "nel/pacs/local_retriever.h"
# include "nel/pacs/exterior_mesh.h"
2012-05-29 13:31:11 +00:00
# include "mouline.h"
# include "build_surfaces.h"
using namespace std ;
using namespace NLMISC ;
using namespace NLPACS ;
/*
// a reference on an edge
struct CEdgeKey
{
uint32 V0 ;
uint32 V1 ;
CEdgeKey ( ) { }
CEdgeKey ( uint32 v0 , uint32 v1 ) : V0 ( v0 ) , V1 ( v1 ) { }
bool operator ( ) ( const CEdgeKey & a , const CEdgeKey & b )
{
return a . V0 < b . V0 | | ( a . V0 = = b . V0 & & a . V1 < b . V1 ) ;
}
} ;
// the info on an edge
struct CEdgeInfo
{
sint32 Left , LeftEdge ;
sint32 Right , RightEdge ;
CEdgeInfo ( sint32 left = - 1 , sint32 leftEdge = - 1 , sint32 right = - 1 , sint32 rightEdge = - 1 ) : Left ( left ) , LeftEdge ( leftEdge ) , Right ( right ) , RightEdge ( rightEdge ) { }
} ;
typedef map < CEdgeKey , CEdgeInfo , CEdgeKey > TLinkRelloc ;
typedef TLinkRelloc : : iterator ItTLinkRelloc ;
//
void linkMesh ( CCollisionMeshBuild & cmb , bool linkInterior )
{
uint i , j ;
TLinkRelloc relloc ;
// check each edge of each face
for ( i = 0 ; i < cmb . Faces . size ( ) ; + + i )
{
if ( cmb . Faces [ i ] . Surface = = CCollisionFace : : ExteriorSurface & & linkInterior | |
cmb . Faces [ i ] . Surface > = CCollisionFace : : InteriorSurfaceFirst & & ! linkInterior )
continue ;
for ( j = 0 ; j < 3 ; + + j )
{
cmb . Faces [ i ] . Edge [ j ] = - 1 ;
uint edge = ( j + 2 ) % 3 ;
uint32 va = cmb . Faces [ i ] . V [ j ] ,
vb = cmb . Faces [ i ] . V [ ( j + 1 ) % 3 ] ;
ItTLinkRelloc it ;
if ( ( it = relloc . find ( CEdgeKey ( va , vb ) ) ) ! = relloc . end ( ) )
{
// in this case, the left triangle of the edge has already been found.
// should throw an error
nlerror ( " On face %d, edge %d: left side of edge (%d,%d) already linked to face %d " ,
i , edge , va , vb , ( * it ) . second . Left ) ;
}
else if ( ( it = relloc . find ( CEdgeKey ( vb , va ) ) ) ! = relloc . end ( ) )
{
// in this case, we must check the right face has been set yet
if ( ( * it ) . second . Right ! = - 1 )
{
nlerror ( " On face %d, edge %d: right side of edge (%d,%d) already linked to face %d " ,
i , edge , vb , va , ( * it ) . second . Right ) ;
}
( * it ) . second . Right = i ;
( * it ) . second . RightEdge = edge ;
}
else
{
// if the edge wasn't present yet, create it and set it up.
relloc . insert ( make_pair ( CEdgeKey ( va , vb ) , CEdgeInfo ( i , edge , - 1 , - 1 ) ) ) ;
}
}
}
// for each checked edge, update the edge info inside the faces
ItTLinkRelloc it ;
for ( it = relloc . begin ( ) ; it ! = relloc . end ( ) ; + + it )
{
sint32 left , leftEdge ;
sint32 right , rightEdge ;
// get the link info on the edge
left = ( * it ) . second . Left ;
leftEdge = ( * it ) . second . LeftEdge ;
right = ( * it ) . second . Right ;
rightEdge = ( * it ) . second . RightEdge ;
// update both faces
if ( left ! = - 1 )
cmb . Faces [ left ] . Edge [ leftEdge ] = right ;
if ( right ! = - 1 )
cmb . Faces [ right ] . Edge [ rightEdge ] = left ;
}
}
*/
void buildExteriorMesh ( CCollisionMeshBuild & cmb , CExteriorMesh & em )
{
uint startFace = 0 ;
vector < CExteriorMesh : : CEdge > edges ;
uint i ;
for ( i = 0 ; i < cmb . Faces . size ( ) ; + + i )
{
cmb . Faces [ i ] . EdgeFlags [ 0 ] = false ;
cmb . Faces [ i ] . EdgeFlags [ 1 ] = false ;
cmb . Faces [ i ] . EdgeFlags [ 2 ] = false ;
}
while ( true )
{
// find the first non interior face
uint i , edge ;
bool found = false ;
for ( i = startFace ; i < cmb . Faces . size ( ) & & ! found ; + + i )
{
if ( cmb . Faces [ i ] . Surface ! = CCollisionFace : : ExteriorSurface )
continue ;
for ( edge = 0 ; edge < 3 & & ! found ; + + edge )
if ( cmb . Faces [ i ] . Edge [ edge ] = = - 1 & & ! cmb . Faces [ i ] . EdgeFlags [ edge ] )
{
// nlassert(cmb.Faces[i].Material != 0xdeadbeef);
found = true ;
break ;
}
if ( found )
break ;
}
//
if ( ! found )
break ;
// cmb.Faces[i].Material = 0xdeadbeef;
startFace = i + 1 ;
sint32 current = i ;
sint32 next = cmb . Faces [ current ] . Edge [ edge ] ;
sint oedge ;
sint pivot = ( edge + 1 ) % 3 ;
sint nextEdge = edge ;
bool allowThis = true ;
uint numLink = 0 ;
uint firstEdge = ( uint ) edges . size ( ) ;
vector < CExteriorMesh : : CEdge > loop ;
while ( true )
{
if ( cmb . Faces [ current ] . EdgeFlags [ nextEdge ] )
{
// if reaches the end of the border, then quits.
break ;
}
else if ( next = = - 1 )
{
// if the next edge belongs to the border, then go on the same element
cmb . Faces [ current ] . EdgeFlags [ nextEdge ] = true ;
/// \todo get the real edge link
sint link = ( cmb . Faces [ current ] . Visibility [ nextEdge ] ) ? - 1 : 0 ; //(numLink++);
//edges.push_back(CExteriorMesh::CEdge(cmb.Vertices[cmb.Faces[current].V[pivot]], link));
loop . push_back ( CExteriorMesh : : CEdge ( cmb . Vertices [ cmb . Faces [ current ] . V [ pivot ] ] , link ) ) ;
pivot = ( pivot + 1 ) % 3 ;
nextEdge = ( nextEdge + 1 ) % 3 ;
next = cmb . Faces [ current ] . Edge [ nextEdge ] ;
}
else
{
// if the next element is inside the surface, then go to the next element
for ( oedge = 0 ; oedge < 3 & & cmb . Faces [ next ] . Edge [ oedge ] ! = current ; + + oedge )
;
nlassert ( oedge ! = 3 ) ;
current = next ;
pivot = ( oedge + 2 ) % 3 ;
nextEdge = ( oedge + 1 ) % 3 ;
next = cmb . Faces [ current ] . Edge [ nextEdge ] ;
}
}
// mark the end of a ext mesh block
// this way, collisions won't be checked in the pacs engine
if ( loop . size ( ) > = 3 )
{
uint n = ( uint ) loop . size ( ) ;
while ( loop . front ( ) . Link > = 0 & & loop . back ( ) . Link > = 0 & & n > 0 )
{
loop . push_back ( loop . front ( ) ) ;
loop . erase ( loop . begin ( ) ) ;
- - n ;
}
}
loop . push_back ( loop . front ( ) ) ;
loop . back ( ) . Link = - 2 ;
edges . insert ( edges . end ( ) , loop . begin ( ) , loop . end ( ) ) ;
//edges.push_back(edges[firstEdge]);
//edges.back().Link = -2;
}
bool previousWasLink = false ;
sint previousLink = - 1 ;
for ( i = 0 ; i < edges . size ( ) ; + + i )
{
// nldebug("ext-mesh: vertex=%d (%.2f,%.2f,%.2f) link=%d", i, edges[i].Start.x, edges[i].Start.y, edges[i].Start.z, edges[i].Link);
if ( edges [ i ] . Link > = 0 )
{
if ( ! previousWasLink )
+ + previousLink ;
edges [ i ] . Link = previousLink ;
previousWasLink = true ;
}
else
{
previousWasLink = false ;
}
}
em . setEdges ( edges ) ;
}
//
void linkExteriorToInterior ( CLocalRetriever & lr )
{
CExteriorMesh em = lr . getExteriorMesh ( ) ;
vector < CExteriorMesh : : CEdge > edges = em . getEdges ( ) ;
vector < CExteriorMesh : : CLink > links ;
const vector < CChain > & chains = lr . getChains ( ) ;
const vector < COrderedChain3f > & ochains = lr . getFullOrderedChains ( ) ;
const vector < uint16 > & bchains = lr . getBorderChains ( ) ;
{
uint i ;
nlinfo ( " Border chains (to be linked) for this retriever: " ) ;
for ( i = 0 ; i < bchains . size ( ) ; + + i )
{
static char buf [ 512 ] , w [ 256 ] ;
const CChain & chain = chains [ bchains [ i ] ] ;
sprintf ( buf , " Border chain %d: chain=%d " , i , bchains [ i ] ) ;
uint och ;
for ( och = 0 ; och < chain . getSubChains ( ) . size ( ) ; + + och )
{
const COrderedChain3f & ochain = ochains [ chain . getSubChain ( och ) ] ;
sprintf ( w , " subchain=%d " , chain . getSubChain ( och ) ) ;
strcat ( buf , w ) ;
uint v ;
for ( v = 0 ; v < ochain . getVertices ( ) . size ( ) ; + + v )
{
sprintf ( w , " (%.2f,%.2f) " , ochain [ v ] . x , ochain [ v ] . y ) ;
strcat ( buf , w ) ;
}
}
nlinfo ( " %s " , buf ) ;
}
}
uint edge , ch ;
for ( edge = 0 ; edge + 1 < edges . size ( ) ; )
{
if ( edges [ edge ] . Link = = - 1 | | edges [ edge ] . Link = = - 2 )
{
+ + edge ;
continue ;
}
uint startedge = edge ;
uint stopedge ;
for ( stopedge = edge ; stopedge + 1 < edges . size ( ) & & edges [ stopedge + 1 ] . Link = = edges [ startedge ] . Link ; + + stopedge )
;
edge = stopedge + 1 ;
CVector start = edges [ startedge ] . Start , stop = edges [ stopedge + 1 ] . Start ;
bool found = false ;
for ( ch = 0 ; ch < bchains . size ( ) & & ! found ; + + ch )
{
// get the border chain.
//const CChain &chain = chains[bchains[ch]];
const CVector & cstart = lr . getStartVector ( bchains [ ch ] ) ,
& cstop = lr . getStopVector ( bchains [ ch ] ) ;
float d = ( start - cstart ) . norm ( ) + ( stop - cstop ) . norm ( ) ;
if ( d < 1.0e-1 f )
{
found = true ;
break ;
}
}
// create a link
CExteriorMesh : : CLink link ;
if ( ! found )
{
nlwarning ( " in linkInteriorToExterior(): " ) ;
nlwarning ( " couldn't find any link to the exterior edge %d-%d!! " , startedge , stopedge ) ;
}
else
{
// set it up to point on the chain and surface
link . BorderChainId = ch ;
link . ChainId = bchains [ ch ] ;
link . SurfaceId = ( uint16 ) chains [ link . ChainId ] . getLeft ( ) ;
}
// enlarge the links
if ( edges [ startedge ] . Link > = ( sint ) links . size ( ) )
links . resize ( edges [ startedge ] . Link + 1 ) ;
// if the link already exists, warning
if ( links [ edges [ startedge ] . Link ] . BorderChainId ! = 0xFFFF | |
links [ edges [ startedge ] . Link ] . ChainId ! = 0xFFFF | |
links [ edges [ startedge ] . Link ] . SurfaceId ! = 0xFFFF )
{
nlwarning ( " in linkInteriorToExterior(): " ) ;
nlwarning ( " link %d already set!! " , edges [ startedge ] . Link ) ;
}
// setup the link
links [ edges [ startedge ] . Link ] = link ;
}
// em.setEdges(edges);
em . setLinks ( links ) ;
lr . setExteriorMesh ( em ) ;
}
//
void computeRetriever ( CCollisionMeshBuild & cmb , CLocalRetriever & lr , CVector & translation , bool useCmbTrivialTranslation )
{
// set the retriever
lr . setType ( CLocalRetriever : : Interior ) ;
// if should use the own cmb bbox, then compute it
if ( useCmbTrivialTranslation )
{
translation = cmb . computeTrivialTranslation ( ) ;
// snap the translation vector to a meter wide grid
translation . x = ( float ) ceil ( translation . x ) ;
translation . y = ( float ) ceil ( translation . y ) ;
translation . z = 0.0f ;
}
uint i , j ;
for ( i = 0 ; i < cmb . Faces . size ( ) ; + + i )
{
CVector normal = ( ( cmb . Vertices [ cmb . Faces [ i ] . V [ 1 ] ] - cmb . Vertices [ cmb . Faces [ i ] . V [ 0 ] ] ) ^ ( cmb . Vertices [ cmb . Faces [ i ] . V [ 2 ] ] - cmb . Vertices [ cmb . Faces [ i ] . V [ 0 ] ] ) ) . normed ( ) ;
if ( normal . z < 0.0f )
{
nlwarning ( " Face %d in cmb (%s) has negative normal! -- face is flipped " , i , cmb . Faces [ i ] . Surface = = CCollisionFace : : InteriorSurfaceFirst ? " interior " : " exterior " ) ;
/*
std : : swap ( cmb . Faces [ i ] . V [ 1 ] , cmb . Faces [ i ] . V [ 2 ] ) ;
std : : swap ( cmb . Faces [ i ] . Visibility [ 1 ] , cmb . Faces [ i ] . Visibility [ 2 ] ) ;
*/
}
}
// first link faces
/*
linkMesh ( cmb , false ) ;
linkMesh ( cmb , true ) ;
*/
vector < string > errors ;
c mb . link ( false , errors ) ;
cmb . link ( true , errors ) ;
if ( ! errors . empty ( ) )
{
nlwarning ( " Edge issues reported !! " ) ;
uint i ;
for ( i = 0 ; i < errors . size ( ) ; + + i )
nlwarning ( " %s " , errors [ i ] . c_str ( ) ) ;
nlerror ( " Can't continue. " ) ;
}
// translate the meshbuild to the local axis
cmb . translate ( translation ) ;
// find the exterior mesh border
CExteriorMesh extMesh ;
buildExteriorMesh ( cmb , extMesh ) ;
lr . setExteriorMesh ( extMesh ) ;
// build the surfaces in the local retriever
buildSurfaces ( cmb , lr ) ;
// create the snapping faces and vertices
// after the build surfaces because the InternalSurfaceId is filled within buildSurfaces()...
buildSnapping ( cmb , lr ) ;
//
lr . computeLoopsAndTips ( ) ;
lr . findBorderChains ( ) ;
lr . updateChainIds ( ) ;
lr . computeTopologies ( ) ;
lr . unify ( ) ;
lr . computeCollisionChainQuad ( ) ;
/*
//
for ( i = 0 ; i < lr . getSurfaces ( ) . size ( ) ; + + i )
lr . dumpSurface ( i ) ;
*/
//
linkExteriorToInterior ( lr ) ;
// compute the bbox of the retriever
CAABBox bbox ;
bool first = true ;
for ( i = 0 ; i < extMesh . getEdges ( ) . size ( ) ; + + i )
if ( ! first )
bbox . extend ( extMesh . getEdge ( i ) . Start ) ;
else
bbox . setCenter ( extMesh . getEdge ( i ) . Start ) , first = false ;
for ( i = 0 ; i < lr . getOrderedChains ( ) . size ( ) ; + + i )
for ( j = 0 ; j < lr . getOrderedChain ( i ) . getVertices ( ) . size ( ) ; + + j )
if ( ! first )
bbox . extend ( lr . getOrderedChain ( i ) [ j ] . unpack3f ( ) ) ;
else
bbox . setCenter ( lr . getOrderedChain ( i ) [ j ] . unpack3f ( ) ) , first = false ;
CVector bboxhs = bbox . getHalfSize ( ) ;
bboxhs . z = 10000.0f ;
bbox . setHalfSize ( bboxhs ) ;
lr . setBBox ( bbox ) ;
}