576 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			576 lines
		
	
	
		
			14 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 <float.h>
 | |
| #define _USE_MATH_DEFINES
 | |
| #include <math.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <stdarg.h>
 | |
| #include "Recast.h"
 | |
| #include "RecastAlloc.h"
 | |
| #include "RecastAssert.h"
 | |
| 
 | |
| namespace
 | |
| {
 | |
| /// Allocates and constructs an object of the given type, returning a pointer.
 | |
| /// TODO: Support constructor args.
 | |
| /// @param[in]		hint	Hint to the allocator.
 | |
| template <typename T>
 | |
| T* rcNew(rcAllocHint hint) {
 | |
| 	T* ptr = (T*)rcAlloc(sizeof(T), hint);
 | |
| 	::new(rcNewTag(), (void*)ptr) T();
 | |
| 	return ptr;
 | |
| }
 | |
| 
 | |
| /// Destroys and frees an object allocated with rcNew.
 | |
| /// @param[in]     ptr    The object pointer to delete.
 | |
| template <typename T>
 | |
| void rcDelete(T* ptr) {
 | |
| 	if (ptr) {
 | |
| 		ptr->~T();
 | |
| 		rcFree((void*)ptr);
 | |
| 	}
 | |
| }
 | |
| }  // namespace
 | |
| 
 | |
| 
 | |
| float rcSqrt(float x)
 | |
| {
 | |
| 	return sqrtf(x);
 | |
| }
 | |
| 
 | |
| /// @class rcContext
 | |
| /// @par
 | |
| ///
 | |
| /// This class does not provide logging or timer functionality on its 
 | |
| /// own.  Both must be provided by a concrete implementation 
 | |
| /// by overriding the protected member functions.  Also, this class does not 
 | |
| /// provide an interface for extracting log messages. (Only adding them.) 
 | |
| /// So concrete implementations must provide one.
 | |
| ///
 | |
| /// If no logging or timers are required, just pass an instance of this 
 | |
| /// class through the Recast build process.
 | |
| ///
 | |
| 
 | |
| /// @par
 | |
| ///
 | |
| /// Example:
 | |
| /// @code
 | |
| /// // Where ctx is an instance of rcContext and filepath is a char array.
 | |
| /// ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath);
 | |
| /// @endcode
 | |
| void rcContext::log(const rcLogCategory category, const char* format, ...)
 | |
| {
 | |
| 	if (!m_logEnabled)
 | |
| 		return;
 | |
| 	static const int MSG_SIZE = 512;
 | |
| 	char msg[MSG_SIZE];
 | |
| 	va_list ap;
 | |
| 	va_start(ap, format);
 | |
| 	int len = vsnprintf(msg, MSG_SIZE, format, ap);
 | |
| 	if (len >= MSG_SIZE)
 | |
| 	{
 | |
| 		len = MSG_SIZE-1;
 | |
| 		msg[MSG_SIZE-1] = '\0';
 | |
| 	}
 | |
| 	va_end(ap);
 | |
| 	doLog(category, msg, len);
 | |
| }
 | |
| 
 | |
| rcHeightfield* rcAllocHeightfield()
 | |
| {
 | |
| 	return rcNew<rcHeightfield>(RC_ALLOC_PERM);
 | |
| }
 | |
| rcHeightfield::rcHeightfield()
 | |
| 	: width()
 | |
| 	, height()
 | |
| 	, bmin()
 | |
| 	, bmax()
 | |
| 	, cs()
 | |
| 	, ch()
 | |
| 	, spans()
 | |
| 	, pools()
 | |
| 	, freelist()
 | |
| {
 | |
| }
 | |
| 
 | |
| rcHeightfield::~rcHeightfield()
 | |
