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