Jump to content
  • entries
    10
  • comments
    21
  • views
    22,189

Further work on path finding


Chris Paulson

3,498 views

 Share

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);
}

 Share

2 Comments


Recommended Comments

>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 have no idea about Blitz, but have you searched for a program that does a similar job to Swig? Perhaps there is an equivalent for Blitz?

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...