diff options
Diffstat (limited to 'deps/recastnavigation/Recast')
-rw-r--r-- | deps/recastnavigation/Recast/CMakeLists.txt | 34 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/Recast.cpp | 423 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/Recast.h | 688 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastAlloc.cpp | 67 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastAlloc.h | 69 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastArea.cpp | 416 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastAssert.h | 33 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastContour.cpp | 802 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastFilter.cpp | 181 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastMesh.cpp | 1324 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastMeshDetail.cpp | 1237 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastRasterization.cpp | 360 | ||||
-rw-r--r-- | deps/recastnavigation/Recast/RecastRegion.cpp | 1285 |
13 files changed, 23 insertions, 6896 deletions
diff --git a/deps/recastnavigation/Recast/CMakeLists.txt b/deps/recastnavigation/Recast/CMakeLists.txt index 975b4a9c14..30265cb7e4 100644 --- a/deps/recastnavigation/Recast/CMakeLists.txt +++ b/deps/recastnavigation/Recast/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> # # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without @@ -9,15 +9,16 @@ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. set(Recast_STAT_SRCS - Recast.cpp - RecastAlloc.cpp - RecastArea.cpp - RecastContour.cpp - RecastFilter.cpp - RecastMesh.cpp - RecastMeshDetail.cpp - RecastRasterization.cpp - RecastRegion.cpp + Source/Recast.cpp + Source/RecastAlloc.cpp + Source/RecastArea.cpp + Source/RecastContour.cpp + Source/RecastFilter.cpp + Source/RecastLayers.cpp + Source/RecastMesh.cpp + Source/RecastMeshDetail.cpp + Source/RecastRasterization.cpp + Source/RecastRegion.cpp ) if(WIN32) @@ -28,4 +29,15 @@ endif() add_library(Recast STATIC ${Recast_STAT_SRCS}) -target_link_libraries(Recast ${ZLIB_LIBRARIES}) +target_include_directories(Recast + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/Include) + +target_link_libraries(Recast + PUBLIC + zlib) + +set_target_properties(Recast + PROPERTIES + FOLDER + "dep") diff --git a/deps/recastnavigation/Recast/Recast.cpp b/deps/recastnavigation/Recast/Recast.cpp deleted file mode 100644 index d051418e81..0000000000 --- a/deps/recastnavigation/Recast/Recast.cpp +++ /dev/null @@ -1,423 +0,0 @@ -// -// 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" - -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 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() -{ - rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM); - memset(hf, 0, sizeof(rcHeightfield)); - return hf; -} - -void rcFreeHeightField(rcHeightfield* hf) -{ - if (!hf) return; - // Delete span array. - rcFree(hf->spans); - // Delete span pools. - while (hf->pools) - { - rcSpanPool* next = hf->pools->next; - rcFree(hf->pools); - hf->pools = next; - } - rcFree(hf); -} - -rcCompactHeightfield* rcAllocCompactHeightfield() -{ - rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM); - memset(chf, 0, sizeof(rcCompactHeightfield)); - return chf; -} - -void rcFreeCompactHeightfield(rcCompactHeightfield* chf) -{ - if (!chf) return; - rcFree(chf->cells); - rcFree(chf->spans); - rcFree(chf->dist); - rcFree(chf->areas); - rcFree(chf); -} - -rcContourSet* rcAllocContourSet() -{ - rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM); - memset(cset, 0, sizeof(rcContourSet)); - return cset; -} - -void rcFreeContourSet(rcContourSet* cset) -{ - if (!cset) return; - for (int i = 0; i < cset->nconts; ++i) - { - rcFree(cset->conts[i].verts); - rcFree(cset->conts[i].rverts); - } - rcFree(cset->conts); - rcFree(cset); -} - -rcPolyMesh* rcAllocPolyMesh() -{ - rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM); - memset(pmesh, 0, sizeof(rcPolyMesh)); - return pmesh; -} - -void rcFreePolyMesh(rcPolyMesh* pmesh) -{ - if (!pmesh) return; - rcFree(pmesh->verts); - rcFree(pmesh->polys); - rcFree(pmesh->regs); - rcFree(pmesh->flags); - rcFree(pmesh->areas); - rcFree(pmesh); -} - -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); -} - -bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height, - const float* bmin, const float* bmax, - float cs, float ch) -{ - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(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); -} - -void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, - const float* verts, int /*nv*/, - const int* tris, int nt, - unsigned char* areas) -{ - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(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_WALKABLE_AREA; - } -} - -void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, - const float* verts, int /*nv*/, - const int* tris, int nt, - unsigned char* areas) -{ - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(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) -{ - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(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; -} - -bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb, - rcHeightfield& hf, rcCompactHeightfield& chf) -{ - rcAssert(ctx); - - ctx->startTimer(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 idx = k - (int)nc.index; - if (idx < 0 || idx > MAX_LAYERS) - { - tooHighNeighbour = rcMax(tooHighNeighbour, idx); - continue; - } - rcSetCon(s, dir, idx); - break; - } - } - - } - } - } - } - - if (tooHighNeighbour > MAX_LAYERS) - { - ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)", - tooHighNeighbour, MAX_LAYERS); - } - - ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD); - - 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; -} -*/
\ No newline at end of file diff --git a/deps/recastnavigation/Recast/Recast.h b/deps/recastnavigation/Recast/Recast.h deleted file mode 100644 index 0e5f074248..0000000000 --- a/deps/recastnavigation/Recast/Recast.h +++ /dev/null @@ -1,688 +0,0 @@ -// -// 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. -// - -#ifndef RECAST_H -#define RECAST_H - -// Some math headers don't have PI defined. -static const float RC_PI = 3.14159265f; - -enum rcLogCategory -{ - RC_LOG_PROGRESS = 1, - RC_LOG_WARNING, - RC_LOG_ERROR, -}; - -enum rcTimerLabel -{ - RC_TIMER_TOTAL, - RC_TIMER_TEMP, - RC_TIMER_RASTERIZE_TRIANGLES, - RC_TIMER_BUILD_COMPACTHEIGHTFIELD, - RC_TIMER_BUILD_CONTOURS, - RC_TIMER_BUILD_CONTOURS_TRACE, - RC_TIMER_BUILD_CONTOURS_SIMPLIFY, - RC_TIMER_FILTER_BORDER, - RC_TIMER_FILTER_WALKABLE, - RC_TIMER_MEDIAN_AREA, - RC_TIMER_FILTER_LOW_OBSTACLES, - RC_TIMER_BUILD_POLYMESH, - RC_TIMER_MERGE_POLYMESH, - RC_TIMER_ERODE_AREA, - RC_TIMER_MARK_BOX_AREA, - RC_TIMER_MARK_CONVEXPOLY_AREA, - RC_TIMER_BUILD_DISTANCEFIELD, - RC_TIMER_BUILD_DISTANCEFIELD_DIST, - RC_TIMER_BUILD_DISTANCEFIELD_BLUR, - RC_TIMER_BUILD_REGIONS, - RC_TIMER_BUILD_REGIONS_WATERSHED, - RC_TIMER_BUILD_REGIONS_EXPAND, - RC_TIMER_BUILD_REGIONS_FLOOD, - RC_TIMER_BUILD_REGIONS_FILTER, - RC_TIMER_BUILD_POLYMESHDETAIL, - RC_TIMER_MERGE_POLYMESHDETAIL, - RC_MAX_TIMERS -}; - -// Build context provides several optional utilities needed for the build process, -// such as timing, logging, and build time collecting. -class rcContext -{ -public: - inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {} - virtual ~rcContext() {} - - // Enables or disables logging. - inline void enableLog(bool state) { m_logEnabled = state; } - // Resets log. - inline void resetLog() { if (m_logEnabled) doResetLog(); } - // Logs a message. - void log(const rcLogCategory category, const char* format, ...); - - // Enables or disables timer. - inline void enableTimer(bool state) { m_timerEnabled = state; } - // Resets all timers. - inline void resetTimers() { if (m_timerEnabled) doResetTimers(); } - // Starts timer, used for performance timing. - inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); } - // Stops timer, used for performance timing. - inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); } - // Returns time accumulated between timer start/stop. - inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; } - -protected: - // Virtual functions to override for custom implementations. - virtual void doResetLog() {} - virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {} - virtual void doResetTimers() {} - virtual void doStartTimer(const rcTimerLabel /*label*/) {} - virtual void doStopTimer(const rcTimerLabel /*label*/) {} - virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; } - - bool m_logEnabled; - bool m_timerEnabled; -}; - - -// The units of the parameters are specified in parenthesis as follows: -// (vx) voxels, (wu) world units -struct rcConfig -{ - int width, height; // Dimensions of the rasterized heightfield (vx) - int tileSize; // Width and Height of a tile (vx) - int borderSize; // Non-navigable Border around the heightfield (vx) - float cs, ch; // Grid cell size and height (wu) - float bmin[3], bmax[3]; // Grid bounds (wu) - float walkableSlopeAngle; // Maximum walkable slope angle in degrees. - int walkableHeight; // Minimum height where the agent can still walk (vx) - int walkableClimb; // Maximum height between grid cells the agent can climb (vx) - int walkableRadius; // Radius of the agent in cells (vx) - int maxEdgeLen; // Maximum contour edge length (vx) - float maxSimplificationError; // Maximum distance error from contour to cells (vx) - int minRegionArea; // Regions whose area is smaller than this threshold will be removed. (vx) - int mergeRegionArea; // Regions whose area is smaller than this threshold will be merged (vx) - int maxVertsPerPoly; // Max number of vertices per polygon - float detailSampleDist; // Detail mesh sample spacing. - float detailSampleMaxError; // Detail mesh simplification max sample error. -}; - -// Define number of bits in the above structure for smin/smax. -// The max height is used for clamping rasterized values. -static const int RC_SPAN_HEIGHT_BITS = 16; -static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1; - -// Heightfield span. -struct rcSpan -{ - unsigned int smin : 16; // Span min height. - unsigned int smax : 16; // Span max height. - unsigned char area; // Span area type. - rcSpan* next; // Next span in column. -}; - -// Number of spans allocated per pool. -static const int RC_SPANS_PER_POOL = 2048; - -// Memory pool used for quick span allocation. -struct rcSpanPool -{ - rcSpanPool* next; // Pointer to next pool. - rcSpan items[RC_SPANS_PER_POOL]; // Array of spans. -}; - -// Dynamic span-heightfield. -struct rcHeightfield -{ - int width, height; // Dimension of the heightfield. - float bmin[3], bmax[3]; // Bounding box of the heightfield - float cs, ch; // Cell size and height. - rcSpan** spans; // Heightfield of spans (width*height). - rcSpanPool* pools; // Linked list of span pools. - rcSpan* freelist; // Pointer to next free span. -}; - -rcHeightfield* rcAllocHeightfield(); -void rcFreeHeightField(rcHeightfield* hf); - - -struct rcCompactCell -{ - unsigned int index : 24; // Index to first span in column. - unsigned int count : 8; // Number of spans in this column. -}; - -struct rcCompactSpan -{ - unsigned short y; // Bottom coordinate of the span. - unsigned short reg; - unsigned int con : 24; // Connections to neighbour cells. - unsigned int h : 8; // Height of the span. -}; - -// Compact static heightfield. -struct rcCompactHeightfield -{ - int width, height; // Width and height of the heightfield. - int spanCount; // Number of spans in the heightfield. - int walkableHeight, walkableClimb; // Agent properties. - unsigned short maxDistance; // Maximum distance value stored in heightfield. - unsigned short maxRegions; // Maximum Region Id stored in heightfield. - float bmin[3], bmax[3]; // Bounding box of the heightfield. - float cs, ch; // Cell size and height. - rcCompactCell* cells; // Pointer to width*height cells. - rcCompactSpan* spans; // Pointer to spans. - unsigned short* dist; // Pointer to per span distance to border. - unsigned char* areas; // Pointer to per span area ID. -}; - -rcCompactHeightfield* rcAllocCompactHeightfield(); -void rcFreeCompactHeightfield(rcCompactHeightfield* chf); - - -struct rcContour -{ - int* verts; // Vertex coordinates, each vertex contains 4 components. - int nverts; // Number of vertices. - int* rverts; // Raw vertex coordinates, each vertex contains 4 components. - int nrverts; // Number of raw vertices. - unsigned short reg; // Region ID of the contour. - unsigned char area; // Area ID of the contour. -}; - -struct rcContourSet -{ - rcContour* conts; // Pointer to all contours. - int nconts; // Number of contours. - float bmin[3], bmax[3]; // Bounding box of the heightfield. - float cs, ch; // Cell size and height. -}; - -rcContourSet* rcAllocContourSet(); -void rcFreeContourSet(rcContourSet* cset); - - -// Polymesh store a connected mesh of polygons. -// The polygons are store in an array where each polygons takes -// 'nvp*2' elements. The first 'nvp' elements are indices to vertices -// and the second 'nvp' elements are indices to neighbour polygons. -// If a polygon has less than 'bvp' vertices, the remaining indices -// are set to RC_MESH_NULL_IDX. If an polygon edge does not have a neighbour -// the neighbour index is set to RC_MESH_NULL_IDX. -// Vertices can be transformed into world space as follows: -// x = bmin[0] + verts[i*3+0]*cs; -// y = bmin[1] + verts[i*3+1]*ch; -// z = bmin[2] + verts[i*3+2]*cs; -struct rcPolyMesh -{ - unsigned short* verts; // Vertices of the mesh, 3 elements per vertex. - unsigned short* polys; // Polygons of the mesh, nvp*2 elements per polygon. - unsigned short* regs; // Region ID of the polygons. - unsigned short* flags; // Per polygon flags. - unsigned char* areas; // Area ID of polygons. - int nverts; // Number of vertices. - int npolys; // Number of polygons. - int maxpolys; // Number of allocated polygons. - int nvp; // Max number of vertices per polygon. - float bmin[3], bmax[3]; // Bounding box of the mesh. - float cs, ch; // Cell size and height. -}; - -rcPolyMesh* rcAllocPolyMesh(); -void rcFreePolyMesh(rcPolyMesh* pmesh); - - -// Detail mesh generated from a rcPolyMesh. -// Each submesh represents a polygon in the polymesh and they are stored in -// exactly same order. Each submesh is described as 4 values: -// base vertex, vertex count, base triangle, triangle count. That is, -// const unsigned char* t = &dmesh.tris[(tbase+i)*3]; and -// const float* v = &dmesh.verts[(vbase+t[j])*3]; -// If the input polygon has 'n' vertices, those vertices are first in the -// submesh vertex list. This allows to compres the mesh by not storing the -// first vertices and using the polymesh vertices instead. -// Max number of vertices per submesh is 127 and -// max number of triangles per submesh is 255. - -struct rcPolyMeshDetail -{ - unsigned int* meshes; // Pointer to all mesh data. - float* verts; // Pointer to all vertex data. - unsigned char* tris; // Pointer to all triangle data. - int nmeshes; // Number of meshes. - int nverts; // Number of total vertices. - int ntris; // Number of triangles. -}; - -rcPolyMeshDetail* rcAllocPolyMeshDetail(); -void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh); - - -// If heightfield region ID has the following bit set, the region is on border area -// and excluded from many calculations. -static const unsigned short RC_BORDER_REG = 0x8000; - -// If contour region ID has the following bit set, the vertex will be later -// removed in order to match the segments and vertices at tile boundaries. -static const int RC_BORDER_VERTEX = 0x10000; - -static const int RC_AREA_BORDER = 0x20000; - -enum rcBuildContoursFlags -{ - RC_CONTOUR_TESS_WALL_EDGES = 0x01, // Tessellate wall edges - RC_CONTOUR_TESS_AREA_EDGES = 0x02, // Tessellate edges between areas. -}; - -// Mask used with contours to extract region id. -static const int RC_CONTOUR_REG_MASK = 0xffff; - -// Null index which is used with meshes to mark unset or invalid indices. -static const unsigned short RC_MESH_NULL_IDX = 0xffff; - -// Area ID that is considered empty. -static const unsigned char RC_NULL_AREA = 0; - -// Area ID that is considered generally walkable. -static const unsigned char RC_WALKABLE_AREA = 63; - -// Value returned by rcGetCon() if the direction is not connected. -static const int RC_NOT_CONNECTED = 0x3f; - -// Compact span neighbour helpers. -inline void rcSetCon(rcCompactSpan& s, int dir, int i) -{ - const unsigned int shift = (unsigned int)dir*6; - unsigned int con = s.con; - s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift); -} - -inline int rcGetCon(const rcCompactSpan& s, int dir) -{ - const unsigned int shift = (unsigned int)dir*6; - return (s.con >> shift) & 0x3f; -} - -inline int rcGetDirOffsetX(int dir) -{ - const int offset[4] = { -1, 0, 1, 0, }; - return offset[dir&0x03]; -} - -inline int rcGetDirOffsetY(int dir) -{ - const int offset[4] = { 0, 1, 0, -1 }; - return offset[dir&0x03]; -} - -// Common helper functions -template<class T> inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; } -template<class T> inline T rcMin(T a, T b) { return a < b ? a : b; } -template<class T> inline T rcMax(T a, T b) { return a > b ? a : b; } -template<class T> inline T rcAbs(T a) { return a < 0 ? -a : a; } -template<class T> inline T rcSqr(T a) { return a*a; } -template<class T> inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); } -float rcSqrt(float x); - -// Common vector helper functions. -inline void rcVcross(float* dest, const float* v1, const float* v2) -{ - dest[0] = v1[1]*v2[2] - v1[2]*v2[1]; - dest[1] = v1[2]*v2[0] - v1[0]*v2[2]; - dest[2] = v1[0]*v2[1] - v1[1]*v2[0]; -} - -inline float rcVdot(const float* v1, const float* v2) -{ - return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; -} - -inline void rcVmad(float* dest, const float* v1, const float* v2, const float s) -{ - dest[0] = v1[0]+v2[0]*s; - dest[1] = v1[1]+v2[1]*s; - dest[2] = v1[2]+v2[2]*s; -} - -inline void rcVadd(float* dest, const float* v1, const float* v2) -{ - dest[0] = v1[0]+v2[0]; - dest[1] = v1[1]+v2[1]; - dest[2] = v1[2]+v2[2]; -} - -inline void rcVsub(float* dest, const float* v1, const float* v2) -{ - dest[0] = v1[0]-v2[0]; - dest[1] = v1[1]-v2[1]; - dest[2] = v1[2]-v2[2]; -} - -inline void rcVmin(float* mn, const float* v) -{ - mn[0] = rcMin(mn[0], v[0]); - mn[1] = rcMin(mn[1], v[1]); - mn[2] = rcMin(mn[2], v[2]); -} - -inline void rcVmax(float* mx, const float* v) -{ - mx[0] = rcMax(mx[0], v[0]); - mx[1] = rcMax(mx[1], v[1]); - mx[2] = rcMax(mx[2], v[2]); -} - -inline void rcVcopy(float* dest, const float* v) -{ - dest[0] = v[0]; - dest[1] = v[1]; - dest[2] = v[2]; -} - -inline float rcVdist(const float* v1, const float* v2) -{ - float dx = v2[0] - v1[0]; - float dy = v2[1] - v1[1]; - float dz = v2[2] - v1[2]; - return rcSqrt(dx*dx + dy*dy + dz*dz); -} - -inline float rcVdistSqr(const float* v1, const float* v2) -{ - float dx = v2[0] - v1[0]; - float dy = v2[1] - v1[1]; - float dz = v2[2] - v1[2]; - return dx*dx + dy*dy + dz*dz; -} - -inline void rcVnormalize(float* v) -{ - float d = 1.0f / rcSqrt(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2])); - v[0] *= d; - v[1] *= d; - v[2] *= d; -} - -inline bool rcVequal(const float* p0, const float* p1) -{ - static const float thr = rcSqr(1.0f/16384.0f); - const float d = rcVdistSqr(p0, p1); - return d < thr; -} - -// Calculated bounding box of array of vertices. -// Params: -// verts - (in) array of vertices -// nv - (in) vertex count -// bmin, bmax - (out) bounding box -void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax); - -// Calculates grid size based on bounding box and grid cell size. -// Params: -// bmin, bmax - (in) bounding box -// cs - (in) grid cell size -// w - (out) grid width -// h - (out) grid height -void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h); - -// Creates and initializes new heightfield. -// Params: -// hf - (in/out) heightfield to initialize. -// width - (in) width of the heightfield. -// height - (in) height of the heightfield. -// bmin, bmax - (in) bounding box of the heightfield -// cs - (in) grid cell size -// ch - (in) grid cell height -bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, - const float* bmin, const float* bmax, - float cs, float ch); - -// Sets the RC_WALKABLE_AREA for every triangle whose slope is below -// the maximum walkable slope angle. -// Params: -// walkableSlopeAngle - (in) maximum slope angle in degrees. -// verts - (in) array of vertices -// nv - (in) vertex count -// tris - (in) array of triangle vertex indices -// nt - (in) triangle count -// areas - (out) array of triangle area types -void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, - const int* tris, int nt, unsigned char* areas); - -// Sets the RC_NULL_AREA for every triangle whose slope is steeper than -// the maximum walkable slope angle. -// Params: -// walkableSlopeAngle - (in) maximum slope angle in degrees. -// verts - (in) array of vertices -// nv - (in) vertex count -// tris - (in) array of triangle vertex indices -// nt - (in) triangle count -// areas - (out) array of triangle are types -void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, - const int* tris, int nt, unsigned char* areas); - -// Adds span to heightfield. -// The span addition can set to favor flags. If the span is merged to -// another span and the new smax is within 'flagMergeThr' units away -// from the existing span the span flags are merged and stored. -// Params: -// solid - (in) heightfield where the spans is added to -// x,y - (in) location on the heightfield where the span is added -// smin,smax - (in) spans min/max height -// flags - (in) span flags (zero or WALKABLE) -// flagMergeThr - (in) merge threshold. -void rcAddSpan(rcContext* ctx, rcHeightfield& solid, const int x, const int y, - const unsigned short smin, const unsigned short smax, - const unsigned short area, const int flagMergeThr); - -// Rasterizes a triangle into heightfield spans. -// Params: -// v0,v1,v2 - (in) the vertices of the triangle. -// area - (in) area type of the triangle. -// solid - (in) heightfield where the triangle is rasterized -// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable. -void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, - const unsigned char area, rcHeightfield& solid, - const int flagMergeThr = 1); - -// Rasterizes indexed triangle mesh into heightfield spans. -// Params: -// verts - (in) array of vertices -// nv - (in) vertex count -// tris - (in) array of triangle vertex indices -// area - (in) array of triangle area types. -// nt - (in) triangle count -// solid - (in) heightfield where the triangles are rasterized -// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable. -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, - const int* tris, const unsigned char* areas, const int nt, - rcHeightfield& solid, const int flagMergeThr = 1); - -// Rasterizes indexed triangle mesh into heightfield spans. -// Params: -// verts - (in) array of vertices -// nv - (in) vertex count -// tris - (in) array of triangle vertex indices -// area - (in) array of triangle area types. -// nt - (in) triangle count -// solid - (in) heightfield where the triangles are rasterized -// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable. -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, - const unsigned short* tris, const unsigned char* areas, const int nt, - rcHeightfield& solid, const int flagMergeThr = 1); - -// Rasterizes the triangles into heightfield spans. -// Params: -// verts - (in) array of vertices -// area - (in) array of triangle area types. -// nt - (in) triangle count -// solid - (in) heightfield where the triangles are rasterized -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, - rcHeightfield& solid, const int flagMergeThr = 1); - -// Marks non-walkable low obstacles as walkable if they are closer than walkableClimb -// from a walkable surface. Applying this filter allows to step over low hanging -// low obstacles. -// Params: -// walkableHeight - (in) minimum height where the agent can still walk -// solid - (in/out) heightfield describing the solid space -// TODO: Missuses ledge flag, must be called before rcFilterLedgeSpans! -void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid); - -// Removes WALKABLE flag from all spans that are at ledges. This filtering -// removes possible overestimation of the conservative voxelization so that -// the resulting mesh will not have regions hanging in air over ledges. -// Params: -// walkableHeight - (in) minimum height where the agent can still walk -// walkableClimb - (in) maximum height between grid cells the agent can climb -// solid - (in/out) heightfield describing the solid space -void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, - const int walkableClimb, rcHeightfield& solid); - -// Removes WALKABLE flag from all spans which have smaller than -// 'walkableHeight' clearance above them. -// Params: -// walkableHeight - (in) minimum height where the agent can still walk -// solid - (in/out) heightfield describing the solid space -void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid); - -// Returns number of spans contained in a heightfield. -// Params: -// hf - (in) heightfield to be compacted -// Returns number of spans. -int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf); - -// Builds compact representation of the heightfield. -// Params: -// walkableHeight - (in) minimum height where the agent can still walk -// walkableClimb - (in) maximum height between grid cells the agent can climb -// flags - (in) require flags for a cell to be included in the compact heightfield. -// hf - (in) heightfield to be compacted -// chf - (out) compact heightfield representing the open space. -// Returns false if operation ran out of memory. -bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb, - rcHeightfield& hf, rcCompactHeightfield& chf); - -// Erodes walkable area. -// Params: -// radius - (in) radius of erosion (max 255). -// chf - (in/out) compact heightfield to erode. -// Returns false if operation ran out of memory. -bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf); - -// Applies median filter to walkable area types, removing noise. -// Params: -// chf - (in/out) compact heightfield to erode. -// Returns false if operation ran out of memory. -bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf); - -// Marks the area of the convex polygon into the area type of the compact heightfield. -// Params: -// bmin/bmax - (in) bounds of the axis aligned box. -// areaId - (in) area ID to mark. -// chf - (in/out) compact heightfield to mark. -void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId, - rcCompactHeightfield& chf); - -// Marks the area of the convex polygon into the area type of the compact heightfield. -// Params: -// verts - (in) vertices of the convex polygon. -// nverts - (in) number of vertices in the polygon. -// hmin/hmax - (in) min and max height of the polygon. -// areaId - (in) area ID to mark. -// chf - (in/out) compact heightfield to mark. -void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, - const float hmin, const float hmax, unsigned char areaId, - rcCompactHeightfield& chf); - -// Builds distance field and stores it into the combat heightfield. -// Params: -// chf - (in/out) compact heightfield representing the open space. -// Returns false if operation ran out of memory. -bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); - -// Divides the walkable heighfied into simple regions using watershed partitioning. -// Each region has only one contour and no overlaps. -// The regions are stored in the compact heightfield 'reg' field. -// The process sometimes creates small regions. If the area of a regions is -// smaller than 'mergeRegionArea' then the region will be merged with a neighbour -// region if possible. If multiple regions form an area which is smaller than -// 'minRegionArea' all the regions belonging to that area will be removed. -// Here area means the count of spans in an area. -// Params: -// chf - (in/out) compact heightfield representing the open space. -// minRegionArea - (in) the smallest allowed region area. -// maxMergeRegionArea - (in) the largest allowed region area which can be merged. -// Returns false if operation ran out of memory. -bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int minRegionArea, const int mergeRegionArea); - -// Divides the walkable heighfied into simple regions using simple monotone partitioning. -// Each region has only one contour and no overlaps. -// The regions are stored in the compact heightfield 'reg' field. -// The process sometimes creates small regions. If the area of a regions is -// smaller than 'mergeRegionArea' then the region will be merged with a neighbour -// region if possible. If multiple regions form an area which is smaller than -// 'minRegionArea' all the regions belonging to that area will be removed. -// Here area means the count of spans in an area. -// Params: -// chf - (in/out) compact heightfield representing the open space. -// minRegionArea - (in) the smallest allowed regions size. -// maxMergeRegionArea - (in) the largest allowed regions size which can be merged. -// Returns false if operation ran out of memory. -bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int minRegionArea, const int mergeRegionArea); - -// Builds simplified contours from the regions outlines. -// Params: -// chf - (in) compact heightfield which has regions set. -// maxError - (in) maximum allowed distance between simplified contour and cells. -// maxEdgeLen - (in) maximum allowed contour edge length in cells. -// cset - (out) Resulting contour set. -// flags - (in) build flags, see rcBuildContoursFlags. -// Returns false if operation ran out of memory. -bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, - const float maxError, const int maxEdgeLen, - rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES); - -// Builds connected convex polygon mesh from contour polygons. -// Params: -// cset - (in) contour set. -// nvp - (in) maximum number of vertices per polygon. -// mesh - (out) poly mesh. -// Returns false if operation ran out of memory. -bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh); - -bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh); - -// Builds detail triangle mesh for each polygon in the poly mesh. -// Params: -// mesh - (in) poly mesh to detail. -// chf - (in) compact height field, used to query height for new vertices. -// sampleDist - (in) spacing between height samples used to generate more detail into mesh. -// sampleMaxError - (in) maximum allowed distance between simplified detail mesh and height sample. -// pmdtl - (out) detail mesh. -// Returns false if operation ran out of memory. -bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf, - const float sampleDist, const float sampleMaxError, - rcPolyMeshDetail& dmesh); - -bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh); - - -#endif // RECAST_H diff --git a/deps/recastnavigation/Recast/RecastAlloc.cpp b/deps/recastnavigation/Recast/RecastAlloc.cpp deleted file mode 100644 index 2c7396a1bf..0000000000 --- a/deps/recastnavigation/Recast/RecastAlloc.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// -// 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 <stdlib.h> -#include <string.h> -#include "RecastAlloc.h" - -static void *rcAllocDefault(int size, rcAllocHint) -{ - return malloc(size); -} - -static void rcFreeDefault(void *ptr) -{ - free(ptr); -} - -static rcAllocFunc* sRecastAllocFunc = rcAllocDefault; -static rcFreeFunc* sRecastFreeFunc = rcFreeDefault; - -void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc) -{ - sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault; - sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault; -} - -void* rcAlloc(int size, rcAllocHint hint) -{ - return sRecastAllocFunc(size, hint); -} - -void rcFree(void* ptr) -{ - if (ptr) - sRecastFreeFunc(ptr); -} - - -void rcIntArray::resize(int n) -{ - if (n > m_cap) - { - if (!m_cap) m_cap = n; - while (m_cap < n) m_cap *= 2; - int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP); - if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int)); - rcFree(m_data); - m_data = newData; - } - m_size = n; -} - diff --git a/deps/recastnavigation/Recast/RecastAlloc.h b/deps/recastnavigation/Recast/RecastAlloc.h deleted file mode 100644 index 9a316374a7..0000000000 --- a/deps/recastnavigation/Recast/RecastAlloc.h +++ /dev/null @@ -1,69 +0,0 @@ -// -// 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. -// - -#ifndef RECASTALLOC_H -#define RECASTALLOC_H - -enum rcAllocHint -{ - RC_ALLOC_PERM, // Memory persist after a function call. - RC_ALLOC_TEMP // Memory used temporarily within a function. -}; - -typedef void* (rcAllocFunc)(int size, rcAllocHint hint); -typedef void (rcFreeFunc)(void* ptr); - -void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc); - -void* rcAlloc(int size, rcAllocHint hint); -void rcFree(void* ptr); - - - -// Simple dynamic array ints. -class rcIntArray -{ - int* m_data; - int m_size, m_cap; - inline rcIntArray(const rcIntArray&); - inline rcIntArray& operator=(const rcIntArray&); -public: - inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {} - inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); } - inline ~rcIntArray() { rcFree(m_data); } - void resize(int n); - inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } - inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; } - inline const int& operator[](int i) const { return m_data[i]; } - inline int& operator[](int i) { return m_data[i]; } - inline int size() const { return m_size; } -}; - -// Simple internal helper class to delete array in scope -template<class T> class rcScopedDelete -{ - T* ptr; - inline T* operator=(T* p); -public: - inline rcScopedDelete() : ptr(0) {} - inline rcScopedDelete(T* p) : ptr(p) {} - inline ~rcScopedDelete() { rcFree(ptr); } - inline operator T*() { return ptr; } -}; - -#endif diff --git a/deps/recastnavigation/Recast/RecastArea.cpp b/deps/recastnavigation/Recast/RecastArea.cpp deleted file mode 100644 index c18277b878..0000000000 --- a/deps/recastnavigation/Recast/RecastArea.cpp +++ /dev/null @@ -1,416 +0,0 @@ -// -// 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 "Recast.h" -#include "RecastAlloc.h" -#include "RecastAssert.h" - - -bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf) -{ - rcAssert(ctx); - - const int w = chf.width; - const int h = chf.height; - - ctx->startTimer(RC_TIMER_ERODE_AREA); - - unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); - if (!dist) - { - ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount); - return false; - } - - // Init distance. - memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount); - - // Mark boundary cells. - 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) - { - if (chf.areas[i] != RC_NULL_AREA) - { - const rcCompactSpan& s = chf.spans[i]; - int nc = 0; - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - nc++; - } - // At least one missing neighbour. - if (nc != 4) - dist[i] = 0; - } - } - } - } - - unsigned char nd; - - // Pass 1 - 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) - { - const rcCompactSpan& s = chf.spans[i]; - - if (rcGetCon(s, 0) != RC_NOT_CONNECTED) - { - // (-1,0) - const int ax = x + rcGetDirOffsetX(0); - const int ay = y + rcGetDirOffsetY(0); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); - const rcCompactSpan& as = chf.spans[ai]; - nd = (unsigned char)rcMin((int)dist[ai]+2, 255); - if (nd < dist[i]) - dist[i] = nd; - - // (-1,-1) - if (rcGetCon(as, 3) != RC_NOT_CONNECTED) - { - const int aax = ax + rcGetDirOffsetX(3); - const int aay = ay + rcGetDirOffsetY(3); - const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3); - nd = (unsigned char)rcMin((int)dist[aai]+3, 255); - if (nd < dist[i]) - dist[i] = nd; - } - } - if (rcGetCon(s, 3) != RC_NOT_CONNECTED) - { - // (0,-1) - const int ax = x + rcGetDirOffsetX(3); - const int ay = y + rcGetDirOffsetY(3); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); - const rcCompactSpan& as = chf.spans[ai]; - nd = (unsigned char)rcMin((int)dist[ai]+2, 255); - if (nd < dist[i]) - dist[i] = nd; - - // (1,-1) - if (rcGetCon(as, 2) != RC_NOT_CONNECTED) - { - const int aax = ax + rcGetDirOffsetX(2); - const int aay = ay + rcGetDirOffsetY(2); - const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2); - nd = (unsigned char)rcMin((int)dist[aai]+3, 255); - if (nd < dist[i]) - dist[i] = nd; - } - } - } - } - } - - // Pass 2 - for (int y = h-1; y >= 0; --y) - { - for (int x = w-1; x >= 0; --x) - { - const rcCompactCell& c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - const rcCompactSpan& s = chf.spans[i]; - - if (rcGetCon(s, 2) != RC_NOT_CONNECTED) - { - // (1,0) - const int ax = x + rcGetDirOffsetX(2); - const int ay = y + rcGetDirOffsetY(2); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2); - const rcCompactSpan& as = chf.spans[ai]; - nd = (unsigned char)rcMin((int)dist[ai]+2, 255); - if (nd < dist[i]) - dist[i] = nd; - - // (1,1) - if (rcGetCon(as, 1) != RC_NOT_CONNECTED) - { - const int aax = ax + rcGetDirOffsetX(1); - const int aay = ay + rcGetDirOffsetY(1); - const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1); - nd = (unsigned char)rcMin((int)dist[aai]+3, 255); - if (nd < dist[i]) - dist[i] = nd; - } - } - if (rcGetCon(s, 1) != RC_NOT_CONNECTED) - { - // (0,1) - const int ax = x + rcGetDirOffsetX(1); - const int ay = y + rcGetDirOffsetY(1); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1); - const rcCompactSpan& as = chf.spans[ai]; - nd = (unsigned char)rcMin((int)dist[ai]+2, 255); - if (nd < dist[i]) - dist[i] = nd; - - // (-1,1) - if (rcGetCon(as, 0) != RC_NOT_CONNECTED) - { - const int aax = ax + rcGetDirOffsetX(0); - const int aay = ay + rcGetDirOffsetY(0); - const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0); - nd = (unsigned char)rcMin((int)dist[aai]+3, 255); - if (nd < dist[i]) - dist[i] = nd; - } - } - } - } - } - - const unsigned char thr = (unsigned char)(radius*2); - for (int i = 0; i < chf.spanCount; ++i) - if (dist[i] < thr) - chf.areas[i] = RC_NULL_AREA; - - rcFree(dist); - - ctx->stopTimer(RC_TIMER_ERODE_AREA); - - return true; -} - -static void insertSort(unsigned char* a, const int n) -{ - int i, j; - for (i = 1; i < n; i++) - { - const unsigned char value = a[i]; - for (j = i - 1; j >= 0 && a[j] > value; j--) - a[j+1] = a[j]; - a[j+1] = value; - } -} - - -bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf) -{ - rcAssert(ctx); - - const int w = chf.width; - const int h = chf.height; - - ctx->startTimer(RC_TIMER_MEDIAN_AREA); - - unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); - if (!areas) - { - ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount); - return false; - } - - // Init distance. - memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount); - - 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) - { - const rcCompactSpan& s = chf.spans[i]; - if (chf.areas[i] == RC_NULL_AREA) - { - areas[i] = chf.areas[i]; - continue; - } - - unsigned char nei[9]; - for (int j = 0; j < 9; ++j) - nei[j] = chf.areas[i]; - - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - if (chf.areas[ai] != RC_NULL_AREA) - nei[dir*2+0] = chf.areas[ai]; - - const rcCompactSpan& as = chf.spans[ai]; - const int dir2 = (dir+1) & 0x3; - if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) - { - const int ax2 = ax + rcGetDirOffsetX(dir2); - const int ay2 = ay + rcGetDirOffsetY(dir2); - const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2); - if (chf.areas[ai2] != RC_NULL_AREA) - nei[dir*2+1] = chf.areas[ai2]; - } - } - } - insertSort(nei, 9); - areas[i] = nei[4]; - } - } - } - - memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount); - - rcFree(areas); - - ctx->stopTimer(RC_TIMER_MEDIAN_AREA); - - return true; -} - -void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId, - rcCompactHeightfield& chf) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_MARK_BOX_AREA); - - int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs); - int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); - int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs); - int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs); - int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch); - int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs); - - if (maxx < 0) return; - if (minx >= chf.width) return; - if (maxz < 0) return; - if (minz >= chf.height) return; - - if (minx < 0) minx = 0; - if (maxx >= chf.width) maxx = chf.width-1; - if (minz < 0) minz = 0; - if (maxz >= chf.height) maxz = chf.height-1; - - for (int z = minz; z <= maxz; ++z) - { - for (int x = minx; x <= maxx; ++x) - { - const rcCompactCell& c = chf.cells[x+z*chf.width]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - rcCompactSpan& s = chf.spans[i]; - if ((int)s.y >= miny && (int)s.y <= maxy) - { - if (chf.areas[i] != RC_NULL_AREA) - chf.areas[i] = areaId; - } - } - } - } - - ctx->stopTimer(RC_TIMER_MARK_BOX_AREA); - -} - - -static int pointInPoly(int nvert, const float* verts, const float* p) -{ - int i, j, c = 0; - for (i = 0, j = nvert-1; i < nvert; j = i++) - { - const float* vi = &verts[i*3]; - const float* vj = &verts[j*3]; - if (((vi[2] > p[2]) != (vj[2] > p[2])) && - (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) ) - c = !c; - } - return c; -} - -void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, - const float hmin, const float hmax, unsigned char areaId, - rcCompactHeightfield& chf) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA); - - float bmin[3], bmax[3]; - rcVcopy(bmin, verts); - rcVcopy(bmax, verts); - for (int i = 1; i < nverts; ++i) - { - rcVmin(bmin, &verts[i*3]); - rcVmax(bmax, &verts[i*3]); - } - bmin[1] = hmin; - bmax[1] = hmax; - - int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs); - int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); - int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs); - int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs); - int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch); - int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs); - - if (maxx < 0) return; - if (minx >= chf.width) return; - if (maxz < 0) return; - if (minz >= chf.height) return; - - if (minx < 0) minx = 0; - if (maxx >= chf.width) maxx = chf.width-1; - if (minz < 0) minz = 0; - if (maxz >= chf.height) maxz = chf.height-1; - - - // TODO: Optimize. - for (int z = minz; z <= maxz; ++z) - { - for (int x = minx; x <= maxx; ++x) - { - const rcCompactCell& c = chf.cells[x+z*chf.width]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - rcCompactSpan& s = chf.spans[i]; - if (chf.areas[i] == RC_NULL_AREA) - continue; - if ((int)s.y >= miny && (int)s.y <= maxy) - { - float p[3]; - p[0] = chf.bmin[0] + (x+0.5f)*chf.cs; - p[1] = 0; - p[2] = chf.bmin[2] + (z+0.5f)*chf.cs; - - if (pointInPoly(nverts, verts, p)) - { - chf.areas[i] = areaId; - } - } - } - } - } - - ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA); -} diff --git a/deps/recastnavigation/Recast/RecastAssert.h b/deps/recastnavigation/Recast/RecastAssert.h deleted file mode 100644 index b58b8fcd28..0000000000 --- a/deps/recastnavigation/Recast/RecastAssert.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// 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. -// - -#ifndef RECASTASSERT_H -#define RECASTASSERT_H - -// Note: This header file's only purpose is to include define assert. -// Feel free to change the file and include your own implementation instead. - -#ifdef NDEBUG -// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ -# define rcAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false) -#else -# include <assert.h> -# define rcAssert assert -#endif - -#endif // RECASTASSERT_H diff --git a/deps/recastnavigation/Recast/RecastContour.cpp b/deps/recastnavigation/Recast/RecastContour.cpp deleted file mode 100644 index 4ba8deac89..0000000000 --- a/deps/recastnavigation/Recast/RecastContour.cpp +++ /dev/null @@ -1,802 +0,0 @@ -// -// 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. -// - -#define _USE_MATH_DEFINES -#include <math.h> -#include <string.h> -#include <stdio.h> -#include "Recast.h" -#include "RecastAlloc.h" -#include "RecastAssert.h" - - -static int getCornerHeight(int x, int y, int i, int dir, - const rcCompactHeightfield& chf, - bool& isBorderVertex) -{ - const rcCompactSpan& s = chf.spans[i]; - int ch = (int)s.y; - int dirp = (dir+1) & 0x3; - - unsigned int regs[4] = {0,0,0,0}; - - // Combine region and area codes in order to prevent - // border vertices which are in between two areas to be removed. - regs[0] = chf.spans[i].reg | (chf.areas[i] << 16); - - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - const rcCompactSpan& as = chf.spans[ai]; - ch = rcMax(ch, (int)as.y); - regs[1] = chf.spans[ai].reg | (chf.areas[ai] << 16); - if (rcGetCon(as, dirp) != RC_NOT_CONNECTED) - { - const int ax2 = ax + rcGetDirOffsetX(dirp); - const int ay2 = ay + rcGetDirOffsetY(dirp); - const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp); - const rcCompactSpan& as2 = chf.spans[ai2]; - ch = rcMax(ch, (int)as2.y); - regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16); - } - } - if (rcGetCon(s, dirp) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dirp); - const int ay = y + rcGetDirOffsetY(dirp); - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp); - const rcCompactSpan& as = chf.spans[ai]; - ch = rcMax(ch, (int)as.y); - regs[3] = chf.spans[ai].reg | (chf.areas[ai] << 16); - if (rcGetCon(as, dir) != RC_NOT_CONNECTED) - { - const int ax2 = ax + rcGetDirOffsetX(dir); - const int ay2 = ay + rcGetDirOffsetY(dir); - const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir); - const rcCompactSpan& as2 = chf.spans[ai2]; - ch = rcMax(ch, (int)as2.y); - regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16); - } - } - - // Check if the vertex is special edge vertex, these vertices will be removed later. - for (int j = 0; j < 4; ++j) - { - const int a = j; - const int b = (j+1) & 0x3; - const int c = (j+2) & 0x3; - const int d = (j+3) & 0x3; - - // The vertex is a border vertex there are two same exterior cells in a row, - // followed by two interior cells and none of the regions are out of bounds. - const bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b]; - const bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0; - const bool intsSameArea = (regs[c]>>16) == (regs[d]>>16); - const bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0; - if (twoSameExts && twoInts && intsSameArea && noZeros) - { - isBorderVertex = true; - break; - } - } - - return ch; -} - -static void walkContour(int x, int y, int i, - rcCompactHeightfield& chf, - unsigned char* flags, rcIntArray& points) -{ - // Choose the first non-connected edge - unsigned char dir = 0; - while ((flags[i] & (1 << dir)) == 0) - dir++; - - unsigned char startDir = dir; - int starti = i; - - const unsigned char area = chf.areas[i]; - - int iter = 0; - while (++iter < 40000) - { - if (flags[i] & (1 << dir)) - { - // Choose the edge corner - bool isBorderVertex = false; - bool isAreaBorder = false; - int px = x; - int py = getCornerHeight(x, y, i, dir, chf, isBorderVertex); - int pz = y; - switch(dir) - { - case 0: pz++; break; - case 1: px++; pz++; break; - case 2: px++; break; - } - int r = 0; - const rcCompactSpan& s = chf.spans[i]; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - r = (int)chf.spans[ai].reg; - if (area != chf.areas[ai]) - isAreaBorder = true; - } - if (isBorderVertex) - r |= RC_BORDER_VERTEX; - if (isAreaBorder) - r |= RC_AREA_BORDER; - points.push(px); - points.push(py); - points.push(pz); - points.push(r); - - flags[i] &= ~(1 << dir); // Remove visited edges - dir = (dir+1) & 0x3; // Rotate CW - } - else - { - int ni = -1; - const int nx = x + rcGetDirOffsetX(dir); - const int ny = y + rcGetDirOffsetY(dir); - const rcCompactSpan& s = chf.spans[i]; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const rcCompactCell& nc = chf.cells[nx+ny*chf.width]; - ni = (int)nc.index + rcGetCon(s, dir); - } - if (ni == -1) - { - // Should not happen. - return; - } - x = nx; - y = ny; - i = ni; - dir = (dir+3) & 0x3; // Rotate CCW - } - - if (starti == i && startDir == dir) - { - break; - } - } -} - -static float distancePtSeg(const int x, const int z, - const int px, const int pz, - const int qx, const int qz) -{ -/* float pqx = (float)(qx - px); - float pqy = (float)(qy - py); - float pqz = (float)(qz - pz); - float dx = (float)(x - px); - float dy = (float)(y - py); - float dz = (float)(z - pz); - float d = pqx*pqx + pqy*pqy + pqz*pqz; - float t = pqx*dx + pqy*dy + pqz*dz; - if (d > 0) - t /= d; - if (t < 0) - t = 0; - else if (t > 1) - t = 1; - - dx = px + t*pqx - x; - dy = py + t*pqy - y; - dz = pz + t*pqz - z; - - return dx*dx + dy*dy + dz*dz;*/ - - float pqx = (float)(qx - px); - float pqz = (float)(qz - pz); - float dx = (float)(x - px); - float dz = (float)(z - pz); - float d = pqx*pqx + pqz*pqz; - float t = pqx*dx + pqz*dz; - if (d > 0) - t /= d; - if (t < 0) - t = 0; - else if (t > 1) - t = 1; - - dx = px + t*pqx - x; - dz = pz + t*pqz - z; - - return dx*dx + dz*dz; -} - -static void simplifyContour(rcIntArray& points, rcIntArray& simplified, - const float maxError, const int maxEdgeLen, const int buildFlags) -{ - // Add initial points. - bool hasConnections = false; - for (int i = 0; i < points.size(); i += 4) - { - if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0) - { - hasConnections = true; - break; - } - } - - if (hasConnections) - { - // The contour has some portals to other regions. - // Add a new point to every location where the region changes. - for (int i = 0, ni = points.size()/4; i < ni; ++i) - { - int ii = (i+1) % ni; - const bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK); - const bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER); - if (differentRegs || areaBorders) - { - simplified.push(points[i*4+0]); - simplified.push(points[i*4+1]); - simplified.push(points[i*4+2]); - simplified.push(i); - } - } - } - - if (simplified.size() == 0) - { - // If there is no connections at all, - // create some initial points for the simplification process. - // Find lower-left and upper-right vertices of the contour. - int llx = points[0]; - int lly = points[1]; - int llz = points[2]; - int lli = 0; - int urx = points[0]; - int ury = points[1]; - int urz = points[2]; - int uri = 0; - for (int i = 0; i < points.size(); i += 4) - { - int x = points[i+0]; - int y = points[i+1]; - int z = points[i+2]; - if (x < llx || (x == llx && z < llz)) - { - llx = x; - lly = y; - llz = z; - lli = i/4; - } - if (x > urx || (x == urx && z > urz)) - { - urx = x; - ury = y; - urz = z; - uri = i/4; - } - } - simplified.push(llx); - simplified.push(lly); - simplified.push(llz); - simplified.push(lli); - - simplified.push(urx); - simplified.push(ury); - simplified.push(urz); - simplified.push(uri); - } - - // Add points until all raw points are within - // error tolerance to the simplified shape. - const int pn = points.size()/4; - for (int i = 0; i < simplified.size()/4; ) - { - int ii = (i+1) % (simplified.size()/4); - - const int ax = simplified[i*4+0]; - const int az = simplified[i*4+2]; - const int ai = simplified[i*4+3]; - - const int bx = simplified[ii*4+0]; - const int bz = simplified[ii*4+2]; - const int bi = simplified[ii*4+3]; - - // Find maximum deviation from the segment. - float maxd = 0; - int maxi = -1; - int ci, cinc, endi; - - // Traverse the segment in lexilogical order so that the - // max deviation is calculated similarly when traversing - // opposite segments. - if (bx > ax || (bx == ax && bz > az)) - { - cinc = 1; - ci = (ai+cinc) % pn; - endi = bi; - } - else - { - cinc = pn-1; - ci = (bi+cinc) % pn; - endi = ai; - } - - // Tessellate only outer edges oredges between areas. - if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 || - (points[ci*4+3] & RC_AREA_BORDER)) - { - while (ci != endi) - { - float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz); - if (d > maxd) - { - maxd = d; - maxi = ci; - } - ci = (ci+cinc) % pn; - } - } - - - // If the max deviation is larger than accepted error, - // add new point, else continue to next segment. - if (maxi != -1 && maxd > (maxError*maxError)) - { - // Add space for the new point. - simplified.resize(simplified.size()+4); - const int n = simplified.size()/4; - for (int j = n-1; j > i; --j) - { - simplified[j*4+0] = simplified[(j-1)*4+0]; - simplified[j*4+1] = simplified[(j-1)*4+1]; - simplified[j*4+2] = simplified[(j-1)*4+2]; - simplified[j*4+3] = simplified[(j-1)*4+3]; - } - // Add the point. - simplified[(i+1)*4+0] = points[maxi*4+0]; - simplified[(i+1)*4+1] = points[maxi*4+1]; - simplified[(i+1)*4+2] = points[maxi*4+2]; - simplified[(i+1)*4+3] = maxi; - } - else - { - ++i; - } - } - - // Split too long edges. - if (maxEdgeLen > 0 && (buildFlags & (RC_CONTOUR_TESS_WALL_EDGES|RC_CONTOUR_TESS_AREA_EDGES)) != 0) - { - for (int i = 0; i < simplified.size()/4; ) - { - const int ii = (i+1) % (simplified.size()/4); - - const int ax = simplified[i*4+0]; - const int az = simplified[i*4+2]; - const int ai = simplified[i*4+3]; - - const int bx = simplified[ii*4+0]; - const int bz = simplified[ii*4+2]; - const int bi = simplified[ii*4+3]; - - // Find maximum deviation from the segment. - int maxi = -1; - int ci = (ai+1) % pn; - - // Tessellate only outer edges or edges between areas. - bool tess = false; - // Wall edges. - if ((buildFlags & RC_CONTOUR_TESS_WALL_EDGES) && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0) - tess = true; - // Edges between areas. - if ((buildFlags & RC_CONTOUR_TESS_AREA_EDGES) && (points[ci*4+3] & RC_AREA_BORDER)) - tess = true; - - if (tess) - { - int dx = bx - ax; - int dz = bz - az; - if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen) - { - // Round based on the segments in lexilogical order so that the - // max tesselation is consistent regardles in which direction - // segments are traversed. - const int n = bi < ai ? (bi+pn - ai) : (bi - ai); - if (n > 1) - { - if (bx > ax || (bx == ax && bz > az)) - maxi = (ai + n/2) % pn; - else - maxi = (ai + (n+1)/2) % pn; - } - } - } - - // If the max deviation is larger than accepted error, - // add new point, else continue to next segment. - if (maxi != -1) - { - // Add space for the new point. - simplified.resize(simplified.size()+4); - const int n = simplified.size()/4; - for (int j = n-1; j > i; --j) - { - simplified[j*4+0] = simplified[(j-1)*4+0]; - simplified[j*4+1] = simplified[(j-1)*4+1]; - simplified[j*4+2] = simplified[(j-1)*4+2]; - simplified[j*4+3] = simplified[(j-1)*4+3]; - } - // Add the point. - simplified[(i+1)*4+0] = points[maxi*4+0]; - simplified[(i+1)*4+1] = points[maxi*4+1]; - simplified[(i+1)*4+2] = points[maxi*4+2]; - simplified[(i+1)*4+3] = maxi; - } - else - { - ++i; - } - } - } - - for (int i = 0; i < simplified.size()/4; ++i) - { - // The edge vertex flag is take from the current raw point, - // and the neighbour region is take from the next raw point. - const int ai = (simplified[i*4+3]+1) % pn; - const int bi = simplified[i*4+3]; - simplified[i*4+3] = (points[ai*4+3] & (RC_CONTOUR_REG_MASK|RC_AREA_BORDER)) | (points[bi*4+3] & RC_BORDER_VERTEX); - } - -} - -static void removeDegenerateSegments(rcIntArray& simplified) -{ - // Remove adjacent vertices which are equal on xz-plane, - // or else the triangulator will get confused. - for (int i = 0; i < simplified.size()/4; ++i) - { - int ni = i+1; - if (ni >= (simplified.size()/4)) - ni = 0; - - if (simplified[i*4+0] == simplified[ni*4+0] && - simplified[i*4+2] == simplified[ni*4+2]) - { - // Degenerate segment, remove. - for (int j = i; j < simplified.size()/4-1; ++j) - { - simplified[j*4+0] = simplified[(j+1)*4+0]; - simplified[j*4+1] = simplified[(j+1)*4+1]; - simplified[j*4+2] = simplified[(j+1)*4+2]; - simplified[j*4+3] = simplified[(j+1)*4+3]; - } - simplified.resize(simplified.size()-4); - } - } -} - -static int calcAreaOfPolygon2D(const int* verts, const int nverts) -{ - int area = 0; - for (int i = 0, j = nverts-1; i < nverts; j=i++) - { - const int* vi = &verts[i*4]; - const int* vj = &verts[j*4]; - area += vi[0] * vj[2] - vj[0] * vi[2]; - } - return (area+1) / 2; -} - -inline bool ileft(const int* a, const int* b, const int* c) -{ - return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0; -} - -static void getClosestIndices(const int* vertsa, const int nvertsa, - const int* vertsb, const int nvertsb, - int& ia, int& ib) -{ - int closestDist = 0xfffffff; - ia = -1, ib = -1; - for (int i = 0; i < nvertsa; ++i) - { - const int in = (i+1) % nvertsa; - const int ip = (i+nvertsa-1) % nvertsa; - const int* va = &vertsa[i*4]; - const int* van = &vertsa[in*4]; - const int* vap = &vertsa[ip*4]; - - for (int j = 0; j < nvertsb; ++j) - { - const int* vb = &vertsb[j*4]; - // vb must be "infront" of va. - if (ileft(vap,va,vb) && ileft(va,van,vb)) - { - const int dx = vb[0] - va[0]; - const int dz = vb[2] - va[2]; - const int d = dx*dx + dz*dz; - if (d < closestDist) - { - ia = i; - ib = j; - closestDist = d; - } - } - } - } -} - -static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) -{ - const int maxVerts = ca.nverts + cb.nverts + 2; - int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM); - if (!verts) - return false; - - int nv = 0; - - // Copy contour A. - for (int i = 0; i <= ca.nverts; ++i) - { - int* dst = &verts[nv*4]; - const int* src = &ca.verts[((ia+i)%ca.nverts)*4]; - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; - nv++; - } - - // Copy contour B - for (int i = 0; i <= cb.nverts; ++i) - { - int* dst = &verts[nv*4]; - const int* src = &cb.verts[((ib+i)%cb.nverts)*4]; - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; - nv++; - } - - rcFree(ca.verts); - ca.verts = verts; - ca.nverts = nv; - - rcFree(cb.verts); - cb.verts = 0; - cb.nverts = 0; - - return true; -} - -bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, - const float maxError, const int maxEdgeLen, - rcContourSet& cset, const int buildFlags) -{ - rcAssert(ctx); - - const int w = chf.width; - const int h = chf.height; - - ctx->startTimer(RC_TIMER_BUILD_CONTOURS); - - rcVcopy(cset.bmin, chf.bmin); - rcVcopy(cset.bmax, chf.bmax); - cset.cs = chf.cs; - cset.ch = chf.ch; - - int maxContours = rcMax((int)chf.maxRegions, 8); - cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); - if (!cset.conts) - return false; - cset.nconts = 0; - - rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); - if (!flags) - { - ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount); - return false; - } - - ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); - - // Mark boundaries. - 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) - { - unsigned char res = 0; - const rcCompactSpan& s = chf.spans[i]; - if (!chf.spans[i].reg || (chf.spans[i].reg & RC_BORDER_REG)) - { - flags[i] = 0; - continue; - } - for (int dir = 0; dir < 4; ++dir) - { - unsigned short r = 0; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - r = chf.spans[ai].reg; - } - if (r == chf.spans[i].reg) - res |= (1 << dir); - } - flags[i] = res ^ 0xf; // Inverse, mark non connected edges. - } - } - } - - ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); - - ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); - - rcIntArray verts(256); - rcIntArray simplified(64); - - 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) - { - if (flags[i] == 0 || flags[i] == 0xf) - { - flags[i] = 0; - continue; - } - const unsigned short reg = chf.spans[i].reg; - if (!reg || (reg & RC_BORDER_REG)) - continue; - const unsigned char area = chf.areas[i]; - - verts.resize(0); - simplified.resize(0); - walkContour(x, y, i, chf, flags, verts); - simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags); - removeDegenerateSegments(simplified); - - // Store region->contour remap info. - // Create contour. - if (simplified.size()/4 >= 3) - { - if (cset.nconts >= maxContours) - { - // Allocate more contours. - // This can happen when there are tiny holes in the heightfield. - const int oldMax = maxContours; - maxContours *= 2; - rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); - for (int j = 0; j < cset.nconts; ++j) - { - newConts[j] = cset.conts[j]; - // Reset source pointers to prevent data deletion. - cset.conts[j].verts = 0; - cset.conts[j].rverts = 0; - } - rcFree(cset.conts); - cset.conts = newConts; - - ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours); - } - - rcContour* cont = &cset.conts[cset.nconts++]; - - cont->nverts = simplified.size()/4; - cont->verts = (int*)rcAlloc(sizeof(int)*cont->nverts*4, RC_ALLOC_PERM); - if (!cont->verts) - { - ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' (%d).", cont->nverts); - return false; - } - memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4); - - cont->nrverts = verts.size()/4; - cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM); - if (!cont->rverts) - { - ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' (%d).", cont->nrverts); - return false; - } - memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4); - -/* cont->cx = cont->cy = cont->cz = 0; - for (int i = 0; i < cont->nverts; ++i) - { - cont->cx += cont->verts[i*4+0]; - cont->cy += cont->verts[i*4+1]; - cont->cz += cont->verts[i*4+2]; - } - cont->cx /= cont->nverts; - cont->cy /= cont->nverts; - cont->cz /= cont->nverts;*/ - - cont->reg = reg; - cont->area = area; - } - } - } - } - - // Check and merge droppings. - // Sometimes the previous algorithms can fail and create several contours - // per area. This pass will try to merge the holes into the main region. - for (int i = 0; i < cset.nconts; ++i) - { - rcContour& cont = cset.conts[i]; - // Check if the contour is would backwards. - if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0) - { - // Find another contour which has the same region ID. - int mergeIdx = -1; - for (int j = 0; j < cset.nconts; ++j) - { - if (i == j) continue; - if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg) - { - // Make sure the polygon is correctly oriented. - if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts)) - { - mergeIdx = j; - break; - } - } - } - if (mergeIdx == -1) - { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i); - } - else - { - rcContour& mcont = cset.conts[mergeIdx]; - // Merge by closest points. - int ia = 0, ib = 0; - getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib); - if (ia == -1 || ib == -1) - { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx); - continue; - } - if (!mergeContours(mcont, cont, ia, ib)) - { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx); - continue; - } - } - } - } - - ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); - - ctx->stopTimer(RC_TIMER_BUILD_CONTOURS); - - return true; -} diff --git a/deps/recastnavigation/Recast/RecastFilter.cpp b/deps/recastnavigation/Recast/RecastFilter.cpp deleted file mode 100644 index 66af37a413..0000000000 --- a/deps/recastnavigation/Recast/RecastFilter.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// -// 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. -// - -#define _USE_MATH_DEFINES -#include <math.h> -#include <stdio.h> -#include "Recast.h" -#include "RecastAssert.h" - - -void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES); - - const int w = solid.width; - const int h = solid.height; - - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - rcSpan* ps = 0; - bool previousWalkable = false; - unsigned char previousArea = RC_NULL_AREA; - - for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next) - { - const bool walkable = s->area != RC_NULL_AREA; - // If current span is not walkable, but there is walkable - // span just below it, mark the span above it walkable too. - if (!walkable && previousWalkable) - { - if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb) - s->area = previousArea; - } - // Copy walkable flag so that it cannot propagate - // past multiple non-walkable objects. - previousWalkable = walkable; - previousArea = s->area; - } - } - } - - ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES); -} - -void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb, - rcHeightfield& solid) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_FILTER_BORDER); - - const int w = solid.width; - const int h = solid.height; - const int MAX_HEIGHT = 0xffff; - - // Mark border spans. - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next) - { - // Skip non walkable spans. - if (s->area == RC_NULL_AREA) - continue; - - const int bot = (int)(s->smax); - const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; - - // Find neighbours minimum height. - int minh = MAX_HEIGHT; - - // Min and max height of accessible neighbours. - int asmin = s->smax; - int asmax = s->smax; - - for (int dir = 0; dir < 4; ++dir) - { - int dx = x + rcGetDirOffsetX(dir); - int dy = y + rcGetDirOffsetY(dir); - // Skip neighbours which are out of bounds. - if (dx < 0 || dy < 0 || dx >= w || dy >= h) - { - minh = rcMin(minh, -walkableClimb - bot); - continue; - } - - // From minus infinity to the first span. - rcSpan* ns = solid.spans[dx + dy*w]; - int nbot = -walkableClimb; - int ntop = ns ? (int)ns->smin : MAX_HEIGHT; - // Skip neightbour if the gap between the spans is too small. - if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) - minh = rcMin(minh, nbot - bot); - - // Rest of the spans. - for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next) - { - nbot = (int)ns->smax; - ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT; - // Skip neightbour if the gap between the spans is too small. - if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) - { - minh = rcMin(minh, nbot - bot); - - // Find min/max accessible neighbour height. - if (rcAbs(nbot - bot) <= walkableClimb) - { - if (nbot < asmin) asmin = nbot; - if (nbot > asmax) asmax = nbot; - } - - } - } - } - - // The current span is close to a ledge if the drop to any - // neighbour span is less than the walkableClimb. - if (minh < -walkableClimb) - s->area = RC_NULL_AREA; - - // If the difference between all neighbours is too large, - // we are at steep slope, mark the span as ledge. - if ((asmax - asmin) > walkableClimb) - { - s->area = RC_NULL_AREA; - } - } - } - } - - ctx->stopTimer(RC_TIMER_FILTER_BORDER); -} - -void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_FILTER_WALKABLE); - - const int w = solid.width; - const int h = solid.height; - const int MAX_HEIGHT = 0xffff; - - // Remove walkable flag from spans which do not have enough - // space above them for the agent to stand there. - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next) - { - const int bot = (int)(s->smax); - const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; - if ((top - bot) <= walkableHeight) - s->area = RC_NULL_AREA; - } - } - } - - ctx->stopTimer(RC_TIMER_FILTER_WALKABLE); -} diff --git a/deps/recastnavigation/Recast/RecastMesh.cpp b/deps/recastnavigation/Recast/RecastMesh.cpp deleted file mode 100644 index e7e2397dd6..0000000000 --- a/deps/recastnavigation/Recast/RecastMesh.cpp +++ /dev/null @@ -1,1324 +0,0 @@ -// -// 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. -// - -#define _USE_MATH_DEFINES -#include <math.h> -#include <string.h> -#include <stdio.h> -#include "Recast.h" -#include "RecastAlloc.h" -#include "RecastAssert.h" - -struct rcEdge -{ - unsigned short vert[2]; - unsigned short polyEdge[2]; - unsigned short poly[2]; -}; - -static bool buildMeshAdjacency(unsigned short* polys, const int npolys, - const int nverts, const int vertsPerPoly) -{ - // Based on code by Eric Lengyel from: - // http://www.terathon.com/code/edges.php - - int maxEdgeCount = npolys*vertsPerPoly; - unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP); - if (!firstEdge) - return false; - unsigned short* nextEdge = firstEdge + nverts; - int edgeCount = 0; - - rcEdge* edges = (rcEdge*)rcAlloc(sizeof(rcEdge)*maxEdgeCount, RC_ALLOC_TEMP); - if (!edges) - { - rcFree(firstEdge); - return false; - } - - for (int i = 0; i < nverts; i++) - firstEdge[i] = RC_MESH_NULL_IDX; - - for (int i = 0; i < npolys; ++i) - { - unsigned short* t = &polys[i*vertsPerPoly*2]; - for (int j = 0; j < vertsPerPoly; ++j) - { - if (t[j] == RC_MESH_NULL_IDX) break; - unsigned short v0 = t[j]; - unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1]; - if (v0 < v1) - { - rcEdge& edge = edges[edgeCount]; - edge.vert[0] = v0; - edge.vert[1] = v1; - edge.poly[0] = (unsigned short)i; - edge.polyEdge[0] = (unsigned short)j; - edge.poly[1] = (unsigned short)i; - edge.polyEdge[1] = 0; - // Insert edge - nextEdge[edgeCount] = firstEdge[v0]; - firstEdge[v0] = (unsigned short)edgeCount; - edgeCount++; - } - } - } - - for (int i = 0; i < npolys; ++i) - { - unsigned short* t = &polys[i*vertsPerPoly*2]; - for (int j = 0; j < vertsPerPoly; ++j) - { - if (t[j] == RC_MESH_NULL_IDX) break; - unsigned short v0 = t[j]; - unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1]; - if (v0 > v1) - { - for (unsigned short e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = nextEdge[e]) - { - rcEdge& edge = edges[e]; - if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1]) - { - edge.poly[1] = (unsigned short)i; - edge.polyEdge[1] = (unsigned short)j; - break; - } - } - } - } - } - - // Store adjacency - for (int i = 0; i < edgeCount; ++i) - { - const rcEdge& e = edges[i]; - if (e.poly[0] != e.poly[1]) - { - unsigned short* p0 = &polys[e.poly[0]*vertsPerPoly*2]; - unsigned short* p1 = &polys[e.poly[1]*vertsPerPoly*2]; - p0[vertsPerPoly + e.polyEdge[0]] = e.poly[1]; - p1[vertsPerPoly + e.polyEdge[1]] = e.poly[0]; - } - } - - rcFree(firstEdge); - rcFree(edges); - - return true; -} - - -static const int VERTEX_BUCKET_COUNT = (1<<12); - -inline int computeVertexHash(int x, int y, int z) -{ - const unsigned int h1 = 0x8da6b343; // Large multiplicative constants; - const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes - const unsigned int h3 = 0xcb1ab31f; - unsigned int n = h1 * x + h2 * y + h3 * z; - return (int)(n & (VERTEX_BUCKET_COUNT-1)); -} - -static unsigned short addVertex(unsigned short x, unsigned short y, unsigned short z, - unsigned short* verts, int* firstVert, int* nextVert, int& nv) -{ - int bucket = computeVertexHash(x, 0, z); - int i = firstVert[bucket]; - - while (i != -1) - { - const unsigned short* v = &verts[i*3]; - if (v[0] == x && (rcAbs(v[1] - y) <= 2) && v[2] == z) - return (unsigned short)i; - i = nextVert[i]; // next - } - - // Could not find, create new. - i = nv; nv++; - unsigned short* v = &verts[i*3]; - v[0] = x; - v[1] = y; - v[2] = z; - nextVert[i] = firstVert[bucket]; - firstVert[bucket] = i; - - return (unsigned short)i; -} - -inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; } -inline int next(int i, int n) { return i+1 < n ? i+1 : 0; } - -inline int area2(const int* a, const int* b, const int* c) -{ - return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]); -} - -// Exclusive or: true iff exactly one argument is true. -// The arguments are negated to ensure that they are 0/1 -// values. Then the bitwise Xor operator may apply. -// (This idea is due to Michael Baldwin.) -inline bool xorb(bool x, bool y) -{ - return !x ^ !y; -} - -// Returns true iff c is strictly to the left of the directed -// line through a to b. -inline bool left(const int* a, const int* b, const int* c) -{ - return area2(a, b, c) < 0; -} - -inline bool leftOn(const int* a, const int* b, const int* c) -{ - return area2(a, b, c) <= 0; -} - -inline bool collinear(const int* a, const int* b, const int* c) -{ - return area2(a, b, c) == 0; -} - -// Returns true iff ab properly intersects cd: they share -// a point interior to both segments. The properness of the -// intersection is ensured by using strict leftness. -bool intersectProp(const int* a, const int* b, const int* c, const int* d) -{ - // Eliminate improper cases. - if (collinear(a,b,c) || collinear(a,b,d) || - collinear(c,d,a) || collinear(c,d,b)) - return false; - - return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b)); -} - -// Returns T iff (a,b,c) are collinear and point c lies -// on the closed segement ab. -static bool between(const int* a, const int* b, const int* c) -{ - if (!collinear(a, b, c)) - return false; - // If ab not vertical, check betweenness on x; else on y. - if (a[0] != b[0]) - return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0])); - else - return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2])); -} - -// Returns true iff segments ab and cd intersect, properly or improperly. -static bool intersect(const int* a, const int* b, const int* c, const int* d) -{ - if (intersectProp(a, b, c, d)) - return true; - else if (between(a, b, c) || between(a, b, d) || - between(c, d, a) || between(c, d, b)) - return true; - else - return false; -} - -static bool vequal(const int* a, const int* b) -{ - return a[0] == b[0] && a[2] == b[2]; -} - -// Returns T iff (v_i, v_j) is a proper internal *or* external -// diagonal of P, *ignoring edges incident to v_i and v_j*. -static bool diagonalie(int i, int j, int n, const int* verts, int* indices) -{ - const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4]; - const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4]; - - // For each edge (k,k+1) of P - for (int k = 0; k < n; k++) - { - int k1 = next(k, n); - // Skip edges incident to i or j - if (!((k == i) || (k1 == i) || (k == j) || (k1 == j))) - { - const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4]; - const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4]; - - if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1)) - continue; - - if (intersect(d0, d1, p0, p1)) - return false; - } - } - return true; -} - -// Returns true iff the diagonal (i,j) is strictly internal to the -// polygon P in the neighborhood of the i endpoint. -static bool inCone(int i, int j, int n, const int* verts, int* indices) -{ - const int* pi = &verts[(indices[i] & 0x0fffffff) * 4]; - const int* pj = &verts[(indices[j] & 0x0fffffff) * 4]; - const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4]; - const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4]; - - // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ]. - if (leftOn(pin1, pi, pi1)) - return left(pi, pj, pin1) && left(pj, pi, pi1); - // Assume (i-1,i,i+1) not collinear. - // else P[i] is reflex. - return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1)); -} - -// Returns T iff (v_i, v_j) is a proper internal -// diagonal of P. -static bool diagonal(int i, int j, int n, const int* verts, int* indices) -{ - return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices); -} - -static int triangulate(int n, const int* verts, int* indices, int* tris) -{ - int ntris = 0; - int* dst = tris; - - // The last bit of the index is used to indicate if the vertex can be removed. - for (int i = 0; i < n; i++) - { - int i1 = next(i, n); - int i2 = next(i1, n); - if (diagonal(i, i2, n, verts, indices)) - indices[i1] |= 0x80000000; - } - - while (n > 3) - { - int minLen = -1; - int mini = -1; - for (int i = 0; i < n; i++) - { - int i1 = next(i, n); - if (indices[i1] & 0x80000000) - { - const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4]; - const int* p2 = &verts[(indices[next(i1, n)] & 0x0fffffff) * 4]; - - int dx = p2[0] - p0[0]; - int dy = p2[2] - p0[2]; - int len = dx*dx + dy*dy; - - if (minLen < 0 || len < minLen) - { - minLen = len; - mini = i; - } - } - } - - if (mini == -1) - { - // Should not happen. -/* printf("mini == -1 ntris=%d n=%d\n", ntris, n); - for (int i = 0; i < n; i++) - { - printf("%d ", indices[i] & 0x0fffffff); - } - printf("\n");*/ - return -ntris; - } - - int i = mini; - int i1 = next(i, n); - int i2 = next(i1, n); - - *dst++ = indices[i] & 0x0fffffff; - *dst++ = indices[i1] & 0x0fffffff; - *dst++ = indices[i2] & 0x0fffffff; - ntris++; - - // Removes P[i1] by copying P[i+1]...P[n-1] left one index. - n--; - for (int k = i1; k < n; k++) - indices[k] = indices[k+1]; - - if (i1 >= n) i1 = 0; - i = prev(i1,n); - // Update diagonal flags. - if (diagonal(prev(i, n), i1, n, verts, indices)) - indices[i] |= 0x80000000; - else - indices[i] &= 0x0fffffff; - - if (diagonal(i, next(i1, n), n, verts, indices)) - indices[i1] |= 0x80000000; - else - indices[i1] &= 0x0fffffff; - } - - // Append the remaining triangle. - *dst++ = indices[0] & 0x0fffffff; - *dst++ = indices[1] & 0x0fffffff; - *dst++ = indices[2] & 0x0fffffff; - ntris++; - - return ntris; -} - -static int countPolyVerts(const unsigned short* p, const int nvp) -{ - for (int i = 0; i < nvp; ++i) - if (p[i] == RC_MESH_NULL_IDX) - return i; - return nvp; -} - -inline bool uleft(const unsigned short* a, const unsigned short* b, const unsigned short* c) -{ - return ((int)b[0] - (int)a[0]) * ((int)c[2] - (int)a[2]) - - ((int)c[0] - (int)a[0]) * ((int)b[2] - (int)a[2]) < 0; -} - -static int getPolyMergeValue(unsigned short* pa, unsigned short* pb, - const unsigned short* verts, int& ea, int& eb, - const int nvp) -{ - const int na = countPolyVerts(pa, nvp); - const int nb = countPolyVerts(pb, nvp); - - // If the merged polygon would be too big, do not merge. - if (na+nb-2 > nvp) - return -1; - - // Check if the polygons share an edge. - ea = -1; - eb = -1; - - for (int i = 0; i < na; ++i) - { - unsigned short va0 = pa[i]; - unsigned short va1 = pa[(i+1) % na]; - if (va0 > va1) - rcSwap(va0, va1); - for (int j = 0; j < nb; ++j) - { - unsigned short vb0 = pb[j]; - unsigned short vb1 = pb[(j+1) % nb]; - if (vb0 > vb1) - rcSwap(vb0, vb1); - if (va0 == vb0 && va1 == vb1) - { - ea = i; - eb = j; - break; - } - } - } - - // No common edge, cannot merge. - if (ea == -1 || eb == -1) - return -1; - - // Check to see if the merged polygon would be convex. - unsigned short va, vb, vc; - - va = pa[(ea+na-1) % na]; - vb = pa[ea]; - vc = pb[(eb+2) % nb]; - if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3])) - return -1; - - va = pb[(eb+nb-1) % nb]; - vb = pb[eb]; - vc = pa[(ea+2) % na]; - if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3])) - return -1; - - va = pa[ea]; - vb = pa[(ea+1)%na]; - - int dx = (int)verts[va*3+0] - (int)verts[vb*3+0]; - int dy = (int)verts[va*3+2] - (int)verts[vb*3+2]; - - return dx*dx + dy*dy; -} - -static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb, - unsigned short* tmp, const int nvp) -{ - const int na = countPolyVerts(pa, nvp); - const int nb = countPolyVerts(pb, nvp); - - // Merge polygons. - memset(tmp, 0xff, sizeof(unsigned short)*nvp); - int n = 0; - // Add pa - for (int i = 0; i < na-1; ++i) - tmp[n++] = pa[(ea+1+i) % na]; - // Add pb - for (int i = 0; i < nb-1; ++i) - tmp[n++] = pb[(eb+1+i) % nb]; - - memcpy(pa, tmp, sizeof(unsigned short)*nvp); -} - -static void pushFront(int v, int* arr, int& an) -{ - an++; - for (int i = an-1; i > 0; --i) arr[i] = arr[i-1]; - arr[0] = v; -} - -static void pushBack(int v, int* arr, int& an) -{ - arr[an] = v; - an++; -} - -static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem) -{ - const int nvp = mesh.nvp; - - // Count number of polygons to remove. - int numRemovedVerts = 0; - int numTouchedVerts = 0; - int numRemainingEdges = 0; - for (int i = 0; i < mesh.npolys; ++i) - { - unsigned short* p = &mesh.polys[i*nvp*2]; - const int nv = countPolyVerts(p, nvp); - int numRemoved = 0; - int numVerts = 0; - for (int j = 0; j < nv; ++j) - { - if (p[j] == rem) - { - numTouchedVerts++; - numRemoved++; - } - numVerts++; - } - if (numRemoved) - { - numRemovedVerts += numRemoved; - numRemainingEdges += numVerts-(numRemoved+1); - } - } - - // There would be too few edges remaining to create a polygon. - // This can happen for example when a tip of a triangle is marked - // as deletion, but there are no other polys that share the vertex. - // In this case, the vertex should not be removed. - if (numRemainingEdges <= 2) - return false; - - // Find edges which share the removed vertex. - const int maxEdges = numTouchedVerts*2; - int nedges = 0; - rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP); - if (!edges) - { - ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3); - return false; - } - - for (int i = 0; i < mesh.npolys; ++i) - { - unsigned short* p = &mesh.polys[i*nvp*2]; - const int nv = countPolyVerts(p, nvp); - - // Collect edges which touches the removed vertex. - for (int j = 0, k = nv-1; j < nv; k = j++) - { - if (p[j] == rem || p[k] == rem) - { - // Arrange edge so that a=rem. - int a = p[j], b = p[k]; - if (b == rem) - rcSwap(a,b); - - // Check if the edge exists - bool exists = false; - for (int k = 0; k < nedges; ++k) - { - int* e = &edges[k*3]; - if (e[1] == b) - { - // Exists, increment vertex share count. - e[2]++; - exists = true; - } - } - // Add new edge. - if (!exists) - { - int* e = &edges[nedges*3]; - e[0] = a; - e[1] = b; - e[2] = 1; - nedges++; - } - } - } - } - - // There should be no more than 2 open edges. - // This catches the case that two non-adjacent polygons - // share the removed vertex. In that case, do not remove the vertex. - int numOpenEdges = 0; - for (int i = 0; i < nedges; ++i) - { - if (edges[i*3+2] < 2) - numOpenEdges++; - } - if (numOpenEdges > 2) - return false; - - return true; -} - -static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem, const int maxTris) -{ - const int nvp = mesh.nvp; - - // Count number of polygons to remove. - int numRemovedVerts = 0; - for (int i = 0; i < mesh.npolys; ++i) - { - unsigned short* p = &mesh.polys[i*nvp*2]; - const int nv = countPolyVerts(p, nvp); - for (int j = 0; j < nv; ++j) - { - if (p[j] == rem) - numRemovedVerts++; - } - } - - int nedges = 0; - rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP); - if (!edges) - { - ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4); - return false; - } - - int nhole = 0; - rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); - if (!hole) - { - ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp); - return false; - } - - int nhreg = 0; - rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); - if (!hreg) - { - ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp); - return false; - } - - int nharea = 0; - rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); - if (!harea) - { - ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp); - return false; - } - - for (int i = 0; i < mesh.npolys; ++i) - { - unsigned short* p = &mesh.polys[i*nvp*2]; - const int nv = countPolyVerts(p, nvp); - bool hasRem = false; - for (int j = 0; j < nv; ++j) - if (p[j] == rem) hasRem = true; - if (hasRem) - { - // Collect edges which does not touch the removed vertex. - for (int j = 0, k = nv-1; j < nv; k = j++) - { - if (p[j] != rem && p[k] != rem) - { - int* e = &edges[nedges*4]; - e[0] = p[k]; - e[1] = p[j]; - e[2] = mesh.regs[i]; - e[3] = mesh.areas[i]; - nedges++; - } - } - // Remove the polygon. - unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2]; - memcpy(p,p2,sizeof(unsigned short)*nvp); - memset(p+nvp,0xff,sizeof(unsigned short)*nvp); - mesh.regs[i] = mesh.regs[mesh.npolys-1]; - mesh.areas[i] = mesh.areas[mesh.npolys-1]; - mesh.npolys--; - --i; - } - } - - // Remove vertex. - for (int i = (int)rem; i < mesh.nverts; ++i) - { - mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0]; - mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1]; - mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2]; - } - mesh.nverts--; - - // Adjust indices to match the removed vertex layout. - for (int i = 0; i < mesh.npolys; ++i) - { - unsigned short* p = &mesh.polys[i*nvp*2]; - const int nv = countPolyVerts(p, nvp); - for (int j = 0; j < nv; ++j) - if (p[j] > rem) p[j]--; - } - for (int i = 0; i < nedges; ++i) - { - if (edges[i*4+0] > rem) edges[i*4+0]--; - if (edges[i*4+1] > rem) edges[i*4+1]--; - } - - if (nedges == 0) - return true; - - // Start with one vertex, keep appending connected - // segments to the start and end of the hole. - pushBack(edges[0], hole, nhole); - pushBack(edges[2], hreg, nhreg); - pushBack(edges[3], harea, nharea); - - while (nedges) - { - bool match = false; - - for (int i = 0; i < nedges; ++i) - { - const int ea = edges[i*4+0]; - const int eb = edges[i*4+1]; - const int r = edges[i*4+2]; - const int a = edges[i*4+3]; - bool add = false; - if (hole[0] == eb) - { - // The segment matches the beginning of the hole boundary. - pushFront(ea, hole, nhole); - pushFront(r, hreg, nhreg); - pushFront(a, harea, nharea); - add = true; - } - else if (hole[nhole-1] == ea) - { - // The segment matches the end of the hole boundary. - pushBack(eb, hole, nhole); - pushBack(r, hreg, nhreg); - pushBack(a, harea, nharea); - add = true; - } - if (add) - { - // The edge segment was added, remove it. - edges[i*4+0] = edges[(nedges-1)*4+0]; - edges[i*4+1] = edges[(nedges-1)*4+1]; - edges[i*4+2] = edges[(nedges-1)*4+2]; - edges[i*4+3] = edges[(nedges-1)*4+3]; - --nedges; - match = true; - --i; - } - } - - if (!match) - break; - } - - rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP); - if (!tris) - { - ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3); - return false; - } - - rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP); - if (!tverts) - { - ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4); - return false; - } - - rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP); - if (!tverts) - { - ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole); - return false; - } - - // Generate temp vertex array for triangulation. - for (int i = 0; i < nhole; ++i) - { - const int pi = hole[i]; - tverts[i*4+0] = mesh.verts[pi*3+0]; - tverts[i*4+1] = mesh.verts[pi*3+1]; - tverts[i*4+2] = mesh.verts[pi*3+2]; - tverts[i*4+3] = 0; - thole[i] = i; - } - - // Triangulate the hole. - int ntris = triangulate(nhole, &tverts[0], &thole[0], tris); - if (ntris < 0) - { - ntris = -ntris; - ctx->log(RC_LOG_WARNING, "removeVertex: triangulate() returned bad results."); - } - - // Merge the hole triangles back to polygons. - rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP); - if (!polys) - { - ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp); - return false; - } - rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP); - if (!pregs) - { - ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris); - return false; - } - rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP); - if (!pregs) - { - ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris); - return false; - } - - unsigned short* tmpPoly = &polys[ntris*nvp]; - - // Build initial polygons. - int npolys = 0; - memset(polys, 0xff, ntris*nvp*sizeof(unsigned short)); - for (int j = 0; j < ntris; ++j) - { - int* t = &tris[j*3]; - if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2]) - { - polys[npolys*nvp+0] = (unsigned short)hole[t[0]]; - polys[npolys*nvp+1] = (unsigned short)hole[t[1]]; - polys[npolys*nvp+2] = (unsigned short)hole[t[2]]; - pregs[npolys] = (unsigned short)hreg[t[0]]; - pareas[npolys] = (unsigned char)harea[t[0]]; - npolys++; - } - } - if (!npolys) - return true; - - // Merge polygons. - if (nvp > 3) - { - for (;;) - { - // Find best polygons to merge. - int bestMergeVal = 0; - int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; - - for (int j = 0; j < npolys-1; ++j) - { - unsigned short* pj = &polys[j*nvp]; - for (int k = j+1; k < npolys; ++k) - { - unsigned short* pk = &polys[k*nvp]; - int ea, eb; - int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp); - if (v > bestMergeVal) - { - bestMergeVal = v; - bestPa = j; - bestPb = k; - bestEa = ea; - bestEb = eb; - } - } - } - - if (bestMergeVal > 0) - { - // Found best, merge. - unsigned short* pa = &polys[bestPa*nvp]; - unsigned short* pb = &polys[bestPb*nvp]; - mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); - memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); - pregs[bestPb] = pregs[npolys-1]; - pareas[bestPb] = pareas[npolys-1]; - npolys--; - } - else - { - // Could not merge any polygons, stop. - break; - } - } - } - - // Store polygons. - for (int i = 0; i < npolys; ++i) - { - if (mesh.npolys >= maxTris) break; - unsigned short* p = &mesh.polys[mesh.npolys*nvp*2]; - memset(p,0xff,sizeof(unsigned short)*nvp*2); - for (int j = 0; j < nvp; ++j) - p[j] = polys[i*nvp+j]; - mesh.regs[mesh.npolys] = pregs[i]; - mesh.areas[mesh.npolys] = pareas[i]; - mesh.npolys++; - if (mesh.npolys > maxTris) - { - ctx->log(RC_LOG_ERROR, "removeVertex: Too many polygons %d (max:%d).", mesh.npolys, maxTris); - return false; - } - } - - return true; -} - - -bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_BUILD_POLYMESH); - - rcVcopy(mesh.bmin, cset.bmin); - rcVcopy(mesh.bmax, cset.bmax); - mesh.cs = cset.cs; - mesh.ch = cset.ch; - - int maxVertices = 0; - int maxTris = 0; - int maxVertsPerCont = 0; - for (int i = 0; i < cset.nconts; ++i) - { - // Skip null contours. - if (cset.conts[i].nverts < 3) continue; - maxVertices += cset.conts[i].nverts; - maxTris += cset.conts[i].nverts - 2; - maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts); - } - - if (maxVertices >= 0xfffe) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices); - return false; - } - - rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP); - if (!vflags) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); - return false; - } - memset(vflags, 0, maxVertices); - - mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertices*3, RC_ALLOC_PERM); - if (!mesh.verts) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); - return false; - } - mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2*2, RC_ALLOC_PERM); - if (!mesh.polys) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2); - return false; - } - mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris, RC_ALLOC_PERM); - if (!mesh.regs) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris); - return false; - } - mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris, RC_ALLOC_PERM); - if (!mesh.areas) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' (%d).", maxTris); - return false; - } - - mesh.nverts = 0; - mesh.npolys = 0; - mesh.nvp = nvp; - mesh.maxpolys = maxTris; - - memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3); - memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2); - memset(mesh.regs, 0, sizeof(unsigned short)*maxTris); - memset(mesh.areas, 0, sizeof(unsigned char)*maxTris); - - rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP); - if (!nextVert) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices); - return false; - } - memset(nextVert, 0, sizeof(int)*maxVertices); - - rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); - if (!firstVert) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); - return false; - } - for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) - firstVert[i] = -1; - - rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP); - if (!indices) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont); - return false; - } - rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP); - if (!tris) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3); - return false; - } - rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP); - if (!polys) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp); - return false; - } - unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp]; - - for (int i = 0; i < cset.nconts; ++i) - { - rcContour& cont = cset.conts[i]; - - // Skip null contours. - if (cont.nverts < 3) - continue; - - // Triangulate contour - for (int j = 0; j < cont.nverts; ++j) - indices[j] = j; - - int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]); - if (ntris <= 0) - { - // Bad triangulation, should not happen. -/* printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]); - printf("\tconst float cs = %ff;\n", cset.cs); - printf("\tconst float ch = %ff;\n", cset.ch); - printf("\tconst int verts[] = {\n"); - for (int k = 0; k < cont.nverts; ++k) - { - const int* v = &cont.verts[k*4]; - printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]); - } - printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/ - ctx->log(RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour %d.", i); - ntris = -ntris; - } - - // Add and merge vertices. - for (int j = 0; j < cont.nverts; ++j) - { - const int* v = &cont.verts[j*4]; - indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2], - mesh.verts, firstVert, nextVert, mesh.nverts); - if (v[3] & RC_BORDER_VERTEX) - { - // This vertex should be removed. - vflags[indices[j]] = 1; - } - } - - // Build initial polygons. - int npolys = 0; - memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short)); - for (int j = 0; j < ntris; ++j) - { - int* t = &tris[j*3]; - if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2]) - { - polys[npolys*nvp+0] = (unsigned short)indices[t[0]]; - polys[npolys*nvp+1] = (unsigned short)indices[t[1]]; - polys[npolys*nvp+2] = (unsigned short)indices[t[2]]; - npolys++; - } - } - if (!npolys) - continue; - - // Merge polygons. - if (nvp > 3) - { - for(;;) - { - // Find best polygons to merge. - int bestMergeVal = 0; - int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; - - for (int j = 0; j < npolys-1; ++j) - { - unsigned short* pj = &polys[j*nvp]; - for (int k = j+1; k < npolys; ++k) - { - unsigned short* pk = &polys[k*nvp]; - int ea, eb; - int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp); - if (v > bestMergeVal) - { - bestMergeVal = v; - bestPa = j; - bestPb = k; - bestEa = ea; - bestEb = eb; - } - } - } - - if (bestMergeVal > 0) - { - // Found best, merge. - unsigned short* pa = &polys[bestPa*nvp]; - unsigned short* pb = &polys[bestPb*nvp]; - mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); - memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); - npolys--; - } - else - { - // Could not merge any polygons, stop. - break; - } - } - } - - // Store polygons. - for (int j = 0; j < npolys; ++j) - { - unsigned short* p = &mesh.polys[mesh.npolys*nvp*2]; - unsigned short* q = &polys[j*nvp]; - for (int k = 0; k < nvp; ++k) - p[k] = q[k]; - mesh.regs[mesh.npolys] = cont.reg; - mesh.areas[mesh.npolys] = cont.area; - mesh.npolys++; - if (mesh.npolys > maxTris) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons %d (max:%d).", mesh.npolys, maxTris); - return false; - } - } - } - - - // Remove edge vertices. - for (int i = 0; i < mesh.nverts; ++i) - { - if (vflags[i]) - { - if (!canRemoveVertex(ctx, mesh, (unsigned short)i)) - continue; - if (!removeVertex(ctx, mesh, (unsigned short)i, maxTris)) - { - // Failed to remove vertex - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex %d.", i); - return false; - } - // Remove vertex - // Note: mesh.nverts is already decremented inside removeVertex()! - for (int j = i; j < mesh.nverts; ++j) - vflags[j] = vflags[j+1]; - --i; - } - } - - // Calculate adjacency. - if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp)) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed."); - return false; - } - - // Just allocate the mesh flags array. The user is resposible to fill it. - mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM); - if (!mesh.flags) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' (%d).", mesh.npolys); - return false; - } - memset(mesh.flags, 0, sizeof(unsigned short) * mesh.npolys); - - if (mesh.nverts > 0xffff) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff); - } - if (mesh.npolys > 0xffff) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff); - } - - ctx->stopTimer(RC_TIMER_BUILD_POLYMESH); - - return true; -} - -bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) -{ - rcAssert(ctx); - - if (!nmeshes || !meshes) - return true; - - ctx->startTimer(RC_TIMER_MERGE_POLYMESH); - - mesh.nvp = meshes[0]->nvp; - mesh.cs = meshes[0]->cs; - mesh.ch = meshes[0]->ch; - rcVcopy(mesh.bmin, meshes[0]->bmin); - rcVcopy(mesh.bmax, meshes[0]->bmax); - - int maxVerts = 0; - int maxPolys = 0; - int maxVertsPerMesh = 0; - for (int i = 0; i < nmeshes; ++i) - { - rcVmin(mesh.bmin, meshes[i]->bmin); - rcVmax(mesh.bmax, meshes[i]->bmax); - maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts); - maxVerts += meshes[i]->nverts; - maxPolys += meshes[i]->npolys; - } - - mesh.nverts = 0; - mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVerts*3, RC_ALLOC_PERM); - if (!mesh.verts) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3); - return false; - } - - mesh.npolys = 0; - mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM); - if (!mesh.polys) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp); - return false; - } - memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp); - - mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM); - if (!mesh.regs) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys); - return false; - } - memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys); - - mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxPolys, RC_ALLOC_PERM); - if (!mesh.areas) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' (%d).", maxPolys); - return false; - } - memset(mesh.areas, 0, sizeof(unsigned char)*maxPolys); - - mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM); - if (!mesh.flags) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' (%d).", maxPolys); - return false; - } - memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys); - - rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP); - if (!nextVert) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts); - return false; - } - memset(nextVert, 0, sizeof(int)*maxVerts); - - rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); - if (!firstVert) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); - return false; - } - for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) - firstVert[i] = -1; - - rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM); - if (!vremap) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh); - return false; - } - memset(vremap, 0, sizeof(unsigned short)*maxVertsPerMesh); - - for (int i = 0; i < nmeshes; ++i) - { - const rcPolyMesh* pmesh = meshes[i]; - - const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f); - const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f); - - for (int j = 0; j < pmesh->nverts; ++j) - { - unsigned short* v = &pmesh->verts[j*3]; - vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz, - mesh.verts, firstVert, nextVert, mesh.nverts); - } - - for (int j = 0; j < pmesh->npolys; ++j) - { - unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp]; - unsigned short* src = &pmesh->polys[j*2*mesh.nvp]; - mesh.regs[mesh.npolys] = pmesh->regs[j]; - mesh.areas[mesh.npolys] = pmesh->areas[j]; - mesh.flags[mesh.npolys] = pmesh->flags[j]; - mesh.npolys++; - for (int k = 0; k < mesh.nvp; ++k) - { - if (src[k] == RC_MESH_NULL_IDX) break; - tgt[k] = vremap[src[k]]; - } - } - } - - // Calculate adjacency. - if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp)) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed."); - return false; - } - - if (mesh.nverts > 0xffff) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff); - } - if (mesh.npolys > 0xffff) - { - ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff); - } - - ctx->stopTimer(RC_TIMER_MERGE_POLYMESH); - - return true; -} diff --git a/deps/recastnavigation/Recast/RecastMeshDetail.cpp b/deps/recastnavigation/Recast/RecastMeshDetail.cpp deleted file mode 100644 index ffb4b58ee9..0000000000 --- a/deps/recastnavigation/Recast/RecastMeshDetail.cpp +++ /dev/null @@ -1,1237 +0,0 @@ -// -// 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 "Recast.h" -#include "RecastAlloc.h" -#include "RecastAssert.h" - - -static const unsigned RC_UNSET_HEIGHT = 0xffff; - -struct rcHeightPatch -{ - inline rcHeightPatch() : data(0), xmin(0), ymin(0), width(0), height(0) {} - inline ~rcHeightPatch() { rcFree(data); } - unsigned short* data; - int xmin, ymin, width, height; -}; - - -inline float vdot2(const float* a, const float* b) -{ - return a[0]*b[0] + a[2]*b[2]; -} - -inline float vdistSq2(const float* p, const float* q) -{ - const float dx = q[0] - p[0]; - const float dy = q[2] - p[2]; - return dx*dx + dy*dy; -} - -inline float vdist2(const float* p, const float* q) -{ - return sqrtf(vdistSq2(p,q)); -} - -inline float vcross2(const float* p1, const float* p2, const float* p3) -{ - const float u1 = p2[0] - p1[0]; - const float v1 = p2[2] - p1[2]; - const float u2 = p3[0] - p1[0]; - const float v2 = p3[2] - p1[2]; - return u1 * v2 - v1 * u2; -} - -static bool circumCircle(const float* p1, const float* p2, const float* p3, - float* c, float& r) -{ - static const float EPS = 1e-6f; - - const float cp = vcross2(p1, p2, p3); - if (fabsf(cp) > EPS) - { - const float p1Sq = vdot2(p1,p1); - const float p2Sq = vdot2(p2,p2); - const float p3Sq = vdot2(p3,p3); - c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp); - c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp); - r = vdist2(c, p1); - return true; - } - - c[0] = p1[0]; - c[2] = p1[2]; - r = 0; - return false; -} - -static float distPtTri(const float* p, const float* a, const float* b, const float* c) -{ - float v0[3], v1[3], v2[3]; - rcVsub(v0, c,a); - rcVsub(v1, b,a); - rcVsub(v2, p,a); - - const float dot00 = vdot2(v0, v0); - const float dot01 = vdot2(v0, v1); - const float dot02 = vdot2(v0, v2); - const float dot11 = vdot2(v1, v1); - const float dot12 = vdot2(v1, v2); - - // Compute barycentric coordinates - const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); - const float u = (dot11 * dot02 - dot01 * dot12) * invDenom; - float v = (dot00 * dot12 - dot01 * dot02) * invDenom; - - // If point lies inside the triangle, return interpolated y-coord. - static const float EPS = 1e-4f; - if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS) - { - const float y = a[1] + v0[1]*u + v1[1]*v; - return fabsf(y-p[1]); - } - return FLT_MAX; -} - -static float distancePtSeg(const float* pt, const float* p, const float* q) -{ - float pqx = q[0] - p[0]; - float pqy = q[1] - p[1]; - float pqz = q[2] - p[2]; - float dx = pt[0] - p[0]; - float dy = pt[1] - p[1]; - float dz = pt[2] - p[2]; - float d = pqx*pqx + pqy*pqy + pqz*pqz; - float t = pqx*dx + pqy*dy + pqz*dz; - if (d > 0) - t /= d; - if (t < 0) - t = 0; - else if (t > 1) - t = 1; - - dx = p[0] + t*pqx - pt[0]; - dy = p[1] + t*pqy - pt[1]; - dz = p[2] + t*pqz - pt[2]; - - return dx*dx + dy*dy + dz*dz; -} - -static float distancePtSeg2d(const float* pt, const float* p, const float* q) -{ - float pqx = q[0] - p[0]; - float pqz = q[2] - p[2]; - float dx = pt[0] - p[0]; - float dz = pt[2] - p[2]; - float d = pqx*pqx + pqz*pqz; - float t = pqx*dx + pqz*dz; - if (d > 0) - t /= d; - if (t < 0) - t = 0; - else if (t > 1) - t = 1; - - dx = p[0] + t*pqx - pt[0]; - dz = p[2] + t*pqz - pt[2]; - - return dx*dx + dz*dz; -} - -static float distToTriMesh(const float* p, const float* verts, const int /*nverts*/, const int* tris, const int ntris) -{ - float dmin = FLT_MAX; - for (int i = 0; i < ntris; ++i) - { - const float* va = &verts[tris[i*4+0]*3]; - const float* vb = &verts[tris[i*4+1]*3]; - const float* vc = &verts[tris[i*4+2]*3]; - float d = distPtTri(p, va,vb,vc); - if (d < dmin) - dmin = d; - } - if (dmin == FLT_MAX) return -1; - return dmin; -} - -static float distToPoly(int nvert, const float* verts, const float* p) -{ - - float dmin = FLT_MAX; - int i, j, c = 0; - for (i = 0, j = nvert-1; i < nvert; j = i++) - { - const float* vi = &verts[i*3]; - const float* vj = &verts[j*3]; - if (((vi[2] > p[2]) != (vj[2] > p[2])) && - (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) ) - c = !c; - dmin = rcMin(dmin, distancePtSeg2d(p, vj, vi)); - } - return c ? -dmin : dmin; -} - - -static unsigned short getHeight(const float fx, const float fy, const float fz, - const float /*cs*/, const float ics, const float ch, - const rcHeightPatch& hp) -{ - int ix = (int)floorf(fx*ics + 0.01f); - int iz = (int)floorf(fz*ics + 0.01f); - ix = rcClamp(ix-hp.xmin, 0, hp.width); - iz = rcClamp(iz-hp.ymin, 0, hp.height); - unsigned short h = hp.data[ix+iz*hp.width]; - if (h == RC_UNSET_HEIGHT) - { - // Special case when data might be bad. - // Find nearest neighbour pixel which has valid height. - const int off[8*2] = { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1}; - float dmin = FLT_MAX; - for (int i = 0; i < 8; ++i) - { - const int nx = ix+off[i*2+0]; - const int nz = iz+off[i*2+1]; - if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue; - const unsigned short nh = hp.data[nx+nz*hp.width]; - if (nh == RC_UNSET_HEIGHT) continue; - - const float d = fabsf(nh*ch - fy); - if (d < dmin) - { - h = nh; - dmin = d; - } - -/* const float dx = (nx+0.5f)*cs - fx; - const float dz = (nz+0.5f)*cs - fz; - const float d = dx*dx+dz*dz; - if (d < dmin) - { - h = nh; - dmin = d; - } */ - } - } - return h; -} - - -enum EdgeValues -{ - UNDEF = -1, - HULL = -2, -}; - -static int findEdge(const int* edges, int nedges, int s, int t) -{ - for (int i = 0; i < nedges; i++) - { - const int* e = &edges[i*4]; - if ((e[0] == s && e[1] == t) || (e[0] == t && e[1] == s)) - return i; - } - return UNDEF; -} - -static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, int s, int t, int l, int r) -{ - if (nedges >= maxEdges) - { - ctx->log(RC_LOG_ERROR, "addEdge: Too many edges (%d/%d).", nedges, maxEdges); - return UNDEF; - } - - // Add edge if not already in the triangulation. - int e = findEdge(edges, nedges, s, t); - if (e == UNDEF) - { - int* e = &edges[nedges*4]; - e[0] = s; - e[1] = t; - e[2] = l; - e[3] = r; - return nedges++; - } - else - { - return UNDEF; - } -} - -static void updateLeftFace(int* e, int s, int t, int f) -{ - if (e[0] == s && e[1] == t && e[2] == UNDEF) - e[2] = f; - else if (e[1] == s && e[0] == t && e[3] == UNDEF) - e[3] = f; -} - -static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d) -{ - const float a1 = vcross2(a, b, d); - const float a2 = vcross2(a, b, c); - if (a1*a2 < 0.0f) - { - float a3 = vcross2(c, d, a); - float a4 = a3 + a2 - a1; - if (a3 * a4 < 0.0f) - return 1; - } - return 0; -} - -static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, int t1) -{ - for (int i = 0; i < nedges; ++i) - { - const int s0 = edges[i*4+0]; - const int t0 = edges[i*4+1]; - // Same or connected edges do not overlap. - if (s0 == s1 || s0 == t1 || t0 == s1 || t0 == t1) - continue; - if (overlapSegSeg2d(&pts[s0*3],&pts[t0*3], &pts[s1*3],&pts[t1*3])) - return true; - } - return false; -} - -static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e) -{ - static const float EPS = 1e-5f; - - int* edge = &edges[e*4]; - - // Cache s and t. - int s,t; - if (edge[2] == UNDEF) - { - s = edge[0]; - t = edge[1]; - } - else if (edge[3] == UNDEF) - { - s = edge[1]; - t = edge[0]; - } - else - { - // Edge already completed. - return; - } - - // Find best point on left of edge. - int pt = npts; - float c[3] = {0,0,0}; - float r = -1; - for (int u = 0; u < npts; ++u) - { - if (u == s || u == t) continue; - if (vcross2(&pts[s*3], &pts[t*3], &pts[u*3]) > EPS) - { - if (r < 0) - { - // The circle is not updated yet, do it now. - pt = u; - circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); - continue; - } - const float d = vdist2(c, &pts[u*3]); - const float tol = 0.001f; - if (d > r*(1+tol)) - { - // Outside current circumcircle, skip. - continue; - } - else if (d < r*(1-tol)) - { - // Inside safe circumcircle, update circle. - pt = u; - circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); - } - else - { - // Inside epsilon circum circle, do extra tests to make sure the edge is valid. - // s-u and t-u cannot overlap with s-pt nor t-pt if they exists. - if (overlapEdges(pts, edges, nedges, s,u)) - continue; - if (overlapEdges(pts, edges, nedges, t,u)) - continue; - // Edge is valid. - pt = u; - circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); - } - } - } - - // Add new triangle or update edge info if s-t is on hull. - if (pt < npts) - { - // Update face information of edge being completed. - updateLeftFace(&edges[e*4], s, t, nfaces); - - // Add new edge or update face info of old edge. - e = findEdge(edges, nedges, pt, s); - if (e == UNDEF) - addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF); - else - updateLeftFace(&edges[e*4], pt, s, nfaces); - - // Add new edge or update face info of old edge. - e = findEdge(edges, nedges, t, pt); - if (e == UNDEF) - addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF); - else - updateLeftFace(&edges[e*4], t, pt, nfaces); - - nfaces++; - } - else - { - updateLeftFace(&edges[e*4], s, t, HULL); - } -} - -static void delaunayHull(rcContext* ctx, const int npts, const float* pts, - const int nhull, const int* hull, - rcIntArray& tris, rcIntArray& edges) -{ - int nfaces = 0; - int nedges = 0; - const int maxEdges = npts*10; - edges.resize(maxEdges*4); - - for (int i = 0, j = nhull-1; i < nhull; j=i++) - addEdge(ctx, &edges[0], nedges, maxEdges, hull[j],hull[i], HULL, UNDEF); - - int currentEdge = 0; - while (currentEdge < nedges) - { - if (edges[currentEdge*4+2] == UNDEF) - completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge); - if (edges[currentEdge*4+3] == UNDEF) - completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge); - currentEdge++; - } - - // Create tris - tris.resize(nfaces*4); - for (int i = 0; i < nfaces*4; ++i) - tris[i] = -1; - - for (int i = 0; i < nedges; ++i) - { - const int* e = &edges[i*4]; - if (e[3] >= 0) - { - // Left face - int* t = &tris[e[3]*4]; - if (t[0] == -1) - { - t[0] = e[0]; - t[1] = e[1]; - } - else if (t[0] == e[1]) - t[2] = e[0]; - else if (t[1] == e[0]) - t[2] = e[1]; - } - if (e[2] >= 0) - { - // Right - int* t = &tris[e[2]*4]; - if (t[0] == -1) - { - t[0] = e[1]; - t[1] = e[0]; - } - else if (t[0] == e[0]) - t[2] = e[1]; - else if (t[1] == e[1]) - t[2] = e[0]; - } - } - - for (int i = 0; i < tris.size()/4; ++i) - { - int* t = &tris[i*4]; - if (t[0] == -1 || t[1] == -1 || t[2] == -1) - { - ctx->log(RC_LOG_WARNING, "delaunayHull: Removing dangling face %d [%d,%d,%d].", i, t[0],t[1],t[2]); - t[0] = tris[tris.size()-4]; - t[1] = tris[tris.size()-3]; - t[2] = tris[tris.size()-2]; - t[3] = tris[tris.size()-1]; - tris.resize(tris.size()-4); - --i; - } - } -} - - -inline float getJitterX(const int i) -{ - return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f; -} - -inline float getJitterY(const int i) -{ - return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f; -} - -static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, - const float sampleDist, const float sampleMaxError, - const rcCompactHeightfield& chf, const rcHeightPatch& hp, - float* verts, int& nverts, rcIntArray& tris, - rcIntArray& edges, rcIntArray& samples) -{ - static const int MAX_VERTS = 127; - static const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts). - static const int MAX_VERTS_PER_EDGE = 32; - float edge[(MAX_VERTS_PER_EDGE+1)*3]; - int hull[MAX_VERTS]; - int nhull = 0; - - nverts = 0; - - for (int i = 0; i < nin; ++i) - rcVcopy(&verts[i*3], &in[i*3]); - nverts = nin; - - const float cs = chf.cs; - const float ics = 1.0f/cs; - - // Tessellate outlines. - // This is done in separate pass in order to ensure - // seamless height values across the ply boundaries. - if (sampleDist > 0) - { - for (int i = 0, j = nin-1; i < nin; j=i++) - { - const float* vj = &in[j*3]; - const float* vi = &in[i*3]; - bool swapped = false; - // Make sure the segments are always handled in same order - // using lexological sort or else there will be seams. - if (fabsf(vj[0]-vi[0]) < 1e-6f) - { - if (vj[2] > vi[2]) - { - rcSwap(vj,vi); - swapped = true; - } - } - else - { - if (vj[0] > vi[0]) - { - rcSwap(vj,vi); - swapped = true; - } - } - // Create samples along the edge. - float dx = vi[0] - vj[0]; - float dy = vi[1] - vj[1]; - float dz = vi[2] - vj[2]; - float d = sqrtf(dx*dx + dz*dz); - int nn = 1 + (int)floorf(d/sampleDist); - if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1; - if (nverts+nn >= MAX_VERTS) - nn = MAX_VERTS-1-nverts; - - for (int k = 0; k <= nn; ++k) - { - float u = (float)k/(float)nn; - float* pos = &edge[k*3]; - pos[0] = vj[0] + dx*u; - pos[1] = vj[1] + dy*u; - pos[2] = vj[2] + dz*u; - pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, hp)*chf.ch; - } - // Simplify samples. - int idx[MAX_VERTS_PER_EDGE] = {0,nn}; - int nidx = 2; - for (int k = 0; k < nidx-1; ) - { - const int a = idx[k]; - const int b = idx[k+1]; - const float* va = &edge[a*3]; - const float* vb = &edge[b*3]; - // Find maximum deviation along the segment. - float maxd = 0; - int maxi = -1; - for (int m = a+1; m < b; ++m) - { - float d = distancePtSeg(&edge[m*3],va,vb); - if (d > maxd) - { - maxd = d; - maxi = m; - } - } - // If the max deviation is larger than accepted error, - // add new point, else continue to next segment. - if (maxi != -1 && maxd > rcSqr(sampleMaxError)) - { - for (int m = nidx; m > k; --m) - idx[m] = idx[m-1]; - idx[k+1] = maxi; - nidx++; - } - else - { - ++k; - } - } - - hull[nhull++] = j; - // Add new vertices. - if (swapped) - { - for (int k = nidx-2; k > 0; --k) - { - rcVcopy(&verts[nverts*3], &edge[idx[k]*3]); - hull[nhull++] = nverts; - nverts++; - } - } - else - { - for (int k = 1; k < nidx-1; ++k) - { - rcVcopy(&verts[nverts*3], &edge[idx[k]*3]); - hull[nhull++] = nverts; - nverts++; - } - } - } - } - - - // Tessellate the base mesh. - edges.resize(0); - tris.resize(0); - - delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); - - if (tris.size() == 0) - { - // Could not triangulate the poly, make sure there is some valid data there. - ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data."); - for (int i = 2; i < nverts; ++i) - { - tris.push(0); - tris.push(i-1); - tris.push(i); - tris.push(0); - } - return true; - } - - if (sampleDist > 0) - { - // Create sample locations in a grid. - float bmin[3], bmax[3]; - rcVcopy(bmin, in); - rcVcopy(bmax, in); - for (int i = 1; i < nin; ++i) - { - rcVmin(bmin, &in[i*3]); - rcVmax(bmax, &in[i*3]); - } - int x0 = (int)floorf(bmin[0]/sampleDist); - int x1 = (int)ceilf(bmax[0]/sampleDist); - int z0 = (int)floorf(bmin[2]/sampleDist); - int z1 = (int)ceilf(bmax[2]/sampleDist); - samples.resize(0); - for (int z = z0; z < z1; ++z) - { - for (int x = x0; x < x1; ++x) - { - float pt[3]; - pt[0] = x*sampleDist; - pt[1] = (bmax[1]+bmin[1])*0.5f; - pt[2] = z*sampleDist; - // Make sure the samples are not too close to the edges. - if (distToPoly(nin,in,pt) > -sampleDist/2) continue; - samples.push(x); - samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp)); - samples.push(z); - samples.push(0); // Not added - } - } - - // Add the samples starting from the one that has the most - // error. The procedure stops when all samples are added - // or when the max error is within treshold. - const int nsamples = samples.size()/4; - for (int iter = 0; iter < nsamples; ++iter) - { - if (nverts >= MAX_VERTS) - break; - - // Find sample with most error. - float bestpt[3] = {0,0,0}; - float bestd = 0; - int besti = -1; - for (int i = 0; i < nsamples; ++i) - { - const int* s = &samples[i*4]; - if (s[3]) continue; // skip added. - float pt[3]; - // The sample location is jittered to get rid of some bad triangulations - // which are cause by symmetrical data from the grid structure. - pt[0] = s[0]*sampleDist + getJitterX(i)*cs*0.1f; - pt[1] = s[1]*chf.ch; - pt[2] = s[2]*sampleDist + getJitterY(i)*cs*0.1f; - float d = distToTriMesh(pt, verts, nverts, &tris[0], tris.size()/4); - if (d < 0) continue; // did not hit the mesh. - if (d > bestd) - { - bestd = d; - besti = i; - rcVcopy(bestpt,pt); - } - } - // If the max error is within accepted threshold, stop tesselating. - if (bestd <= sampleMaxError || besti == -1) - break; - // Mark sample as added. - samples[besti*4+3] = 1; - // Add the new sample point. - rcVcopy(&verts[nverts*3],bestpt); - nverts++; - - // Create new triangulation. - // TODO: Incremental add instead of full rebuild. - edges.resize(0); - tris.resize(0); - delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); - } - } - - const int ntris = tris.size()/4; - if (ntris > MAX_TRIS) - { - tris.resize(MAX_TRIS*4); - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS); - } - - return true; -} - -static void getHeightData(const rcCompactHeightfield& chf, - const unsigned short* poly, const int npoly, - const unsigned short* verts, - rcHeightPatch& hp, rcIntArray& stack) -{ - // Floodfill the heightfield to get 2D height data, - // starting at vertex locations as seeds. - - memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height); - - stack.resize(0); - - static const int offset[9*2] = - { - 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0, - }; - - // Use poly vertices as seed points for the flood fill. - for (int j = 0; j < npoly; ++j) - { - int cx = 0, cz = 0, ci =-1; - int dmin = RC_UNSET_HEIGHT; - for (int k = 0; k < 9; ++k) - { - const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0]; - const int ay = (int)verts[poly[j]*3+1]; - const int az = (int)verts[poly[j]*3+2] + offset[k*2+1]; - if (ax < hp.xmin || ax >= hp.xmin+hp.width || - az < hp.ymin || az >= hp.ymin+hp.height) - continue; - - const rcCompactCell& c = chf.cells[ax+az*chf.width]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - const rcCompactSpan& s = chf.spans[i]; - int d = rcAbs(ay - (int)s.y); - if (d < dmin) - { - cx = ax; - cz = az; - ci = i; - dmin = d; - } - } - } - if (ci != -1) - { - stack.push(cx); - stack.push(cz); - stack.push(ci); - } - } - - // Find center of the polygon using flood fill. - int pcx = 0, pcz = 0; - for (int j = 0; j < npoly; ++j) - { - pcx += (int)verts[poly[j]*3+0]; - pcz += (int)verts[poly[j]*3+2]; - } - pcx /= npoly; - pcz /= npoly; - - for (int i = 0; i < stack.size(); i += 3) - { - int cx = stack[i+0]; - int cy = stack[i+1]; - int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; - hp.data[idx] = 1; - } - - while (stack.size() > 0) - { - int ci = stack.pop(); - int cy = stack.pop(); - int cx = stack.pop(); - - // Check if close to center of the polygon. - if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1) - { - stack.resize(0); - stack.push(cx); - stack.push(cy); - stack.push(ci); - break; - } - - const rcCompactSpan& cs = chf.spans[ci]; - - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; - - const int ax = cx + rcGetDirOffsetX(dir); - const int ay = cy + rcGetDirOffsetY(dir); - - if (ax < hp.xmin || ax >= (hp.xmin+hp.width) || - ay < hp.ymin || ay >= (hp.ymin+hp.height)) - continue; - - if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0) - continue; - - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir); - - int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; - hp.data[idx] = 1; - - stack.push(ax); - stack.push(ay); - stack.push(ai); - } - } - - memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); - - // Mark start locations. - for (int i = 0; i < stack.size(); i += 3) - { - int cx = stack[i+0]; - int cy = stack[i+1]; - int ci = stack[i+2]; - int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; - const rcCompactSpan& cs = chf.spans[ci]; - hp.data[idx] = cs.y; - } - - static const int RETRACT_SIZE = 256; - int head = 0; - - while (head*3 < stack.size()) - { - int cx = stack[head*3+0]; - int cy = stack[head*3+1]; - int ci = stack[head*3+2]; - head++; - if (head >= RETRACT_SIZE) - { - head = 0; - if (stack.size() > RETRACT_SIZE*3) - memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3)); - stack.resize(stack.size()-RETRACT_SIZE*3); - } - - const rcCompactSpan& cs = chf.spans[ci]; - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; - - const int ax = cx + rcGetDirOffsetX(dir); - const int ay = cy + rcGetDirOffsetY(dir); - - if (ax < hp.xmin || ax >= (hp.xmin+hp.width) || - ay < hp.ymin || ay >= (hp.ymin+hp.height)) - continue; - - if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT) - continue; - - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir); - - const rcCompactSpan& as = chf.spans[ai]; - int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; - hp.data[idx] = as.y; - - stack.push(ax); - stack.push(ay); - stack.push(ai); - } - } - -} - -static unsigned char getEdgeFlags(const float* va, const float* vb, - const float* vpoly, const int npoly) -{ - // Return true if edge (va,vb) is part of the polygon. - static const float thrSqr = rcSqr(0.001f); - for (int i = 0, j = npoly-1; i < npoly; j=i++) - { - if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr && - distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr) - return 1; - } - return 0; -} - -static unsigned char getTriFlags(const float* va, const float* vb, const float* vc, - const float* vpoly, const int npoly) -{ - unsigned char flags = 0; - flags |= getEdgeFlags(va,vb,vpoly,npoly) << 0; - flags |= getEdgeFlags(vb,vc,vpoly,npoly) << 2; - flags |= getEdgeFlags(vc,va,vpoly,npoly) << 4; - return flags; -} - - - -bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf, - const float sampleDist, const float sampleMaxError, - rcPolyMeshDetail& dmesh) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL); - - if (mesh.nverts == 0 || mesh.npolys == 0) - return true; - - const int nvp = mesh.nvp; - const float cs = mesh.cs; - const float ch = mesh.ch; - const float* orig = mesh.bmin; - - rcIntArray edges(64); - rcIntArray tris(512); - rcIntArray stack(512); - rcIntArray samples(512); - float verts[256*3]; - rcHeightPatch hp; - int nPolyVerts = 0; - int maxhw = 0, maxhh = 0; - - rcScopedDelete<int> bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP); - if (!bounds) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4); - return false; - } - rcScopedDelete<float> poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP); - if (!poly) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3); - return false; - } - - // Find max size for a polygon area. - for (int i = 0; i < mesh.npolys; ++i) - { - const unsigned short* p = &mesh.polys[i*nvp*2]; - int& xmin = bounds[i*4+0]; - int& xmax = bounds[i*4+1]; - int& ymin = bounds[i*4+2]; - int& ymax = bounds[i*4+3]; - xmin = chf.width; - xmax = 0; - ymin = chf.height; - ymax = 0; - for (int j = 0; j < nvp; ++j) - { - if(p[j] == RC_MESH_NULL_IDX) break; - const unsigned short* v = &mesh.verts[p[j]*3]; - xmin = rcMin(xmin, (int)v[0]); - xmax = rcMax(xmax, (int)v[0]); - ymin = rcMin(ymin, (int)v[2]); - ymax = rcMax(ymax, (int)v[2]); - nPolyVerts++; - } - xmin = rcMax(0,xmin-1); - xmax = rcMin(chf.width,xmax+1); - ymin = rcMax(0,ymin-1); - ymax = rcMin(chf.height,ymax+1); - if (xmin >= xmax || ymin >= ymax) continue; - maxhw = rcMax(maxhw, xmax-xmin); - maxhh = rcMax(maxhh, ymax-ymin); - } - - hp.data = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxhw*maxhh, RC_ALLOC_TEMP); - if (!hp.data) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh); - return false; - } - - dmesh.nmeshes = mesh.npolys; - dmesh.nverts = 0; - dmesh.ntris = 0; - dmesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*dmesh.nmeshes*4, RC_ALLOC_PERM); - if (!dmesh.meshes) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4); - return false; - } - - int vcap = nPolyVerts+nPolyVerts/2; - int tcap = vcap*2; - - dmesh.nverts = 0; - dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); - if (!dmesh.verts) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3); - return false; - } - dmesh.ntris = 0; - dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM); - if (!dmesh.tris) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4); - return false; - } - - for (int i = 0; i < mesh.npolys; ++i) - { - const unsigned short* p = &mesh.polys[i*nvp*2]; - - // Store polygon vertices for processing. - int npoly = 0; - for (int j = 0; j < nvp; ++j) - { - if(p[j] == RC_MESH_NULL_IDX) break; - const unsigned short* v = &mesh.verts[p[j]*3]; - poly[j*3+0] = v[0]*cs; - poly[j*3+1] = v[1]*ch; - poly[j*3+2] = v[2]*cs; - npoly++; - } - - // Get the height data from the area of the polygon. - hp.xmin = bounds[i*4+0]; - hp.ymin = bounds[i*4+2]; - hp.width = bounds[i*4+1]-bounds[i*4+0]; - hp.height = bounds[i*4+3]-bounds[i*4+2]; - getHeightData(chf, p, npoly, mesh.verts, hp, stack); - - // Build detail mesh. - int nverts = 0; - if (!buildPolyDetail(ctx, poly, npoly, - sampleDist, sampleMaxError, - chf, hp, verts, nverts, tris, - edges, samples)) - { - return false; - } - - // Move detail verts to world space. - for (int j = 0; j < nverts; ++j) - { - verts[j*3+0] += orig[0]; - verts[j*3+1] += orig[1] + chf.ch; // Is this offset necessary? - verts[j*3+2] += orig[2]; - } - // Offset poly too, will be used to flag checking. - for (int j = 0; j < npoly; ++j) - { - poly[j*3+0] += orig[0]; - poly[j*3+1] += orig[1]; - poly[j*3+2] += orig[2]; - } - - // Store detail submesh. - const int ntris = tris.size()/4; - - dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts; - dmesh.meshes[i*4+1] = (unsigned int)nverts; - dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris; - dmesh.meshes[i*4+3] = (unsigned int)ntris; - - // Store vertices, allocate more memory if necessary. - if (dmesh.nverts+nverts > vcap) - { - while (dmesh.nverts+nverts > vcap) - vcap += 256; - - float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); - if (!newv) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3); - return false; - } - if (dmesh.nverts) - memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts); - rcFree(dmesh.verts); - dmesh.verts = newv; - } - for (int j = 0; j < nverts; ++j) - { - dmesh.verts[dmesh.nverts*3+0] = verts[j*3+0]; - dmesh.verts[dmesh.nverts*3+1] = verts[j*3+1]; - dmesh.verts[dmesh.nverts*3+2] = verts[j*3+2]; - dmesh.nverts++; - } - - // Store triangles, allocate more memory if necessary. - if (dmesh.ntris+ntris > tcap) - { - while (dmesh.ntris+ntris > tcap) - tcap += 256; - unsigned char* newt = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM); - if (!newt) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4); - return false; - } - if (dmesh.ntris) - memcpy(newt, dmesh.tris, sizeof(unsigned char)*4*dmesh.ntris); - rcFree(dmesh.tris); - dmesh.tris = newt; - } - for (int j = 0; j < ntris; ++j) - { - const int* t = &tris[j*4]; - dmesh.tris[dmesh.ntris*4+0] = (unsigned char)t[0]; - dmesh.tris[dmesh.ntris*4+1] = (unsigned char)t[1]; - dmesh.tris[dmesh.ntris*4+2] = (unsigned char)t[2]; - dmesh.tris[dmesh.ntris*4+3] = getTriFlags(&verts[t[0]*3], &verts[t[1]*3], &verts[t[2]*3], poly, npoly); - dmesh.ntris++; - } - } - - ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL); - - return true; -} - -bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL); - - int maxVerts = 0; - int maxTris = 0; - int maxMeshes = 0; - - for (int i = 0; i < nmeshes; ++i) - { - if (!meshes[i]) continue; - maxVerts += meshes[i]->nverts; - maxTris += meshes[i]->ntris; - maxMeshes += meshes[i]->nmeshes; - } - - mesh.nmeshes = 0; - mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM); - if (!mesh.meshes) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4); - return false; - } - - mesh.ntris = 0; - mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM); - if (!mesh.tris) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4); - return false; - } - - mesh.nverts = 0; - mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM); - if (!mesh.verts) - { - ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3); - return false; - } - - // Merge datas. - for (int i = 0; i < nmeshes; ++i) - { - rcPolyMeshDetail* dm = meshes[i]; - if (!dm) continue; - for (int j = 0; j < dm->nmeshes; ++j) - { - unsigned int* dst = &mesh.meshes[mesh.nmeshes*4]; - unsigned int* src = &dm->meshes[j*4]; - dst[0] = (unsigned int)mesh.nverts+src[0]; - dst[1] = src[1]; - dst[2] = (unsigned int)mesh.ntris+src[2]; - dst[3] = src[3]; - mesh.nmeshes++; - } - - for (int k = 0; k < dm->nverts; ++k) - { - rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]); - mesh.nverts++; - } - for (int k = 0; k < dm->ntris; ++k) - { - mesh.tris[mesh.ntris*4+0] = dm->tris[k*4+0]; - mesh.tris[mesh.ntris*4+1] = dm->tris[k*4+1]; - mesh.tris[mesh.ntris*4+2] = dm->tris[k*4+2]; - mesh.tris[mesh.ntris*4+3] = dm->tris[k*4+3]; - mesh.ntris++; - } - } - - ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL); - - return true; -} - diff --git a/deps/recastnavigation/Recast/RecastRasterization.cpp b/deps/recastnavigation/Recast/RecastRasterization.cpp deleted file mode 100644 index 71adfb6732..0000000000 --- a/deps/recastnavigation/Recast/RecastRasterization.cpp +++ /dev/null @@ -1,360 +0,0 @@ -// -// 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. -// - -#define _USE_MATH_DEFINES -#include <math.h> -#include <stdio.h> -#include "Recast.h" -#include "RecastAlloc.h" -#include "RecastAssert.h" - -inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax) -{ - bool overlap = true; - overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; - overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; - overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; - return overlap; -} - -inline bool overlapInterval(unsigned short amin, unsigned short amax, - unsigned short bmin, unsigned short bmax) -{ - if (amax < bmin) return false; - if (amin > bmax) return false; - return true; -} - - -static rcSpan* allocSpan(rcHeightfield& hf) -{ - // If running out of memory, allocate new page and update the freelist. - if (!hf.freelist || !hf.freelist->next) - { - // Create new page. - // Allocate memory for the new pool. - rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); - if (!pool) return 0; - pool->next = 0; - // Add the pool into the list of pools. - pool->next = hf.pools; - hf.pools = pool; - // Add new items to the free list. - rcSpan* freelist = hf.freelist; - rcSpan* head = &pool->items[0]; - rcSpan* it = &pool->items[RC_SPANS_PER_POOL]; - do - { - --it; - it->next = freelist; - freelist = it; - } - while (it != head); - hf.freelist = it; - } - - // Pop item from in front of the free list. - rcSpan* it = hf.freelist; - hf.freelist = hf.freelist->next; - return it; -} - -static void freeSpan(rcHeightfield& hf, rcSpan* ptr) -{ - if (!ptr) return; - // Add the node in front of the free list. - ptr->next = hf.freelist; - hf.freelist = ptr; -} - -static void addSpan(rcHeightfield& hf, const int x, const int y, - const unsigned short smin, const unsigned short smax, - const unsigned char area, const int flagMergeThr) -{ - - int idx = x + y*hf.width; - - rcSpan* s = allocSpan(hf); - s->smin = smin; - s->smax = smax; - s->area = area; - s->next = 0; - - // Empty cell, add he first span. - if (!hf.spans[idx]) - { - hf.spans[idx] = s; - return; - } - rcSpan* prev = 0; - rcSpan* cur = hf.spans[idx]; - - // Insert and merge spans. - while (cur) - { - if (cur->smin > s->smax) - { - // Current span is further than the new span, break. - break; - } - else if (cur->smax < s->smin) - { - // Current span is before the new span advance. - prev = cur; - cur = cur->next; - } - else - { - // Merge spans. - if (cur->smin < s->smin) - s->smin = cur->smin; - if (cur->smax > s->smax) - s->smax = cur->smax; - - // Merge flags. - if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr) - s->area = rcMax(s->area, cur->area); - - // Remove current span. - rcSpan* next = cur->next; - freeSpan(hf, cur); - if (prev) - prev->next = next; - else - hf.spans[idx] = next; - cur = next; - } - } - - // Insert new span. - if (prev) - { - s->next = prev->next; - prev->next = s; - } - else - { - s->next = hf.spans[idx]; - hf.spans[idx] = s; - } -} - -void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y, - const unsigned short smin, const unsigned short smax, - const unsigned char area, const int flagMergeThr) -{ -// rcAssert(ctx); - addSpan(hf, x,y, smin, smax, area, flagMergeThr); -} - -static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd) -{ - float d[12]; - for (int i = 0; i < n; ++i) - d[i] = pnx*in[i*3+0] + pnz*in[i*3+2] + pd; - - int m = 0; - for (int i = 0, j = n-1; i < n; j=i, ++i) - { - bool ina = d[j] >= 0; - bool inb = d[i] >= 0; - if (ina != inb) - { - float s = d[j] / (d[j] - d[i]); - out[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s; - out[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s; - out[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s; - m++; - } - if (inb) - { - out[m*3+0] = in[i*3+0]; - out[m*3+1] = in[i*3+1]; - out[m*3+2] = in[i*3+2]; - m++; - } - } - return m; -} - -static void rasterizeTri(const float* v0, const float* v1, const float* v2, - const unsigned char area, rcHeightfield& hf, - const float* bmin, const float* bmax, - const float cs, const float ics, const float ich, - const int flagMergeThr) -{ - const int w = hf.width; - const int h = hf.height; - float tmin[3], tmax[3]; - const float by = bmax[1] - bmin[1]; - - // Calculate the bounding box of the triangle. - rcVcopy(tmin, v0); - rcVcopy(tmax, v0); - rcVmin(tmin, v1); - rcVmin(tmin, v2); - rcVmax(tmax, v1); - rcVmax(tmax, v2); - - // If the triangle does not touch the bbox of the heightfield, skip the triagle. - if (!overlapBounds(bmin, bmax, tmin, tmax)) - return; - - // Calculate the footpring of the triangle on the grid. - int x0 = (int)((tmin[0] - bmin[0])*ics); - int y0 = (int)((tmin[2] - bmin[2])*ics); - int x1 = (int)((tmax[0] - bmin[0])*ics); - int y1 = (int)((tmax[2] - bmin[2])*ics); - x0 = rcClamp(x0, 0, w-1); - y0 = rcClamp(y0, 0, h-1); - x1 = rcClamp(x1, 0, w-1); - y1 = rcClamp(y1, 0, h-1); - - // Clip the triangle into all grid cells it touches. - float in[7*3], out[7*3], inrow[7*3]; - - for (int y = y0; y <= y1; ++y) - { - // Clip polygon to row. - rcVcopy(&in[0], v0); - rcVcopy(&in[1*3], v1); - rcVcopy(&in[2*3], v2); - int nvrow = 3; - const float cz = bmin[2] + y*cs; - nvrow = clipPoly(in, nvrow, out, 0, 1, -cz); - if (nvrow < 3) continue; - nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs); - if (nvrow < 3) continue; - - for (int x = x0; x <= x1; ++x) - { - // Clip polygon to column. - int nv = nvrow; - const float cx = bmin[0] + x*cs; - nv = clipPoly(inrow, nv, out, 1, 0, -cx); - if (nv < 3) continue; - nv = clipPoly(out, nv, in, -1, 0, cx+cs); - if (nv < 3) continue; - - // Calculate min and max of the span. - float smin = in[1], smax = in[1]; - for (int i = 1; i < nv; ++i) - { - smin = rcMin(smin, in[i*3+1]); - smax = rcMax(smax, in[i*3+1]); - } - smin -= bmin[1]; - smax -= bmin[1]; - // Skip the span if it is outside the heightfield bbox - if (smax < 0.0f) continue; - if (smin > by) continue; - // Clamp the span to the heightfield bbox. - if (smin < 0.0f) smin = 0; - if (smax > by) smax = by; - - // Snap the span to the heightfield height grid. - unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT); - unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT); - - addSpan(hf, x, y, ismin, ismax, area, flagMergeThr); - } - } -} - -void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, - const unsigned char area, rcHeightfield& solid, - const int flagMergeThr) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); - - const float ics = 1.0f/solid.cs; - const float ich = 1.0f/solid.ch; - rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); - - ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); -} - -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, - const int* tris, const unsigned char* areas, const int nt, - rcHeightfield& solid, const int flagMergeThr) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); - - const float ics = 1.0f/solid.cs; - const float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) - { - const float* v0 = &verts[tris[i*3+0]*3]; - const float* v1 = &verts[tris[i*3+1]*3]; - const float* v2 = &verts[tris[i*3+2]*3]; - // Rasterize. - rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); - } - - ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); -} - -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, - const unsigned short* tris, const unsigned char* areas, const int nt, - rcHeightfield& solid, const int flagMergeThr) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); - - const float ics = 1.0f/solid.cs; - const float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) - { - const float* v0 = &verts[tris[i*3+0]*3]; - const float* v1 = &verts[tris[i*3+1]*3]; - const float* v2 = &verts[tris[i*3+2]*3]; - // Rasterize. - rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); - } - - ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); -} - -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, - rcHeightfield& solid, const int flagMergeThr) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); - - const float ics = 1.0f/solid.cs; - const float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) - { - const float* v0 = &verts[(i*3+0)*3]; - const float* v1 = &verts[(i*3+1)*3]; - const float* v2 = &verts[(i*3+2)*3]; - // Rasterize. - rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); - } - - ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); -} diff --git a/deps/recastnavigation/Recast/RecastRegion.cpp b/deps/recastnavigation/Recast/RecastRegion.cpp deleted file mode 100644 index c624bf6619..0000000000 --- a/deps/recastnavigation/Recast/RecastRegion.cpp +++ /dev/null @@ -1,1285 +0,0 @@ -// -// 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 "Recast.h" -#include "RecastAlloc.h" -#include "RecastAssert.h" -#include <new> - - -static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist) -{ - const int w = chf.width; - const int h = chf.height; - - // Init distance and points. - for (int i = 0; i < chf.spanCount; ++i) - src[i] = 0xffff; - - // Mark boundary cells. - 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) - { - const rcCompactSpan& s = chf.spans[i]; - const unsigned char area = chf.areas[i]; - - int nc = 0; - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - if (area == chf.areas[ai]) - nc++; - } - } - if (nc != 4) - src[i] = 0; - } - } - } - - - // Pass 1 - 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) - { - const rcCompactSpan& s = chf.spans[i]; - - if (rcGetCon(s, 0) != RC_NOT_CONNECTED) - { - // (-1,0) - const int ax = x + rcGetDirOffsetX(0); - const int ay = y + rcGetDirOffsetY(0); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); - const rcCompactSpan& as = chf.spans[ai]; - if (src[ai]+2 < src[i]) - src[i] = src[ai]+2; - - // (-1,-1) - if (rcGetCon(as, 3) != RC_NOT_CONNECTED) - { - const int aax = ax + rcGetDirOffsetX(3); - const int aay = ay + rcGetDirOffsetY(3); - const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3); - if (src[aai]+3 < src[i]) - src[i] = src[aai]+3; - } - } - if (rcGetCon(s, 3) != RC_NOT_CONNECTED) - { - // (0,-1) - const int ax = x + rcGetDirOffsetX(3); - const int ay = y + rcGetDirOffsetY(3); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); - const rcCompactSpan& as = chf.spans[ai]; - if (src[ai]+2 < src[i]) - src[i] = src[ai]+2; - - // (1,-1) - if (rcGetCon(as, 2) != RC_NOT_CONNECTED) - { - const int aax = ax + rcGetDirOffsetX(2); - const int aay = ay + rcGetDirOffsetY(2); - const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2); - if (src[aai]+3 < src[i]) - src[i] = src[aai]+3; - } - } - } - } - } - - // Pass 2 - for (int y = h-1; y >= 0; --y) - { - for (int x = w-1; x >= 0; --x) - { - const rcCompactCell& c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - const rcCompactSpan& s = chf.spans[i]; - - if (rcGetCon(s, 2) != RC_NOT_CONNECTED) - { - // (1,0) - const int ax = x + rcGetDirOffsetX(2); - const int ay = y + rcGetDirOffsetY(2); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2); - const rcCompactSpan& as = chf.spans[ai]; - if (src[ai]+2 < src[i]) - src[i] = src[ai]+2; - - // (1,1) - if (rcGetCon(as, 1) != RC_NOT_CONNECTED) - { - const int aax = ax + rcGetDirOffsetX(1); - const int aay = ay + rcGetDirOffsetY(1); - const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1); - if (src[aai]+3 < src[i]) - src[i] = src[aai]+3; - } - } - if (rcGetCon(s, 1) != RC_NOT_CONNECTED) - { - // (0,1) - const int ax = x + rcGetDirOffsetX(1); - const int ay = y + rcGetDirOffsetY(1); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1); - const rcCompactSpan& as = chf.spans[ai]; - if (src[ai]+2 < src[i]) - src[i] = src[ai]+2; - - // (-1,1) - if (rcGetCon(as, 0) != RC_NOT_CONNECTED) - { - const int aax = ax + rcGetDirOffsetX(0); - const int aay = ay + rcGetDirOffsetY(0); - const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0); - if (src[aai]+3 < src[i]) - src[i] = src[aai]+3; - } - } - } - } - } - - maxDist = 0; - for (int i = 0; i < chf.spanCount; ++i) - maxDist = rcMax(src[i], maxDist); - -} - -static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr, - unsigned short* src, unsigned short* dst) -{ - const int w = chf.width; - const int h = chf.height; - - thr *= 2; - - 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) - { - const rcCompactSpan& s = chf.spans[i]; - const unsigned short cd = src[i]; - if (cd <= thr) - { - dst[i] = cd; - continue; - } - - int d = (int)cd; - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - d += (int)src[ai]; - - const rcCompactSpan& as = chf.spans[ai]; - const int dir2 = (dir+1) & 0x3; - if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) - { - const int ax2 = ax + rcGetDirOffsetX(dir2); - const int ay2 = ay + rcGetDirOffsetY(dir2); - const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2); - d += (int)src[ai2]; - } - else - { - d += cd; - } - } - else - { - d += cd*2; - } - } - dst[i] = (unsigned short)((d+5)/9); - } - } - } - return dst; -} - - -static bool floodRegion(int x, int y, int i, - unsigned short level, unsigned short r, - rcCompactHeightfield& chf, - unsigned short* srcReg, unsigned short* srcDist, - rcIntArray& stack) -{ - const int w = chf.width; - - const unsigned char area = chf.areas[i]; - - // Flood fill mark region. - stack.resize(0); - stack.push((int)x); - stack.push((int)y); - stack.push((int)i); - srcReg[i] = r; - srcDist[i] = 0; - - unsigned short lev = level >= 2 ? level-2 : 0; - int count = 0; - - while (stack.size() > 0) - { - int ci = stack.pop(); - int cy = stack.pop(); - int cx = stack.pop(); - - const rcCompactSpan& cs = chf.spans[ci]; - - // Check if any of the neighbours already have a valid region set. - unsigned short ar = 0; - for (int dir = 0; dir < 4; ++dir) - { - // 8 connected - if (rcGetCon(cs, dir) != RC_NOT_CONNECTED) - { - const int ax = cx + rcGetDirOffsetX(dir); - const int ay = cy + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir); - if (chf.areas[ai] != area) - continue; - unsigned short nr = srcReg[ai]; - if (nr & RC_BORDER_REG) // Do not take borders into account. - continue; - if (nr != 0 && nr != r) - ar = nr; - - const rcCompactSpan& as = chf.spans[ai]; - - const int dir2 = (dir+1) & 0x3; - if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) - { - const int ax2 = ax + rcGetDirOffsetX(dir2); - const int ay2 = ay + rcGetDirOffsetY(dir2); - const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2); - if (chf.areas[ai2] != area) - continue; - unsigned short nr = srcReg[ai2]; - if (nr != 0 && nr != r) - ar = nr; - } - } - } - if (ar != 0) - { - srcReg[ci] = 0; - continue; - } - count++; - - // Expand neighbours. - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(cs, dir) != RC_NOT_CONNECTED) - { - const int ax = cx + rcGetDirOffsetX(dir); - const int ay = cy + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir); - if (chf.areas[ai] != area) - continue; - if (chf.dist[ai] >= lev) - { - if (srcReg[ai] == 0) - { - srcReg[ai] = r; - srcDist[ai] = 0; - stack.push(ax); - stack.push(ay); - stack.push(ai); - } - } - } - } - } - - return count > 0; -} - -static unsigned short* expandRegions(int maxIter, unsigned short level, - rcCompactHeightfield& chf, - unsigned short* srcReg, unsigned short* srcDist, - unsigned short* dstReg, unsigned short* dstDist, - rcIntArray& stack) -{ - const int w = chf.width; - const int h = chf.height; - - // Find cells revealed by the raised level. - stack.resize(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) - { - if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA) - { - stack.push(x); - stack.push(y); - stack.push(i); - } - } - } - } - - int iter = 0; - while (stack.size() > 0) - { - int failed = 0; - - memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount); - memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount); - - for (int j = 0; j < stack.size(); j += 3) - { - int x = stack[j+0]; - int y = stack[j+1]; - int i = stack[j+2]; - if (i < 0) - { - failed++; - continue; - } - - unsigned short r = srcReg[i]; - unsigned short d2 = 0xffff; - const unsigned char area = chf.areas[i]; - const rcCompactSpan& s = chf.spans[i]; - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(s, dir) == RC_NOT_CONNECTED) continue; - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - if (chf.areas[ai] != area) continue; - if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0) - { - if ((int)srcDist[ai]+2 < (int)d2) - { - r = srcReg[ai]; - d2 = srcDist[ai]+2; - } - } - } - if (r) - { - stack[j+2] = -1; // mark as used - dstReg[i] = r; - dstDist[i] = d2; - } - else - { - failed++; - } - } - - // rcSwap source and dest. - rcSwap(srcReg, dstReg); - rcSwap(srcDist, dstDist); - - if (failed*3 == stack.size()) - break; - - if (level > 0) - { - ++iter; - if (iter >= maxIter) - break; - } - } - - return srcReg; -} - - -struct rcRegion -{ - inline rcRegion(unsigned short i) : - spanCount(0), - id(i), - areaType(0), - remap(false), - visited(false) - {} - - int spanCount; // Number of spans belonging to this region - unsigned short id; // ID of the region - unsigned char areaType; // Are type. - bool remap; - bool visited; - rcIntArray connections; - rcIntArray floors; -}; - -static void removeAdjacentNeighbours(rcRegion& reg) -{ - // Remove adjacent duplicates. - for (int i = 0; i < reg.connections.size() && reg.connections.size() > 1; ) - { - int ni = (i+1) % reg.connections.size(); - if (reg.connections[i] == reg.connections[ni]) - { - // Remove duplicate - for (int j = i; j < reg.connections.size()-1; ++j) - reg.connections[j] = reg.connections[j+1]; - reg.connections.pop(); - } - else - ++i; - } -} - -static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId) -{ - bool neiChanged = false; - for (int i = 0; i < reg.connections.size(); ++i) - { - if (reg.connections[i] == oldId) - { - reg.connections[i] = newId; - neiChanged = true; - } - } - for (int i = 0; i < reg.floors.size(); ++i) - { - if (reg.floors[i] == oldId) - reg.floors[i] = newId; - } - if (neiChanged) - removeAdjacentNeighbours(reg); -} - -static bool canMergeWithRegion(const rcRegion& rega, const rcRegion& regb) -{ - if (rega.areaType != regb.areaType) - return false; - int n = 0; - for (int i = 0; i < rega.connections.size(); ++i) - { - if (rega.connections[i] == regb.id) - n++; - } - if (n > 1) - return false; - for (int i = 0; i < rega.floors.size(); ++i) - { - if (rega.floors[i] == regb.id) - return false; - } - return true; -} - -static void addUniqueFloorRegion(rcRegion& reg, int n) -{ - for (int i = 0; i < reg.floors.size(); ++i) - if (reg.floors[i] == n) - return; - reg.floors.push(n); -} - -static bool mergeRegions(rcRegion& rega, rcRegion& regb) -{ - unsigned short aid = rega.id; - unsigned short bid = regb.id; - - // Duplicate current neighbourhood. - rcIntArray acon; - acon.resize(rega.connections.size()); - for (int i = 0; i < rega.connections.size(); ++i) - acon[i] = rega.connections[i]; - rcIntArray& bcon = regb.connections; - - // Find insertion point on A. - int insa = -1; - for (int i = 0; i < acon.size(); ++i) - { - if (acon[i] == bid) - { - insa = i; - break; - } - } - if (insa == -1) - return false; - - // Find insertion point on B. - int insb = -1; - for (int i = 0; i < bcon.size(); ++i) - { - if (bcon[i] == aid) - { - insb = i; - break; - } - } - if (insb == -1) - return false; - - // Merge neighbours. - rega.connections.resize(0); - for (int i = 0, ni = acon.size(); i < ni-1; ++i) - rega.connections.push(acon[(insa+1+i) % ni]); - - for (int i = 0, ni = bcon.size(); i < ni-1; ++i) - rega.connections.push(bcon[(insb+1+i) % ni]); - - removeAdjacentNeighbours(rega); - - for (int j = 0; j < regb.floors.size(); ++j) - addUniqueFloorRegion(rega, regb.floors[j]); - rega.spanCount += regb.spanCount; - regb.spanCount = 0; - regb.connections.resize(0); - - return true; -} - -static bool isRegionConnectedToBorder(const rcRegion& reg) -{ - // Region is connected to border if - // one of the neighbours is null id. - for (int i = 0; i < reg.connections.size(); ++i) - { - if (reg.connections[i] == 0) - return true; - } - return false; -} - -static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg, - int x, int y, int i, int dir) -{ - const rcCompactSpan& s = chf.spans[i]; - unsigned short r = 0; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - r = srcReg[ai]; - } - if (r == srcReg[i]) - return false; - return true; -} - -static void walkContour(int x, int y, int i, int dir, - rcCompactHeightfield& chf, - unsigned short* srcReg, - rcIntArray& cont) -{ - int startDir = dir; - int starti = i; - - const rcCompactSpan& ss = chf.spans[i]; - unsigned short curReg = 0; - if (rcGetCon(ss, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir); - curReg = srcReg[ai]; - } - cont.push(curReg); - - int iter = 0; - while (++iter < 40000) - { - const rcCompactSpan& s = chf.spans[i]; - - if (isSolidEdge(chf, srcReg, x, y, i, dir)) - { - // Choose the edge corner - unsigned short r = 0; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - r = srcReg[ai]; - } - if (r != curReg) - { - curReg = r; - cont.push(curReg); - } - - dir = (dir+1) & 0x3; // Rotate CW - } - else - { - int ni = -1; - const int nx = x + rcGetDirOffsetX(dir); - const int ny = y + rcGetDirOffsetY(dir); - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - const rcCompactCell& nc = chf.cells[nx+ny*chf.width]; - ni = (int)nc.index + rcGetCon(s, dir); - } - if (ni == -1) - { - // Should not happen. - return; - } - x = nx; - y = ny; - i = ni; - dir = (dir+3) & 0x3; // Rotate CCW - } - - if (starti == i && startDir == dir) - { - break; - } - } - - // Remove adjacent duplicates. - if (cont.size() > 1) - { - for (int i = 0; i < cont.size(); ) - { - int ni = (i+1) % cont.size(); - if (cont[i] == cont[ni]) - { - for (int j = i; j < cont.size()-1; ++j) - cont[j] = cont[j+1]; - cont.pop(); - } - else - ++i; - } - } -} - -static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize, - unsigned short& maxRegionId, - rcCompactHeightfield& chf, - unsigned short* srcReg) -{ - const int w = chf.width; - const int h = chf.height; - - const int nreg = maxRegionId+1; - rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); - if (!regions) - { - ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg); - return false; - } - - // Construct regions - for (int i = 0; i < nreg; ++i) - new(®ions[i]) rcRegion((unsigned short)i); - - // Find edge of a region and find connections around the contour. - 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) - { - unsigned short r = srcReg[i]; - if (r == 0 || r >= nreg) - continue; - - rcRegion& reg = regions[r]; - reg.spanCount++; - - - // Update floors. - for (int j = (int)c.index; j < ni; ++j) - { - if (i == j) continue; - unsigned short floorId = srcReg[j]; - if (floorId == 0 || floorId >= nreg) - continue; - addUniqueFloorRegion(reg, floorId); - } - - // Have found contour - if (reg.connections.size() > 0) - continue; - - reg.areaType = chf.areas[i]; - - // Check if this cell is next to a border. - int ndir = -1; - for (int dir = 0; dir < 4; ++dir) - { - if (isSolidEdge(chf, srcReg, x, y, i, dir)) - { - ndir = dir; - break; - } - } - - if (ndir != -1) - { - // The cell is at border. - // Walk around the contour to find all the neighbours. - walkContour(x, y, i, ndir, chf, srcReg, reg.connections); - } - } - } - } - - // Remove too small regions. - rcIntArray stack(32); - rcIntArray trace(32); - for (int i = 0; i < nreg; ++i) - { - rcRegion& reg = regions[i]; - if (reg.id == 0 || (reg.id & RC_BORDER_REG)) - continue; - if (reg.spanCount == 0) - continue; - if (reg.visited) - continue; - - // Count the total size of all the connected regions. - // Also keep track of the regions connects to a tile border. - bool connectsToBorder = false; - int spanCount = 0; - stack.resize(0); - trace.resize(0); - - reg.visited = true; - stack.push(i); - - while (stack.size()) - { - // Pop - int ri = stack.pop(); - - rcRegion& creg = regions[ri]; - - spanCount += creg.spanCount; - trace.push(ri); - - for (int j = 0; j < creg.connections.size(); ++j) - { - if (creg.connections[j] & RC_BORDER_REG) - { - connectsToBorder = true; - continue; - } - rcRegion& nreg = regions[creg.connections[j]]; - if (nreg.visited) - continue; - if (nreg.id == 0 || (nreg.id & RC_BORDER_REG)) - continue; - // Visit - stack.push(nreg.id); - nreg.visited = true; - } - } - - // If the accumulated regions size is too small, remove it. - // Do not remove areas which connect to tile borders - // as their size cannot be estimated correctly and removing them - // can potentially remove necessary areas. - if (spanCount < minRegionArea && !connectsToBorder) - { - // Kill all visited regions. - for (int j = 0; j < trace.size(); ++j) - { - regions[trace[j]].spanCount = 0; - regions[trace[j]].id = 0; - } - } - } - - // Merge too small regions to neighbour regions. - int mergeCount = 0 ; - do - { - mergeCount = 0; - for (int i = 0; i < nreg; ++i) - { - rcRegion& reg = regions[i]; - if (reg.id == 0 || (reg.id & RC_BORDER_REG)) - continue; - if (reg.spanCount == 0) - continue; - - // Check to see if the region should be merged. - if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg)) - continue; - - // Small region with more than 1 connection. - // Or region which is not connected to a border at all. - // Find smallest neighbour region that connects to this one. - int smallest = 0xfffffff; - unsigned short mergeId = reg.id; - for (int j = 0; j < reg.connections.size(); ++j) - { - if (reg.connections[j] & RC_BORDER_REG) continue; - rcRegion& mreg = regions[reg.connections[j]]; - if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue; - if (mreg.spanCount < smallest && - canMergeWithRegion(reg, mreg) && - canMergeWithRegion(mreg, reg)) - { - smallest = mreg.spanCount; - mergeId = mreg.id; - } - } - // Found new id. - if (mergeId != reg.id) - { - unsigned short oldId = reg.id; - rcRegion& target = regions[mergeId]; - - // Merge neighbours. - if (mergeRegions(target, reg)) - { - // Fixup regions pointing to current region. - for (int j = 0; j < nreg; ++j) - { - if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG)) continue; - // If another region was already merged into current region - // change the nid of the previous region too. - if (regions[j].id == oldId) - regions[j].id = mergeId; - // Replace the current region with the new one if the - // current regions is neighbour. - replaceNeighbour(regions[j], oldId, mergeId); - } - mergeCount++; - } - } - } - } - while (mergeCount > 0); - - // Compress region Ids. - for (int i = 0; i < nreg; ++i) - { - regions[i].remap = false; - if (regions[i].id == 0) continue; // Skip nil regions. - if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. - regions[i].remap = true; - } - - unsigned short regIdGen = 0; - for (int i = 0; i < nreg; ++i) - { - if (!regions[i].remap) - continue; - unsigned short oldId = regions[i].id; - unsigned short newId = ++regIdGen; - for (int j = i; j < nreg; ++j) - { - if (regions[j].id == oldId) - { - regions[j].id = newId; - regions[j].remap = false; - } - } - } - maxRegionId = regIdGen; - - // Remap regions. - for (int i = 0; i < chf.spanCount; ++i) - { - if ((srcReg[i] & RC_BORDER_REG) == 0) - srcReg[i] = regions[srcReg[i]].id; - } - - for (int i = 0; i < nreg; ++i) - regions[i].~rcRegion(); - rcFree(regions); - - return true; -} - - -bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD); - - if (chf.dist) - { - rcFree(chf.dist); - chf.dist = 0; - } - - unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); - if (!src) - { - ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount); - return false; - } - unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); - if (!dst) - { - ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount); - rcFree(src); - return false; - } - - unsigned short maxDist = 0; - - ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST); - - calculateDistanceField(chf, src, maxDist); - chf.maxDistance = maxDist; - - ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST); - - ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR); - - // Blur - if (boxBlur(chf, 1, src, dst) != src) - rcSwap(src, dst); - - // Store distance. - chf.dist = src; - - ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR); - - ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD); - - rcFree(dst); - - return true; -} - -static void paintRectRegion(int minx, int maxx, int miny, int maxy, unsigned short regId, - rcCompactHeightfield& chf, unsigned short* srcReg) -{ - const int w = chf.width; - for (int y = miny; y < maxy; ++y) - { - for (int x = minx; x < maxx; ++x) - { - const rcCompactCell& c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - if (chf.areas[i] != RC_NULL_AREA) - srcReg[i] = regId; - } - } - } -} - - -static const unsigned short RC_NULL_NEI = 0xffff; - -struct rcSweepSpan -{ - unsigned short rid; // row id - unsigned short id; // region id - unsigned short ns; // number samples - unsigned short nei; // neighbour id -}; - -bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int minRegionArea, const int mergeRegionArea) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_BUILD_REGIONS); - - const int w = chf.width; - const int h = chf.height; - unsigned short id = 1; - - rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); - if (!srcReg) - { - ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); - return false; - } - memset(srcReg,0,sizeof(unsigned short)*chf.spanCount); - - const int nsweeps = rcMax(chf.width,chf.height); - rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP); - if (!sweeps) - { - ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); - return false; - } - - - // Mark border regions. - if (borderSize > 0) - { - // Make sure border will not overflow. - const int bw = rcMin(w, borderSize); - const int bh = rcMin(h, borderSize); - // Paint regions - paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; - paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; - paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++; - paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++; - } - - rcIntArray prev(256); - - // Sweep one line at a time. - for (int y = borderSize; y < h-borderSize; ++y) - { - // Collect spans from this row. - prev.resize(id+1); - memset(&prev[0],0,sizeof(int)*id); - unsigned short rid = 1; - - for (int x = borderSize; x < w-borderSize; ++x) - { - const rcCompactCell& c = chf.cells[x+y*w]; - - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - const rcCompactSpan& s = chf.spans[i]; - if (chf.areas[i] == RC_NULL_AREA) continue; - - // -x - unsigned short previd = 0; - if (rcGetCon(s, 0) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(0); - const int ay = y + rcGetDirOffsetY(0); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); - if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai]) - previd = srcReg[ai]; - } - - if (!previd) - { - previd = rid++; - sweeps[previd].rid = previd; - sweeps[previd].ns = 0; - sweeps[previd].nei = 0; - } - - // -y - if (rcGetCon(s,3) != RC_NOT_CONNECTED) - { - const int ax = x + rcGetDirOffsetX(3); - const int ay = y + rcGetDirOffsetY(3); - const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); - if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai]) - { - unsigned short nr = srcReg[ai]; - if (!sweeps[previd].nei || sweeps[previd].nei == nr) - { - sweeps[previd].nei = nr; - sweeps[previd].ns++; - prev[nr]++; - } - else - { - sweeps[previd].nei = RC_NULL_NEI; - } - } - } - - srcReg[i] = previd; - } - } - - // Create unique ID. - for (int i = 1; i < rid; ++i) - { - if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 && - prev[sweeps[i].nei] == (int)sweeps[i].ns) - { - sweeps[i].id = sweeps[i].nei; - } - else - { - sweeps[i].id = id++; - } - } - - // Remap IDs - for (int x = borderSize; x < w-borderSize; ++x) - { - const rcCompactCell& c = chf.cells[x+y*w]; - - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - if (srcReg[i] > 0 && srcReg[i] < rid) - srcReg[i] = sweeps[srcReg[i]].id; - } - } - } - - ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); - - // Filter out small regions. - chf.maxRegions = id; - if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) - return false; - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); - - // Store the result out. - for (int i = 0; i < chf.spanCount; ++i) - chf.spans[i].reg = srcReg[i]; - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS); - - return true; -} - -bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int minRegionArea, const int mergeRegionArea) -{ - rcAssert(ctx); - - ctx->startTimer(RC_TIMER_BUILD_REGIONS); - - const int w = chf.width; - const int h = chf.height; - - rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP); - if (!buf) - { - ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); - return false; - } - - ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); - - rcIntArray stack(1024); - rcIntArray visited(1024); - - unsigned short* srcReg = buf; - unsigned short* srcDist = buf+chf.spanCount; - unsigned short* dstReg = buf+chf.spanCount*2; - unsigned short* dstDist = buf+chf.spanCount*3; - - memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount); - memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount); - - unsigned short regionId = 1; - unsigned short level = (chf.maxDistance+1) & ~1; - - // TODO: Figure better formula, expandIters defines how much the - // watershed "overflows" and simplifies the regions. Tying it to - // agent radius was usually good indication how greedy it could be. -// const int expandIters = 4 + walkableRadius * 2; - const int expandIters = 8; - - // Mark border regions. - paintRectRegion(0, borderSize, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; - paintRectRegion(w-borderSize, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; - paintRectRegion(0, w, 0, borderSize, regionId|RC_BORDER_REG, chf, srcReg); regionId++; - paintRectRegion(0, w, h-borderSize, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; - - while (level > 0) - { - level = level >= 2 ? level-2 : 0; - - ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); - - // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) - { - rcSwap(srcReg, dstReg); - rcSwap(srcDist, dstDist); - } - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND); - - ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD); - - // Mark new regions with IDs. - 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) - { - if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA) - continue; - - if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) - regionId++; - } - } - } - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD); - - } - - // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) - { - rcSwap(srcReg, dstReg); - rcSwap(srcDist, dstDist); - } - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); - - ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); - - // Filter out small regions. - chf.maxRegions = regionId; - if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) - return false; - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); - - // Write the result out. - for (int i = 0; i < chf.spanCount; ++i) - chf.spans[i].reg = srcReg[i]; - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS); - - return true; -} - - |