Further work on path finding
Since my last update I have tried to get recast callable from within blitz to make it available for everyone. However I have failed as I have not got enough Blitz/C++/DLL skills. I'm hoping someone will offer some help.
I was unhappy how I handled dynamic movable objects as there was a chance the steering wouldn't avoid hitting them. Because of this I rewrote my interface to recast to use a tiled nav mesh, this means I could dynamically update a tile if a object moved in/out of a tile.
The regeneration of a tile could take upto 20ms which would cause nasty frame rate spikes so I had to move the processing and pathfinding etc into a different thread. This has done the trick and I now get smooth frame rates even when causing lots of tile regenerations.
I found out while doing this however that Leadwerks doesn't like threading, if ANY LW commands is issued while a thread is doings a LW command the program crashes. This mean't I had to implement mutex locks around LW commands.
Here's the code for handling the navmesh requests:-
#include "leadcast/include/navmesh.h"
#include <time.h>
#include "gamelib.h"
#define DEBUG_PATH
NavMesh::NavMesh() :
m_keepInterResults(false),
m_tileMesh(NULL),
m_chunkyMesh(0),
m_triflags(0),
m_solid(0),
m_chf(0),
m_cset(0),
m_pmesh(0),
m_dmesh(0),
m_tileSize(32),
m_tileTriCount(0),
m_leadMesh(NULL),
m_showMesh(false),
m_stoprequested(false)
{
resetCommonSettings();
memset(m_tileBmin, 0, sizeof(m_tileBmin));
memset(m_tileBmax, 0, sizeof(m_tileBmax));
m_polyPickExt[0] = 2;
m_polyPickExt[1] = 4;
m_polyPickExt[2] = 2;
}
static void getPolyCenter(dtTiledNavMesh* navMesh, dtTilePolyRef ref, float* center)
{
const dtTilePoly* p = navMesh->getPolyByRef(ref);
if (!p) return;
const float* verts = navMesh->getPolyVertsByRef(ref);
center[0] = 0;
center[1] = 0;
center[2] = 0;
for (int i = 0; i < (int)p->nv; ++i)
{
const float* v = &verts[p->v[i]*3];
center[0] += v[0];
center[1] += v[1];
center[2] += v[2];
}
const float s = 1.0f / p->nv;
center[0] *= s;
center[1] *= s;
center[2] *= s;
}
void NavMesh::makeTileMesh(const dtTileHeader* header)
{
TMaterial material;
for (int i = 0; i < header->npolys; ++i)
{
const dtTilePoly* p = &header->polys[i];
const dtTilePolyDetail* pd = &header->dmeshes[i];
int vp;
material = CreateMaterial();
SetMaterialColor( material , Vec4( float(rand()) / float(RAND_MAX), float(rand()) / float(RAND_MAX), float(rand()) / float(RAND_MAX), 1) );
TSurface surface = CreateSurface(m_leadMesh, material);
int vidx = 0;
for (int j = 0; j < pd->ntris; ++j)
{
const unsigned char* t = &header->dtris[(pd->tbase+j)*4];
for (int k = 0; k < 3; ++k)
{
if (t[k] < p->nv)
{
vp = p->v[t[k]]*3;
AddVertex( surface, Vec3( header->verts[ vp ]*-1, header->verts[ vp+1], header->verts[ vp+2]),Vec3(0,0,1) );
}
else
{
vp = (pd->vbase+t[k]-p->nv)*3;
AddVertex( surface, Vec3( header->dverts[ vp ]*-1, header->dverts[ vp+1], header->dverts[ vp+2]),Vec3(0,0,1) );
}
}
AddTriangle( surface, vidx+1, vidx, vidx+2 );
vidx +=3;
}
}
}
void NavMesh::makeTiledNavMesh(const dtTiledNavMesh* mesh)
{
if (!mesh) return;
if (m_leadMesh)
{
FreeEntity(m_leadMesh);
}
m_leadMesh = CreateMesh(NULL);
for (int i = 0; i < DT_MAX_TILES; ++i)
{
const dtTile* tile = mesh->getTile(i);
if (!tile->header) continue;
makeTileMesh(tile->header);
}
// FlipMesh(m_leadMesh);
UpdateMesh( m_leadMesh );
}
void NavMesh::buildAllTiles()
{
const float ts = m_tileSize*m_cellSize;
float x,y,z;
float sTime = clock();
char buf[256];
y = m_bmin[1];
for(x = m_bmin[0]-ts; x<=(m_bmax[0]+ts);x+=ts)
{
for(z = m_bmin[2]-ts; z<=(m_bmax[2]+ts);z+=ts)
{
buildTile( Vec3(x,y,z) );
}
}
sprintf(buf, "Time %f", clock() - sTime);
AppLogMode(1);
AppLog(buf);
}
bool NavMesh::init()
{
if (!m_verts || !m_tris)
{
printf("No verts or tris\n");
return false;
}
// delete m_navMesh;
m_tileMesh = NULL;
m_tileMesh = new dtTiledNavMesh;
if (!m_tileMesh)
{
AppLog("Could not allocate navmehs");
return false;
}
if (!m_tileMesh->init(m_bmin, m_tileSize*m_cellSize, m_agentMaxClimb*m_cellHeight))
{
AppLog("Could not init navmesh");
return false;
}
// Build chunky mesh.
// delete m_chunkyMesh;
m_chunkyMesh = new rcChunkyTriMesh;
if (!m_chunkyMesh)
{
AppLog("buildTiledNavigation: Out of memory 'm_chunkyMesh'.");
return false;
}
if (!rcCreateChunkyTriMesh(m_verts, m_tris, m_ntris, 256, m_chunkyMesh))
{
AppLog("buildTiledNavigation: Could not build chunky mesh.");
return false;
}
return true;
}
void NavMesh::buildTile(const TVec3 pos)
{
if (!m_tileMesh)
return;
const float ts = m_tileSize*m_cellSize;
int tx = (int)floorf((pos.X-m_bmin[0]) / ts);
int ty = (int)floorf((pos.Z-m_bmin[2]) / ts);
if (tx < 0 || ty < 0)
return;
rcMeshLoaderSBX *loader = new rcMeshLoaderSBX;
m_tileBmin[0] = m_bmin[0] + tx*ts;
m_tileBmin[1] = m_bmin[1];
m_tileBmin[2] = m_bmin[2] + ty*ts;
m_tileBmax[0] = m_bmin[0] + (tx+1)*ts;
m_tileBmax[1] = m_bmax[1];
m_tileBmax[2] = m_bmin[2] + (ty+1)*ts;
{
boost::mutex::scoped_lock l(m_mutex);
TVec6 box;
box.X1 = m_tileBmin[0]*-1;
box.Y0 = m_tileBmin[1];
box.Z0 = m_tileBmin[2];
box.X0 = m_tileBmax[0]*-1;
box.Y1 = m_tileBmax[1];
box.Z1 = m_tileBmax[2];
loader->processDynamic( box );
m_dverts = loader->getVerts();
m_ndverts = loader->getVertCount();
m_dtris = loader->getTris();
m_ndtris = loader->getTriCount();
}
// m_tileCol[0] = 0.3f; m_tileCol[1] = 0.8f; m_tileCol[2] = 0; m_tileCol[3] = 1;
int dataSize = 0;
unsigned char* data = buildTileMesh(m_tileBmin, m_tileBmax, dataSize);
if (data)
{
boost::mutex::scoped_lock l(m_mutex);
// Remove any previous data (navmesh owns and deletes the data).
m_tileMesh->removeTileAt(tx,ty,0,0);
// Let the navmesh own the data.
if (!m_tileMesh->addTileAt(tx,ty,data,dataSize,true))
delete [] data;
}
delete loader;
}
void NavMesh::removeTile(const TVec3 pos)
{
if (!m_tileMesh)
return;
const float ts = m_tileSize*m_cellSize;
const int tx = (int)floorf((pos.X-m_bmin[0]) / ts);
const int ty = (int)floorf((pos.Z-m_bmin[2]) / ts);
m_tileBmin[0] = m_bmin[0] + tx*ts;
m_tileBmin[1] = m_bmin[1];
m_tileBmin[2] = m_bmin[2] + ty*ts;
m_tileBmax[0] = m_bmin[0] + (tx+1)*ts;
m_tileBmax[1] = m_bmax[1];
m_tileBmax[2] = m_bmin[2] + (ty+1)*ts;
m_tileCol[0] = 0.8f; m_tileCol[1] = 0.1f; m_tileCol[2] = 0; m_tileCol[3] = 1;
unsigned char* rdata = 0;
int rdataSize = 0;
if (m_tileMesh->removeTileAt(tx,ty,&rdata,&rdataSize))
delete [] rdata;
}
void NavMesh::cleanup()
{
delete [] m_triflags;
m_triflags = 0;
delete m_solid;
m_solid = 0;
delete m_chf;
m_chf = 0;
delete m_cset;
m_cset = 0;
delete m_pmesh;
m_pmesh = 0;
delete m_dmesh;
m_dmesh = 0;
}
unsigned char* NavMesh::buildTileMesh(const float* bmin, const float* bmax, int& dataSize)
{
if (!m_verts || ! m_tris)
{
AppLog( "buildNavigation: Input mesh is not specified.");
return 0;
}
cleanup();
// Init build configuration from GUI
memset(&m_cfg, 0, sizeof(m_cfg));
m_cfg.cs = m_cellSize;
m_cfg.ch = m_cellHeight;
m_cfg.walkableSlopeAngle = m_agentMaxSlope;
m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch);
m_cfg.walkableClimb = (int)ceilf(m_agentMaxClimb / m_cfg.ch);
m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs);
m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize);
m_cfg.maxSimplificationError = m_edgeMaxError;
m_cfg.minRegionSize = (int)rcSqr(m_regionMinSize);
m_cfg.mergeRegionSize = (int)rcSqr(m_regionMergeSize);
m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
m_cfg.tileSize = (int)m_tileSize;
m_cfg.borderSize = m_cfg.walkableRadius*2 + 2; // Reserve enough padding.
m_cfg.width = m_cfg.tileSize + m_cfg.borderSize*2;
m_cfg.height = m_cfg.tileSize + m_cfg.borderSize*2;
m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist;
m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError;
vcopy(m_cfg.bmin, bmin);
vcopy(m_cfg.bmax, bmax);
m_cfg.bmin[0] -= m_cfg.borderSize*m_cfg.cs;
m_cfg.bmin[2] -= m_cfg.borderSize*m_cfg.cs;
m_cfg.bmax[0] += m_cfg.borderSize*m_cfg.cs;
m_cfg.bmax[2] += m_cfg.borderSize*m_cfg.cs;
// Allocate voxel heighfield where we rasterize our input data to.
m_solid = new rcHeightfield;
if (!m_solid)
{
AppLog("buildNavigation: Out of memory 'solid'.");
return 0;
}
if (!rcCreateHeightfield(*m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch))
{
AppLog( "buildNavigation: Could not create solid heightfield.");
return 0;
}
// Allocate array that can hold triangle flags.
// If you have multiple meshes you need to process, allocate
// and array which can hold the max number of triangles you need to process.
m_triflags = new unsigned char[__max(m_chunkyMesh->maxTrisPerChunk,m_ndtris)];
if (!m_triflags)
{
AppLog( "buildNavigation: Out of memory 'triangleFlags' (%d).", m_chunkyMesh->maxTrisPerChunk);
return 0;
}
float tbmin[2], tbmax[2];
tbmin[0] = m_cfg.bmin[0];
tbmin[1] = m_cfg.bmin[2];
tbmax[0] = m_cfg.bmax[0];
tbmax[1] = m_cfg.bmax[2];
int cid[256];// TODO: Make grow when returning too many items.
int ncid = rcGetChunksInRect(m_chunkyMesh, tbmin, tbmax, cid, 256);
if (!ncid)
return 0;
m_tileTriCount = 0;
for (int i = 0; i < ncid; ++i)
{
const rcChunkyTriMeshNode& node = m_chunkyMesh->nodes[cid[i]];
const int* tris = &m_chunkyMesh->tris[node.i*3];
const int ntris = node.n;
m_tileTriCount += ntris;
memset(m_triflags, 0, ntris*sizeof(unsigned char));
rcMarkWalkableTriangles(m_cfg.walkableSlopeAngle,
m_verts, m_nverts, tris, ntris, m_triflags);
rcRasterizeTriangles(m_verts, m_nverts, tris, m_triflags, ntris, *m_solid);
}
// Do dynamic stuff here
if (m_ndverts)
{
memset(m_triflags, 0, m_ndtris*sizeof(unsigned char));
m_tileTriCount += m_ndtris;
rcMarkWalkableTriangles(m_cfg.walkableSlopeAngle,
m_dverts, m_ndverts, m_dtris, m_ndtris, m_triflags);
rcRasterizeTriangles(m_dverts, m_ndverts, m_dtris, m_triflags, m_ndtris, *m_solid);
}
if (!m_keepInterResults)
{
delete [] m_triflags;
m_triflags = 0;
}
// Once all geoemtry is rasterized, we do initial pass of filtering to
// remove unwanted overhangs caused by the conservative rasterization
// as well as filter spans where the character cannot possibly stand.
rcFilterLedgeSpans(m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid);
rcFilterWalkableLowHeightSpans(m_cfg.walkableHeight, *m_solid);
// Compact the heightfield so that it is faster to handle from now on.
// This will result more cache coherent data as well as the neighbours
// between walkable cells will be calculated.
m_chf = new rcCompactHeightfield;
if (!m_chf)
{
AppLog( "buildNavigation: Out of memory 'chf'.");
return 0;
}
if (!rcBuildCompactHeightfield(m_cfg.walkableHeight, m_cfg.walkableClimb, RC_WALKABLE, *m_solid, *m_chf))
{
AppLog( "buildNavigation: Could not build compact data.");
return 0;
}
if (!m_keepInterResults)
{
delete m_solid;
m_solid = 0;
}
// Prepare for region partitioning, by calculating distance field along the walkable surface.
if (!rcBuildDistanceField(*m_chf))
{
AppLog( "buildNavigation: Could not build distance field.");
return 0;
}
// Partition the walkable surface into simple regions without holes.
if (!rcBuildRegions(*m_chf, m_cfg.walkableRadius, m_cfg.borderSize, m_cfg.minRegionSize, m_cfg.mergeRegionSize))
{
AppLog( "buildNavigation: Could not build regions.");
return 0;
}
// Create contours.
m_cset = new rcContourSet;
if (!m_cset)
{
AppLog("buildNavigation: Out of memory 'cset'.");
return 0;
}
if (!rcBuildContours(*m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset))
{
AppLog( "buildNavigation: Could not create contours.");
return 0;
}
// Build polygon navmesh from the contours.
m_pmesh = new rcPolyMesh;
if (!m_pmesh)
{
AppLog("buildNavigation: Out of memory 'pmesh'.");
return 0;
}
if (!rcBuildPolyMesh(*m_cset, m_cfg.maxVertsPerPoly, *m_pmesh))
{
AppLog( "buildNavigation: Could not triangulate contours.");
return 0;
}
// Build detail mesh.
m_dmesh = new rcPolyMeshDetail;
if (!m_dmesh)
{
AppLog( "buildNavigation: Out of memory 'dmesh'.");
return 0;
}
if (!rcBuildPolyMeshDetail(*m_pmesh, *m_chf,
m_cfg.detailSampleDist, m_cfg.detailSampleMaxError,
*m_dmesh))
{
AppLog( "buildNavigation: Could build polymesh detail.");
return 0;
}
if (!m_keepInterResults)
{
delete m_chf;
m_chf = 0;
delete m_cset;
m_cset = 0;
}
unsigned char* navData = 0;
int navDataSize = 0;
if (m_cfg.maxVertsPerPoly == DT_TILE_VERTS_PER_POLYGON)
{
// Remove padding from the polymesh data. TODO: Remove this odditity.
for (int i = 0; i < m_pmesh->nverts; ++i)
{
unsigned short* v = &m_pmesh->verts[i*3];
v[0] -= (unsigned short)m_cfg.borderSize;
v[2] -= (unsigned short)m_cfg.borderSize;
}
if (!dtCreateNavMeshTileData(m_pmesh->verts, m_pmesh->nverts,
m_pmesh->polys, m_pmesh->npolys, m_pmesh->nvp,
m_dmesh->meshes, m_dmesh->verts, m_dmesh->nverts, m_dmesh->tris, m_dmesh->ntris,
bmin, bmax, m_cfg.cs, m_cfg.ch, m_cfg.tileSize, m_cfg.walkableClimb, &navData, &navDataSize))
{
AppLog( "Could not build Detour navmesh.");
}
}
m_tileMemUsage = navDataSize/1024.0f;
dataSize = navDataSize;
return navData;
}
void NavMesh::testBuild()
{
float meshBMin[3], meshBMax[3];
m_loader = new rcMeshLoaderSBX();
m_loader->process();
resetCommonSettings();
rcCalcBounds(m_loader->getVerts(), m_loader->getVertCount(), m_bmin, m_bmax);
m_verts = m_loader->getVerts();
m_tris = m_loader->getTris();
m_ntris = m_loader->getTriCount();
init();
buildAllTiles();
if (m_showMesh)
makeTiledNavMesh( m_tileMesh );
}
void NavMesh::calcTilePos( TVec3 pos, TilePos &tpos)
{
const float ts = m_tileSize*m_cellSize;
tpos.tx = (int)floorf((pos.X-m_bmin[0]) / ts);
tpos.ty = (int)floorf((pos.Z-m_bmin[2]) / ts);
}
void NavMesh::addBuildRequest( TVec3 pos, TVec6 box, TEntity e )
{
TilePos tpos;
box.X0 *=-1;
box.X1 *=-1;
//bl
pos = Vec3(box.X0,box.Y0,box.Z0);
calcTilePos( pos, tpos );
m_rebuildRequest[ tpos ] = pos;
//tl
pos = Vec3( box.X0, box.Y0, box.Z1);
calcTilePos( pos, tpos );
m_rebuildRequest[ tpos ] = pos;
//br
pos = Vec3( box.X1, box.Y0, box.Z0);
calcTilePos( pos, tpos );
m_rebuildRequest[ tpos ] = pos;
//tr
pos = Vec3( box.X1, box.Y0, box.Z1);
calcTilePos( pos, tpos );
m_rebuildRequest[ tpos ] = pos;
}
NavRequest* NavMesh::newPathRequest( TVec3 spos, TVec3 epos )
{
if (spos == epos)
return NULL;
NavRequest *req = new NavRequest;
req->m_spos[0] = spos.X*-1;
req->m_spos[1] = spos.Y;
req->m_spos[2] = spos.Z;
req->m_epos[0] = epos.X*-1;
req->m_epos[1] = epos.Y;
req->m_epos[2] = epos.Z;
req->m_startPos = spos;
req->m_endPos = spos;
req->m_mesh = m_tileMesh;
m_pathRequests[ req ] = req;
return req;
}
void NavMesh::removePathRequest( NavRequest *nav)
{
m_pathRequests.erase( nav );
delete nav;
}
float NavMesh::findDistanceToWall( TVec3 p, float dist, float *hit, float *hitNormal, int *npolys, float *navhit )
{
float spos[3], epos[3];
float wallDist;
dtTilePolyRef startRef;
spos[0] = p.X*-1;
spos[1] = p.Y;
spos[2] = p.Z;
epos[0] = p.X*-1;
epos[1] = p.Y;
epos[2] = p.Z;
startRef = m_tileMesh->findNearestPoly(spos, m_polyPickExt);
if (!startRef)
{
AppLog( "Not anywhere near navmesh");
return 999999;
}
*npolys = m_tileMesh->raycast( startRef, spos, epos, *navhit, m_polys, MAX_POLYS);
wallDist = m_tileMesh->findDistanceToWall(startRef, spos, dist, hit, hitNormal);
return wallDist;
}
void NavMesh::startThread()
{
m_thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&NavMesh::handleThread, this)));
}
void NavMesh::handleThread()
{
while (!m_stoprequested)
{
update();
Sleep(30);
}
}
void NavMesh::update()
{
// float startTime = AppTime();
NavRequest *navreq;
std::map<TilePos,TVec3,TilePosCompare>::iterator req;
std::map<NavRequest*,NavRequest*>::iterator findList;
bool doneOne = false;
req=m_rebuildRequest.begin();
if (req!=m_rebuildRequest.end())
{
//removeTile(req->second);
buildTile( req->second );
m_rebuildRequest.erase(req);
navVersion++;
doneOne = true;
}
if (m_showMesh && doneOne)
makeTiledNavMesh( m_tileMesh );
{
boost::mutex::scoped_lock l(m_mutex);
findList = m_pathRequests.begin();
while (findList != m_pathRequests.end())
{
findList->second->processPathRequest();
m_pathRequests.erase(findList++);
}
}
}
void NavRequest::makeBasicPath()
{
m_navVersion = navVersion;
m_startRef = m_mesh->findNearestPoly(m_spos, m_polyPickExt);
m_endRef = m_mesh->findNearestPoly(m_epos, m_polyPickExt);
m_npolys = m_mesh->findPath(m_startRef, m_endRef, m_spos, m_epos, m_polys, MAX_POLYS);
if (m_npolys)
m_nstraightPath = m_mesh->findStraightPath(m_spos, m_epos, m_polys, m_npolys, m_straightPath, MAX_POLYS);
m_status = NAV_COMPLETED;
}
void NavRequest::processPathRequest()
{
makeBasicPath();
makeSteerPath( m_startPos, m_nstraightPath );
}
TVec3 toVec3Open( OpenSteer::Vec3 p )
{
return Vec3(p.x,p.y,p.z);
}
void NavRequest::render( TCamera cam )
{
#ifdef DEBUG_PATH
TVec3 p1;
TVec3 p2;
if (m_nstraightPath < 2) return;
SetBlend(BLEND_ALPHA);
SetColor( Vec4(1,0,0,1) ); // red
for( int i=0;i<m_nstraightPath-1;i++)
{
tdDraw( cam, pathVec3( i ), pathVec3( i+1 ) );
}
SetColor( Vec4(0,1,0,1) ); // red
for( int i=0;i<m_numPoints-1;i++)
{
tdDraw( cam, toVec3Open(m_pathPoints[i]) , toVec3Open(m_pathPoints[i+1]) );
}
#endif
}
OpenSteer::Vec3 *toOpenVec3( TVec3 pos )
{
OpenSteer::Vec3 *rpos = new OpenSteer::Vec3;
rpos->x = pos.X;
rpos->y = pos.Y;
rpos->z = pos.Z;
return rpos;
}
bool NavRequest::makeSteerPath( TVec3 startPos, int maxPoints )
{
if (!complete()) return false;
maxPoints = MAX_POLYS; // temp fix
// Check if recalc needed
if (m_navVersion != navVersion)
makeBasicPath();
else
{
float spos[3];
spos[0] = startPos.X*-1;
spos[1] = startPos.Y;
spos[2] = startPos.Z;
m_nstraightPath = m_mesh->findStraightPath(spos, m_epos, m_polys, m_npolys, m_straightPath, maxPoints);
}
m_numPoints = __min( m_nstraightPath, maxPoints );
if (m_numPoints <= 2 && startPos == m_endPos)
{
m_numPoints = 0;
return false;
}
if (m_openPath)
{
delete m_openPath;
m_openPath = NULL;
}
if (m_pathPoints)
{
delete m_pathPoints;
m_pathPoints = NULL;
}
m_openPath = new OpenSteer::PolylineSegmentedPathwaySingleRadius;
if (m_numPoints > 2)
{
m_pathPoints = new OpenSteer::Vec3[m_numPoints];
for( int i=0;i < m_numPoints;i++)
{
m_pathPoints[i] = *toOpenVec3( pathVec3( i ) );
}
m_numPoints -=1;
}
else
{
m_pathPoints = new OpenSteer::Vec3[2];
m_pathPoints[0] = *toOpenVec3(startPos);
m_pathPoints[1] = *toOpenVec3(m_endPos);
m_numPoints = 2;
}
m_openPath->setPathway( m_numPoints, m_pathPoints, 0.01, false );
return true;
}
Here's the recast vert loader code: -
#include "leo.h"
#include "recast.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include "ledcast.h"
#include "ledcast_statmeshSimple.h"
#include "rcMeshLoaderSBX.h"
using namespace LEO;
rcMeshLoaderSBX *currentLoader;
bool processMesh( TEntity entity )
{
TMesh mesh = (TMesh)entity;
int SurfCnt = CountSurfaces( mesh );
TSurface surface;
TVec3 vec;
int vertIdx;
float factor=1;
vertIdx = currentLoader->getVertCount();
for( int s = 1; s <= SurfCnt; s++ )
{
surface = GetSurface( mesh, s );
if (surface)
{
for( int t = 0; t < CountTriangles( surface ); t++)
{
for( int v = 0; v < 3; v++)
{
vec = GetVertexPosition( surface, TriangleVertex( surface, t, v ));
vec=TFormPoint(Vec3(vec.X,vec.Y,vec.Z),entity,NULL);
currentLoader->addVertex(
(vec.X)*-factor,
(vec.Y)*factor,
(vec.Z)*factor,
currentLoader->vcap );
}
currentLoader->addTriangle( vertIdx+1, vertIdx, vertIdx+2, currentLoader->tcap );
vertIdx=vertIdx+3;
}
}
else
break;
}
return true;
}
int _stdcall recastLoadMesh( TEntity entity, byte* extra )
{
TMesh mesh = (TMesh)entity;
std::string entityClass, cn, en;
int type, type1;
// Only bother with collision type 1
cn=GetEntityKey(entity,"classname");
en=GetEntityKey(entity,"name");
entityClass = GetEntityKey(entity, "class","");
type = atoi( GetEntityKey(entity, "collisiontype","1") );
if (type >0)
EntityType(entity, type, 1);
type1 = GetEntityType(entity);
if (currentLoader->m_dynamic)
{
if (type1 != 2)
return true;
}else{
if (type1 != 1)
return true;
}
if (entityClass != "Model") return true;
if(
(cn=="water")||
(cn=="physics_prop")||
(cn=="physics_pivot")||
(cn=="joint_motor")||
(cn=="info_waypoint")||
(cn=="info_playerstart")||
(cn=="player")||
(cn=="light_directional")||
(cn=="light_spot")||
(cn=="light_point")||
(cn=="env_emitter")||
(cn=="env_sound"))
{
HideEntity(entity);
return true;
}
processMesh( GetChild(entity, 1) );
return true;
}
int _stdcall recastLoadTerrain( TEntity terrain, byte* extra )
{
std::string entityClass;
entityClass = GetEntityKey(terrain, "class","");
if (entityClass != "Terrain") return false;
float mapSize = 1024;
float x;
float z;
float mapHalfSize = 512;
float height;
int vertIdx;
float minx = 0, minz =0, maxx=0, maxz=0;
if (!terrain) return false;
vertIdx = currentLoader->getVertCount();
for(x = -mapHalfSize; x<mapHalfSize - 1; x++)
{
for(z = -mapHalfSize; z<mapHalfSize - 1; z++)
{
height = TerrainElevation( terrain, x, z);
if ((height != 0) || ((x>=minx && x<=maxx) && (z>=minz && z <= maxz)))
{
minx = __min(minx, x);
minz = __min(minz, z);
maxx = __max(maxx, x);
maxz = __max(maxz, z);
//br
height = TerrainElevation( terrain, (float)x+1, (float)z);
currentLoader->addVertex(
(x+1)*-1,
height,
z,
currentLoader->vcap );
//tr
height = TerrainElevation( terrain, (float)x+1, (float)z+1);
currentLoader->addVertex(
(x+1)*-1,
height,
z+1,
currentLoader->vcap );
//bl
height = TerrainElevation( terrain, x, z);
currentLoader->addVertex(
x*-1,
height,
z,
currentLoader->vcap );
currentLoader->addTriangle( vertIdx, vertIdx+1, vertIdx+2, currentLoader->tcap );
vertIdx=vertIdx+3;
//Trianlge 2
//tl
height = TerrainElevation( terrain, (float)x, (float)z+1);
currentLoader->addVertex(
x*-1,
height,
z+1,
currentLoader->vcap );
//bl
height = TerrainElevation( terrain, (float)x, (float)z);
currentLoader->addVertex(
x*-1,
height,
z,
currentLoader->vcap );
//tr
height = TerrainElevation( terrain, (float)x+1, (float)z+1);
currentLoader->addVertex(
(x+1)*-1,
height,
z+1,
currentLoader->vcap );
currentLoader->addTriangle( vertIdx, vertIdx+1, vertIdx+2, currentLoader->tcap );
vertIdx=vertIdx+3;
}
else
height = -1;
}
}
return true;
}
int _stdcall recastLoadMeshNormals( TEntity entity, byte* extra )
{
TMesh mesh = (TMesh)entity;
std::string entityClass;
entityClass = GetEntityKey(entity, "class","");
if (entityClass != "Mesh") return false;
TSurface surface;
int SurfCnt = CountSurfaces( mesh );
float *normals;
normals = (float *)(currentLoader->getNormals());
TVec3 vnorm;
for( int s = 1; s <= SurfCnt; s++ )
{
surface = GetSurface( mesh, s );
for( int t = 0; t < CountTriangles( surface ); t++)
{
vnorm = GetVertexNormal( surface, TriangleVertex( surface, t, 1 ));
normals[currentLoader->nidx] = vnorm.X;
normals[currentLoader->nidx+1] = vnorm.Y;
normals[currentLoader->nidx+2] = vnorm.Z;
currentLoader->nidx=currentLoader->nidx+3;
}
}
return true;
}
bool rcMeshLoaderSBX::load(const char* filename)
{
std::string f = filename;
if (f.find("sbx"))
scene = LoadScene((str)filename);
else if (f.find("gmf")) scene = LoadModel((str)filename);
if (!scene) return false;
return process();
}
bool rcMeshLoaderSBX::process()
{
currentLoader = this;
m_dynamic = false;
ForEachEntityDo( (BP)recastLoadMesh,NULL, ENTITY_MESH|ENTITY_MODEL );
ForEachEntityDo( (BP)recastLoadTerrain,NULL, ENTITY_TERRAIN );
setNormals( new float[getTriCount()*3] );
nidx = 0;
calcNormals();
return true;
}
bool rcMeshLoaderSBX::processDynamic( TVec6 box )
{
currentLoader = this;
m_dynamic = true;
ForEachEntityInAABBDo( box, (BP)recastLoadMesh, (byte*)this, ENTITY_MESH|ENTITY_MODEL );
return true;
}
rcMeshLoaderSBX::~rcMeshLoaderSBX()
{
if (scene) FreeEntity(scene);
}
2 Comments
Recommended Comments