Jump to content

Further work on path finding

Chris Paulson

2,103 views

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

>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?

Share this comment


Link to comment

I forgot to thank you for posting this Chris. I'm really looking forward to playing with it, when I get back. :D

Share this comment


Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

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...