# Forum > World of Warcraft > World of Warcraft Bots and Programs > WoW Memory Editing >  Recast + Detour - Connecting tiles

## Tanaris4

All-

Sorry for all the general posts lately, but I've been making a lot of progress in the pathing space (good fun on OS X  :Wink:  ). I can load my nav meshes fine into RecastDemo, and into my own application. The issue i'm running into is I can't generate a path across tiles. If it's within one tile, it's fine.

Anyone have any suggestions? I've seen many topics on the same problem, but didn't really see a solution. I have tried most things, like when generating the tiles, include all of the tiles around it (in case of walls, cliffs, etc...)

Anyone have any ideas on how I can fix my generation? Here are my parameters:



```
#define m_cellSize (1.0f / 3.0f)
#define m_cellHeight 0.3f

#define m_agentHeight 1.652778f
#define m_agentRadius 0.35f

#define m_agentMaxClimb 1.0f
#define m_agentMaxSlope 50.0f

#define m_regionMinSize 8       // 20
#define m_regionMergeSize 20    // 40

#define m_edgeMaxLen 12.0f
#define m_edgeMaxError 1.0f

#define m_vertsPerPoly 6.0f
#define m_detailSampleDist 3.0f
#define m_detailSampleMaxError 1.0f

#define m_tileSize 1600.0f

#define m_keepInterResults false

#define m_monotonePartitioning false
```

And here is how I'm actually generating it (basically straight out of RecastDemo):