| {
 | |
| 	// Delete span array.
 | |
| 	rcFree(spans);
 | |
| 	// Delete span pools.
 | |
| 	while (pools)
 | |
| 	{
 | |
| 		rcSpanPool* next = pools->next;
 | |
| 		rcFree(pools);
 | |
| 		pools = next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void rcFreeHeightField(rcHeightfield* hf)
 | |
| {
 | |
| 	rcDelete(hf);
 | |
| }
 | |
| 
 | |
| rcCompactHeightfield* rcAllocCompactHeightfield()
 | |
| {
 | |
| 	return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);
 | |
| }
 | |
| 
 | |
| void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
 | |
| {
 | |
| 	rcDelete(chf);
 | |
| }
 | |
| 
 | |
| rcCompactHeightfield::rcCompactHeightfield()
 | |
| 	: width(),
 | |
| 	height(),
 | |
| 	spanCount(),
 | |
| 	walkableHeight(),
 | |
| 	walkableClimb(),
 | |
| 	borderSize(),
 | |
| 	maxDistance(),
 | |
| 	maxRegions(),
 | |
| 	bmin(),
 | |
| 	bmax(),
 | |
| 	cs(),
 | |
| 	ch(),
 | |
| 	cells(),
 | |
| 	spans(),
 | |
| 	dist(),
 | |
| 	areas()
 | |
| {
 | |
| }
 | |
| rcCompactHeightfield::~rcCompactHeightfield()
 | |
| {
 | |
| 	rcFree(cells);
 | |
| 	rcFree(spans);
 | |
| 	rcFree(dist);
 | |
| 	rcFree(areas);
 | |
| }
 | |
| 
 | |
| rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
 | |
| {
 | |
| 	return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM);
 | |
| }
 | |
| void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset)
 | |
| {
 | |
| 	rcDelete(lset);
 | |
| }
 | |
| 
 | |
| rcHeightfieldLayerSet::rcHeightfieldLayerSet()
 | |
| 	: layers(),	nlayers() {}
 | |
| rcHeightfieldLayerSet::~rcHeightfieldLayerSet()
 | |
| {
 | |
| 	for (int i = 0; i < nlayers; ++i)
 | |
| 	{
 | |
| 		rcFree(layers[i].heights);
 | |
| 		rcFree(layers[i].areas);
 | |
| 		rcFree(layers[i].cons);
 | |
| 	}
 | |
| 	rcFree(layers);
 | |
| }
 | |
| 
 | |
| 
 | |
| rcContourSet* rcAllocContourSet()
 | |
| {
 | |
| 	return rcNew<rcContourSet>(RC_ALLOC_PERM);
 | |
| }
 | |
| void rcFreeContourSet(rcContourSet* cset)
 | |
| {
 | |
| 	rcDelete(cset);
 | |
| }
 | |
| 
 | |
| rcContourSet::rcContourSet()
 | |
| 	: conts(),
 | |
| 	nconts(),
 | |
| 	bmin(),
 | |
| 	bmax(),
 | |
| 	cs(),
 | |
| 	ch(),
 | |
| 	width(),
 | |
| 	height(),
 | |
| 	borderSize(),
 | |
| 	maxError() {}
 | |
| rcContourSet::~rcContourSet()
 | |
| {
 | |
| 	for (int i = 0; i < nconts; ++i)
 | |
| 	{
 | |
| 		rcFree(conts[i].verts);
 | |
| 		rcFree(conts[i].rverts);
 | |
| 	}
 | |
| 	rcFree(conts);
 | |
| }
 | |
| 
 | |
| 
 | |
| rcPolyMesh* rcAllocPolyMesh()
 | |
| {
 | |
| 	return rcNew<rcPolyMesh>(RC_ALLOC_PERM);
 | |
| }
 | |
| void rcFreePolyMesh(rcPolyMesh* pmesh)
 | |
| {
 | |
| 	rcDelete(pmesh);
 | |
| }
 | |
| 
 | |
| rcPolyMesh::rcPolyMesh()
 | |
| 	: verts(),
 | |
| 	polys(),
 | |
| 	regs(),
 | |
| 	flags(),
 | |
| 	areas(),
 | |
| 	nverts(),
 | |
| 	npolys(),
 | |
| 	maxpolys(),
 | |
| 	nvp(),
 | |
| 	bmin(),
 | |
| 	bmax(),
 | |
| 	cs(),
 | |
| 	ch(),
 | |
