Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/recastnavigation/Recast/Source/RecastArea.cpp
9913 views
1
//
2
// Copyright (c) 2009-2010 Mikko Mononen [email protected]
3
//
4
// This software is provided 'as-is', without any express or implied
5
// warranty. In no event will the authors be held liable for any damages
6
// arising from the use of this software.
7
// Permission is granted to anyone to use this software for any purpose,
8
// including commercial applications, and to alter it and redistribute it
9
// freely, subject to the following restrictions:
10
// 1. The origin of this software must not be misrepresented; you must not
11
// claim that you wrote the original software. If you use this software
12
// in a product, an acknowledgment in the product documentation would be
13
// appreciated but is not required.
14
// 2. Altered source versions must be plainly marked as such, and must not be
15
// misrepresented as being the original software.
16
// 3. This notice may not be removed or altered from any source distribution.
17
//
18
19
#include <float.h>
20
#include <math.h>
21
#include <string.h>
22
#include <stdlib.h>
23
#include <stdio.h>
24
#include "Recast.h"
25
#include "RecastAlloc.h"
26
#include "RecastAssert.h"
27
28
/// @par
29
///
30
/// Basically, any spans that are closer to a boundary or obstruction than the specified radius
31
/// are marked as unwalkable.
32
///
33
/// This method is usually called immediately after the heightfield has been built.
34
///
35
/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
36
bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
37
{
38
rcAssert(ctx);
39
40
const int w = chf.width;
41
const int h = chf.height;
42
43
rcScopedTimer timer(ctx, RC_TIMER_ERODE_AREA);
44
45
unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
46
if (!dist)
47
{
48
ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount);
49
return false;
50
}
51
52
// Init distance.
53
memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount);
54
55
// Mark boundary cells.
56
for (int y = 0; y < h; ++y)
57
{
58
for (int x = 0; x < w; ++x)
59
{
60
const rcCompactCell& c = chf.cells[x+y*w];
61
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
62
{
63
if (chf.areas[i] == RC_NULL_AREA)
64
{
65
dist[i] = 0;
66
}
67
else
68
{
69
const rcCompactSpan& s = chf.spans[i];
70
int nc = 0;
71
for (int dir = 0; dir < 4; ++dir)
72
{
73
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
74
{
75
const int nx = x + rcGetDirOffsetX(dir);
76
const int ny = y + rcGetDirOffsetY(dir);
77
const int nidx = (int)chf.cells[nx+ny*w].index + rcGetCon(s, dir);
78
if (chf.areas[nidx] != RC_NULL_AREA)
79
{
80
nc++;
81
}
82
}
83
}
84
// At least one missing neighbour.
85
if (nc != 4)
86
dist[i] = 0;
87
}
88
}
89
}
90
}
91
92
unsigned char nd;
93
94
// Pass 1
95
for (int y = 0; y < h; ++y)
96
{
97
for (int x = 0; x < w; ++x)
98
{
99
const rcCompactCell& c = chf.cells[x+y*w];
100
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
101
{
102
const rcCompactSpan& s = chf.spans[i];
103
104
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
105
{
106
// (-1,0)
107
const int ax = x + rcGetDirOffsetX(0);
108
const int ay = y + rcGetDirOffsetY(0);
109
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
110
const rcCompactSpan& as = chf.spans[ai];
111
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
112
if (nd < dist[i])
113
dist[i] = nd;
114
115
// (-1,-1)
116
if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
117
{
118
const int aax = ax + rcGetDirOffsetX(3);
119
const int aay = ay + rcGetDirOffsetY(3);
120
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
121
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
122
if (nd < dist[i])
123
dist[i] = nd;
124
}
125
}
126
if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
127
{
128
// (0,-1)
129
const int ax = x + rcGetDirOffsetX(3);
130
const int ay = y + rcGetDirOffsetY(3);
131
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
132
const rcCompactSpan& as = chf.spans[ai];
133
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
134
if (nd < dist[i])
135
dist[i] = nd;
136
137
// (1,-1)
138
if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
139
{
140
const int aax = ax + rcGetDirOffsetX(2);
141
const int aay = ay + rcGetDirOffsetY(2);
142
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
143
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
144
if (nd < dist[i])
145
dist[i] = nd;
146
}
147
}
148
}
149
}
150
}
151
152
// Pass 2
153
for (int y = h-1; y >= 0; --y)
154
{
155
for (int x = w-1; x >= 0; --x)
156
{
157
const rcCompactCell& c = chf.cells[x+y*w];
158
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
159
{
160
const rcCompactSpan& s = chf.spans[i];
161
162
if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
163
{
164
// (1,0)
165
const int ax = x + rcGetDirOffsetX(2);
166
const int ay = y + rcGetDirOffsetY(2);
167
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
168
const rcCompactSpan& as = chf.spans[ai];
169
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
170
if (nd < dist[i])
171
dist[i] = nd;
172
173
// (1,1)
174
if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
175
{
176
const int aax = ax + rcGetDirOffsetX(1);
177
const int aay = ay + rcGetDirOffsetY(1);
178
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
179
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
180
if (nd < dist[i])
181
dist[i] = nd;
182
}
183
}
184
if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
185
{
186
// (0,1)
187
const int ax = x + rcGetDirOffsetX(1);
188
const int ay = y + rcGetDirOffsetY(1);
189
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
190
const rcCompactSpan& as = chf.spans[ai];
191
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
192
if (nd < dist[i])
193
dist[i] = nd;
194
195
// (-1,1)
196
if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
197
{
198
const int aax = ax + rcGetDirOffsetX(0);
199
const int aay = ay + rcGetDirOffsetY(0);
200
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
201
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
202
if (nd < dist[i])
203
dist[i] = nd;
204
}
205
}
206
}
207
}
208
}
209
210
const unsigned char thr = (unsigned char)(radius*2);
211
for (int i = 0; i < chf.spanCount; ++i)
212
if (dist[i] < thr)
213
chf.areas[i] = RC_NULL_AREA;
214
215
rcFree(dist);
216
217
return true;
218
}
219
220
static void insertSort(unsigned char* a, const int n)
221
{
222
int i, j;
223
for (i = 1; i < n; i++)
224
{
225
const unsigned char value = a[i];
226
for (j = i - 1; j >= 0 && a[j] > value; j--)
227
a[j+1] = a[j];
228
a[j+1] = value;
229
}
230
}
231
232
/// @par
233
///
234
/// This filter is usually applied after applying area id's using functions
235
/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.
236
///
237
/// @see rcCompactHeightfield
238
bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
239
{
240
rcAssert(ctx);
241
242
const int w = chf.width;
243
const int h = chf.height;
244
245
rcScopedTimer timer(ctx, RC_TIMER_MEDIAN_AREA);
246
247
unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
248
if (!areas)
249
{
250
ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount);
251
return false;
252
}
253
254
// Init distance.
255
memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount);
256
257
for (int y = 0; y < h; ++y)
258
{
259
for (int x = 0; x < w; ++x)
260
{
261
const rcCompactCell& c = chf.cells[x+y*w];
262
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
263
{
264
const rcCompactSpan& s = chf.spans[i];
265
if (chf.areas[i] == RC_NULL_AREA)
266
{
267
areas[i] = chf.areas[i];
268
continue;
269
}
270
271
unsigned char nei[9];
272
for (int j = 0; j < 9; ++j)
273
nei[j] = chf.areas[i];
274
275
for (int dir = 0; dir < 4; ++dir)
276
{
277
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
278
{
279
const int ax = x + rcGetDirOffsetX(dir);
280
const int ay = y + rcGetDirOffsetY(dir);
281
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
282
if (chf.areas[ai] != RC_NULL_AREA)
283
nei[dir*2+0] = chf.areas[ai];
284
285
const rcCompactSpan& as = chf.spans[ai];
286
const int dir2 = (dir+1) & 0x3;
287
if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
288
{
289
const int ax2 = ax + rcGetDirOffsetX(dir2);
290
const int ay2 = ay + rcGetDirOffsetY(dir2);
291
const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
292
if (chf.areas[ai2] != RC_NULL_AREA)
293
nei[dir*2+1] = chf.areas[ai2];
294
}
295
}
296
}
297
insertSort(nei, 9);
298
areas[i] = nei[4];
299
}
300
}
301
}
302
303
memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);
304
305
rcFree(areas);
306
307
return true;
308
}
309
310
/// @par
311
///
312
/// The value of spacial parameters are in world units.
313
///
314
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
315
void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
316
rcCompactHeightfield& chf)
317
{
318
rcAssert(ctx);
319
320
rcScopedTimer timer(ctx, RC_TIMER_MARK_BOX_AREA);
321
322
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
323
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
324
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
325
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
326
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
327
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
328
329
if (maxx < 0) return;
330
if (minx >= chf.width) return;
331
if (maxz < 0) return;
332
if (minz >= chf.height) return;
333
334
if (minx < 0) minx = 0;
335
if (maxx >= chf.width) maxx = chf.width-1;
336
if (minz < 0) minz = 0;
337
if (maxz >= chf.height) maxz = chf.height-1;
338
339
for (int z = minz; z <= maxz; ++z)
340
{
341
for (int x = minx; x <= maxx; ++x)
342
{
343
const rcCompactCell& c = chf.cells[x+z*chf.width];
344
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
345
{
346
rcCompactSpan& s = chf.spans[i];
347
if ((int)s.y >= miny && (int)s.y <= maxy)
348
{
349
if (chf.areas[i] != RC_NULL_AREA)
350
chf.areas[i] = areaId;
351
}
352
}
353
}
354
}
355
}
356
357
358
static int pointInPoly(int nvert, const float* verts, const float* p)
359
{
360
int i, j, c = 0;
361
for (i = 0, j = nvert-1; i < nvert; j = i++)
362
{
363
const float* vi = &verts[i*3];
364
const float* vj = &verts[j*3];
365
if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
366
(p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
367
c = !c;
368
}
369
return c;
370
}
371
372
/// @par
373
///
374
/// The value of spacial parameters are in world units.
375
///
376
/// The y-values of the polygon vertices are ignored. So the polygon is effectively
377
/// projected onto the xz-plane at @p hmin, then extruded to @p hmax.
378
///
379
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
380
void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
381
const float hmin, const float hmax, unsigned char areaId,
382
rcCompactHeightfield& chf)
383
{
384
rcAssert(ctx);
385
386
rcScopedTimer timer(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA);
387
388
float bmin[3], bmax[3];
389
rcVcopy(bmin, verts);
390
rcVcopy(bmax, verts);
391
for (int i = 1; i < nverts; ++i)
392
{
393
rcVmin(bmin, &verts[i*3]);
394
rcVmax(bmax, &verts[i*3]);
395
}
396
bmin[1] = hmin;
397
bmax[1] = hmax;
398
399
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
400
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
401
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
402
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
403
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
404
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
405
406
if (maxx < 0) return;
407
if (minx >= chf.width) return;
408
if (maxz < 0) return;
409
if (minz >= chf.height) return;
410
411
if (minx < 0) minx = 0;
412
if (maxx >= chf.width) maxx = chf.width-1;
413
if (minz < 0) minz = 0;
414
if (maxz >= chf.height) maxz = chf.height-1;
415
416
417
// TODO: Optimize.
418
for (int z = minz; z <= maxz; ++z)
419
{
420
for (int x = minx; x <= maxx; ++x)
421
{
422
const rcCompactCell& c = chf.cells[x+z*chf.width];
423
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
424
{
425
rcCompactSpan& s = chf.spans[i];
426
if (chf.areas[i] == RC_NULL_AREA)
427
continue;
428
if ((int)s.y >= miny && (int)s.y <= maxy)
429
{
430
float p[3];
431
p[0] = chf.bmin[0] + (x+0.5f)*chf.cs;
432
p[1] = 0;
433
p[2] = chf.bmin[2] + (z+0.5f)*chf.cs;
434
435
if (pointInPoly(nverts, verts, p))
436
{
437
chf.areas[i] = areaId;
438
}
439
}
440
}
441
}
442
}
443
}
444
445
int rcOffsetPoly(const float* verts, const int nverts, const float offset,
446
float* outVerts, const int maxOutVerts)
447
{
448
const float MITER_LIMIT = 1.20f;
449
450
int n = 0;
451
452
for (int i = 0; i < nverts; i++)
453
{
454
const int a = (i+nverts-1) % nverts;
455
const int b = i;
456
const int c = (i+1) % nverts;
457
const float* va = &verts[a*3];
458
const float* vb = &verts[b*3];
459
const float* vc = &verts[c*3];
460
float dx0 = vb[0] - va[0];
461
float dy0 = vb[2] - va[2];
462
float d0 = dx0*dx0 + dy0*dy0;
463
if (d0 > 1e-6f)
464
{
465
d0 = 1.0f/rcSqrt(d0);
466
dx0 *= d0;
467
dy0 *= d0;
468
}
469
float dx1 = vc[0] - vb[0];
470
float dy1 = vc[2] - vb[2];
471
float d1 = dx1*dx1 + dy1*dy1;
472
if (d1 > 1e-6f)
473
{
474
d1 = 1.0f/rcSqrt(d1);
475
dx1 *= d1;
476
dy1 *= d1;
477
}
478
const float dlx0 = -dy0;
479
const float dly0 = dx0;
480
const float dlx1 = -dy1;
481
const float dly1 = dx1;
482
float cross = dx1*dy0 - dx0*dy1;
483
float dmx = (dlx0 + dlx1) * 0.5f;
484
float dmy = (dly0 + dly1) * 0.5f;
485
float dmr2 = dmx*dmx + dmy*dmy;
486
bool bevel = dmr2 * MITER_LIMIT*MITER_LIMIT < 1.0f;
487
if (dmr2 > 1e-6f)
488
{
489
const float scale = 1.0f / dmr2;
490
dmx *= scale;
491
dmy *= scale;
492
}
493
494
if (bevel && cross < 0.0f)
495
{
496
if (n+2 >= maxOutVerts)
497
return 0;
498
float d = (1.0f - (dx0*dx1 + dy0*dy1))*0.5f;
499
outVerts[n*3+0] = vb[0] + (-dlx0+dx0*d)*offset;
500
outVerts[n*3+1] = vb[1];
501
outVerts[n*3+2] = vb[2] + (-dly0+dy0*d)*offset;
502
n++;
503
outVerts[n*3+0] = vb[0] + (-dlx1-dx1*d)*offset;
504
outVerts[n*3+1] = vb[1];
505
outVerts[n*3+2] = vb[2] + (-dly1-dy1*d)*offset;
506
n++;
507
}
508
else
509
{
510
if (n+1 >= maxOutVerts)
511
return 0;
512
outVerts[n*3+0] = vb[0] - dmx*offset;
513
outVerts[n*3+1] = vb[1];
514
outVerts[n*3+2] = vb[2] - dmy*offset;
515
n++;
516
}
517
}
518
519
return n;
520
}
521
522
523
/// @par
524
///
525
/// The value of spacial parameters are in world units.
526
///
527
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
528
void rcMarkCylinderArea(rcContext* ctx, const float* pos,
529
const float r, const float h, unsigned char areaId,
530
rcCompactHeightfield& chf)
531
{
532
rcAssert(ctx);
533
534
rcScopedTimer timer(ctx, RC_TIMER_MARK_CYLINDER_AREA);
535
536
float bmin[3], bmax[3];
537
bmin[0] = pos[0] - r;
538
bmin[1] = pos[1];
539
bmin[2] = pos[2] - r;
540
bmax[0] = pos[0] + r;
541
bmax[1] = pos[1] + h;
542
bmax[2] = pos[2] + r;
543
const float r2 = r*r;
544
545
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
546
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
547
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
548
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
549
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
550
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
551
552
if (maxx < 0) return;
553
if (minx >= chf.width) return;
554
if (maxz < 0) return;
555
if (minz >= chf.height) return;
556
557
if (minx < 0) minx = 0;
558
if (maxx >= chf.width) maxx = chf.width-1;
559
if (minz < 0) minz = 0;
560
if (maxz >= chf.height) maxz = chf.height-1;
561
562
563
for (int z = minz; z <= maxz; ++z)
564
{
565
for (int x = minx; x <= maxx; ++x)
566
{
567
const rcCompactCell& c = chf.cells[x+z*chf.width];
568
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
569
{
570
rcCompactSpan& s = chf.spans[i];
571
572
if (chf.areas[i] == RC_NULL_AREA)
573
continue;
574
575
if ((int)s.y >= miny && (int)s.y <= maxy)
576
{
577
const float sx = chf.bmin[0] + (x+0.5f)*chf.cs;
578
const float sz = chf.bmin[2] + (z+0.5f)*chf.cs;
579
const float dx = sx - pos[0];
580
const float dz = sz - pos[2];
581
582
if (dx*dx + dz*dz < r2)
583
{
584
chf.areas[i] = areaId;
585
}
586
}
587
}
588
}
589
}
590
}
591
592