Path: blob/master/thirdparty/recastnavigation/Recast/Source/RecastArea.cpp
9913 views
//1// Copyright (c) 2009-2010 Mikko Mononen [email protected]2//3// This software is provided 'as-is', without any express or implied4// warranty. In no event will the authors be held liable for any damages5// arising from the use of this software.6// Permission is granted to anyone to use this software for any purpose,7// including commercial applications, and to alter it and redistribute it8// freely, subject to the following restrictions:9// 1. The origin of this software must not be misrepresented; you must not10// claim that you wrote the original software. If you use this software11// in a product, an acknowledgment in the product documentation would be12// appreciated but is not required.13// 2. Altered source versions must be plainly marked as such, and must not be14// misrepresented as being the original software.15// 3. This notice may not be removed or altered from any source distribution.16//1718#include <float.h>19#include <math.h>20#include <string.h>21#include <stdlib.h>22#include <stdio.h>23#include "Recast.h"24#include "RecastAlloc.h"25#include "RecastAssert.h"2627/// @par28///29/// Basically, any spans that are closer to a boundary or obstruction than the specified radius30/// are marked as unwalkable.31///32/// This method is usually called immediately after the heightfield has been built.33///34/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius35bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)36{37rcAssert(ctx);3839const int w = chf.width;40const int h = chf.height;4142rcScopedTimer timer(ctx, RC_TIMER_ERODE_AREA);4344unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);45if (!dist)46{47ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount);48return false;49}5051// Init distance.52memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount);5354// Mark boundary cells.55for (int y = 0; y < h; ++y)56{57for (int x = 0; x < w; ++x)58{59const rcCompactCell& c = chf.cells[x+y*w];60for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)61{62if (chf.areas[i] == RC_NULL_AREA)63{64dist[i] = 0;65}66else67{68const rcCompactSpan& s = chf.spans[i];69int nc = 0;70for (int dir = 0; dir < 4; ++dir)71{72if (rcGetCon(s, dir) != RC_NOT_CONNECTED)73{74const int nx = x + rcGetDirOffsetX(dir);75const int ny = y + rcGetDirOffsetY(dir);76const int nidx = (int)chf.cells[nx+ny*w].index + rcGetCon(s, dir);77if (chf.areas[nidx] != RC_NULL_AREA)78{79nc++;80}81}82}83// At least one missing neighbour.84if (nc != 4)85dist[i] = 0;86}87}88}89}9091unsigned char nd;9293// Pass 194for (int y = 0; y < h; ++y)95{96for (int x = 0; x < w; ++x)97{98const rcCompactCell& c = chf.cells[x+y*w];99for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)100{101const rcCompactSpan& s = chf.spans[i];102103if (rcGetCon(s, 0) != RC_NOT_CONNECTED)104{105// (-1,0)106const int ax = x + rcGetDirOffsetX(0);107const int ay = y + rcGetDirOffsetY(0);108const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);109const rcCompactSpan& as = chf.spans[ai];110nd = (unsigned char)rcMin((int)dist[ai]+2, 255);111if (nd < dist[i])112dist[i] = nd;113114// (-1,-1)115if (rcGetCon(as, 3) != RC_NOT_CONNECTED)116{117const int aax = ax + rcGetDirOffsetX(3);118const int aay = ay + rcGetDirOffsetY(3);119const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);120nd = (unsigned char)rcMin((int)dist[aai]+3, 255);121if (nd < dist[i])122dist[i] = nd;123}124}125if (rcGetCon(s, 3) != RC_NOT_CONNECTED)126{127// (0,-1)128const int ax = x + rcGetDirOffsetX(3);129const int ay = y + rcGetDirOffsetY(3);130const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);131const rcCompactSpan& as = chf.spans[ai];132nd = (unsigned char)rcMin((int)dist[ai]+2, 255);133if (nd < dist[i])134dist[i] = nd;135136// (1,-1)137if (rcGetCon(as, 2) != RC_NOT_CONNECTED)138{139const int aax = ax + rcGetDirOffsetX(2);140const int aay = ay + rcGetDirOffsetY(2);141const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);142nd = (unsigned char)rcMin((int)dist[aai]+3, 255);143if (nd < dist[i])144dist[i] = nd;145}146}147}148}149}150151// Pass 2152for (int y = h-1; y >= 0; --y)153{154for (int x = w-1; x >= 0; --x)155{156const rcCompactCell& c = chf.cells[x+y*w];157for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)158{159const rcCompactSpan& s = chf.spans[i];160161if (rcGetCon(s, 2) != RC_NOT_CONNECTED)162{163// (1,0)164const int ax = x + rcGetDirOffsetX(2);165const int ay = y + rcGetDirOffsetY(2);166const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);167const rcCompactSpan& as = chf.spans[ai];168nd = (unsigned char)rcMin((int)dist[ai]+2, 255);169if (nd < dist[i])170dist[i] = nd;171172// (1,1)173if (rcGetCon(as, 1) != RC_NOT_CONNECTED)174{175const int aax = ax + rcGetDirOffsetX(1);176const int aay = ay + rcGetDirOffsetY(1);177const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);178nd = (unsigned char)rcMin((int)dist[aai]+3, 255);179if (nd < dist[i])180dist[i] = nd;181}182}183if (rcGetCon(s, 1) != RC_NOT_CONNECTED)184{185// (0,1)186const int ax = x + rcGetDirOffsetX(1);187const int ay = y + rcGetDirOffsetY(1);188const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);189const rcCompactSpan& as = chf.spans[ai];190nd = (unsigned char)rcMin((int)dist[ai]+2, 255);191if (nd < dist[i])192dist[i] = nd;193194// (-1,1)195if (rcGetCon(as, 0) != RC_NOT_CONNECTED)196{197const int aax = ax + rcGetDirOffsetX(0);198const int aay = ay + rcGetDirOffsetY(0);199const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);200nd = (unsigned char)rcMin((int)dist[aai]+3, 255);201if (nd < dist[i])202dist[i] = nd;203}204}205}206}207}208209const unsigned char thr = (unsigned char)(radius*2);210for (int i = 0; i < chf.spanCount; ++i)211if (dist[i] < thr)212chf.areas[i] = RC_NULL_AREA;213214rcFree(dist);215216return true;217}218219static void insertSort(unsigned char* a, const int n)220{221int i, j;222for (i = 1; i < n; i++)223{224const unsigned char value = a[i];225for (j = i - 1; j >= 0 && a[j] > value; j--)226a[j+1] = a[j];227a[j+1] = value;228}229}230231/// @par232///233/// This filter is usually applied after applying area id's using functions234/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.235///236/// @see rcCompactHeightfield237bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)238{239rcAssert(ctx);240241const int w = chf.width;242const int h = chf.height;243244rcScopedTimer timer(ctx, RC_TIMER_MEDIAN_AREA);245246unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);247if (!areas)248{249ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount);250return false;251}252253// Init distance.254memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount);255256for (int y = 0; y < h; ++y)257{258for (int x = 0; x < w; ++x)259{260const rcCompactCell& c = chf.cells[x+y*w];261for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)262{263const rcCompactSpan& s = chf.spans[i];264if (chf.areas[i] == RC_NULL_AREA)265{266areas[i] = chf.areas[i];267continue;268}269270unsigned char nei[9];271for (int j = 0; j < 9; ++j)272nei[j] = chf.areas[i];273274for (int dir = 0; dir < 4; ++dir)275{276if (rcGetCon(s, dir) != RC_NOT_CONNECTED)277{278const int ax = x + rcGetDirOffsetX(dir);279const int ay = y + rcGetDirOffsetY(dir);280const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);281if (chf.areas[ai] != RC_NULL_AREA)282nei[dir*2+0] = chf.areas[ai];283284const rcCompactSpan& as = chf.spans[ai];285const int dir2 = (dir+1) & 0x3;286if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)287{288const int ax2 = ax + rcGetDirOffsetX(dir2);289const int ay2 = ay + rcGetDirOffsetY(dir2);290const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);291if (chf.areas[ai2] != RC_NULL_AREA)292nei[dir*2+1] = chf.areas[ai2];293}294}295}296insertSort(nei, 9);297areas[i] = nei[4];298}299}300}301302memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);303304rcFree(areas);305306return true;307}308309/// @par310///311/// The value of spacial parameters are in world units.312///313/// @see rcCompactHeightfield, rcMedianFilterWalkableArea314void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,315rcCompactHeightfield& chf)316{317rcAssert(ctx);318319rcScopedTimer timer(ctx, RC_TIMER_MARK_BOX_AREA);320321int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);322int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);323int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);324int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);325int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);326int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);327328if (maxx < 0) return;329if (minx >= chf.width) return;330if (maxz < 0) return;331if (minz >= chf.height) return;332333if (minx < 0) minx = 0;334if (maxx >= chf.width) maxx = chf.width-1;335if (minz < 0) minz = 0;336if (maxz >= chf.height) maxz = chf.height-1;337338for (int z = minz; z <= maxz; ++z)339{340for (int x = minx; x <= maxx; ++x)341{342const rcCompactCell& c = chf.cells[x+z*chf.width];343for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)344{345rcCompactSpan& s = chf.spans[i];346if ((int)s.y >= miny && (int)s.y <= maxy)347{348if (chf.areas[i] != RC_NULL_AREA)349chf.areas[i] = areaId;350}351}352}353}354}355356357static int pointInPoly(int nvert, const float* verts, const float* p)358{359int i, j, c = 0;360for (i = 0, j = nvert-1; i < nvert; j = i++)361{362const float* vi = &verts[i*3];363const float* vj = &verts[j*3];364if (((vi[2] > p[2]) != (vj[2] > p[2])) &&365(p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )366c = !c;367}368return c;369}370371/// @par372///373/// The value of spacial parameters are in world units.374///375/// The y-values of the polygon vertices are ignored. So the polygon is effectively376/// projected onto the xz-plane at @p hmin, then extruded to @p hmax.377///378/// @see rcCompactHeightfield, rcMedianFilterWalkableArea379void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,380const float hmin, const float hmax, unsigned char areaId,381rcCompactHeightfield& chf)382{383rcAssert(ctx);384385rcScopedTimer timer(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA);386387float bmin[3], bmax[3];388rcVcopy(bmin, verts);389rcVcopy(bmax, verts);390for (int i = 1; i < nverts; ++i)391{392rcVmin(bmin, &verts[i*3]);393rcVmax(bmax, &verts[i*3]);394}395bmin[1] = hmin;396bmax[1] = hmax;397398int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);399int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);400int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);401int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);402int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);403int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);404405if (maxx < 0) return;406if (minx >= chf.width) return;407if (maxz < 0) return;408if (minz >= chf.height) return;409410if (minx < 0) minx = 0;411if (maxx >= chf.width) maxx = chf.width-1;412if (minz < 0) minz = 0;413if (maxz >= chf.height) maxz = chf.height-1;414415416// TODO: Optimize.417for (int z = minz; z <= maxz; ++z)418{419for (int x = minx; x <= maxx; ++x)420{421const rcCompactCell& c = chf.cells[x+z*chf.width];422for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)423{424rcCompactSpan& s = chf.spans[i];425if (chf.areas[i] == RC_NULL_AREA)426continue;427if ((int)s.y >= miny && (int)s.y <= maxy)428{429float p[3];430p[0] = chf.bmin[0] + (x+0.5f)*chf.cs;431p[1] = 0;432p[2] = chf.bmin[2] + (z+0.5f)*chf.cs;433434if (pointInPoly(nverts, verts, p))435{436chf.areas[i] = areaId;437}438}439}440}441}442}443444int rcOffsetPoly(const float* verts, const int nverts, const float offset,445float* outVerts, const int maxOutVerts)446{447const float MITER_LIMIT = 1.20f;448449int n = 0;450451for (int i = 0; i < nverts; i++)452{453const int a = (i+nverts-1) % nverts;454const int b = i;455const int c = (i+1) % nverts;456const float* va = &verts[a*3];457const float* vb = &verts[b*3];458const float* vc = &verts[c*3];459float dx0 = vb[0] - va[0];460float dy0 = vb[2] - va[2];461float d0 = dx0*dx0 + dy0*dy0;462if (d0 > 1e-6f)463{464d0 = 1.0f/rcSqrt(d0);465dx0 *= d0;466dy0 *= d0;467}468float dx1 = vc[0] - vb[0];469float dy1 = vc[2] - vb[2];470float d1 = dx1*dx1 + dy1*dy1;471if (d1 > 1e-6f)472{473d1 = 1.0f/rcSqrt(d1);474dx1 *= d1;475dy1 *= d1;476}477const float dlx0 = -dy0;478const float dly0 = dx0;479const float dlx1 = -dy1;480const float dly1 = dx1;481float cross = dx1*dy0 - dx0*dy1;482float dmx = (dlx0 + dlx1) * 0.5f;483float dmy = (dly0 + dly1) * 0.5f;484float dmr2 = dmx*dmx + dmy*dmy;485bool bevel = dmr2 * MITER_LIMIT*MITER_LIMIT < 1.0f;486if (dmr2 > 1e-6f)487{488const float scale = 1.0f / dmr2;489dmx *= scale;490dmy *= scale;491}492493if (bevel && cross < 0.0f)494{495if (n+2 >= maxOutVerts)496return 0;497float d = (1.0f - (dx0*dx1 + dy0*dy1))*0.5f;498outVerts[n*3+0] = vb[0] + (-dlx0+dx0*d)*offset;499outVerts[n*3+1] = vb[1];500outVerts[n*3+2] = vb[2] + (-dly0+dy0*d)*offset;501n++;502outVerts[n*3+0] = vb[0] + (-dlx1-dx1*d)*offset;503outVerts[n*3+1] = vb[1];504outVerts[n*3+2] = vb[2] + (-dly1-dy1*d)*offset;505n++;506}507else508{509if (n+1 >= maxOutVerts)510return 0;511outVerts[n*3+0] = vb[0] - dmx*offset;512outVerts[n*3+1] = vb[1];513outVerts[n*3+2] = vb[2] - dmy*offset;514n++;515}516}517518return n;519}520521522/// @par523///524/// The value of spacial parameters are in world units.525///526/// @see rcCompactHeightfield, rcMedianFilterWalkableArea527void rcMarkCylinderArea(rcContext* ctx, const float* pos,528const float r, const float h, unsigned char areaId,529rcCompactHeightfield& chf)530{531rcAssert(ctx);532533rcScopedTimer timer(ctx, RC_TIMER_MARK_CYLINDER_AREA);534535float bmin[3], bmax[3];536bmin[0] = pos[0] - r;537bmin[1] = pos[1];538bmin[2] = pos[2] - r;539bmax[0] = pos[0] + r;540bmax[1] = pos[1] + h;541bmax[2] = pos[2] + r;542const float r2 = r*r;543544int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);545int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);546int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);547int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);548int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);549int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);550551if (maxx < 0) return;552if (minx >= chf.width) return;553if (maxz < 0) return;554if (minz >= chf.height) return;555556if (minx < 0) minx = 0;557if (maxx >= chf.width) maxx = chf.width-1;558if (minz < 0) minz = 0;559if (maxz >= chf.height) maxz = chf.height-1;560561562for (int z = minz; z <= maxz; ++z)563{564for (int x = minx; x <= maxx; ++x)565{566const rcCompactCell& c = chf.cells[x+z*chf.width];567for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)568{569rcCompactSpan& s = chf.spans[i];570571if (chf.areas[i] == RC_NULL_AREA)572continue;573574if ((int)s.y >= miny && (int)s.y <= maxy)575{576const float sx = chf.bmin[0] + (x+0.5f)*chf.cs;577const float sz = chf.bmin[2] + (z+0.5f)*chf.cs;578const float dx = sx - pos[0];579const float dz = sz - pos[2];580581if (dx*dx + dz*dz < r2)582{583chf.areas[i] = areaId;584}585}586}587}588}589}590591592