123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- //
- // Copyright (c) 2009-2010 Mikko Mononen [email protected]
- //
- // 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 "Recast.h"
- #include "RecastAlloc.h"
- #include "RecastAssert.h"
- #include <math.h>
- #include <string.h>
- #include <stdio.h>
- #include <stdarg.h>
- namespace
- {
- /// Allocates and constructs an object of the given type, returning a pointer.
- /// @param[in] allocLifetime Allocation lifetime hint
- template<typename T>
- T* rcNew(const rcAllocHint allocLifetime)
- {
- T* ptr = (T*)rcAlloc(sizeof(T), allocLifetime);
- ::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);
- }
- }
- } // anonymous namespace
- float rcSqrt(float x)
- {
- return sqrtf(x);
- }
- 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 argList;
- va_start(argList, format);
- int len = vsnprintf(msg, MSG_SIZE, format, argList);
- if (len >= MSG_SIZE)
- {
- len = MSG_SIZE - 1;
- msg[MSG_SIZE - 1] = '\0';
- const char* errorMessage = "Log message was truncated";
- doLog(RC_LOG_ERROR, errorMessage, (int)strlen(errorMessage));
- }
- va_end(argList);
- doLog(category, msg, len);
- }
- void rcContext::doResetLog()
- {
- // Defined out of line to fix the weak v-tables warning
- }
- rcHeightfield* rcAllocHeightfield()
- {
- return rcNew<rcHeightfield>(RC_ALLOC_PERM);
- }
- void rcFreeHeightField(rcHeightfield* heightfield)
- {
- rcDelete(heightfield);
- }
- 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;
- }
- }
- rcCompactHeightfield* rcAllocCompactHeightfield()
- {
- return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);
- }
- void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield)
- {
- rcDelete(compactHeightfield);
- }
- 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* layerSet)
- {
- rcDelete(layerSet);
- }
- 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* contourSet)
- {
- rcDelete(contourSet);
- }
- 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* polyMesh)
- {
- rcDelete(polyMesh);
- }
- 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()
- {
- return rcNew<rcPolyMeshDetail>(RC_ALLOC_PERM);
- }
- void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh)
- {
- if (detailMesh == NULL)
- {
- return;
- }
- rcFree(detailMesh->meshes);
- rcFree(detailMesh->verts);
- rcFree(detailMesh->tris);
- rcFree(detailMesh);
- }
- rcPolyMeshDetail::rcPolyMeshDetail()
- : meshes()
- , verts()
- , tris()
- , nmeshes()
- , nverts()
- , ntris()
- {
- }
- void rcCalcBounds(const float* verts, int numVerts, float* minBounds, float* maxBounds)
- {
- // Calculate bounding box.
- rcVcopy(minBounds, verts);
- rcVcopy(maxBounds, verts);
- for (int i = 1; i < numVerts; ++i)
- {
- const float* v = &verts[i * 3];
- rcVmin(minBounds, v);
- rcVmax(maxBounds, v);
- }
- }
- void rcCalcGridSize(const float* minBounds, const float* maxBounds, const float cellSize, int* sizeX, int* sizeZ)
- {
- *sizeX = (int)((maxBounds[0] - minBounds[0]) / cellSize + 0.5f);
- *sizeZ = (int)((maxBounds[2] - minBounds[2]) / cellSize + 0.5f);
- }
- bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ,
- const float* minBounds, const float* maxBounds,
- float cellSize, float cellHeight)
- {
- rcIgnoreUnused(context);
- heightfield.width = sizeX;
- heightfield.height = sizeZ;
- rcVcopy(heightfield.bmin, minBounds);
- rcVcopy(heightfield.bmax, maxBounds);
- heightfield.cs = cellSize;
- heightfield.ch = cellHeight;
- heightfield.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*) * heightfield.width * heightfield.height, RC_ALLOC_PERM);
- if (!heightfield.spans)
- {
- return false;
- }
- memset(heightfield.spans, 0, sizeof(rcSpan*) * heightfield.width * heightfield.height);
- return true;
- }
- static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* faceNormal)
- {
- float e0[3], e1[3];
- rcVsub(e0, v1, v0);
- rcVsub(e1, v2, v0);
- rcVcross(faceNormal, e0, e1);
- rcVnormalize(faceNormal);
- }
- void rcMarkWalkableTriangles(rcContext* context, const float walkableSlopeAngle,
- const float* verts, const int numVerts,
- const int* tris, const int numTris,
- unsigned char* triAreaIDs)
- {
- rcIgnoreUnused(context);
- rcIgnoreUnused(numVerts);
- const float walkableThr = cosf(walkableSlopeAngle / 180.0f * RC_PI);
- float norm[3];
- for (int i = 0; i < numTris; ++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)
- {
- triAreaIDs[i] = RC_WALKABLE_AREA;
- }
- }
- }
- void rcClearUnwalkableTriangles(rcContext* context, const float walkableSlopeAngle,
- const float* verts, int numVerts,
- const int* tris, int numTris,
- unsigned char* triAreaIDs)
- {
- rcIgnoreUnused(context);
- rcIgnoreUnused(numVerts);
- // The minimum Y value for a face normal of a triangle with a walkable slope.
- const float walkableLimitY = cosf(walkableSlopeAngle / 180.0f * RC_PI);
- float faceNormal[3];
- for (int i = 0; i < numTris; ++i)
- {
- const int* tri = &tris[i * 3];
- calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], faceNormal);
- // Check if the face is walkable.
- if (faceNormal[1] <= walkableLimitY)
- {
- triAreaIDs[i] = RC_NULL_AREA;
- }
- }
- }
- int rcGetHeightFieldSpanCount(rcContext* context, const rcHeightfield& heightfield)
- {
- rcIgnoreUnused(context);
- const int numCols = heightfield.width * heightfield.height;
- int spanCount = 0;
- for (int columnIndex = 0; columnIndex < numCols; ++columnIndex)
- {
- for (rcSpan* span = heightfield.spans[columnIndex]; span != NULL; span = span->next)
- {
- if (span->area != RC_NULL_AREA)
- {
- spanCount++;
- }
- }
- }
- return spanCount;
- }
- bool rcBuildCompactHeightfield(rcContext* context, const int walkableHeight, const int walkableClimb,
- const rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield)
- {
- rcAssert(context);
- rcScopedTimer timer(context, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
- const int xSize = heightfield.width;
- const int zSize = heightfield.height;
- const int spanCount = rcGetHeightFieldSpanCount(context, heightfield);
- // Fill in header.
- compactHeightfield.width = xSize;
- compactHeightfield.height = zSize;
- compactHeightfield.spanCount = spanCount;
- compactHeightfield.walkableHeight = walkableHeight;
- compactHeightfield.walkableClimb = walkableClimb;
- compactHeightfield.maxRegions = 0;
- rcVcopy(compactHeightfield.bmin, heightfield.bmin);
- rcVcopy(compactHeightfield.bmax, heightfield.bmax);
- compactHeightfield.bmax[1] += walkableHeight * heightfield.ch;
- compactHeightfield.cs = heightfield.cs;
- compactHeightfield.ch = heightfield.ch;
- compactHeightfield.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell) * xSize * zSize, RC_ALLOC_PERM);
- if (!compactHeightfield.cells)
- {
- context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", xSize * zSize);
- return false;
- }
- memset(compactHeightfield.cells, 0, sizeof(rcCompactCell) * xSize * zSize);
- compactHeightfield.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan) * spanCount, RC_ALLOC_PERM);
- if (!compactHeightfield.spans)
- {
- context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
- return false;
- }
- memset(compactHeightfield.spans, 0, sizeof(rcCompactSpan) * spanCount);
- compactHeightfield.areas = (unsigned char*)rcAlloc(sizeof(unsigned char) * spanCount, RC_ALLOC_PERM);
- if (!compactHeightfield.areas)
- {
- context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
- return false;
- }
- memset(compactHeightfield.areas, RC_NULL_AREA, sizeof(unsigned char) * spanCount);
- const int MAX_HEIGHT = 0xffff;
- // Fill in cells and spans.
- int currentCellIndex = 0;
- const int numColumns = xSize * zSize;
- for (int columnIndex = 0; columnIndex < numColumns; ++columnIndex)
- {
- const rcSpan* span = heightfield.spans[columnIndex];
-
- // If there are no spans at this cell, just leave the data to index=0, count=0.
- if (span == NULL)
- {
- continue;
- }
-
- rcCompactCell& cell = compactHeightfield.cells[columnIndex];
- cell.index = currentCellIndex;
- cell.count = 0;
- for (; span != NULL; span = span->next)
- {
- if (span->area != RC_NULL_AREA)
- {
- const int bot = (int)span->smax;
- const int top = span->next ? (int)span->next->smin : MAX_HEIGHT;
- compactHeightfield.spans[currentCellIndex].y = (unsigned short)rcClamp(bot, 0, 0xffff);
- compactHeightfield.spans[currentCellIndex].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
- compactHeightfield.areas[currentCellIndex] = span->area;
- currentCellIndex++;
- cell.count++;
- }
- }
- }
-
- // Find neighbour connections.
- const int MAX_LAYERS = RC_NOT_CONNECTED - 1;
- int maxLayerIndex = 0;
- const int zStride = xSize; // for readability
- for (int z = 0; z < zSize; ++z)
- {
- for (int x = 0; x < xSize; ++x)
- {
- const rcCompactCell& cell = compactHeightfield.cells[x + z * zStride];
- for (int i = (int)cell.index, ni = (int)(cell.index + cell.count); i < ni; ++i)
- {
- rcCompactSpan& span = compactHeightfield.spans[i];
- for (int dir = 0; dir < 4; ++dir)
- {
- rcSetCon(span, dir, RC_NOT_CONNECTED);
- const int neighborX = x + rcGetDirOffsetX(dir);
- const int neighborZ = z + rcGetDirOffsetY(dir);
- // First check that the neighbour cell is in bounds.
- if (neighborX < 0 || neighborZ < 0 || neighborX >= xSize || neighborZ >= zSize)
- {
- continue;
- }
- // Iterate over all neighbour spans and check if any of the is
- // accessible from current cell.
- const rcCompactCell& neighborCell = compactHeightfield.cells[neighborX + neighborZ * zStride];
- for (int k = (int)neighborCell.index, nk = (int)(neighborCell.index + neighborCell.count); k < nk; ++k)
- {
- const rcCompactSpan& neighborSpan = compactHeightfield.spans[k];
- const int bot = rcMax(span.y, neighborSpan.y);
- const int top = rcMin(span.y + span.h, neighborSpan.y + neighborSpan.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)neighborSpan.y - (int)span.y) <= walkableClimb)
- {
- // Mark direction as walkable.
- const int layerIndex = k - (int)neighborCell.index;
- if (layerIndex < 0 || layerIndex > MAX_LAYERS)
- {
- maxLayerIndex = rcMax(maxLayerIndex, layerIndex);
- continue;
- }
- rcSetCon(span, dir, layerIndex);
- break;
- }
- }
- }
- }
- }
- }
- if (maxLayerIndex > MAX_LAYERS)
- {
- context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
- maxLayerIndex, MAX_LAYERS);
- }
- return true;
- }
|