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