```
struct ObjData
{
	Vertices_t vertices;
	Indices32_t indices;
	Normals_t normals;
    AdtCoords_t *coords;
	
	void clear(){
		vertices.clear();
		indices.clear();
		normals.clear();
	}
};
// objData = our tile and the tiles bordering it
// obj = our tile
- (void)generateMeshesWithObj:(ObjData*)objData toDirectory:(NSString*)directory andX:(int)tx andY:(int)ty andTile:(ObjData*)obj{
    
    // create our mesh loader basd on all this data (3x3 based on the tile we want the mesh for)
	rcMeshLoaderObj *loader = CreateMeshLoader(objData);
    
    if ( !loader ){
        NSLog(@"Unable to create loader for (%d, %d)", tx, ty);
        return;
    }
    
	InputGeom *geom = new InputGeom();
	if ( !geom->loadMesh(0, loader) ){
        NSLog(@"Unable to load ObjData");
		return;
	}
    
    if ( !geom || !geom->getMesh() || !geom->getChunkyMesh() ){
		NSLog(@"buildNavigation: Input mesh is not specified.");
		return;
	}
    
    const float *bmin = geom->getMeshBoundsMin();
	const float *bmax = geom->getMeshBoundsMax();
	const float* verts = geom->getMesh()->getVerts();
	const int nverts = geom->getMesh()->getVertCount();
	const rcChunkyTriMesh* chunkyMesh = geom->getChunkyMesh();
    
	rcConfig m_cfg;	
	// 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)floorf(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.minRegionArea = (int)rcSqr(m_regionMinSize);		// Note: area = size*size
	m_cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize);	// Note: area = size*size
	m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
	m_cfg.tileSize = (int)m_tileSize;
	m_cfg.borderSize = m_cfg.walkableRadius + 3; // 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;
	
    rcVcopy(m_cfg.bmin, bmin);
	rcVcopy(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;
    
    NSLog(@"Overall...");
    NSLog(@" min: {%0.2f, %0.2f, %0.2f}", bmin[0], bmin[1], bmin[2]);
    NSLog(@" max: {%0.2f, %0.2f, %0.2f}", bmax[0], bmax[1], bmax[2]);
    
    rcContext *ctx = NULL;
    NSDate *start = [NSDate date];
    
    // clean up from previous run
    if ( ctx ){
        delete ctx;
    }
    
    // we only care about the coords for this tile, so lets set our bounds correctly!
    NSLog(@"(%d, %d) Generating mesh...", tx, ty);
    
    // ** Get our bounds for the tile we care about
    rcMeshLoaderObj *loader2 = CreateMeshLoader(obj);
	InputGeom *geomTile = new InputGeom();
	if ( !geomTile->loadMesh(0, loader2) ){
        NSLog(@"Unable to load ObjData2(%d, %d)", tx, ty);
		return;
	}
    
    if ( !geomTile || !geomTile->getMesh() || !geomTile->getChunkyMesh() ){
		NSLog(@"buildNavigation: Input mesh is not specified.2(%d, %d)", tx, ty);
		return;
	}
    const float *bminTile = geomTile->getMeshBoundsMin();
	const float *bmaxTile = geomTile->getMeshBoundsMax();
    
    m_cfg.bmin[0] = bminTile[0];
    m_cfg.bmin[2] = bminTile[2];
    m_cfg.bmax[0] = bmaxTile[0];
    m_cfg.bmax[2] = bmaxTile[2];
    
    // we want to go over the tile's edge a bit!
    /*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;*/
    // ** End - Get our bounds
    
    NSLog(@" min: {%0.2f, %0.2f, %0.2f}", bminTile[0], bminTile[1], bminTile[2]);
    NSLog(@" max: {%0.2f, %0.2f, %0.2f}", bmaxTile[0], bmaxTile[1], bmaxTile[2]);
    
    // set up our context
    ctx = new rcContext();
    ctx->resetTimers();
    ctx->startTimer(RC_TIMER_TOTAL);
    
    // Allocate voxel heightfield where we rasterize our input data to.
    rcHeightfield *m_solid = rcAllocHeightfield();
    if ( !m_solid ){
        NSLog(@"buildNavigation: Out of memory 'solid'.");
        return;
    }
    if ( !rcCreateHeightfield(ctx, *m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch) ){
        NSLog(@"buildNavigation: Could not create solid heightfield.");
        return;
    }
    
    // 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.
    unsigned char *m_triareas = new unsigned char[chunkyMesh->maxTrisPerChunk];
    if ( !m_triareas ){
        NSLog(@"buildNavigation: Out of memory 'm_triareas' (%d).", chunkyMesh->maxTrisPerChunk);
        return;
    }
    
    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[10000];// TODO: Make grow when returning too many items.
    const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 10000);
    if ( !ncid ){
        NSLog(@"ncid error");
        return;
    }
    
    int m_tileTriCount = 0;
    
    for ( int i = 0; i < ncid; ++i ){
        const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]];
        const int* tris = &chunkyMesh->tris[node.i*3];
        const int ntris = node.n;
        
        m_tileTriCount += ntris;
        
        memset(m_triareas, 0, ntris*sizeof(unsigned char));
        rcMarkWalkableTriangles( ctx, m_cfg.walkableSlopeAngle, verts, nverts, tris, ntris, m_triareas );
        
        rcRasterizeTriangles( ctx, verts, nverts, tris, m_triareas, ntris, *m_solid, m_cfg.walkableClimb );
    }
    
    if ( !m_keepInterResults ){
        delete [] m_triareas;
        m_triareas = 0;
    }
    
    // Once all geometry 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.
    rcFilterLowHangingWalkableObstacles(ctx, m_cfg.walkableClimb, *m_solid);
    rcFilterLedgeSpans(ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid);
    rcFilterWalkableLowHeightSpans(ctx, 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.
    rcCompactHeightfield *m_chf = rcAllocCompactHeightfield();
    if ( !m_chf ){
        NSLog(@"buildNavigation: Out of memory 'chf'.");
        return;
    }
    
    if ( !rcBuildCompactHeightfield(ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf) ){
        NSLog(@"buildNavigation: Could not build compact data.");
        return;
    }
    
    if ( !m_keepInterResults ){
        rcFreeHeightField(m_solid);
        m_solid = 0;
    }
    
    // Erode the walkable area by agent radius.
    if ( !rcErodeWalkableArea(ctx, m_cfg.walkableRadius, *m_chf) ){
        NSLog(@"buildNavigation: Could not erode.");
        return;
    }
    
    // (Optional) Mark areas.
    const ConvexVolume* vols = geom->getConvexVolumes();
    for (int i  = 0; i < geom->getConvexVolumeCount(); ++i)
        rcMarkConvexPolyArea(ctx, vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf);
    
    if ( m_monotonePartitioning ){
        // Partition the walkable surface into simple regions without holes.
        if ( !rcBuildRegionsMonotone(ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea) ){
            NSLog(@"buildNavigation: Could not build regions.");
            return;
        }
    }
    else{
        // Prepare for region partitioning, by calculating distance field along the walkable surface.
        if ( !rcBuildDistanceField(ctx, *m_chf) ){
            NSLog(@"buildNavigation: Could not build distance field.");
            return;
        }
        
        // Partition the walkable surface into simple regions without holes.
        if ( !rcBuildRegions(ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea) ){
            NSLog(@"buildNavigation: Could not build regions.");
            return;
        }
    }
    
    // Create contours.
    rcContourSet *m_cset = rcAllocContourSet();
    if ( !m_cset ){
        NSLog(@"buildNavigation: Out of memory 'cset'.");
        return;
    }
    if ( !rcBuildContours(ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset) ){
        NSLog(@"buildNavigation: Could not create contours.");
        return;
    }
    
    if ( m_cset->nconts == 0 ){
        NSLog(@"No contours ");
        return;
    }
    
    // Build polygon navmesh from the contours.
    rcPolyMesh *m_pmesh = rcAllocPolyMesh();
    if ( !m_pmesh ){
        NSLog(@"buildNavigation: Out of memory 'pmesh'.");
        return;
    }
    if ( !rcBuildPolyMesh(ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh) ){
        NSLog(@"buildNavigation: Could not triangulate contours.");
        return;
    }
    
    // Build detail mesh.
    rcPolyMeshDetail *m_dmesh = rcAllocPolyMeshDetail();
    if ( !m_dmesh ){
        NSLog(@"buildNavigation: Out of memory 'dmesh'.");
        return;
    }
    
    if ( !rcBuildPolyMeshDetail(ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh) ){
        NSLog(@"buildNavigation: Could build polymesh detail.");
        return;
    }
    
    if ( !m_keepInterResults ){
        rcFreeCompactHeightfield(m_chf);
        m_chf = 0;
        rcFreeContourSet(m_cset);
        m_cset = 0;
    }
    
    unsigned char *navData = NULL;
    int navDataSize;
    
    if ( m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON ){
        
        if ( m_pmesh->nverts >= 0xffff ){
            // The vertex indices are ushorts, and cannot point to more than 0xffff vertices.
            NSLog(@"Too many vertices per tile %d (max: %d).", m_pmesh->nverts, 0xffff);
            return;
        }
        
        // Update poly flags from areas.
        for ( int i = 0; i < m_pmesh->npolys; ++i ){
            if (m_pmesh->areas[i] == RC_WALKABLE_AREA)
                m_pmesh->areas[i] = SAMPLE_POLYAREA_GROUND;
            
            if ( m_pmesh->areas[i] == SAMPLE_POLYAREA_GROUND || m_pmesh->areas[i] == SAMPLE_POLYAREA_GRASS || m_pmesh->areas[i] == SAMPLE_POLYAREA_ROAD ){
                m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK;
            }
            else if ( m_pmesh->areas[i] == SAMPLE_POLYAREA_WATER ){
                m_pmesh->flags[i] = SAMPLE_POLYFLAGS_SWIM;
            }
            else if ( m_pmesh->areas[i] == SAMPLE_POLYAREA_DOOR ){
                m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR;
            }
        }
        
        dtNavMeshCreateParams params;
        memset(&params, 0, sizeof(params));
        params.verts = m_pmesh->verts;
        params.vertCount = m_pmesh->nverts;
        params.polys = m_pmesh->polys;
        params.polyAreas = m_pmesh->areas;
        params.polyFlags = m_pmesh->flags;
        params.polyCount = m_pmesh->npolys;
        params.nvp = m_pmesh->nvp;
        params.detailMeshes = m_dmesh->meshes;
        params.detailVerts = m_dmesh->verts;
        params.detailVertsCount = m_dmesh->nverts;
        params.detailTris = m_dmesh->tris;
        params.detailTriCount = m_dmesh->ntris;
        params.offMeshConVerts = geom->getOffMeshConnectionVerts();
        params.offMeshConRad = geom->getOffMeshConnectionRads();
        params.offMeshConDir = geom->getOffMeshConnectionDirs();
        params.offMeshConAreas = geom->getOffMeshConnectionAreas();
        params.offMeshConFlags = geom->getOffMeshConnectionFlags();
        params.offMeshConUserID = geom->getOffMeshConnectionId();
        params.offMeshConCount = geom->getOffMeshConnectionCount();
        params.walkableHeight = m_agentHeight;
        params.walkableRadius = m_agentRadius;
        params.walkableClimb = m_agentMaxClimb;
        params.tileX = tx;
        params.tileY = ty;
        params.tileLayer = 0;
        rcVcopy(params.bmin, m_pmesh->bmin);
        rcVcopy(params.bmax, m_pmesh->bmax);
        params.cs = m_cfg.cs;
        params.ch = m_cfg.ch;
        params.buildBvTree = true;
        
        NSLog(@" min: {%0.2f, %0.2f, %0.2f}", m_pmesh->bmin[0], m_pmesh->bmin[1], m_pmesh->bmin[2]);
        NSLog(@" max: {%0.2f, %0.2f, %0.2f}", m_pmesh->bmax[0], m_pmesh->bmax[1], m_pmesh->bmax[2]);
        
        if ( !dtCreateNavMeshData(&params, &navData, &navDataSize) ){
            NSLog(@"Could not build Detour navmesh.");
            return;
        }		
    }
    
    ctx->stopTimer(RC_TIMER_TOTAL);
    
    // save!
    if ( navDataSize > 0 ){
        
        NSString *path = [NSString stringWithFormat:@"%@%d_%d.mesh", directory, tx, ty];
        FILE* fp = fopen([path UTF8String], "wb");
        if (!fp)
            return;
        
        // Store header
        NavMeshSetHeader header;
        header.magic = NAVMESHSET_MAGIC;
        header.version = NAVMESHSET_VERSION;
        header.numTiles = 1;
        fwrite(&header, sizeof(NavMeshSetHeader), 1, fp);
        
        // Store tile
        NavMeshTileHeader tileHeader;
        tileHeader.dataSize = navDataSize;
        fwrite(&tileHeader, sizeof(tileHeader), 1, fp);
        fwrite(navData, navDataSize, 1, fp);
        
        fclose(fp);
    }
    
    NSLog(@"Completed in %0.2f seconds", [start timeIntervalSinceNow] * -1.0f);
    
    if ( geom ){
        delete geom;
    }
}
```