| 	borderSize(),
 | |
| 	maxEdgeError() {}
 | |
| 
 | |
| rcPolyMesh::~rcPolyMesh()
 | |
| {
 | |
| 	rcFree(verts);
 | |
| 	rcFree(polys);
 | |
| 	rcFree(regs);
 | |
| 	rcFree(flags);
 | |
| 	rcFree(areas);
 | |
| }
 | |
| 
 | |
| rcPolyMeshDetail* rcAllocPolyMeshDetail()
 | |
| {
 | |
| 	rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM);
 | |
| 	memset(dmesh, 0, sizeof(rcPolyMeshDetail));
 | |
| 	return dmesh;
 | |
| }
 | |
| 
 | |
| void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
 | |
| {
 | |
| 	if (!dmesh) return;
 | |
| 	rcFree(dmesh->meshes);
 | |
| 	rcFree(dmesh->verts);
 | |
| 	rcFree(dmesh->tris);
 | |
| 	rcFree(dmesh);
 | |
| }
 | |
| 
 | |
| void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
 | |
| {
 | |
| 	// Calculate bounding box.
 | |
| 	rcVcopy(bmin, verts);
 | |
| 	rcVcopy(bmax, verts);
 | |
| 	for (int i = 1; i < nv; ++i)
 | |
| 	{
 | |
| 		const float* v = &verts[i*3];
 | |
| 		rcVmin(bmin, v);
 | |
| 		rcVmax(bmax, v);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h)
 | |
| {
 | |
| 	*w = (int)((bmax[0] - bmin[0])/cs+0.5f);
 | |
| 	*h = (int)((bmax[2] - bmin[2])/cs+0.5f);
 | |
| }
 | |
| 
 | |
| /// @par
 | |
| ///
 | |
| /// See the #rcConfig documentation for more information on the configuration parameters.
 | |
| /// 
 | |
| /// @see rcAllocHeightfield, rcHeightfield 
 | |
| bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
 | |
| 						 const float* bmin, const float* bmax,
 | |
| 						 float cs, float ch)
 | |
| {
 | |
| 	rcIgnoreUnused(ctx);
 | |
| 	
 | |
| 	hf.width = width;
 | |
| 	hf.height = height;
 | |
| 	rcVcopy(hf.bmin, bmin);
 | |
| 	rcVcopy(hf.bmax, bmax);
 | |
| 	hf.cs = cs;
 | |
| 	hf.ch = ch;
 | |
| 	hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM);
 | |
| 	if (!hf.spans)
 | |
| 		return false;
 | |
| 	memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
 | |
| {
 | |
| 	float e0[3], e1[3];
 | |
| 	rcVsub(e0, v1, v0);
 | |
| 	rcVsub(e1, v2, v0);
 | |
| 	rcVcross(norm, e0, e1);
 | |
| 	rcVnormalize(norm);
 | |
| }
 | |
| 
 | |
| /// @par
 | |
| ///
 | |
| /// Only sets the area id's for the walkable triangles.  Does not alter the
 | |
| /// area id's for unwalkable triangles.
 | |
| /// 
 | |
| /// See the #rcConfig documentation for more information on the configuration parameters.
 | |
| /// 
 | |
| /// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
 | |
| void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
 | |
| 							 const float* verts, int nv,
 | |
| 							 const int* tris, int nt,
 | |
| 							 unsigned char* areas)
 | |
