virtualx-engine/thirdparty/recastnavigation/Recast/Source/Recast.cpp

543 lines
13 KiB
C++
Raw Normal View History

2024-02-18 21:31:05 +01:00
//
// 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 "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;
}