forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			803 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			803 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | //
 | ||
|  | // Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
 | ||
|  | //
 | ||
|  | // This software is provided 'as-is', without any express or implied
 | ||
|  | // warranty.  In no event will the authors be held liable for any damages
 | ||
|  | // arising from the use of this software.
 | ||
|  | // Permission is granted to anyone to use this software for any purpose,
 | ||
|  | // including commercial applications, and to alter it and redistribute it
 | ||
|  | // freely, subject to the following restrictions:
 | ||
|  | // 1. The origin of this software must not be misrepresented; you must not
 | ||
|  | //    claim that you wrote the original software. If you use this software
 | ||
|  | //    in a product, an acknowledgment in the product documentation would be
 | ||
|  | //    appreciated but is not required.
 | ||
|  | // 2. Altered source versions must be plainly marked as such, and must not be
 | ||
|  | //    misrepresented as being the original software.
 | ||
|  | // 3. This notice may not be removed or altered from any source distribution.
 | ||
|  | //
 | ||
|  | 
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <stdlib.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <float.h>
 | ||
|  | #include "DetourNavMesh.h"
 | ||
|  | #include "DetourCommon.h"
 | ||
|  | #include "DetourMath.h"
 | ||
|  | #include "DetourNavMeshBuilder.h"
 | ||
|  | #include "DetourAlloc.h"
 | ||
|  | #include "DetourAssert.h"
 | ||
|  | 
 | ||
|  | static unsigned short MESH_NULL_IDX = 0xffff; | ||
|  | 
 | ||
|  | 
 | ||
|  | struct BVItem | ||
|  | { | ||
|  | 	unsigned short bmin[3]; | ||
|  | 	unsigned short bmax[3]; | ||
|  | 	int i; | ||
|  | }; | ||
|  | 
 | ||
