Path: blob/master/thirdparty/recastnavigation/Recast/Source/Recast.cpp
9913 views
//1// Copyright (c) 2009-2010 Mikko Mononen [email protected]2//3// This software is provided 'as-is', without any express or implied4// warranty. In no event will the authors be held liable for any damages5// arising from the use of this software.6// Permission is granted to anyone to use this software for any purpose,7// including commercial applications, and to alter it and redistribute it8// freely, subject to the following restrictions:9// 1. The origin of this software must not be misrepresented; you must not10// claim that you wrote the original software. If you use this software11// in a product, an acknowledgment in the product documentation would be12// appreciated but is not required.13// 2. Altered source versions must be plainly marked as such, and must not be14// misrepresented as being the original software.15// 3. This notice may not be removed or altered from any source distribution.16//1718#include "Recast.h"19#include "RecastAlloc.h"20#include "RecastAssert.h"2122#include <math.h>23#include <string.h>24#include <stdio.h>25#include <stdarg.h>2627namespace28{29/// Allocates and constructs an object of the given type, returning a pointer.30/// @param[in] allocLifetime Allocation lifetime hint31template<typename T>32T* rcNew(const rcAllocHint allocLifetime)33{34T* ptr = (T*)rcAlloc(sizeof(T), allocLifetime);35::new(rcNewTag(), (void*)ptr) T();36return ptr;37}3839/// Destroys and frees an object allocated with rcNew.40/// @param[in] ptr The object pointer to delete.41template<typename T>42void rcDelete(T* ptr)43{44if (ptr)45{46ptr->~T();47rcFree((void*)ptr);48}49}50} // anonymous namespace5152float rcSqrt(float x)53{54return sqrtf(x);55}5657void rcContext::log(const rcLogCategory category, const char* format, ...)58{59if (!m_logEnabled)60{61return;62}63static const int MSG_SIZE = 512;64char msg[MSG_SIZE];65va_list argList;66va_start(argList, format);67int len = vsnprintf(msg, MSG_SIZE, format, argList);68if (len >= MSG_SIZE)69{70len = MSG_SIZE - 1;71msg[MSG_SIZE - 1] = '\0';7273const char* errorMessage = "Log message was truncated";74doLog(RC_LOG_ERROR, errorMessage, (int)strlen(errorMessage));75}76va_end(argList);77doLog(category, msg, len);78}7980void rcContext::doResetLog()81{82// Defined out of line to fix the weak v-tables warning83}8485rcHeightfield* rcAllocHeightfield()86{87return rcNew<rcHeightfield>(RC_ALLOC_PERM);88}8990void rcFreeHeightField(rcHeightfield* heightfield)91{92rcDelete(heightfield);93}9495rcHeightfield::rcHeightfield()96: width()97, height()98, bmin()99, bmax()100, cs()101, ch()102, spans()103, pools()104, freelist()105{106}107108rcHeightfield::~rcHeightfield()109{110// Delete span array.111rcFree(spans);112// Delete span pools.113while (pools)114{115rcSpanPool* next = pools->next;116rcFree(pools);117pools = next;118}119}120121rcCompactHeightfield* rcAllocCompactHeightfield()122{123return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);124}125126void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield)127{128rcDelete(compactHeightfield);129}130131rcCompactHeightfield::rcCompactHeightfield()132: width()133, height()134, spanCount()135, walkableHeight()136, walkableClimb()137, borderSize()138, maxDistance()139, maxRegions()140, bmin()141, bmax()142, cs()143, ch()144, cells()145, spans()146, dist()147, areas()148{149}150151rcCompactHeightfield::~rcCompactHeightfield()152{153rcFree(cells);154rcFree(spans);155rcFree(dist);156rcFree(areas);157}158159rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()160{161return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM);162}163164void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* layerSet)165{166rcDelete(layerSet);167}168169rcHeightfieldLayerSet::rcHeightfieldLayerSet()170: layers()171, nlayers()172{173}174175rcHeightfieldLayerSet::~rcHeightfieldLayerSet()176{177for (int i = 0; i < nlayers; ++i)178{179rcFree(layers[i].heights);180rcFree(layers[i].areas);181rcFree(layers[i].cons);182}183rcFree(layers);184}185186187rcContourSet* rcAllocContourSet()188{189return rcNew<rcContourSet>(RC_ALLOC_PERM);190}191192void rcFreeContourSet(rcContourSet* contourSet)193{194rcDelete(contourSet);195}196197rcContourSet::rcContourSet()198: conts()199, nconts()200, bmin()201, bmax()202, cs()203, ch()204, width()205, height()206, borderSize()207, maxError()208{209}210211rcContourSet::~rcContourSet()212{213for (int i = 0; i < nconts; ++i)214{215rcFree(conts[i].verts);216rcFree(conts[i].rverts);217}218rcFree(conts);219}220221rcPolyMesh* rcAllocPolyMesh()222{223return rcNew<rcPolyMesh>(RC_ALLOC_PERM);224}225226void rcFreePolyMesh(rcPolyMesh* polyMesh)227{228rcDelete(polyMesh);229}230231rcPolyMesh::rcPolyMesh()232: verts()233, polys()234, regs()235, flags()236, areas()237, nverts()238, npolys()239, maxpolys()240, nvp()241, bmin()242, bmax()243, cs()244, ch()245, borderSize()246, maxEdgeError()247{248}249250rcPolyMesh::~rcPolyMesh()251{252rcFree(verts);253rcFree(polys);254rcFree(regs);255rcFree(flags);256rcFree(areas);257}258259rcPolyMeshDetail* rcAllocPolyMeshDetail()260{261return rcNew<rcPolyMeshDetail>(RC_ALLOC_PERM);262}263264void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh)265{266if (detailMesh == NULL)267{268return;269}270rcFree(detailMesh->meshes);271rcFree(detailMesh->verts);272rcFree(detailMesh->tris);273rcFree(detailMesh);274}275276rcPolyMeshDetail::rcPolyMeshDetail()277: meshes()278, verts()279, tris()280, nmeshes()281, nverts()282, ntris()283{284}285286void rcCalcBounds(const float* verts, int numVerts, float* minBounds, float* maxBounds)287{288// Calculate bounding box.289rcVcopy(minBounds, verts);290rcVcopy(maxBounds, verts);291for (int i = 1; i < numVerts; ++i)292{293const float* v = &verts[i * 3];294rcVmin(minBounds, v);295rcVmax(maxBounds, v);296}297}298299void rcCalcGridSize(const float* minBounds, const float* maxBounds, const float cellSize, int* sizeX, int* sizeZ)300{301*sizeX = (int)((maxBounds[0] - minBounds[0]) / cellSize + 0.5f);302*sizeZ = (int)((maxBounds[2] - minBounds[2]) / cellSize + 0.5f);303}304305bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ,306const float* minBounds, const float* maxBounds,307float cellSize, float cellHeight)308{309rcIgnoreUnused(context);310311heightfield.width = sizeX;312heightfield.height = sizeZ;313rcVcopy(heightfield.bmin, minBounds);314rcVcopy(heightfield.bmax, maxBounds);315heightfield.cs = cellSize;316heightfield.ch = cellHeight;317heightfield.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*) * heightfield.width * heightfield.height, RC_ALLOC_PERM);318if (!heightfield.spans)319{320return false;321}322memset(heightfield.spans, 0, sizeof(rcSpan*) * heightfield.width * heightfield.height);323return true;324}325326static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* faceNormal)327{328float e0[3], e1[3];329rcVsub(e0, v1, v0);330rcVsub(e1, v2, v0);331rcVcross(faceNormal, e0, e1);332rcVnormalize(faceNormal);333}334335void rcMarkWalkableTriangles(rcContext* context, const float walkableSlopeAngle,336const float* verts, const int numVerts,337const int* tris, const int numTris,338unsigned char* triAreaIDs)339{340rcIgnoreUnused(context);341rcIgnoreUnused(numVerts);342343const float walkableThr = cosf(walkableSlopeAngle / 180.0f * RC_PI);344345float norm[3];346347for (int i = 0; i < numTris; ++i)348{349const int* tri = &tris[i * 3];350calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], norm);351// Check if the face is walkable.352if (norm[1] > walkableThr)353{354triAreaIDs[i] = RC_WALKABLE_AREA;355}356}357}358359void rcClearUnwalkableTriangles(rcContext* context, const float walkableSlopeAngle,360const float* verts, int numVerts,361const int* tris, int numTris,362unsigned char* triAreaIDs)363{364rcIgnoreUnused(context);365rcIgnoreUnused(numVerts);366367// The minimum Y value for a face normal of a triangle with a walkable slope.368const float walkableLimitY = cosf(walkableSlopeAngle / 180.0f * RC_PI);369370float faceNormal[3];371for (int i = 0; i < numTris; ++i)372{373const int* tri = &tris[i * 3];374calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], faceNormal);375// Check if the face is walkable.376if (faceNormal[1] <= walkableLimitY)377{378triAreaIDs[i] = RC_NULL_AREA;379}380}381}382383int rcGetHeightFieldSpanCount(rcContext* context, const rcHeightfield& heightfield)384{385rcIgnoreUnused(context);386387const int numCols = heightfield.width * heightfield.height;388int spanCount = 0;389for (int columnIndex = 0; columnIndex < numCols; ++columnIndex)390{391for (rcSpan* span = heightfield.spans[columnIndex]; span != NULL; span = span->next)392{393if (span->area != RC_NULL_AREA)394{395spanCount++;396}397}398}399return spanCount;400}401402bool rcBuildCompactHeightfield(rcContext* context, const int walkableHeight, const int walkableClimb,403const rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield)404{405rcAssert(context);406407rcScopedTimer timer(context, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);408409const int xSize = heightfield.width;410const int zSize = heightfield.height;411const int spanCount = rcGetHeightFieldSpanCount(context, heightfield);412413// Fill in header.414compactHeightfield.width = xSize;415compactHeightfield.height = zSize;416compactHeightfield.spanCount = spanCount;417compactHeightfield.walkableHeight = walkableHeight;418compactHeightfield.walkableClimb = walkableClimb;419compactHeightfield.maxRegions = 0;420rcVcopy(compactHeightfield.bmin, heightfield.bmin);421rcVcopy(compactHeightfield.bmax, heightfield.bmax);422compactHeightfield.bmax[1] += walkableHeight * heightfield.ch;423compactHeightfield.cs = heightfield.cs;424compactHeightfield.ch = heightfield.ch;425compactHeightfield.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell) * xSize * zSize, RC_ALLOC_PERM);426if (!compactHeightfield.cells)427{428context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", xSize * zSize);429return false;430}431memset(compactHeightfield.cells, 0, sizeof(rcCompactCell) * xSize * zSize);432compactHeightfield.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan) * spanCount, RC_ALLOC_PERM);433if (!compactHeightfield.spans)434{435context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);436return false;437}438memset(compactHeightfield.spans, 0, sizeof(rcCompactSpan) * spanCount);439compactHeightfield.areas = (unsigned char*)rcAlloc(sizeof(unsigned char) * spanCount, RC_ALLOC_PERM);440if (!compactHeightfield.areas)441{442context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);443return false;444}445memset(compactHeightfield.areas, RC_NULL_AREA, sizeof(unsigned char) * spanCount);446447const int MAX_HEIGHT = 0xffff;448449// Fill in cells and spans.450int currentCellIndex = 0;451const int numColumns = xSize * zSize;452for (int columnIndex = 0; columnIndex < numColumns; ++columnIndex)453{454const rcSpan* span = heightfield.spans[columnIndex];455456// If there are no spans at this cell, just leave the data to index=0, count=0.457if (span == NULL)458{459continue;460}461462rcCompactCell& cell = compactHeightfield.cells[columnIndex];463cell.index = currentCellIndex;464cell.count = 0;465466for (; span != NULL; span = span->next)467{468if (span->area != RC_NULL_AREA)469{470const int bot = (int)span->smax;471const int top = span->next ? (int)span->next->smin : MAX_HEIGHT;472compactHeightfield.spans[currentCellIndex].y = (unsigned short)rcClamp(bot, 0, 0xffff);473compactHeightfield.spans[currentCellIndex].h = (unsigned char)rcClamp(top - bot, 0, 0xff);474compactHeightfield.areas[currentCellIndex] = span->area;475currentCellIndex++;476cell.count++;477}478}479}480481// Find neighbour connections.482const int MAX_LAYERS = RC_NOT_CONNECTED - 1;483int maxLayerIndex = 0;484const int zStride = xSize; // for readability485for (int z = 0; z < zSize; ++z)486{487for (int x = 0; x < xSize; ++x)488{489const rcCompactCell& cell = compactHeightfield.cells[x + z * zStride];490for (int i = (int)cell.index, ni = (int)(cell.index + cell.count); i < ni; ++i)491{492rcCompactSpan& span = compactHeightfield.spans[i];493494for (int dir = 0; dir < 4; ++dir)495{496rcSetCon(span, dir, RC_NOT_CONNECTED);497const int neighborX = x + rcGetDirOffsetX(dir);498const int neighborZ = z + rcGetDirOffsetY(dir);499// First check that the neighbour cell is in bounds.500if (neighborX < 0 || neighborZ < 0 || neighborX >= xSize || neighborZ >= zSize)501{502continue;503}504505// Iterate over all neighbour spans and check if any of the is506// accessible from current cell.507const rcCompactCell& neighborCell = compactHeightfield.cells[neighborX + neighborZ * zStride];508for (int k = (int)neighborCell.index, nk = (int)(neighborCell.index + neighborCell.count); k < nk; ++k)509{510const rcCompactSpan& neighborSpan = compactHeightfield.spans[k];511const int bot = rcMax(span.y, neighborSpan.y);512const int top = rcMin(span.y + span.h, neighborSpan.y + neighborSpan.h);513514// Check that the gap between the spans is walkable,515// and that the climb height between the gaps is not too high.516if ((top - bot) >= walkableHeight && rcAbs((int)neighborSpan.y - (int)span.y) <= walkableClimb)517{518// Mark direction as walkable.519const int layerIndex = k - (int)neighborCell.index;520if (layerIndex < 0 || layerIndex > MAX_LAYERS)521{522maxLayerIndex = rcMax(maxLayerIndex, layerIndex);523continue;524}525rcSetCon(span, dir, layerIndex);526break;527}528}529}530}531}532}533534if (maxLayerIndex > MAX_LAYERS)535{536context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",537maxLayerIndex, MAX_LAYERS);538}539540return true;541}542543544