The path I generate is never completed and always returns DT_OUT_OF_NODES and DT_PARTIAL_RESULT

Thanks again all!
~ Tanaris

----------


## Tanaris4

Sorry to bump - but there have been quite a few views - does anyone have any ideas?

Thanks in advance!
~ Tanaris

----------


## hamburger12

Your tile Location maybe wrong^^ it has something to do with the Orign

----------


## Tanaris4

Any suggestion on how I could debug that? And if the tiles don't match up EXACTLY, will it not work? Should there be no overlap?

----------


## namreeb

Correct. They must line up exactly. This means that your cell size must exactly divide the tile size. Search this forum for my recast settings. I explained in some detail in that post what variables depend on the others.

----------


## Tanaris4

Just used your parameters namreeb and I came up with this



so... close...! The tiles still aren't connected and I'm really not sure why. I took some code from riboncore to get to this point, I believe the offending code is here:



```
	rcConfig m_cfg;	
	// Init build configuration from GUI
	memset(&m_cfg, 0, sizeof(m_cfg));
	m_cfg.cs = CellSize;
	m_cfg.ch = CellHeight;
	m_cfg.walkableSlopeAngle = WalkableSlopeAngle;
	m_cfg.walkableHeight = (int)ceilf(WorldUnitWalkableHeight / m_cfg.ch);
	m_cfg.walkableClimb = (int)floorf(WorldUnitWalkableClimb / m_cfg.ch);
	m_cfg.walkableRadius = (int)ceilf(WorldUnitWalkableRadius / m_cfg.cs);
	m_cfg.maxEdgeLen = 8;(int)MaxEdgeLen;
	m_cfg.maxSimplificationError = MaxSimplificationError;
	m_cfg.minRegionArea = (int)rcSqr(MinRegionSize);		// Note: area = size*size
	m_cfg.mergeRegionArea = (int)rcSqr(MergeRegionSize);	// Note: area = size*size
	m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
	m_cfg.tileSize = (int)TileSize;
	m_cfg.borderSize = m_cfg.walkableRadius + 3; // 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 = DetailSampleDist;
	m_cfg.detailSampleMaxError = DetailSampleMaxError;
    
    NSLog(@"Cell size: %0.2f", m_cfg.cs);
    NSLog(@"Cell height: %0.2f", m_cfg.ch);
    NSLog(@"Walkable slope angle: %0.2f", m_cfg.walkableSlopeAngle);
    NSLog(@"Walkable height: %d", m_cfg.walkableHeight);
    NSLog(@"Walkable climb: %d", m_cfg.walkableClimb);
    NSLog(@"Walkable radius: %d", m_cfg.walkableRadius);
    NSLog(@"Max edge length: %d", m_cfg.maxEdgeLen);
    NSLog(@"Tile size: %d", m_cfg.tileSize);
    NSLog(@"Border size: %d", m_cfg.borderSize);
    NSLog(@"Height: %d", m_cfg.height);
    NSLog(@"Width: %d", m_cfg.width);
    
    NSLog(@"Max edge length float: %0.2f", (WorldUnitWalkableRadius / CellSize) * 8);
    NSLog(@"%f %f", WorldUnitWalkableRadius, CellSize);

    rcVcopy(m_cfg.bmin, bmin);
	rcVcopy(m_cfg.bmax, bmax);

    // pad bounds with a border
	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;
    
    NSLog(@"Generating mesh (%d, %d) ", tx, ty);
    
    NSLog(@" 3x3 bounds...");
    NSLog(@"  min: {%0.2f, %0.2f, %0.2f}", bmin[0], bmin[1], bmin[2]);
    NSLog(@"  max: {%0.2f, %0.2f, %0.2f}", bmax[0], bmax[1], bmax[2]);
    
    // ** Get our bounds for the tile we care about
    rcMeshLoaderObj *loader2 = CreateMeshLoader(obj);
	InputGeom *geomTile = new InputGeom();
	if ( !geomTile->loadMesh(0, loader2) ){
        NSLog(@"Unable to load ObjData2(%d, %d)", tx, ty);
		return;
	}
    
    if ( !geomTile || !geomTile->getMesh() || !geomTile->getChunkyMesh() ){
		NSLog(@"buildNavigation: Input mesh is not specified.2(%d, %d)", tx, ty);
		return;
	}
    const float *bminTile = geomTile->getMeshBoundsMin();
	const float *bmaxTile = geomTile->getMeshBoundsMax();
    
    // save the tile's max/min not the 3x3!
    rcVcopy(m_cfg.bmin, bminTile);
	rcVcopy(m_cfg.bmax, bmaxTile);
    
    // this sets the dimensions of the heightfield - should maybe happen before border padding
    rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height);
    
    NSLog(@"Height: %d", m_cfg.height);
    NSLog(@"Width: %d", m_cfg.width);
    
    // pad bounds with a border
	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;
    
    NSLog(@" Tile bounds...");
    NSLog(@"  min: {%0.2f, %0.2f, %0.2f}", m_cfg.bmin[0], m_cfg.bmin[1], m_cfg.bmin[2]);
    NSLog(@"  max: {%0.2f, %0.2f, %0.2f}", m_cfg.bmax[0], m_cfg.bmax[1], m_cfg.bmax[2]);
```