|  | static int compareItemX(const void* va, const void* vb) | ||
|  | { | ||
|  | 	const BVItem* a = (const BVItem*)va; | ||
|  | 	const BVItem* b = (const BVItem*)vb; | ||
|  | 	if (a->bmin[0] < b->bmin[0]) | ||
|  | 		return -1; | ||
|  | 	if (a->bmin[0] > b->bmin[0]) | ||
|  | 		return 1; | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static int compareItemY(const void* va, const void* vb) | ||
|  | { | ||
|  | 	const BVItem* a = (const BVItem*)va; | ||
|  | 	const BVItem* b = (const BVItem*)vb; | ||
|  | 	if (a->bmin[1] < b->bmin[1]) | ||
|  | 		return -1; | ||
|  | 	if (a->bmin[1] > b->bmin[1]) | ||
|  | 		return 1; | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static int compareItemZ(const void* va, const void* vb) | ||
|  | { | ||
|  | 	const BVItem* a = (const BVItem*)va; | ||
|  | 	const BVItem* b = (const BVItem*)vb; | ||
|  | 	if (a->bmin[2] < b->bmin[2]) | ||
|  | 		return -1; | ||
|  | 	if (a->bmin[2] > b->bmin[2]) | ||
|  | 		return 1; | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static void calcExtends(BVItem* items, const int /*nitems*/, const int imin, const int imax, | ||
|  | 						unsigned short* bmin, unsigned short* bmax) | ||
|  | { | ||
|  | 	bmin[0] = items[imin].bmin[0]; | ||
|  | 	bmin[1] = items[imin].bmin[1]; | ||
|  | 	bmin[2] = items[imin].bmin[2]; | ||
|  | 	 | ||
|  | 	bmax[0] = items[imin].bmax[0]; | ||
|  | 	bmax[1] = items[imin].bmax[1]; | ||
|  | 	bmax[2] = items[imin].bmax[2]; | ||
|  | 	 | ||
|  | 	for (int i = imin+1; i < imax; ++i) | ||
|  | 	{ | ||
|  | 		const BVItem& it = items[i]; | ||
|  | 		if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0]; | ||
|  | 		if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1]; | ||
|  | 		if (it.bmin[2] < bmin[2]) bmin[2] = it.bmin[2]; | ||
|  | 		 | ||
|  | 		if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0]; | ||
|  | 		if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1]; | ||
|  | 		if (it.bmax[2] > bmax[2]) bmax[2] = it.bmax[2]; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | inline int longestAxis(unsigned short x, unsigned short y, unsigned short z) | ||
|  | { | ||
|  | 	int	axis = 0; | ||
|  | 	unsigned short maxVal = x; | ||
|  | 	if (y > maxVal) | ||
|  | 	{ | ||
|  | 		axis = 1; | ||
|  | 		maxVal = y; | ||
|  | 	} | ||
|  | 	if (z > maxVal) | ||
|  | 	{ | ||
|  | 		axis = 2; | ||
|  | 	} | ||
|  | 	return axis; | ||
|  | } | ||
|  | 
 | ||
|  | static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNode, dtBVNode* nodes) | ||
|  | { | ||
|  | 	int inum = imax - imin; | ||
|  | 	int icur = curNode; | ||
|  | 	 | ||
|  | 	dtBVNode& node = nodes[curNode++]; | ||
|  | 	 | ||
|  | 	if (inum == 1) | ||
|  | 	{ | ||
|  | 		// Leaf
 | ||
|  | 		node.bmin[0] = items[imin].bmin[0]; | ||
|  | 		node.bmin[1] = items[imin].bmin[1]; | ||
|  | 		node.bmin[2] = items[imin].bmin[2]; | ||
|  | 		 | ||
|  | 		node.bmax[0] = items[imin].bmax[0]; | ||
|  | 		node.bmax[1] = items[imin].bmax[1]; | ||
|  | 		node.bmax[2] = items[imin].bmax[2]; | ||
|  | 		 | ||
|  | 		node.i = items[imin].i; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 		// Split
 | ||
|  | 		calcExtends(items, nitems, imin, imax, node.bmin, node.bmax); | ||
|  | 		 | ||
|  | 		int	axis = longestAxis(node.bmax[0] - node.bmin[0], | ||
|  | 							   node.bmax[1] - node.bmin[1], | ||
|  | 							   node.bmax[2] - node.bmin[2]); | ||
|  | 		 | ||
|  | 		if (axis == 0) | ||
|  | 		{ | ||
|  | 			// Sort along x-axis
 | ||
|  | 			qsort(items+imin, inum, sizeof(BVItem), compareItemX); | ||
|  | 		} | ||
|  | 		else if (axis == 1) | ||
|  | 		{ | ||
|  | 			// Sort along y-axis
 | ||
|  | 			qsort(items+imin, inum, sizeof(BVItem), compareItemY); | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			// Sort along z-axis
 | ||
|  | 			qsort(items+imin, inum, sizeof(BVItem), compareItemZ); | ||
|  | 		} | ||
|  | 		 | ||
|  | 		int isplit = imin+inum/2; | ||
|  | 		 | ||
|  | 		// Left
 | ||
|  | 		subdivide(items, nitems, imin, isplit, curNode, nodes); | ||
|  | 		// Right
 | ||
|  | 		subdivide(items, nitems, isplit, imax, curNode, nodes); | ||
|  | 		 | ||
|  | 		int iescape = curNode - icur; | ||
|  | 		// Negative index means escape.
 | ||
|  | 		node.i = -iescape; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static int createBVTree(dtNavMeshCreateParams* params, dtBVNode* nodes, int /*nnodes*/) | ||
|  | { | ||
|  | 	// Build tree
 | ||
|  | 	float quantFactor = 1 / params->cs; | ||
|  | 	BVItem* items = (BVItem*)dtAlloc(sizeof(BVItem)*params->polyCount, DT_ALLOC_TEMP); | ||
|  | 	for (int i = 0; i < params->polyCount; i++) | ||
|  | 	{ | ||
|  | 		BVItem& it = items[i]; | ||
|  | 		it.i = i; | ||
|  | 		// Calc polygon bounds. Use detail meshes if available.
 | ||
|  | 		if (params->detailMeshes) | ||
|  | 		{ | ||
|  | 			int vb = (int)params->detailMeshes[i*4+0]; | ||
|  | 			int ndv = (int)params->detailMeshes[i*4+1]; | ||
|  | 			float bmin[3]; | ||
|  | 			float bmax[3]; | ||
|  | 
 | ||
|  | 			const float* dv = ¶ms->detailVerts[vb*3]; | ||
|  | 			dtVcopy(bmin, dv); | ||
|  | 			dtVcopy(bmax, dv); | ||
|  | 
 | ||
|  | 			for (int j = 1; j < ndv; j++) | ||
|  | 			{ | ||
|  | 				dtVmin(bmin, &dv[j * 3]); | ||
|  | 				dtVmax(bmax, &dv[j * 3]); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// BV-tree uses cs for all dimensions
 | ||
|  | 			it.bmin[0] = (unsigned short)dtClamp((int)((bmin[0] - params->bmin[0])*quantFactor), 0, 0xffff); | ||
|  | 			it.bmin[1] = (unsigned short)dtClamp((int)((bmin[1] - params->bmin[1])*quantFactor), 0, 0xffff); | ||
|  | 			it.bmin[2] = (unsigned short)dtClamp((int)((bmin[2] - params->bmin[2])*quantFactor), 0, 0xffff); | ||
|  | 
 | ||
|  | 			it.bmax[0] = (unsigned short)dtClamp((int)((bmax[0] - params->bmin[0])*quantFactor), 0, 0xffff); | ||
|  | 			it.bmax[1] = (unsigned short)dtClamp((int)((bmax[1] - params->bmin[1])*quantFactor), 0, 0xffff); | ||
|  | 			it.bmax[2] = (unsigned short)dtClamp((int)((bmax[2] - params->bmin[2])*quantFactor), 0, 0xffff); | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			const unsigned short* p = ¶ms->polys[i*params->nvp * 2]; | ||
|  | 			it.bmin[0] = it.bmax[0] = params->verts[p[0] * 3 + 0]; | ||
|  | 			it.bmin[1] = it.bmax[1] = params->verts[p[0] * 3 + 1]; | ||
|  | 			it.bmin[2] = it.bmax[2] = params->verts[p[0] * 3 + 2]; | ||
|  | 
 | ||
|  | 			for (int j = 1; j < params->nvp; ++j) | ||
|  | 			{ | ||
|  | 				if (p[j] == MESH_NULL_IDX) break; | ||
|  | 				unsigned short x = params->verts[p[j] * 3 + 0]; | ||
|  | 				unsigned short y = params->verts[p[j] * 3 + 1]; | ||
|  | 				unsigned short z = params->verts[p[j] * 3 + 2]; | ||
|  | 
 | ||
|  | 				if (x < it.bmin[0]) it.bmin[0] = x; | ||
|  | 				if (y < it.bmin[1]) it.bmin[1] = y; | ||
|  | 				if (z < it.bmin[2]) it.bmin[2] = z; | ||
|  | 
 | ||
|  | 				if (x > it.bmax[0]) it.bmax[0] = x; | ||
|  | 				if (y > it.bmax[1]) it.bmax[1] = y; | ||
|  | 				if (z > it.bmax[2]) it.bmax[2] = z; | ||
|  | 			} | ||
|  | 			// Remap y
 | ||
|  | 			it.bmin[1] = (unsigned short)dtMathFloorf((float)it.bmin[1] * params->ch / params->cs); | ||
|  | 			it.bmax[1] = (unsigned short)dtMathCeilf((float)it.bmax[1] * params->ch / params->cs); | ||
|  | 		} | ||
|  | 	} | ||
|  | 	 | ||
|  | 	int curNode = 0; | ||
|  | 	subdivide(items, params->polyCount, 0, params->polyCount, curNode, nodes); | ||
|  | 	 | ||
|  | 	dtFree(items); | ||
|  | 	 | ||
|  | 	return curNode; | ||
|  | } | ||
|  | 
 | ||
|  | static unsigned char classifyOffMeshPoint(const float* pt, const float* bmin, const float* bmax) | ||
|  | { | ||
|  | 	static const unsigned char XP = 1<<0; | ||
|  | 	static const unsigned char ZP = 1<<1; | ||
|  | 	static const unsigned char XM = 1<<2; | ||
|  | 	static const unsigned char ZM = 1<<3;	 | ||
|  | 
 | ||
|  | 	unsigned char outcode = 0;  | ||
|  | 	outcode |= (pt[0] >= bmax[0]) ? XP : 0; | ||
|  | 	outcode |= (pt[2] >= bmax[2]) ? ZP : 0; | ||
|  | 	outcode |= (pt[0] < bmin[0])  ? XM : 0; | ||
|  | 	outcode |= (pt[2] < bmin[2])  ? ZM : 0; | ||
|  | 
 | ||
|  | 	switch (outcode) | ||
|  | 	{ | ||
|  | 	case XP: return 0; | ||
|  | 	case XP|ZP: return 1; | ||
|  | 	case ZP: return 2; | ||
|  | 	case XM|ZP: return 3; | ||
|  | 	case XM: return 4; | ||
|  | 	case XM|ZM: return 5; | ||
|  | 	case ZM: return 6; | ||
|  | 	case XP|ZM: return 7; | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	return 0xff;	 | ||
|  | } | ||
|  | 
 | ||
|  | // TODO: Better error handling.
 | ||
|  | 
 | ||
|  | /// @par
 | ||
|  | /// 
 | ||
|  | /// The output data array is allocated using the detour allocator (dtAlloc()).  The method
 | ||
|  | /// used to free the memory will be determined by how the tile is added to the navigation
 | ||
|  | /// mesh.
 | ||
|  | ///
 | ||
|  | /// @see dtNavMesh, dtNavMesh::addTile()
 | ||
|  | bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize) | ||
|  | { | ||
|  | 	if (params->nvp > DT_VERTS_PER_POLYGON) | ||
|  | 		return false; | ||
|  | 	if (params->vertCount >= 0xffff) | ||
|  | 		return false; | ||
|  | 	if (!params->vertCount || !params->verts) | ||
|  | 		return false; | ||
|  | 	if (!params->polyCount || !params->polys) | ||
|  | 		return false; | ||
|  | 
 | ||
|  | 	const int nvp = params->nvp; | ||
|  | 	 | ||
|  | 	// Classify off-mesh connection points. We store only the connections
 | ||
|  | 	// whose start point is inside the tile.
 | ||
|  | 	unsigned char* offMeshConClass = 0; | ||
|  | 	int storedOffMeshConCount = 0; | ||
|  | 	int offMeshConLinkCount = 0; | ||
|  | 	 | ||
|  | 	if (params->offMeshConCount > 0) | ||
|  | 	{ | ||
|  | 		offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP); | ||
|  | 		if (!offMeshConClass) | ||
|  | 			return false; | ||
|  | 
 | ||
|  | 		// Find tight heigh bounds, used for culling out off-mesh start locations.
 | ||
|  | 		float hmin = FLT_MAX; | ||
|  | 		float hmax = -FLT_MAX; | ||
|  | 		 | ||
|  | 		if (params->detailVerts && params->detailVertsCount) | ||
|  | 		{ | ||
|  | 			for (int i = 0; i < params->detailVertsCount; ++i) | ||
|  | 			{ | ||
|  | 				const float h = params->detailVerts[i*3+1]; | ||
|  | 				hmin = dtMin(hmin,h); | ||
|  | 				hmax = dtMax(hmax,h); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			for (int i = 0; i < params->vertCount; ++i) | ||
|  | 			{ | ||
|  | 				const unsigned short* iv = ¶ms->verts[i*3]; | ||
|  | 				const float h = params->bmin[1] + iv[1] * params->ch; | ||
|  | 				hmin = dtMin(hmin,h); | ||
|  | 				hmax = dtMax(hmax,h); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		hmin -= params->walkableClimb; | ||
|  | 		hmax += params->walkableClimb; | ||
|  | 		float bmin[3], bmax[3]; | ||
|  | 		dtVcopy(bmin, params->bmin); | ||
|  | 		dtVcopy(bmax, params->bmax); | ||
|  | 		bmin[1] = hmin; | ||
|  | 		bmax[1] = hmax; | ||
|  | 
 | ||
|  | 		for (int i = 0; i < params->offMeshConCount; ++i) | ||
|  | 		{ | ||
|  | 			const float* p0 = ¶ms->offMeshConVerts[(i*2+0)*3]; | ||
|  | 			const float* p1 = ¶ms->offMeshConVerts[(i*2+1)*3]; | ||
|  | 			offMeshConClass[i*2+0] = classifyOffMeshPoint(p0, bmin, bmax); | ||
|  | 			offMeshConClass[i*2+1] = classifyOffMeshPoint(p1, bmin, bmax); | ||
|  | 
 | ||
|  | 			// Zero out off-mesh start positions which are not even potentially touching the mesh.
 | ||
|  | 			if (offMeshConClass[i*2+0] == 0xff) | ||
|  | 			{ | ||
|  | 				if (p0[1] < bmin[1] || p0[1] > bmax[1]) | ||
|  | 					offMeshConClass[i*2+0] = 0; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// Cound how many links should be allocated for off-mesh connections.
 | ||
|  | 			if (offMeshConClass[i*2+0] == 0xff) | ||
|  | 				offMeshConLinkCount++; | ||
|  | 			if (offMeshConClass[i*2+1] == 0xff) | ||
|  | 				offMeshConLinkCount++; | ||
|  | 
 | ||
|  | 			if (offMeshConClass[i*2+0] == 0xff) | ||
|  | 				storedOffMeshConCount++; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	 | ||
|  | 	// Off-mesh connectionss are stored as polygons, adjust values.
 | ||
|  | 	const int totPolyCount = params->polyCount + storedOffMeshConCount; | ||
|  | 	const int totVertCount = params->vertCount + storedOffMeshConCount*2; | ||
|  | 	 | ||
|  | 	// Find portal edges which are at tile borders.
 | ||
|  | 	int edgeCount = 0; | ||
|  | 	int portalCount = 0; | ||
|  | 	for (int i = 0; i < params->polyCount; ++i) | ||
|  | 	{ | ||
|  | 		const unsigned short* p = ¶ms->polys[i*2*nvp]; | ||
|  | 		for (int j = 0; j < nvp; ++j) | ||
|  | 		{ | ||
|  | 			if (p[j] == MESH_NULL_IDX) break; | ||
|  | 			edgeCount++; | ||
|  | 			 | ||
|  | 			if (p[nvp+j] & 0x8000) | ||
|  | 			{ | ||
|  | 				unsigned short dir = p[nvp+j] & 0xf; | ||
|  | 				if (dir != 0xf) | ||
|  | 					portalCount++; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2; | ||
|  | 	 | ||
|  | 	// Find unique detail vertices.
 | ||
|  | 	int uniqueDetailVertCount = 0; | ||
|  | 	int detailTriCount = 0; | ||
|  | 	if (params->detailMeshes) | ||
|  | 	{ | ||
|  | 		// Has detail mesh, count unique detail vertex count and use input detail tri count.
 | ||
|  | 		detailTriCount = params->detailTriCount; | ||
|  | 		for (int i = 0; i < params->polyCount; ++i) | ||
|  | 		{ | ||
|  | 			const unsigned short* p = ¶ms->polys[i*nvp*2]; | ||
|  | 			int ndv = params->detailMeshes[i*4+1]; | ||
|  | 			int nv = 0; | ||
|  | 			for (int j = 0; j < nvp; ++j) | ||
|  | 			{ | ||
|  | 				if (p[j] == MESH_NULL_IDX) break; | ||
|  | 				nv++; | ||
|  | 			} | ||
|  | 			ndv -= nv; | ||
|  | 			uniqueDetailVertCount += ndv; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 		// No input detail mesh, build detail mesh from nav polys.
 | ||
|  | 		uniqueDetailVertCount = 0; // No extra detail verts.
 | ||
|  | 		detailTriCount = 0; | ||
|  | 		for (int i = 0; i < params->polyCount; ++i) | ||
|  | 		{ | ||
|  | 			const unsigned short* p = ¶ms->polys[i*nvp*2]; | ||
|  | 			int nv = 0; | ||
|  | 			for (int j = 0; j < nvp; ++j) | ||
|  | 			{ | ||
|  | 				if (p[j] == MESH_NULL_IDX) break; | ||
|  | 				nv++; | ||
|  | 			} | ||
|  | 			detailTriCount += nv-2; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	 | ||
|  | 	// Calculate data size
 | ||
|  | 	const int headerSize = dtAlign4(sizeof(dtMeshHeader)); | ||
|  | 	const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount); | ||
|  | 	const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount); | ||
|  | 	const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount); | ||
|  | 	const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount); | ||
|  | 	const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount); | ||
|  | 	const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*detailTriCount); | ||
|  | 	const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0; | ||
|  | 	const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount); | ||
|  | 	 | ||
|  | 	const int dataSize = headerSize + vertsSize + polysSize + linksSize + | ||
|  | 						 detailMeshesSize + detailVertsSize + detailTrisSize + | ||
|  | 						 bvTreeSize + offMeshConsSize; | ||
|  | 						  | ||
|  | 	unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM); | ||
|  | 	if (!data) | ||
|  | 	{ | ||
|  | 		dtFree(offMeshConClass); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 	memset(data, 0, dataSize); | ||
|  | 	 | ||
|  | 	unsigned char* d = data; | ||
|  | 
 | ||
|  | 	dtMeshHeader* header = dtGetThenAdvanceBufferPointer<dtMeshHeader>(d, headerSize); | ||
|  | 	float* navVerts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize); | ||
|  | 	dtPoly* navPolys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize); | ||
|  | 	d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load.
 | ||
|  | 	dtPolyDetail* navDMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize); | ||
|  | 	float* navDVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize); | ||
|  | 	unsigned char* navDTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize); | ||
|  | 	dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize); | ||
|  | 	dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize); | ||
|  | 	 | ||
|  | 	 | ||
|  | 	// Store header
 | ||
|  | 	header->magic = DT_NAVMESH_MAGIC; | ||
|  | 	header->version = DT_NAVMESH_VERSION; | ||
|  | 	header->x = params->tileX; | ||
|  | 	header->y = params->tileY; | ||
|  | 	header->layer = params->tileLayer; | ||
|  | 	header->userId = params->userId; | ||
|  | 	header->polyCount = totPolyCount; | ||
|  | 	header->vertCount = totVertCount; | ||
|  | 	header->maxLinkCount = maxLinkCount; | ||
|  | 	dtVcopy(header->bmin, params->bmin); | ||
|  | 	dtVcopy(header->bmax, params->bmax); | ||
|  | 	header->detailMeshCount = params->polyCount; | ||
|  | 	header->detailVertCount = uniqueDetailVertCount; | ||
|  | 	header->detailTriCount = detailTriCount; | ||
|  | 	header->bvQuantFactor = 1.0f / params->cs; | ||
|  | 	header->offMeshBase = params->polyCount; | ||
|  | 	header->walkableHeight = params->walkableHeight; | ||
|  | 	header->walkableRadius = params->walkableRadius; | ||
|  | 	header->walkableClimb = params->walkableClimb; | ||
|  | 	header->offMeshConCount = storedOffMeshConCount; | ||
|  | 	header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0; | ||
|  | 	 | ||
|  | 	const int offMeshVertsBase = params->vertCount; | ||
|  | 	const int offMeshPolyBase = params->polyCount; | ||
|  | 	 | ||
|  | 	// Store vertices
 | ||
|  | 	// Mesh vertices
 | ||
|  | 	for (int i = 0; i < params->vertCount; ++i) | ||
|  | 	{ | ||
|  | 		const unsigned short* iv = ¶ms->verts[i*3]; | ||
|  | 		float* v = &navVerts[i*3]; | ||
|  | 		v[0] = params->bmin[0] + iv[0] * params->cs; | ||
|  | 		v[1] = params->bmin[1] + iv[1] * params->ch; | ||
|  | 		v[2] = params->bmin[2] + iv[2] * params->cs; | ||
|  | 	} | ||
|  | 	// Off-mesh link vertices.
 | ||
|  | 	int n = 0; | ||
|  | 	for (int i = 0; i < params->offMeshConCount; ++i) | ||
|  | 	{ | ||
|  | 		// Only store connections which start from this tile.
 | ||
|  | 		if (offMeshConClass[i*2+0] == 0xff) | ||
|  | 		{ | ||
|  | 			const float* linkv = ¶ms->offMeshConVerts[i*2*3]; | ||
|  | 			float* v = &navVerts[(offMeshVertsBase + n*2)*3]; | ||
|  | 			dtVcopy(&v[0], &linkv[0]); | ||
|  | 			dtVcopy(&v[3], &linkv[3]); | ||
|  | 			n++; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	 | ||
|  | 	// Store polygons
 | ||
|  | 	// Mesh polys
 | ||
|  | 	const unsigned short* src = params->polys; | ||
|  | 	for (int i = 0; i < params->polyCount; ++i) | ||
|  | 	{ | ||
|  | 		dtPoly* p = &navPolys[i]; | ||
|  | 		p->vertCount = 0; | ||
|  | 		p->flags = params->polyFlags[i]; | ||
|  | 		p->setArea(params->polyAreas[i]); | ||
|  | 		p->setType(DT_POLYTYPE_GROUND); | ||
|  | 		for (int j = 0; j < nvp; ++j) | ||
|  | 		{ | ||
|  | 			if (src[j] == MESH_NULL_IDX) break; | ||
|  | 			p->verts[j] = src[j]; | ||
|  | 			if (src[nvp+j] & 0x8000) | ||
|  | 			{ | ||
|  | 				// Border or portal edge.
 | ||
|  | 				unsigned short dir = src[nvp+j] & 0xf; | ||
|  | 				if (dir == 0xf) // Border
 | ||
|  | 					p->neis[j] = 0; | ||
|  | 				else if (dir == 0) // Portal x-
 | ||
|  | 					p->neis[j] = DT_EXT_LINK | 4; | ||
|  | 				else if (dir == 1) // Portal z+
 | ||
|  | 					p->neis[j] = DT_EXT_LINK | 2; | ||
|  | 				else if (dir == 2) // Portal x+
 | ||
|  | 					p->neis[j] = DT_EXT_LINK | 0; | ||
|  | 				else if (dir == 3) // Portal z-
 | ||
|  | 					p->neis[j] = DT_EXT_LINK | 6; | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				// Normal connection
 | ||
|  | 				p->neis[j] = src[nvp+j]+1; | ||
|  | 			} | ||
|  | 			 | ||
|  | 			p->vertCount++; | ||
|  | 		} | ||
|  | 		src += nvp*2; | ||
|  | 	} | ||
|  | 	// Off-mesh connection vertices.
 | ||
|  | 	n = 0; | ||
|  | 	for (int i = 0; i < params->offMeshConCount; ++i) | ||
|  | 	{ | ||
|  | 		// Only store connections which start from this tile.
 | ||
|  | 		if (offMeshConClass[i*2+0] == 0xff) | ||
|  | 		{ | ||
|  | 			dtPoly* p = &navPolys[offMeshPolyBase+n]; | ||
|  | 			p->vertCount = 2; | ||
|  | 			p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0); | ||
|  | 			p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1); | ||
|  | 			p->flags = params->offMeshConFlags[i]; | ||
|  | 			p->setArea(params->offMeshConAreas[i]); | ||
|  | 			p->setType(DT_POLYTYPE_OFFMESH_CONNECTION); | ||
|  | 			n++; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Store detail meshes and vertices.
 | ||
|  | 	// The nav polygon vertices are stored as the first vertices on each mesh.
 | ||
|  | 	// We compress the mesh data by skipping them and using the navmesh coordinates.
 | ||
|  | 	if (params->detailMeshes) | ||
|  | 	{ | ||
|  | 		unsigned short vbase = 0; | ||
|  | 		for (int i = 0; i < params->polyCount; ++i) | ||
|  | 		{ | ||
|  | 			dtPolyDetail& dtl = navDMeshes[i]; | ||
|  | 			const int vb = (int)params->detailMeshes[i*4+0]; | ||
|  | 			const int ndv = (int)params->detailMeshes[i*4+1]; | ||
|  | 			const int nv = navPolys[i].vertCount; | ||
|  | 			dtl.vertBase = (unsigned int)vbase; | ||
|  | 			dtl.vertCount = (unsigned char)(ndv-nv); | ||
|  | 			dtl.triBase = (unsigned int)params->detailMeshes[i*4+2]; | ||
|  | 			dtl.triCount = (unsigned char)params->detailMeshes[i*4+3]; | ||
|  | 			// Copy vertices except the first 'nv' verts which are equal to nav poly verts.
 | ||
|  | 			if (ndv-nv) | ||
|  | 			{ | ||
|  | 				memcpy(&navDVerts[vbase*3], ¶ms->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv)); | ||
|  | 				vbase += (unsigned short)(ndv-nv); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		// Store triangles.
 | ||
|  | 		memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount); | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 		// Create dummy detail mesh by triangulating polys.
 | ||
|  | 		int tbase = 0; | ||
|  | 		for (int i = 0; i < params->polyCount; ++i) | ||
|  | 		{ | ||
|  | 			dtPolyDetail& dtl = navDMeshes[i]; | ||
|  | 			const int nv = navPolys[i].vertCount; | ||
|  | 			dtl.vertBase = 0; | ||
|  | 			dtl.vertCount = 0; | ||
|  | 			dtl.triBase = (unsigned int)tbase; | ||
|  | 			dtl.triCount = (unsigned char)(nv-2); | ||
|  | 			// Triangulate polygon (local indices).
 | ||
|  | 			for (int j = 2; j < nv; ++j) | ||
|  | 			{ | ||
|  | 				unsigned char* t = &navDTris[tbase*4]; | ||
|  | 				t[0] = 0; | ||
|  | 				t[1] = (unsigned char)(j-1); | ||
|  | 				t[2] = (unsigned char)j; | ||
|  | 				// Bit for each edge that belongs to poly boundary.
 | ||
|  | 				t[3] = (1<<2); | ||
|  | 				if (j == 2) t[3] |= (1<<0); | ||
|  | 				if (j == nv-1) t[3] |= (1<<4); | ||
|  | 				tbase++; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Store and create BVtree.
 | ||
|  | 	if (params->buildBvTree) | ||
|  | 	{ | ||
|  | 		createBVTree(params, navBvtree, 2*params->polyCount); | ||
|  | 	} | ||
|  | 	 | ||
|  | 	// Store Off-Mesh connections.
 | ||
|  | 	n = 0; | ||
|  | 	for (int i = 0; i < params->offMeshConCount; ++i) | ||
|  | 	{ | ||
|  | 		// Only store connections which start from this tile.
 | ||
|  | 		if (offMeshConClass[i*2+0] == 0xff) | ||
|  | 		{ | ||
|  | 			dtOffMeshConnection* con = &offMeshCons[n]; | ||
|  | 			con->poly = (unsigned short)(offMeshPolyBase + n); | ||
|  | 			// Copy connection end-points.
 | ||
|  | 			const float* endPts = ¶ms->offMeshConVerts[i*2*3]; | ||
|  | 			dtVcopy(&con->pos[0], &endPts[0]); | ||
|  | 			dtVcopy(&con->pos[3], &endPts[3]); | ||
|  | 			con->rad = params->offMeshConRad[i]; | ||
|  | 			con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0; | ||
|  | 			con->side = offMeshConClass[i*2+1]; | ||
|  | 			if (params->offMeshConUserID) | ||
|  | 				con->userId = params->offMeshConUserID[i]; | ||
|  | 			n++; | ||
|  | 		} | ||
|  | 	} | ||
|  | 		 | ||
|  | 	dtFree(offMeshConClass); | ||
|  | 	 | ||
|  | 	*outData = data; | ||
|  | 	*outDataSize = dataSize; | ||
|  | 	 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/) | ||
|  | { | ||
|  | 	dtMeshHeader* header = (dtMeshHeader*)data; | ||
|  | 	 | ||
|  | 	int swappedMagic = DT_NAVMESH_MAGIC; | ||
|  | 	int swappedVersion = DT_NAVMESH_VERSION; | ||
|  | 	dtSwapEndian(&swappedMagic); | ||
|  | 	dtSwapEndian(&swappedVersion); | ||
|  | 	 | ||
|  | 	if ((header->magic != DT_NAVMESH_MAGIC || header->version != DT_NAVMESH_VERSION) && | ||
|  | 		(header->magic != swappedMagic || header->version != swappedVersion)) | ||
|  | 	{ | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 		 | ||
|  | 	dtSwapEndian(&header->magic); | ||
|  | 	dtSwapEndian(&header->version); | ||
|  | 	dtSwapEndian(&header->x); | ||
|  | 	dtSwapEndian(&header->y); | ||
|  | 	dtSwapEndian(&header->layer); | ||
|  | 	dtSwapEndian(&header->userId); | ||
|  | 	dtSwapEndian(&header->polyCount); | ||
|  | 	dtSwapEndian(&header->vertCount); | ||
|  | 	dtSwapEndian(&header->maxLinkCount); | ||
|  | 	dtSwapEndian(&header->detailMeshCount); | ||
|  | 	dtSwapEndian(&header->detailVertCount); | ||
|  | 	dtSwapEndian(&header->detailTriCount); | ||
|  | 	dtSwapEndian(&header->bvNodeCount); | ||
|  | 	dtSwapEndian(&header->offMeshConCount); | ||
|  | 	dtSwapEndian(&header->offMeshBase); | ||
|  | 	dtSwapEndian(&header->walkableHeight); | ||
|  | 	dtSwapEndian(&header->walkableRadius); | ||
|  | 	dtSwapEndian(&header->walkableClimb); | ||
|  | 	dtSwapEndian(&header->bmin[0]); | ||
|  | 	dtSwapEndian(&header->bmin[1]); | ||
|  | 	dtSwapEndian(&header->bmin[2]); | ||
|  | 	dtSwapEndian(&header->bmax[0]); | ||
|  | 	dtSwapEndian(&header->bmax[1]); | ||
|  | 	dtSwapEndian(&header->bmax[2]); | ||
|  | 	dtSwapEndian(&header->bvQuantFactor); | ||
|  | 
 | ||
|  | 	// Freelist index and pointers are updated when tile is added, no need to swap.
 | ||
|  | 
 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | /// @par
 | ||
|  | ///
 | ||
|  | /// @warning This function assumes that the header is in the correct endianess already. 
 | ||
|  | /// Call #dtNavMeshHeaderSwapEndian() first on the data if the data is expected to be in wrong endianess 
 | ||
|  | /// to start with. Call #dtNavMeshHeaderSwapEndian() after the data has been swapped if converting from 
 | ||
|  | /// native to foreign endianess.
 | ||
|  | bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/) | ||
|  | { | ||
|  | 	// Make sure the data is in right format.
 | ||
|  | 	dtMeshHeader* header = (dtMeshHeader*)data; | ||
|  | 	if (header->magic != DT_NAVMESH_MAGIC) | ||
|  | 		return false; | ||
|  | 	if (header->version != DT_NAVMESH_VERSION) | ||
|  | 		return false; | ||
|  | 	 | ||
|  | 	// Patch header pointers.
 | ||
|  | 	const int headerSize = dtAlign4(sizeof(dtMeshHeader)); | ||
|  | 	const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount); | ||
|  | 	const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount); | ||
|  | 	const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount)); | ||
|  | 	const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount); | ||
|  | 	const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount); | ||
|  | 	const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount); | ||
|  | 	const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount); | ||
|  | 	const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount); | ||
|  | 	 | ||
|  | 	unsigned char* d = data + headerSize; | ||
|  | 	float* verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize); | ||
|  | 	dtPoly* polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize); | ||
|  | 	d += linksSize; // Ignore links; they technically should be endian-swapped but all their data is overwritten on load anyway.
 | ||