| {
 | |
| 	rcIgnoreUnused(ctx);
 | |
| 	rcIgnoreUnused(nv);
 | |
| 	
 | |
| 	const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
 | |
| 
 | |
| 	float norm[3];
 | |
| 	
 | |
| 	for (int i = 0; i < nt; ++i)
 | |
| 	{
 | |
| 		const int* tri = &tris[i*3];
 | |
| 		calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
 | |
| 		// Check if the face is walkable.
 | |
| 		if (norm[1] > walkableThr)
 | |
| 			areas[i] = RC_WALKABLE_AREA;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /// @par
 | |
| ///
 | |
| /// Only sets the area id's for the unwalkable triangles.  Does not alter the
 | |
| /// area id's for walkable triangles.
 | |
| /// 
 | |
| /// See the #rcConfig documentation for more information on the configuration parameters.
 | |
| /// 
 | |
| /// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
 | |
| void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
 | |
| 								const float* verts, int /*nv*/,
 | |
| 								const int* tris, int nt,
 | |
| 								unsigned char* areas)
 | |
| {
 | |
| 	rcIgnoreUnused(ctx);
 | |
| 	
 | |
| 	const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
 | |
| 	
 | |
| 	float norm[3];
 | |
| 	
 | |
| 	for (int i = 0; i < nt; ++i)
 | |
| 	{
 | |
| 		const int* tri = &tris[i*3];
 | |
| 		calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
 | |
| 		// Check if the face is walkable.
 | |
| 		if (norm[1] <= walkableThr)
 | |
| 			areas[i] = RC_NULL_AREA;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf)
 | |
| {
 | |
| 	rcIgnoreUnused(ctx);
 | |
| 	
 | |
| 	const int w = hf.width;
 | |
| 	const int h = hf.height;
 | |
| 	int spanCount = 0;
 | |
| 	for (int y = 0; y < h; ++y)
 | |
| 	{
 | |
| 		for (int x = 0; x < w; ++x)
 | |
| 		{
 | |
| 			for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next)
 | |
| 			{
 | |
| 				if (s->area != RC_NULL_AREA)
 | |
| 					spanCount++;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return spanCount;
 | |
| }
 | |
| 
 | |
| /// @par
 | |
| ///
 | |
| /// This is just the beginning of the process of fully building a compact heightfield.
 | |
| /// Various filters may be applied, then the distance field and regions built.
 | |
| /// E.g: #rcBuildDistanceField and #rcBuildRegions
 | |
| ///
 | |
| /// See the #rcConfig documentation for more information on the configuration parameters.
 | |
| ///
 | |
| /// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig
 | |
| bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
 | |
| 							   rcHeightfield& hf, rcCompactHeightfield& chf)
 | |
| {
 | |
| 	rcAssert(ctx);
 | |
| 	
 | |
| 	rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
 | |
| 	
 | |
| 	const int w = hf.width;
 | |
| 	const int h = hf.height;
 | |
| 	const int spanCount = rcGetHeightFieldSpanCount(ctx, hf);
 | |
| 
 | |
| 	// Fill in header.
 | |
| 	chf.width = w;
 | |
| 	chf.height = h;
 | |
| 	chf.spanCount = spanCount;
 | |
| 	chf.walkableHeight = walkableHeight;
 | |
| 	chf.walkableClimb = walkableClimb;
 | |
| 	chf.maxRegions = 0;
 | |
| 	rcVcopy(chf.bmin, hf.bmin);
 | |
| 	rcVcopy(chf.bmax, hf.bmax);
 | |
| 	chf.bmax[1] += walkableHeight*hf.ch;
 | |
| 	chf.cs = hf.cs;
 | |
| 	chf.ch = hf.ch;
 | |
| 	chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM);
 | |
| 	if (!chf.cells)
 | |
| 	{
 | |
| 		ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
 | |
| 		return false;
 | |
| 	}
 | |
| 	memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
 | |
| 	chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM);
 | |
| 	if (!chf.spans)
 | |
| 	{
 | |
| 		ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
 | |
| 		return false;
 | |
| 	}
 | |
| 	memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount);
 | |
| 	chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM);
 | |
| 	if (!chf.areas)
 | |
| 	{
 | |
| 		ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
 | |
| 		return false;
 | |
| 	}
 | |
| 	memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount);
 | |
| 	
 | |
| 	const int MAX_HEIGHT = 0xffff;
 | |
| 	
 | |
| 	// Fill in cells and spans.
 | |
| 	int idx = 0;
 | |
| 	for (int y = 0; y < h; ++y)
 | |