Anyone have any ideas? I'm sure it's something so simple I'm missing.

Thanks!
~ Tanaris

----------


## namreeb

The cell size does not perfectly divide the tile size. Tile size is 533 + (1/3) so you need to do something like this (which I mentioned in the post you said you read :P):



```
const float TileSize = 533.0f + (1.0f / 3.0f);
const int VoxelCount = 1778;
const float CellSize = TileSize / (float)VoxelCount;
```

This ensures that the cell size will always perfectly divide the tile size. To adjust your voxel size, you change only the voxel count variable.

----------


## Tanaris4

What do you mean by perfect? b/c the above has a result of 0.29996250468691

----------


## namreeb

Meaning TileSize / CellSize must be an integer.

----------


## Tanaris4

I see your point now, but then why does everyone use 533.333333333 as the GRID_SIZE?

Wouldn't you ideally want your cell size to be the same (I guess or slightly above) WorldUnitWalkableRadius (0.2951389f).

So if I set my cell size as 0.3, TileSize at 533, and TileVoxelSize as 1778 I should be good to go?

Sorry I'm asking so many questions about things that should be rather obvious. Just seeking to understand.

Edit: Am I setting these correctly:


```
	m_cfg.width = m_cfg.tileSize;// + m_cfg.borderSize*2;
	m_cfg.height = m_cfg.tileSize;// + m_cfg.borderSize*2;
```