|  | 	//dtLink* links = dtGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
 | ||
|  | 	dtPolyDetail* detailMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize); | ||
|  | 	float* detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize); | ||
|  | 	d += detailTrisSize; // Ignore detail tris; single bytes can't be endian-swapped.
 | ||
|  | 	//unsigned char* detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
 | ||
|  | 	dtBVNode* bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize); | ||
|  | 	dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize); | ||
|  | 	 | ||
|  | 	// Vertices
 | ||
|  | 	for (int i = 0; i < header->vertCount*3; ++i) | ||
|  | 	{ | ||
|  | 		dtSwapEndian(&verts[i]); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Polys
 | ||
|  | 	for (int i = 0; i < header->polyCount; ++i) | ||
|  | 	{ | ||
|  | 		dtPoly* p = &polys[i]; | ||
|  | 		// poly->firstLink is update when tile is added, no need to swap.
 | ||
|  | 		for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j) | ||
|  | 		{ | ||
|  | 			dtSwapEndian(&p->verts[j]); | ||
|  | 			dtSwapEndian(&p->neis[j]); | ||
|  | 		} | ||
|  | 		dtSwapEndian(&p->flags); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Links are rebuild when tile is added, no need to swap.
 | ||
|  | 
 | ||
|  | 	// Detail meshes
 | ||
|  | 	for (int i = 0; i < header->detailMeshCount; ++i) | ||
|  | 	{ | ||
|  | 		dtPolyDetail* pd = &detailMeshes[i]; | ||
|  | 		dtSwapEndian(&pd->vertBase); | ||
|  | 		dtSwapEndian(&pd->triBase); | ||
|  | 	} | ||
|  | 	 | ||
|  | 	// Detail verts
 | ||
|  | 	for (int i = 0; i < header->detailVertCount*3; ++i) | ||
|  | 	{ | ||
|  | 		dtSwapEndian(&detailVerts[i]); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// BV-tree
 | ||
|  | 	for (int i = 0; i < header->bvNodeCount; ++i) | ||
|  | 	{ | ||
|  | 		dtBVNode* node = &bvTree[i]; | ||
|  | 		for (int j = 0; j < 3; ++j) | ||
|  | 		{ | ||
|  | 			dtSwapEndian(&node->bmin[j]); | ||
|  | 			dtSwapEndian(&node->bmax[j]); | ||
|  | 		} | ||
|  | 		dtSwapEndian(&node->i); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Off-mesh Connections.
 | ||
|  | 	for (int i = 0; i < header->offMeshConCount; ++i) | ||
|  | 	{ | ||
|  | 		dtOffMeshConnection* con = &offMeshCons[i]; | ||
|  | 		for (int j = 0; j < 6; ++j) | ||
|  | 			dtSwapEndian(&con->pos[j]); | ||
|  | 		dtSwapEndian(&con->rad); | ||
|  | 		dtSwapEndian(&con->poly); | ||
|  | 	} | ||
|  | 	 | ||
|  | 	return true; | ||
|  | } |