| 	{
 | |
| 		for (int x = 0; x < w; ++x)
 | |
| 		{
 | |
| 			const rcSpan* s = hf.spans[x + y*w];
 | |
| 			// If there are no spans at this cell, just leave the data to index=0, count=0.
 | |
| 			if (!s) continue;
 | |
| 			rcCompactCell& c = chf.cells[x+y*w];
 | |
| 			c.index = idx;
 | |
| 			c.count = 0;
 | |
| 			while (s)
 | |
| 			{
 | |
| 				if (s->area != RC_NULL_AREA)
 | |
| 				{
 | |
| 					const int bot = (int)s->smax;
 | |
| 					const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
 | |
| 					chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff);
 | |
| 					chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
 | |
| 					chf.areas[idx] = s->area;
 | |
| 					idx++;
 | |
| 					c.count++;
 | |
| 				}
 | |
| 				s = s->next;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Find neighbour connections.
 | |
| 	const int MAX_LAYERS = RC_NOT_CONNECTED-1;
 | |
| 	int tooHighNeighbour = 0;
 | |
| 	for (int y = 0; y < h; ++y)
 | |
| 	{
 | |
| 		for (int x = 0; x < w; ++x)
 | |
| 		{
 | |
| 			const rcCompactCell& c = chf.cells[x+y*w];
 | |
| 			for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
 | |
| 			{
 | |
| 				rcCompactSpan& s = chf.spans[i];
 | |
| 				
 | |
| 				for (int dir = 0; dir < 4; ++dir)
 | |
| 				{
 | |
| 					rcSetCon(s, dir, RC_NOT_CONNECTED);
 | |
| 					const int nx = x + rcGetDirOffsetX(dir);
 | |
| 					const int ny = y + rcGetDirOffsetY(dir);
 | |
| 					// First check that the neighbour cell is in bounds.
 | |
| 					if (nx < 0 || ny < 0 || nx >= w || ny >= h)
 | |
| 						continue;
 | |
| 						
 | |
| 					// Iterate over all neighbour spans and check if any of the is
 | |
| 					// accessible from current cell.
 | |
| 					const rcCompactCell& nc = chf.cells[nx+ny*w];
 | |
| 					for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k)
 | |
| 					{
 | |
| 						const rcCompactSpan& ns = chf.spans[k];
 | |
| 						const int bot = rcMax(s.y, ns.y);
 | |
| 						const int top = rcMin(s.y+s.h, ns.y+ns.h);
 | |
| 
 | |
| 						// Check that the gap between the spans is walkable,
 | |
| 						// and that the climb height between the gaps is not too high.
 | |
| 						if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
 | |
| 						{
 | |
| 							// Mark direction as walkable.
 | |
| 							const int lidx = k - (int)nc.index;
 | |
| 							if (lidx < 0 || lidx > MAX_LAYERS)
 | |
| 							{
 | |
| 								tooHighNeighbour = rcMax(tooHighNeighbour, lidx);
 | |
| 								continue;
 | |
| 							}
 | |
| 							rcSetCon(s, dir, lidx);
 | |
| 							break;
 | |
| 						}
 | |
| 					}
 | |
| 					
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	if (tooHighNeighbour > MAX_LAYERS)
 | |
| 	{
 | |
| 		ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
 | |
| 				 tooHighNeighbour, MAX_LAYERS);
 | |
| 	}
 | |
| 	
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
| static int getHeightfieldMemoryUsage(const rcHeightfield& hf)
 | |
| {
 | |
| 	int size = 0;
 | |
| 	size += sizeof(hf);
 | |
| 	size += hf.width * hf.height * sizeof(rcSpan*);
 | |
| 	
 | |
| 	rcSpanPool* pool = hf.pools;
 | |
| 	while (pool)
 | |
| 	{
 | |
| 		size += (sizeof(rcSpanPool) - sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL;
 | |
| 		pool = pool->next;
 | |
| 	}
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
 | |
| {
 | |
| 	int size = 0;
 | |
| 	size += sizeof(rcCompactHeightfield);
 | |
| 	size += sizeof(rcCompactSpan) * chf.spanCount;
 | |
| 	size += sizeof(rcCompactCell) * chf.width * chf.height;
 | |
| 	return size;
 | |
| }
 | |
| */
 |