Or should it be:



```
rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height);
```

Edit 2: Here is the raw parameters printed out:


```
2011-12-16 17:55:27.089 Pocket-Pather[34121:707] Cell size: 0.30
2011-12-16 17:55:27.090 Pocket-Pather[34121:707] Cell height: 0.40
2011-12-16 17:55:27.090 Pocket-Pather[34121:707] Walkable slope angle: 50.00
2011-12-16 17:55:27.091 Pocket-Pather[34121:707] Walkable height: 5
2011-12-16 17:55:27.091 Pocket-Pather[34121:707] Walkable climb: 2
2011-12-16 17:55:27.091 Pocket-Pather[34121:707] Walkable radius: 1
2011-12-16 17:55:27.092 Pocket-Pather[34121:707] Max edge length: 8
2011-12-16 17:55:27.092 Pocket-Pather[34121:707] Tile size: 533
2011-12-16 17:55:27.092 Pocket-Pather[34121:707] Border size: 1
2011-12-16 17:55:27.093 Pocket-Pather[34121:707] Generating mesh (31, 31) 
2011-12-16 17:55:27.093 Pocket-Pather[34121:707]  3x3 bounds...
2011-12-16 17:55:27.093 Pocket-Pather[34121:707]   min: {-1095.64, -132.51, -1104.18}
2011-12-16 17:55:27.094 Pocket-Pather[34121:707]   max: {-0.00, 221.80, -0.00}
2011-12-16 17:55:27.239 Pocket-Pather[34121:707]  Tile bounds...
2011-12-16 17:55:27.240 Pocket-Pather[34121:707]   min: {-533.33, 0.00, -533.33}
2011-12-16 17:55:27.240 Pocket-Pather[34121:707]   max: {-0.00, 163.87, -0.00}
2011-12-16 17:55:27.241 Pocket-Pather[34121:707] Height: 1778
2011-12-16 17:55:27.241 Pocket-Pather[34121:707] Width: 1778
```

----------


## guizmows

check my pm Tanaris4

----------


## Bananenbrot

it's 533.33333333333 (or 1600/3) because one mcnk is 33.3333 (100/3 == (1600 / 3) / 16).
So, if you calculate based on a 533 tile size there will be a rounding error that would reflect itself in one or the other way into your output mesh.
I chose mcnk boundaries as tile size and even that caused overflow errors in triangle indices iirc...

----------


## Tanaris4

So I think I finally got it! But i'm running into a very strange issue. I'm just trying to gen a path from one point in AB to another, but if I literally increase the end point by like 20 yards it fails, which is strange, b/c it's in the same tile. Any ideas? I know RecastDemo is limited to 256 polys (max length), but i definitely haven't hit that, it's just going across about 3 tiles:

Edit: I've uploaded a video, watch the LAST click I make: http://www.youtube.com/watch?v=uupM_h_m2f8

Works:


Fails:


Edit 2: When I try to build the path using findPath, I'm getting back DT_PARTIAL_RESULT and DT_OUT_OF_NODES, which is horribly confusing me

Edit 3: GOT IT! dtStatus status = m_navQuery->init(_navMesh, 65536);

----------

