Pathfind.cpp 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. TODO: investigate 'initSlicedFindPath' function?
  6. TODO: should we switch to DT_POLYREF64?
  7. Recast PathMesh binary data has 'userId' member
  8. which is set to memory address of '_PathMesh' object owning that data.
  9. PathMesh has 2 data:
  10. 'data' recast data without any obstacles
  11. 'obstacle_data' recast data without obstacles
  12. MaxTiles+MaxPolys can use up to 22 bits, salt uses 10 bits, total=32 bits, however after modification of Recast sources, salt uses 3 bits
  13. PM_OBSTACLE will not create triangles at all
  14. PM_BLOCK will create triangles but with type that is not accessible
  15. /******************************************************************************/
  16. ASSERT(PM_OBSTACLE==RC_NULL_AREA
  17. && PM_BLOCKED ==RC_WALKABLE_AREA);
  18. /******************************************************************************
  19. Sample compression results:
  20. chf =105127
  21. RLE chf_compressed= 77362
  22. LZ4-16 chf_compressed= 32275
  23. ZLIB-9 chf_compressed= 16624
  24. ZSTD-22 chf_compressed= 13848
  25. LZMA-3 chf_compressed= 10999
  26. LZMA-4 chf_compressed= 10999
  27. LZMA-5 chf_compressed= 9300
  28. LZMA-6 chf_compressed= 9300
  29. LZMA-9 chf_compressed= 9200
  30. /******************************************************************************/
  31. static const COMPRESS_TYPE CompressType =COMPRESS_LZMA;
  32. static const Int CompressLevel=5;
  33. const UInt PMF_ALL=PMF_WALK|PMF_SWIM;
  34. const Bool MonotonePartitioning=false, // monotone enabled offers worse region building (partitioning) but faster building time (0.28 seconds vs 2.72 seconds on average world 11x11 areas), keep this disabled because when enabled generates considerably worse results
  35. BuildDetailMesh =false; // used only for rendering? building takes too much time, so skip it
  36. const Int MaxTiles =512*512, // 18-bits
  37. MaxPolys = 2048 , // 11-bits (1024 was tested and needed for some maps, 2048 is just for safety)
  38. MaxSearchNodes = 4096 , // 1..65536
  39. MaxVtxsPerPoly = 6;
  40. const Flt EdgeMaxLen =12.0f,
  41. EdgeMaxError = 1.3f, // 0.7
  42. RegionMinSize = 8.0f,
  43. RegionMergeSize =20.0f,
  44. DetailSampleDist = 6.0f,
  45. DetailSampleMaxError= 1.0f;
  46. /******************************************************************************/
  47. // HELPERS
  48. /******************************************************************************/
  49. struct RecastConfig : rcConfig
  50. {
  51. RecastConfig(Flt cell_size, Flt cell_height, Flt ctrl_r, Flt ctrl_h, Flt max_climb)
  52. {
  53. Zero(T);
  54. cs=cell_size;
  55. ch=cell_height;
  56. walkableRadius=Ceil (ctrl_r /cs);
  57. walkableHeight=Ceil (ctrl_h /ch);
  58. walkableClimb =Floor( max_climb/ch);
  59. borderSize =walkableRadius+3; // Reserve enough padding
  60. maxVertsPerPoly =Mid(MaxVtxsPerPoly, 3, DT_VERTS_PER_POLYGON);
  61. maxSimplificationError=EdgeMaxError;
  62. maxEdgeLen =Trunc(EdgeMaxLen/cs);
  63. minRegionArea =Trunc(Sqr(RegionMinSize )); // area=size*size
  64. mergeRegionArea =Trunc(Sqr(RegionMergeSize)); // area=size*size
  65. detailSampleDist =(DetailSampleDist<0.9f ? 0 : cs*DetailSampleDist);
  66. detailSampleMaxError = ch*DetailSampleMaxError;
  67. }
  68. };
  69. /******************************************************************************/
  70. struct RecastHeightfield
  71. {
  72. operator rcHeightfield& () {return *_;}
  73. rcHeightfield* operator-> () {return _;}
  74. void drawVoxel(float minx, float miny, float minz, float maxx, float maxy, float maxz)
  75. {
  76. Box(Vec(minx, miny, minz), Vec(maxx, maxy, maxz)).draw(Color(255, 255, 0, 128), true);
  77. }
  78. void drawVoxels()
  79. {
  80. SetMatrix();
  81. const float* orig=T->bmin;
  82. const float cs=T->cs;
  83. const float ch=T->ch;
  84. const int w=T->width;
  85. const int h=T->height;
  86. REPD(y, h)
  87. REPD(x, w)
  88. {
  89. float fx=orig[0]+x*cs;
  90. float fz=orig[2]+y*cs;
  91. C rcSpan *s=T->spans[x+y*w];
  92. for(; s; s=s->next)drawVoxel(fx, orig[1]+s->smin*ch, fz, fx+cs, orig[1]+s->smax*ch, fz+cs);
  93. }
  94. }
  95. ~RecastHeightfield() { rcFreeHeightField(_); _=null;}
  96. RecastHeightfield() {_=rcAllocHeightfield(); if(!_)Exit("Out of memory");}
  97. private:
  98. rcHeightfield *_;
  99. };
  100. /******************************************************************************/
  101. struct RecastContourSet
  102. {
  103. operator rcContourSet& () {return *_;}
  104. rcContourSet* operator-> () {return _;}
  105. ~RecastContourSet() { rcFreeContourSet(_); _=null;}
  106. RecastContourSet() {_=rcAllocContourSet(); if(!_)Exit("Out of memory");}
  107. private:
  108. rcContourSet *_;
  109. };
  110. /******************************************************************************/
  111. struct RecastPolyMesh
  112. {
  113. operator rcPolyMesh& () {return *_;}
  114. rcPolyMesh* operator-> () {return _;}
  115. ~RecastPolyMesh() { rcFreePolyMesh(_); _=null;}
  116. RecastPolyMesh() {_=rcAllocPolyMesh(); if(!_)Exit("Out of memory");}
  117. private:
  118. rcPolyMesh *_;
  119. };
  120. /******************************************************************************/
  121. struct RecastPolyMeshDetail
  122. {
  123. operator rcPolyMeshDetail& () {return *_;}
  124. rcPolyMeshDetail* operator-> () {return _;}
  125. ~RecastPolyMeshDetail() { rcFreePolyMeshDetail(_); _=null;}
  126. RecastPolyMeshDetail() {_=rcAllocPolyMeshDetail(); if(!_)Exit("Out of memory");}
  127. private:
  128. rcPolyMeshDetail *_;
  129. };
  130. /******************************************************************************/
  131. void RecastCompactHeightfield::operator=(C RecastCompactHeightfield &src)
  132. {
  133. if(this!=&src)
  134. {
  135. del();
  136. Copy(T, src);
  137. cells=(rcCompactCell *)rcAlloc(SIZE(rcCompactCell )*width*height, RC_ALLOC_PERM); CopyN(cells, src.cells, width*height);
  138. spans=(rcCompactSpan *)rcAlloc(SIZE(rcCompactSpan )*spanCount , RC_ALLOC_PERM); CopyN(spans, src.spans, spanCount );
  139. dist =(unsigned short*)rcAlloc(SIZE(unsigned short)*spanCount , RC_ALLOC_PERM); CopyN(dist , src.dist , spanCount );
  140. areas=(unsigned char *)rcAlloc(SIZE(unsigned char )*spanCount , RC_ALLOC_PERM); CopyN(areas, src.areas, spanCount );
  141. }
  142. }
  143. /******************************************************************************/
  144. void RecastCompactHeightfield::del()
  145. {
  146. rcFree(cells);
  147. rcFree(spans);
  148. rcFree(areas);
  149. rcFree(dist );
  150. zero();
  151. }
  152. /******************************************************************************/
  153. Bool RecastCompactHeightfield::is()C
  154. {
  155. return width || height || spanCount;
  156. }
  157. /******************************************************************************/
  158. void RecastCompactHeightfield::clean() // this removes all spans of RC_NULL_AREA
  159. {
  160. // count 'cleaned' and set 'old_to_new' remap
  161. Memt<Int> old_to_new; old_to_new.setNum(spanCount);
  162. Int cleaned=0;
  163. FREP(spanCount)
  164. {
  165. if(areas[i]==RC_NULL_AREA)old_to_new[i]=-1;
  166. else old_to_new[i]=cleaned++;
  167. }
  168. // clean
  169. if(cleaned!=spanCount)
  170. {
  171. // set Span to XY
  172. Memt<VecI2> span_xy; span_xy.setNum(spanCount);
  173. REPD(y, height)
  174. REPD(x, width )
  175. {
  176. rcCompactCell &cell=cells[x+y*width];
  177. REP(cell.count)span_xy[cell.index+i].set(x, y);
  178. }
  179. // spans + area id (before cells)
  180. rcCompactSpan *new_spans=(rcCompactSpan*)rcAlloc(SIZE(*new_spans)*cleaned, RC_ALLOC_PERM);
  181. unsigned char *new_areas=(unsigned char*)rcAlloc(SIZE(*new_areas)*cleaned, RC_ALLOC_PERM);
  182. FREPA(old_to_new)
  183. {
  184. Int n =old_to_new[i];
  185. if( n>=0)
  186. {
  187. C VecI2 &s_xy =span_xy [i];
  188. rcCompactSpan &new_span=new_spans[n];
  189. new_span =spans[i];
  190. new_areas[n]=areas[i];
  191. REPD(dir, 4)
  192. {
  193. Int connection =rcGetCon(new_span, dir);
  194. if( connection!=RC_NOT_CONNECTED)
  195. {
  196. Int new_connection=RC_NOT_CONNECTED,
  197. nx=s_xy.x+rcGetDirOffsetX(dir),
  198. ny=s_xy.y+rcGetDirOffsetY(dir);
  199. if(InRange(nx, width )
  200. && InRange(ny, height))
  201. {
  202. Int valid =0;
  203. rcCompactCell &neighb_cell=cells[nx+ny*width];
  204. FREP(neighb_cell.count)
  205. {
  206. Int span_index=neighb_cell.index+i;
  207. if(old_to_new[span_index]>=0)
  208. {
  209. // code taken from Recast 'rcBuildCompactHeightfield'
  210. rcCompactSpan &test_span=spans[span_index];
  211. Int bot=Max(new_span.y , test_span.y ),
  212. top=Min(new_span.y+new_span.h, test_span.y+test_span.h);
  213. if(top-bot>=walkableHeight && Abs(Int(test_span.y)-Int(new_span.y))<=walkableClimb) // Check that the gap between the spans is walkable, and that the climb height between the gaps is not too high.
  214. {
  215. if(InRange(valid, RC_NOT_CONNECTED))new_connection=valid;
  216. break;
  217. }
  218. valid++;
  219. }
  220. }
  221. }
  222. rcSetCon(new_span, dir, new_connection);
  223. }
  224. }
  225. }
  226. }
  227. rcFree(spans); spans=new_spans;
  228. rcFree(areas); areas=new_areas;
  229. // adjust cells (after spans)
  230. REP(width*height)
  231. {
  232. rcCompactCell &cell=cells[i];
  233. Int new_index=0, new_count=0;
  234. FREP(cell.count)
  235. {
  236. Int n =old_to_new[cell.index+i];
  237. if( n>=0)
  238. {
  239. if(!new_count)new_index=n;
  240. new_count++;
  241. }
  242. }
  243. cell.index=new_index;
  244. cell.count=new_count;
  245. }
  246. // dist
  247. if(dist)
  248. {
  249. unsigned short *new_dist=(unsigned short*)rcAlloc(SIZE(*new_dist)*cleaned, RC_ALLOC_PERM);
  250. FREPA(old_to_new)
  251. {
  252. Int n =old_to_new[i];
  253. if( n>=0)new_dist[n]=dist[i];
  254. }
  255. rcFree(dist); dist=new_dist;
  256. }
  257. // count
  258. spanCount=cleaned;
  259. }
  260. }
  261. /******************************************************************************/
  262. Bool RecastCompactHeightfield::save(File &f)C
  263. {
  264. f.cmpUIntV(0); // version
  265. f.cmpUIntV(width).cmpUIntV(height).cmpUIntV(spanCount).cmpUIntV(walkableHeight).cmpUIntV(walkableClimb).cmpUIntV(borderSize).cmpUIntV(maxDistance).cmpUIntV(maxRegions);
  266. f<<bmin<<bmax<<cs<<ch;
  267. f.putN(cells, width*height);
  268. f.putN(spans, spanCount );
  269. f.putN(areas, spanCount );
  270. f.putBool(dist!=null); if(dist)f.putN(dist, spanCount);
  271. return f.ok();
  272. }
  273. /******************************************************************************/
  274. Bool RecastCompactHeightfield::load(File &f)
  275. {
  276. del();
  277. switch(f.decUIntV())
  278. {
  279. case 0:
  280. {
  281. width=f.decUIntV(); height=f.decUIntV(); spanCount=f.decUIntV(); walkableHeight=f.decUIntV(); walkableClimb=f.decUIntV(); borderSize=f.decUIntV(); maxDistance=f.decUIntV(); maxRegions=f.decUIntV();
  282. f>>bmin>>bmax>>cs>>ch;
  283. cells=(rcCompactCell *)rcAlloc(SIZE(*cells)*width*height, RC_ALLOC_PERM); f.getN(cells, width*height);
  284. spans=(rcCompactSpan *)rcAlloc(SIZE(*spans)*spanCount , RC_ALLOC_PERM); f.getN(spans, spanCount );
  285. areas=(unsigned char *)rcAlloc(SIZE(*areas)*spanCount , RC_ALLOC_PERM); f.getN(areas, spanCount );
  286. if(f.getBool()){dist =(unsigned short*)rcAlloc(SIZE(*dist )*spanCount , RC_ALLOC_PERM); f.getN(dist , spanCount );}
  287. if(f.ok())return true;
  288. }break;
  289. }
  290. del(); return false;
  291. }
  292. /******************************************************************************/
  293. static void ExtendPoly2D(Memt<Vec2> &poly, Flt r, Bool add_points)
  294. {
  295. if(r && poly.elms()>=3)
  296. {
  297. if(add_points)
  298. {
  299. Memt<Vec2> temp; temp.setNum(poly.elms()*2);
  300. Vec2 &prev=poly.last(), &cur=poly[0], prev_nrm=PerpN(prev-cur);
  301. FREPA(poly)
  302. {
  303. Vec2 &cur=poly[i], &next=poly[(i+1)%poly.elms()], next_nrm=PerpN(cur-next);
  304. temp[i*2+0]=cur+prev_nrm*r;
  305. temp[i*2+1]=cur+next_nrm*r;
  306. prev_nrm=next_nrm;
  307. }
  308. Swap(poly, temp);
  309. }else
  310. {
  311. Vec2 &prev=poly.last(), cur_0=poly[0], prev_nrm=PerpN(prev-cur_0); // cur_0 is 'cur' and backup of poly[0]
  312. FREPA(poly)
  313. {
  314. Int next_index=(i+1)%poly.elms();
  315. Vec2 &cur=poly[i], &next=(next_index ? poly[next_index] : cur_0), next_nrm=PerpN(cur-next), avg_nrm=prev_nrm+next_nrm;
  316. avg_nrm.setLength(r);
  317. cur+=avg_nrm;
  318. prev_nrm=next_nrm;
  319. }
  320. }
  321. }
  322. }
  323. static void MarkConvexArea(rcContext &ctx, Vec *point, Int points, Flt ctrl_r, Flt ctrl_h, PATH_MESH_TYPE type, rcCompactHeightfield &chf)
  324. {
  325. if(point && points>=3)
  326. {
  327. Vec min, max; MinMax(point, points, min, max);
  328. Memt<Vec2> poly; CreateConvex2Dxz(poly, point, points);
  329. if(poly.elms()>=3)
  330. {
  331. ExtendPoly2D(poly, ctrl_r, true);
  332. // rcMarkConvexPolyArea expects 'XYZ' vec, but we have 'Vec2', so convert it
  333. Int elms=poly.elms(); // first remember number of vtxs
  334. poly.addNum((elms+1)/2); // we need 'elms' more floats, with each new element we get 2 floats (because this is Vec2 container), so divide by 2 with ceil
  335. Vec2 *vec2=poly.data();
  336. Vec *vec =(Vec*)vec2; // now we can cast to Vec
  337. REP(elms)vec[i].set(vec2[i].x, 0, vec2[i].y); // it's important to go from the end to don't overwrite existing elements
  338. rcMarkConvexPolyArea(&ctx, vec->c, elms, min.y-ctrl_h, max.y, type, chf); // this function ignores 'poly.y' and uses only (hmin..hmax)
  339. }
  340. }
  341. }
  342. // !! Warning: 'chf' will get modified !!
  343. static Bool Build(RecastCompactHeightfield &chf, Shape *shape, Int shapes, Flt ctrl_r, Flt ctrl_h, Flt max_climb, C VecI2 &xy, Ptr user, Mems<Byte> &data)
  344. {
  345. {
  346. RecastConfig cfg(chf.cs, chf.ch, ctrl_r, ctrl_h, max_climb);
  347. rcContext ctx(false);
  348. // Mark areas.
  349. REP(shapes)
  350. {
  351. C Shape &s=shape[i];
  352. switch(s.type)
  353. {
  354. // 'rcMarkCylinderArea' accepts bottom position (height parameter extends up only)
  355. // don't use PM_OBSTACLE because that could result in empty mesh (no data), it would fail to set that mesh into recast world, let's use PM_BLOCKED instead which results in creating the mesh but with polygons that are not crossable
  356. case SHAPE_POINT : {Box b=s.point; b.extendX(ctrl_r).extendZ(ctrl_r).extendY(cfg.ch).min.y-=ctrl_h; rcMarkBoxArea(&ctx, b.min.c, b.max.c, PM_BLOCKED, chf);} break;
  357. case SHAPE_BOX : {Box b=s.box ; b.extendX(ctrl_r).extendZ(ctrl_r) .min.y-=ctrl_h; rcMarkBoxArea(&ctx, b.min.c, b.max.c, PM_BLOCKED, chf);} break;
  358. case SHAPE_BALL : {Flt h=s.ball .r*2; rcMarkCylinderArea(&ctx, (s.ball .pos-Vec(0, h*0.5f+ctrl_h, 0)).c, s.ball .r+ctrl_r+cfg.cs*0.50f, h+ctrl_h, PM_BLOCKED, chf);} break;
  359. case SHAPE_CAPSULE: {Flt h=s.capsule.h ; rcMarkCylinderArea(&ctx, (s.capsule.pos-Vec(0, h*0.5f+ctrl_h, 0)).c, s.capsule.r+ctrl_r+cfg.cs*0.50f, h+ctrl_h, PM_BLOCKED, chf);} break;
  360. case SHAPE_TUBE : {Flt h=s.tube .h ; rcMarkCylinderArea(&ctx, (s.tube .pos-Vec(0, h*0.5f+ctrl_h, 0)).c, s.tube .r+ctrl_r+cfg.cs*0.50f, h+ctrl_h, PM_BLOCKED, chf);} break;
  361. case SHAPE_OBOX : {Vec points[8]; s.obox .toCorners(points); MarkConvexArea(ctx, points, Elms(points), ctrl_r+cfg.cs*0.25f, ctrl_h, PM_BLOCKED, chf);} break;
  362. case SHAPE_PYRAMID: {Vec points[5]; s.pyramid.toCorners(points); MarkConvexArea(ctx, points, Elms(points), ctrl_r+cfg.cs*0.25f, ctrl_h, PM_BLOCKED, chf);} break;
  363. case SHAPE_EDGE :
  364. {
  365. Flt r=ctrl_r+cfg.cs*0.25f;
  366. Vec z=s.edge.delta(); z.y=0; if(!z.setLength(r))z.set(0, 0, r);
  367. Vec x=Perp(z.xz()).x0y();
  368. Vec points[4]=
  369. {
  370. s.edge.p[0]-z-x,
  371. s.edge.p[0]-z+x,
  372. s.edge.p[1]+z-x,
  373. s.edge.p[1]+z+x,
  374. };
  375. MinMax(s.edge.p[0].y, s.edge.p[1].y, points[0].y, points[1].y); points[0].y-=cfg.ch; points[1].y+=cfg.ch; // extend Y by cell height
  376. MarkConvexArea(ctx, points, Elms(points), 0, ctrl_h, PM_BLOCKED, chf);
  377. }break;
  378. }
  379. }
  380. if(MonotonePartitioning)
  381. {
  382. // Partition the walkable surface into simple regions without holes.
  383. if(!rcBuildRegionsMonotone(&ctx, chf, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea))goto error;
  384. }else
  385. {
  386. // Prepare for region partitioning, by calculating distance field along the walkable surface.
  387. if(!rcBuildDistanceField(&ctx, chf))goto error;
  388. // Partition the walkable surface into simple regions without holes.
  389. if(!rcBuildRegions(&ctx, chf, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea))goto error;
  390. }
  391. // Create contours.
  392. RecastContourSet cset;
  393. if(!rcBuildContours(&ctx, chf, cfg.maxSimplificationError, cfg.maxEdgeLen, cset))goto error;
  394. //if(cset->nconts==0)return true; // mesh is empty
  395. // Build polygon navmesh from the contours.
  396. RecastPolyMesh pmesh;
  397. if(!rcBuildPolyMesh(&ctx, cset, cfg.maxVertsPerPoly, pmesh))goto error;
  398. // Build detail mesh.
  399. RecastPolyMeshDetail dmesh;
  400. if(BuildDetailMesh)
  401. if(!rcBuildPolyMeshDetail(&ctx, pmesh, chf, cfg.detailSampleDist, cfg.detailSampleMaxError, dmesh))goto error;
  402. if(pmesh->nverts>0x10000)goto error; // Recast uses U16 indexes
  403. // Update poly flags from areas.
  404. REP(pmesh->npolys)switch(pmesh->areas[i])
  405. {
  406. default : pmesh->flags[i]=0 ; break; // PM_OBSTACLE
  407. case PM_GROUND: pmesh->flags[i]=PMF_WALK; break;
  408. case PM_WATER : pmesh->flags[i]=PMF_SWIM; break;
  409. }
  410. dtNavMeshCreateParams params; Zero(params);
  411. params.verts=pmesh->verts;
  412. params.vertCount=pmesh->nverts;
  413. params.polys=pmesh->polys;
  414. params.polyAreas=pmesh->areas;
  415. params.polyFlags=pmesh->flags;
  416. params.polyCount=pmesh->npolys;
  417. params.nvp=pmesh->nvp;
  418. if(BuildDetailMesh)
  419. {
  420. params.detailMeshes=dmesh->meshes;
  421. params.detailVerts=dmesh->verts;
  422. params.detailVertsCount=dmesh->nverts;
  423. params.detailTris=dmesh->tris;
  424. params.detailTriCount=dmesh->ntris;
  425. }
  426. /*params.offMeshConVerts=geom->getOffMeshConnectionVerts();
  427. params.offMeshConRad=geom->getOffMeshConnectionRads();
  428. params.offMeshConDir=geom->getOffMeshConnectionDirs();
  429. params.offMeshConAreas=geom->getOffMeshConnectionAreas();
  430. params.offMeshConFlags=geom->getOffMeshConnectionFlags();
  431. params.offMeshConUserID=geom->getOffMeshConnectionId();
  432. params.offMeshConCount=geom->getOffMeshConnectionCount();*/
  433. params.walkableRadius=ctrl_r;
  434. params.walkableHeight=ctrl_h;
  435. params.walkableClimb =Max(max_climb, cfg.ch); // set minimum value from 'cell height' because sometimes neighbor tiles couldn't connect (due to small changes between their Y positions)
  436. params.tileX=xy.x;
  437. params.tileY=xy.y;
  438. params.tileLayer=0;
  439. rcVcopy(params.bmin, pmesh->bmin);
  440. rcVcopy(params.bmax, pmesh->bmax);
  441. params.cs=cfg.cs;
  442. params.ch=cfg.ch;
  443. params.buildBvTree=true;
  444. params.userId=(UIntPtr)user;
  445. unsigned char *navData=null;
  446. int navDataSize=0;
  447. if(dtCreateNavMeshData(&params, &navData, &navDataSize))
  448. {
  449. data.setNum(navDataSize).copyFrom(navData); dtFree(navData); return true;
  450. }
  451. }
  452. error:
  453. data.del(); return false;
  454. }
  455. /******************************************************************************/
  456. // _PATH MESH
  457. /******************************************************************************/
  458. Bool _PathMesh::is()C {return data.elms()>0;}
  459. /******************************************************************************/
  460. Box _PathMesh::box()
  461. {
  462. Box box;
  463. if(C dtMeshHeader *header=(dtMeshHeader*)data.data()) // if mesh is available then use it
  464. {
  465. box.min.set(header->bmin[0], header->bmin[1], header->bmin[2]);
  466. box.max.set(header->bmax[0], header->bmax[1], header->bmax[2]);
  467. }else
  468. if(getChf()) // try accessing 'chf'
  469. {
  470. box.min.set(chf.bmin[0], chf.bmin[1], chf.bmin[2]);
  471. box.max.set(chf.bmax[0], chf.bmax[1], chf.bmax[2]);
  472. }else box.zero();
  473. return box;
  474. }
  475. /******************************************************************************/
  476. void _PathMesh::link(PathWorld *world)
  477. {
  478. if(T.world!=world) // if different
  479. {
  480. if(T.world) // unlink from current world
  481. {
  482. T.world->_set(null, xy, false);
  483. T.world=null; // clear in case '_set' won't do this for some reason
  484. }
  485. if(world)world->_set(this, xy, false); // link to new world, it will handle setting 'T.world' on success
  486. }
  487. }
  488. void _PathMesh::del()
  489. {
  490. link(null); xy.zero();
  491. data.del(); obstacle_data.del(); chf_compressed.del();
  492. chf .del();
  493. }
  494. Bool _PathMesh::getChf()
  495. {
  496. if(chf.is())return true; // we already have it
  497. if(chf_compressed.elms()) // if we have 'chf_compressed'
  498. {
  499. File compressed(chf_compressed.data(), chf_compressed.elms()), decompressed; // read from 'chf_compressed'
  500. if(Decompress(compressed, decompressed, true)) // decompress 'compressed' into 'decompressed'
  501. {
  502. chf_compressed.del(); // no longer needed so release it
  503. decompressed.pos(0); return chf.load(decompressed); // load 'chf' from 'decompressed'
  504. }
  505. }
  506. return false;
  507. }
  508. /******************************************************************************/
  509. static void SetChfCompressed(C RecastCompactHeightfield &chf, Mems<Byte> &chf_compressed)
  510. {
  511. File decompressed; chf.save(decompressed.writeMem()); // save 'chf' into 'decompressed'
  512. File compressed; decompressed.pos(0); if(!Compress(decompressed, compressed.writeMem(), CompressType, CompressLevel, false))Exit("SetChfCompressed"); // compress 'decompressed' into 'compressed'
  513. chf_compressed.setNum(compressed.size()); compressed.pos(0); compressed.get(chf_compressed.data(), chf_compressed.elms()); // store 'compressed' into 'chf_compressed'
  514. }
  515. void _PathMesh::preSave()
  516. {
  517. if(is() && !chf_compressed.elms())SetChfCompressed(chf, chf_compressed);
  518. }
  519. Bool _PathMesh::save(File &f)C
  520. {
  521. f.cmpUIntV(0); // version
  522. if(data._saveRaw(f))
  523. {
  524. if(is())
  525. {
  526. Mems<Byte> chf_compressed_temp, *chf_compressed=&ConstCast(T.chf_compressed);
  527. if(!chf_compressed->elms()){chf_compressed=&chf_compressed_temp; SetChfCompressed(chf, *chf_compressed);} // compress into temp so we won't keep it in the memory afterwards
  528. if(!chf_compressed->_saveRaw(f))return false;
  529. }
  530. return f.ok();
  531. }
  532. return false;
  533. }
  534. Bool _PathMesh::load(File &f)
  535. {
  536. del(); switch(f.decUIntV()) // version
  537. {
  538. case 0: if(data._loadRaw(f))
  539. {
  540. if(is())if(!chf_compressed._loadRaw(f))goto error;
  541. if(dtMeshHeader *header=(dtMeshHeader*)data.data())
  542. {
  543. header->userId=(UIntPtr)this; // 'userId' points to memory address of '_PathMesh'
  544. xy.set(header->x, header->y);
  545. }
  546. if(f.ok())return true;
  547. }break;
  548. }
  549. error:
  550. del(); return false;
  551. }
  552. /******************************************************************************/
  553. // PATH SETTINGS
  554. /******************************************************************************/
  555. PathSettings& PathSettings::reset()
  556. {
  557. _area_size=32;
  558. _ctrl_r =0.33f;
  559. _ctrl_h =2.0f;
  560. _max_climb=0.7f;
  561. _max_slope=PI_4;
  562. _cell_size=1.0f/3;
  563. _cell_h =0.1f;
  564. return T;
  565. }
  566. PathSettings& PathSettings::areaSize (Flt size ) {_area_size=Max(size , EPS); return T;}
  567. PathSettings& PathSettings::ctrlRadius(Flt r ) {_ctrl_r =Max(r , 0); return T;}
  568. PathSettings& PathSettings::ctrlHeight(Flt h ) {_ctrl_h =Max(h , 0); return T;}
  569. PathSettings& PathSettings:: maxClimb (Flt climb) {_max_climb=Max(climb, 0); return T;}
  570. PathSettings& PathSettings:: maxSlope (Flt slope) {_max_slope=Mid(slope, 0.0f, PI_2); return T;}
  571. PathSettings& PathSettings::cellSize (Flt size ) {_cell_size=Max(size , 0.001f); return T;}
  572. PathSettings& PathSettings::cellHeight(Flt h ) {_cell_h =Max(h , 0.001f); return T;}
  573. Bool PathSettings::operator!=(C PathSettings &path)C {return !(T==path);}
  574. Bool PathSettings::operator==(C PathSettings &path)C
  575. {
  576. return Equal(_area_size, path._area_size)
  577. && Equal(_ctrl_r , path._ctrl_r ) && Equal(_ctrl_h , path._ctrl_h )
  578. && Equal(_max_climb, path._max_climb) && Equal(_max_slope, path._max_slope)
  579. && Equal(_cell_size, path._cell_size) && Equal(_cell_h , path._cell_h );
  580. }
  581. Bool PathSettings::save(File &f)C
  582. {
  583. f.cmpUIntV(0);
  584. f<<_area_size<<_ctrl_r<<_ctrl_h<<_max_climb<<_max_slope<<_cell_size<<_cell_h;
  585. return f.ok();
  586. }
  587. Bool PathSettings::load(File &f)
  588. {
  589. switch(f.decUIntV())
  590. {
  591. case 0:
  592. {
  593. f>>_area_size>>_ctrl_r>>_ctrl_h>>_max_climb>>_max_slope>>_cell_size>>_cell_h;
  594. if(f.ok())return true;
  595. }break;
  596. }
  597. reset(); return false;
  598. }
  599. /******************************************************************************/
  600. // PATH OBSTACLE
  601. /******************************************************************************/
  602. static Bool ObstacleShapeSupported(SHAPE_TYPE type)
  603. {
  604. switch(type)
  605. {
  606. case SHAPE_POINT :
  607. case SHAPE_EDGE :
  608. case SHAPE_BOX :
  609. case SHAPE_BALL :
  610. case SHAPE_CAPSULE:
  611. case SHAPE_TUBE :
  612. case SHAPE_OBOX :
  613. case SHAPE_PYRAMID: return true;
  614. default: return false;
  615. }
  616. }
  617. void PathObstacle::del()
  618. {
  619. if(_world)
  620. {
  621. if(_world->_obstacles.contains(_shape))
  622. {
  623. _world->changed(*_shape);
  624. _world->_obstacles.removeData(_shape);
  625. }
  626. _world=null;
  627. }
  628. _shape=null;
  629. }
  630. Bool PathObstacle::create(C Shape &shape, C PathWorld &world)
  631. {
  632. PathWorld &pw=ConstCast(world);
  633. if(T._world==&pw) // if is already linked with this world
  634. {
  635. T.shape(shape); // adjust the shape
  636. return T._world!=null || shape.type==SHAPE_NONE;
  637. }
  638. del();
  639. if(ObstacleShapeSupported(shape.type))
  640. {
  641. T._world=&pw;
  642. T._shape=&pw._obstacles.New();
  643. *T._shape= shape;
  644. pw.changed(shape);
  645. return true;
  646. }
  647. return shape.type==SHAPE_NONE;
  648. }
  649. Shape PathObstacle::shape()
  650. {
  651. if(_world && _shape && _world->_obstacles.contains(_shape))return *_shape;
  652. return Shape(); // return empty
  653. }
  654. void PathObstacle::shape(C Shape &shape)
  655. {
  656. if(ObstacleShapeSupported(shape.type) && _world && T._shape && _world->_obstacles.contains(T._shape))
  657. {
  658. _world->changed(*T._shape); // call 'changed' before making the change so old area will get updated too
  659. *T._shape=shape;
  660. _world->changed(*T._shape); // call 'changed' after making the change so new area will get updated
  661. }else del();
  662. }
  663. /******************************************************************************/
  664. // PATH MESH
  665. /******************************************************************************/
  666. PathMesh::~PathMesh() {del(); Delete(_pm);}
  667. PathMesh:: PathMesh() { New (_pm);}
  668. /******************************************************************************/
  669. void PathMesh::del () { _pm->del ();}
  670. Bool PathMesh::is ()C {return _pm->is ();}
  671. void PathMesh::preSave() { _pm->preSave();}
  672. /******************************************************************************/
  673. Bool PathMesh::create(MeshBase &mesh, C VecI2 &area_xy, C PathSettings &settings)
  674. {
  675. del();
  676. rcContext ctx(false);
  677. Memt<VecI> tri_ind ; // triangle indexes combined from mesh tri+quad
  678. Memt<Byte> tri_type; // triangle types
  679. tri_type.setNum(mesh.trisTotal());
  680. if(mesh.quad.elms())
  681. {
  682. tri_ind.setNum(mesh.trisTotal());
  683. Int t=0;
  684. FREPA(mesh.tri ){tri_ind[t]=mesh.tri .ind(i) ; tri_type[t]=(mesh.tri .flag() ? mesh.tri .flag(i) : PM_GROUND); t++;}
  685. FREPA(mesh.quad){tri_ind[t]=mesh.quad.ind(i).tri0(); tri_type[t]=(mesh.quad.flag() ? mesh.quad.flag(i) : PM_GROUND); t++;
  686. tri_ind[t]=mesh.quad.ind(i).tri1(); tri_type[t]=(mesh.quad.flag() ? mesh.quad.flag(i) : PM_GROUND); t++;}
  687. }else
  688. {
  689. if(mesh.tri.flag())tri_type.copyFrom(mesh.tri.flag());
  690. else SetMemN(tri_type.data(), PM_GROUND, tri_type.elms());
  691. }
  692. const float *verts=mesh.vtx.pos ()->c;
  693. const int nverts=mesh.vtx.elms();
  694. const int ntris =(tri_ind.elms() ? tri_ind.elms() : mesh.tri.elms() );
  695. const int *tris =(tri_ind.elms() ? tri_ind[0].c : mesh.tri.ind ()->c);
  696. // area_size=tile_res*cell_size
  697. Int tile_res =Max(1, RoundPos(settings.areaSize()/settings.cellSize()));
  698. Flt cell_size= settings.areaSize()/tile_res;
  699. Box box=mesh;
  700. box.min.x=area_xy.x*settings.areaSize(); box.max.x=box.min.x+settings.areaSize();
  701. box.min.z=area_xy.y*settings.areaSize(); box.max.z=box.min.z+settings.areaSize();
  702. // set configuration
  703. RecastConfig cfg(cell_size, settings.cellHeight(), settings.ctrlRadius(), settings.ctrlHeight(), settings.maxClimb());
  704. cfg.walkableSlopeAngle=RadToDeg(settings.maxSlope());
  705. cfg.tileSize =tile_res;
  706. cfg.width =cfg.tileSize+cfg.borderSize*2;
  707. cfg.height =cfg.tileSize+cfg.borderSize*2;
  708. cfg.bmin[0]=box.min.x; cfg.bmin[1]=box.min.y; cfg.bmin[2]=box.min.z;
  709. cfg.bmax[0]=box.max.x; cfg.bmax[1]=box.max.y; cfg.bmax[2]=box.max.z;
  710. cfg.bmin[0]-=cfg.borderSize*cfg.cs;
  711. cfg.bmin[2]-=cfg.borderSize*cfg.cs;
  712. cfg.bmax[0]+=cfg.borderSize*cfg.cs;
  713. cfg.bmax[2]+=cfg.borderSize*cfg.cs;
  714. // Allocate voxel heightfield where we rasterize our input data to.
  715. RecastHeightfield solid;
  716. if(!rcCreateHeightfield(&ctx, solid, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch))return false;
  717. // Verify triangle slope
  718. Flt max_slope_cos=Cos(settings.maxSlope());
  719. REP(ntris)
  720. {
  721. C int *tri=&tris[i*3];
  722. Bool walkable=(GetNormal(mesh.vtx.pos(tri[0]), mesh.vtx.pos(tri[1]), mesh.vtx.pos(tri[2])).y>=max_slope_cos);
  723. if( !walkable)tri_type[i]=PM_OBSTACLE;
  724. }
  725. Int merge_height=cfg.walkableClimb; // based on this value, recast will merge spans
  726. rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_type.data(), ntris, solid, merge_height);
  727. // Once all geometry is rasterized, we do initial pass of filtering to remove unwanted overhangs caused by the conservative rasterization as well as filter spans where the character cannot possibly stand.
  728. rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, solid);
  729. rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, solid);
  730. rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, solid);
  731. // Compact the heightfield so that it is faster to handle from now on. This will result more cache coherent data as well as the neighbours between walkable cells will be calculated.
  732. if(!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, solid, _pm->chf))return false;
  733. // Erode the walkable area by agent radius.
  734. if(cfg.walkableRadius)
  735. {
  736. if(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, _pm->chf))return false;
  737. _pm->chf.clean(); // clean after erosion
  738. }
  739. // set params
  740. _pm->xy =area_xy;
  741. _pm->chf.cs=cfg.cs;
  742. _pm->chf.ch=cfg.ch;
  743. return Build(_pm->chf, null, 0, settings.ctrlRadius(), settings.ctrlHeight(), settings.maxClimb(), area_xy, _pm, _pm->data);
  744. }
  745. /******************************************************************************/
  746. Bool PathMesh::save(File &f)C {return _pm->save(f);}
  747. Bool PathMesh::load(File &f) {return _pm->load(f);}
  748. /******************************************************************************/
  749. // PATH WORLD
  750. /******************************************************************************/
  751. static Bool ThreadFunc(Thread &thread) {((PathWorld*)thread.user)->threadFunc(); return true;}
  752. void PathWorld::threadFunc()
  753. {
  754. if(_build.elms())
  755. {
  756. Build build;
  757. {
  758. SyncLocker lock(_lock);
  759. if(_build.elms()){Swap(build, _build[0]); _build.remove(0, true);}else goto wait; // goto will also unlock the 'lock'
  760. }
  761. Built built;
  762. if(!build.shapes.elms() // if we don't have any shapes, then we don't need to build, but just return empty 'data', which will be set to 'obstacle_data', and because 'obstacle_data' will be empty, then 'data' will be used (which has no shapes). Even though this operation is fast and could be performed on the main thread, we still need to process it here on the build thread, to preserve the queue order. For example if we would generate 'built' on the main thread, but on this builder thread would be another build going on that was queued before, then once it completes, it would overwrite the empty 'built'.
  763. || ::Build(build.chf, build.shapes.data(), build.shapes.elms(), _ctrl_r, _ctrl_h, _max_climb, build.xy, build.user, built.data))
  764. {
  765. // we need to store 'xy' and 'user' in case 'data' is empty
  766. built.xy =build.xy;
  767. built.user=build.user;
  768. SyncLocker lock(_lock);
  769. Swap(_built.New(), built);
  770. }
  771. }else
  772. {
  773. wait:
  774. _event.wait();
  775. }
  776. }
  777. void PathWorld::zero () {_area_size=_ctrl_r=_ctrl_h=_max_climb=0; _mesh=null; _query=null; _filter=null;}
  778. PathWorld::PathWorld() {zero();}
  779. void PathWorld::del ()
  780. {
  781. // delete the thread first
  782. _thread.stop();
  783. if(_build.elms()){SyncLocker lock(_lock); _build.del();} // cancel build requests
  784. _event .on (); // wake up thread
  785. _thread.del();
  786. // unlink path obstacles?
  787. // unlink path meshes
  788. if(C dtNavMesh *mesh=_mesh)
  789. REP(_mesh->getMaxTiles())
  790. if(C dtMeshTile *tile=mesh->getTile(i))
  791. if(C dtMeshHeader *header=tile->header)
  792. if(_PathMesh *path_mesh=(_PathMesh*)header->userId)path_mesh->world=null;
  793. // free
  794. _changed .del ();
  795. _build .del ();
  796. _built .del ();
  797. _obstacles.clear(); // use 'clear' to not delete the helper memory, in case there are some PathObstacle's still pointing to that memory
  798. dtFreeNavMeshQuery(_query );
  799. dtFreeNavMesh (_mesh );
  800. DeleteN (_filter);
  801. zero();
  802. }
  803. /******************************************************************************/
  804. Bool PathWorld::create(Flt area_size)
  805. {
  806. del();
  807. New(_filter, PMF_ALL+1); REP(PMF_ALL+1)_filter[i].setIncludeFlags(i);
  808. _mesh =dtAllocNavMesh (); if(!_mesh )Exit("Out of Memory");
  809. _query=dtAllocNavMeshQuery(); if(!_query)Exit("Out of Memory");
  810. dtNavMeshParams params; Zero(params);
  811. params.orig[0] =0;
  812. params.orig[1] =0;
  813. params.orig[2] =0;
  814. params.tileWidth =area_size;
  815. params.tileHeight=area_size;
  816. params.maxTiles =MaxTiles;
  817. params.maxPolys =MaxPolys;
  818. if(dtStatusFailed(_mesh ->init(&params)))return false;
  819. if(dtStatusFailed(_query->init(_mesh, MaxSearchNodes)))return false;
  820. T._area_size=area_size;
  821. _thread.create(ThreadFunc, this, 0, false, "EE.PathWorld");
  822. return true;
  823. }
  824. /******************************************************************************/
  825. Box PathWorld::obstacleBox(C Shape &shape)C
  826. {
  827. Box box=shape; box.extendX(_ctrl_r).extendZ(_ctrl_r).min.y-=_ctrl_h;
  828. return box;
  829. }
  830. /******************************************************************************/
  831. _PathMesh* PathWorld::pathMesh(C VecI2 &area_xy)C
  832. {
  833. if(_mesh)
  834. if(C dtMeshTile *tile=_mesh->getTileAt(area_xy.x, area_xy.y, 0))
  835. if(C dtMeshHeader *header=tile->header)
  836. return (_PathMesh*)header->userId;
  837. return null;
  838. }
  839. /******************************************************************************/
  840. void PathWorld::update()
  841. {
  842. if(_changed.elms() || _built.elms())
  843. {
  844. Bool wake_up=(_changed.elms()>0);
  845. {
  846. Memt<Shape> area_shapes;
  847. SyncLocker lock(_lock);
  848. // queue elements for processing
  849. FREPA(_changed) // process in order
  850. {
  851. _PathMesh &path_mesh=*_changed[i]; if(path_mesh.is() && path_mesh.getChf())
  852. {
  853. Box mesh_box=path_mesh.box();
  854. REPA(_obstacles)
  855. {
  856. C Shape &shape=_obstacles[i];
  857. if(Cuts(obstacleBox(shape), mesh_box))area_shapes.add(shape);
  858. }
  859. // we always need to queue on the build thread, even if 'area_shapes' is empty, to preserve the build queue order
  860. Build *build=null; REPA(_build)if(_build[i].xy==path_mesh.xy){build=&_build[i]; break;} if(!build)build=&_build.New(); // if there's already a build queued up for this area, then reuse it with latest data but with the same queue position, otherwise create a new at the end of the queue
  861. build->chf = path_mesh.chf;
  862. build->shapes= area_shapes;
  863. build->xy = path_mesh.xy;
  864. build->user =&path_mesh;
  865. area_shapes.clear();
  866. }
  867. }
  868. _changed.clear();
  869. #if DEBUG && 0 // perform building on this thread
  870. #pragma message("!! Warning: Use this only for debugging !!")
  871. for(wake_up=false; _build.elms(); )threadFunc();
  872. #endif
  873. // process finished elements
  874. REPA(_built) // !! process from latest (this is to handle cases where we've got multiple builds for the same area, we will get the latest one, and inside unlinking, we delete all others) !!
  875. if(InRange(i, _built)) // !! check this, because inside unlinking, we may delete some '_built', and 'i' index may be out of range, so keep going down to zero !!
  876. {
  877. Built &built=_built[i];
  878. if(_PathMesh *pm=pathMesh(built.xy)) // find path mesh at that location
  879. if(pm==built.user) // if the same path mesh ('user' points to memory address of '_PathMesh')
  880. {
  881. Mems<Byte> data; Swap(data, built.data); // move to temp, because unlinking will delete 'built'
  882. // !! don't access 'built' anymore because it gets deleted !!
  883. pm->link(null); Swap(pm->obstacle_data, data); // unlink before changing data, in case unlinking requires the same data that was used for linking
  884. pm->link(this);
  885. // !! don't access 'built' anymore because it gets deleted !!
  886. }
  887. // !! don't access 'built' anymore because it gets deleted !!
  888. }
  889. _built.clear();
  890. }
  891. if(wake_up)_event.on(); // wake up thread
  892. }
  893. }
  894. /******************************************************************************/
  895. void PathWorld::changed(C Shape &shape)
  896. {
  897. Box box=obstacleBox(shape);
  898. RectI areas=worldToArea(box);
  899. for(Int y=areas.min.y; y<=areas.max.y; y++)
  900. for(Int x=areas.min.x; x<=areas.max.x; x++)
  901. if(_PathMesh *pm=pathMesh(VecI2(x, y)))
  902. if(Cuts(box, pm->box()))_changed.include(pm);
  903. }
  904. /******************************************************************************/
  905. Bool PathWorld::_set(_PathMesh *path_mesh, C VecI2 &area_xy, Bool set_obstacles)
  906. {
  907. _PathMesh *old=T.pathMesh(area_xy); if(old==path_mesh)return true;
  908. if(old) // 'old.world' should be set to this
  909. {
  910. old->world=null; // unlink from this world
  911. _changed.exclude(old); // remove from 'changed' as this PathMesh no longer belongs to this world
  912. // TODO: what about areas being currently built on the thread?
  913. if(_built.elms()) // !! this is important, because in PathWorld.update we're processing from the end (latest) and we need to remove all others for this area, so in that loop we process only one and latest 'built' but delete the others !!
  914. {
  915. SyncLocker lock(_lock);
  916. REPA(_built)if(dtMeshHeader *header=(dtMeshHeader*)_built[i].data.data())if(header->userId==(UIntPtr)old)_built.remove(i, true);
  917. }
  918. }
  919. if(path_mesh)path_mesh->link(null); // if we want to link 'path_mesh' to this world, then unlink it from any other world first
  920. if(_mesh)
  921. {
  922. _mesh->removeTile(_mesh->getTileRefAt(area_xy.x, area_xy.y, 0), null, null); // remove any previous tile
  923. if(path_mesh && path_mesh->is())
  924. {
  925. // verify area coordinates
  926. if(path_mesh->xy!=area_xy)return false;
  927. if(!_ctrl_r) // set world controller settings (before setting obstacles)
  928. if(dtMeshHeader *header=(dtMeshHeader*)path_mesh->data.data()){_ctrl_r=header->walkableRadius; _ctrl_h=header->walkableHeight; _max_climb=header->walkableClimb;}
  929. // set current obstacles on the path mesh (after getting controller settings)
  930. if(set_obstacles)
  931. {
  932. Memt<Shape> area_shapes;
  933. Box mesh_box=path_mesh->box();
  934. REPA(_obstacles)
  935. {
  936. C Shape &shape=_obstacles[i];
  937. if(Cuts(obstacleBox(shape), mesh_box))area_shapes.add(shape);
  938. }
  939. if(!area_shapes.elms())path_mesh->obstacle_data.del();else // if no shapes are needed, then delete 'obstacle_data' and use the main 'data' which we already have
  940. {
  941. if(!path_mesh->getChf())return false;
  942. RecastCompactHeightfield chf=path_mesh->chf; // operate on 'chf' copy because it gets modified in 'Build'
  943. if(!::Build(chf, area_shapes.data(), area_shapes.elms(), _ctrl_r, _ctrl_h, _max_climb, area_xy, path_mesh, path_mesh->obstacle_data))return false; // 'userId' points to memory address of '_PathMesh'
  944. }
  945. }
  946. // set path mesh data into recast
  947. Mems<Byte> &data=(path_mesh->obstacle_data.elms() ? path_mesh->obstacle_data : path_mesh->data);
  948. if(data.elms())
  949. {
  950. if(dtStatusFailed(_mesh->addTile(data.data(), data.elms(), 0, 0, null)))return false;
  951. path_mesh->world=this; // link only after 'addTile' succeeded
  952. }
  953. }
  954. return true;
  955. }
  956. return path_mesh==null;
  957. }
  958. Bool PathWorld::set(PathMesh *path_mesh, C VecI2 &area_xy) {return _set(path_mesh ? path_mesh->_pm : null, area_xy, true);}
  959. /******************************************************************************/
  960. Bool PathWorld::find(C Vec &start, C Vec &end, MemPtr<Vec> path, Int max_steps, UInt walkable_flags, Bool allow_partial_paths, C Vec &end_extents)C
  961. {
  962. path.clear();
  963. if(_query)
  964. {
  965. ConstCast(T).update();
  966. dtQueryFilter *filter=&T._filter[walkable_flags&PMF_ALL];
  967. Vec extents(1, 16, 1); // use high vertical extent because when there are no detail meshes, path mesh can be located at big differences when compared to original mesh
  968. dtPolyRef start_ref=0; _query->findNearestPoly(start.c, extents.c, filter, &start_ref, null);
  969. if(start_ref)
  970. {
  971. dtPolyRef end_ref=0; _query->findNearestPoly(end.c, end_extents.c, allow_partial_paths ? &T._filter[PMF_ALL] : filter, &end_ref, null); // use all filters to find the destination (for example if 'start' is on water, but 'end' is on ground, and 'walkable_flags' is set only to swim, then 'end' would not be found, and we wouldn't swim even a little towards the 'end', when 'end' is found using all filters, then we can just swim a little towards it)
  972. if(end_ref)
  973. {
  974. dtPolyRef polys[2048]; Int npolys=0;
  975. _query->findPath(start_ref, end_ref, start.c, end.c, filter, polys, &npolys, Elms(polys));
  976. if(npolys)
  977. {
  978. Vec path_points[2048], new_end=end; Int path_length=0;
  979. if(polys[npolys-1]!=end_ref) // if there's no direct path
  980. {
  981. if(allow_partial_paths)_query->closestPointOnPoly(polys[npolys-1], end.c, new_end.c, null); // In case of partial path, make sure the end point is clamped to the last polygon
  982. else return false;
  983. }
  984. if(dtStatusSucceed(_query->findStraightPath(start.c, end.c, polys, npolys, path_points[0].c, null, null, &path_length, (max_steps>0) ? Min(max_steps, Elms(path_points)) : Elms(path_points))))
  985. {
  986. path.setNum(path_length).copyFrom(path_points);
  987. return true;
  988. }
  989. }
  990. }
  991. }
  992. }
  993. return false;
  994. }
  995. /******************************************************************************/
  996. Bool PathWorld::nearestSurface(C Vec &pos, C Vec &extents, Vec &surface_pos, UInt walkable_flags)C
  997. {
  998. if(_query)
  999. {
  1000. ConstCast(T).update();
  1001. dtQueryFilter *filter=&T._filter[walkable_flags&PMF_ALL];
  1002. dtPolyRef pos_ref;
  1003. if(dtStatusSucceed(_query->findNearestPoly(pos.c, extents.c, filter, &pos_ref, surface_pos.c)))return true;
  1004. }
  1005. return false;
  1006. }
  1007. /******************************************************************************/
  1008. Bool PathWorld::nearestWall(C Vec &pos, Flt max_distance, Flt *hit_distance, Vec *hit_pos, Vec *hit_normal, UInt walkable_flags)C
  1009. {
  1010. if(_query)
  1011. {
  1012. ConstCast(T).update();
  1013. dtQueryFilter *filter=&T._filter[walkable_flags&PMF_ALL];
  1014. Flt dist;
  1015. Vec extents(max_distance), hp, hn;
  1016. dtPolyRef pos_ref=0; _query->findNearestPoly(pos.c, extents.c, filter, &pos_ref, null);
  1017. if(pos_ref && dtStatusSucceed(_query->findDistanceToWall(pos_ref, pos.c, max_distance, filter, &dist, hp.c, hn.c)))
  1018. if(dist<max_distance)
  1019. {
  1020. if(hit_distance)*hit_distance=dist;
  1021. if(hit_pos )*hit_pos =hp ;
  1022. if(hit_normal )*hit_normal =hn ;
  1023. return true;
  1024. }
  1025. }
  1026. if(hit_distance)*hit_distance=0;
  1027. if(hit_pos ) hit_pos ->zero();
  1028. if(hit_normal ) hit_normal->zero();
  1029. return false;
  1030. }
  1031. /******************************************************************************/
  1032. Bool PathWorld::ray(C Vec &start, C Vec &end, Flt *hit_frac, Vec *hit_pos, Vec *hit_normal, UInt walkable_flags)C
  1033. {
  1034. if(_query)
  1035. {
  1036. ConstCast(T).update();
  1037. dtQueryFilter *filter=&T._filter[walkable_flags&PMF_ALL];
  1038. Flt t;
  1039. Vec extents(1, 16, 1), hn; // use high vertical extent because when there are no detail meshes, path mesh can be located at big differences when compared to original mesh
  1040. dtPolyRef start_ref=0; _query->findNearestPoly(start.c, extents.c, filter, &start_ref, null);
  1041. if(start_ref && dtStatusSucceed(_query->raycast(start_ref, start.c, end.c, filter, &t, hn.c, null, null, 0)))
  1042. if(t<=1)
  1043. {
  1044. if(hit_frac )*hit_frac =t;
  1045. if(hit_pos )*hit_pos =Lerp(start, end, t);
  1046. if(hit_normal)*hit_normal=hn;
  1047. return true;
  1048. }
  1049. }
  1050. if(hit_frac )*hit_frac=1;
  1051. if(hit_pos )*hit_pos =end;
  1052. if(hit_normal) hit_normal->zero();
  1053. return false;
  1054. }
  1055. /******************************************************************************/
  1056. static float DistancePtLine2d(C float* pt, C float* p, C float* q)
  1057. {
  1058. float pqx=q[0] - p[0];
  1059. float pqz=q[2] - p[2];
  1060. float dx=pt[0] - p[0];
  1061. float dz=pt[2] - p[2];
  1062. float d=pqx*pqx+pqz*pqz;
  1063. float t=pqx*dx +pqz*dz;
  1064. if(d)t/=d;
  1065. dx=p[0]+t*pqx-pt[0];
  1066. dz=p[2]+t*pqz-pt[2];
  1067. return dx*dx + dz*dz;
  1068. }
  1069. static void DrawPolyBoundaries(C dtMeshTile* tile, C Color &col, C float linew, bool inner)
  1070. {
  1071. if(!col.a)return;
  1072. VI.color(col);
  1073. C float thr=0.01f*0.01f;
  1074. FREP(tile->header->polyCount)
  1075. {
  1076. C dtPoly* p=&tile->polys[i];
  1077. if(p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) continue;
  1078. C dtPolyDetail* pd=&tile->detailMeshes[i];
  1079. for(int j=0, nj=p->vertCount; j<nj; ++j)
  1080. {
  1081. Color c=col;
  1082. if(inner)
  1083. {
  1084. if(p->neis[j]==0)continue;
  1085. if(p->neis[j]&DT_EXT_LINK)
  1086. {
  1087. bool con=false;
  1088. for(unsigned int k=p->firstLink; k!=DT_NULL_LINK; k=tile->links[k].next)if(tile->links[k].edge == j){con=true; break;}
  1089. if(con)c.set(255, 255, 255, 48);
  1090. else c.set( 0, 0, 0, 48);
  1091. }else c.set( 0, 48, 64, 32);
  1092. }else if(p->neis[j])continue;
  1093. C float* v0=&tile->verts[p->verts[j]*3];
  1094. C float* v1=&tile->verts[p->verts[(j+1)%nj]*3];
  1095. for(int k=0; k<pd->triCount; ++k) // Draw detail mesh edges which align with the actual poly edge. This is really slow.
  1096. {
  1097. C unsigned char* t=&tile->detailTris[(pd->triBase+k)*4];
  1098. C float* tv[3];
  1099. for(int m=0; m<3; ++m)
  1100. {
  1101. if(t[m]<p->vertCount)tv[m]=&tile->verts[p->verts[t[m]]*3];
  1102. else tv[m]=&tile->detailVerts[(pd->vertBase+(t[m]-p->vertCount))*3];
  1103. }
  1104. for(int m=0, n=2; m<3; n=m++)
  1105. {
  1106. if(((t[3] >> (n*2)) & 0x3) == 0)continue; // Skip inner detail edges.
  1107. if(DistancePtLine2d(tv[n],v0,v1)<thr
  1108. && DistancePtLine2d(tv[m],v0,v1)<thr)
  1109. {
  1110. C float *a=tv[n], *b=tv[m];
  1111. VI.line(Vec(a[0], a[1], a[2]), Vec(b[0], b[1], b[2]));
  1112. }
  1113. }
  1114. }
  1115. }
  1116. }
  1117. VI.end();
  1118. }
  1119. static void DrawMeshTile(C dtMeshTile *tile, Byte surface_color_alpha, C Color &outer_edge_color, C Color &inner_edge_color)
  1120. {
  1121. if(tile && tile->header)
  1122. {
  1123. if(surface_color_alpha)
  1124. {
  1125. for(int i=0; i<tile->header->polyCount; ++i)
  1126. {
  1127. C dtPoly *p=&tile->polys[i];
  1128. if(p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)continue; // Skip off-mesh links.
  1129. C dtPolyDetail *pd=&tile->detailMeshes[i];
  1130. Color surface_color;
  1131. switch(p->getArea())
  1132. {
  1133. case PM_BLOCKED: surface_color.set(0, 0, 0, 0); break;
  1134. case PM_GROUND : surface_color.set(0, 255, 0, surface_color_alpha); break;
  1135. case PM_WATER : surface_color.set(0, 192, 255, surface_color_alpha); break;
  1136. }
  1137. if(surface_color.a)
  1138. for(int j=0; j<pd->triCount; ++j)
  1139. {
  1140. C unsigned char *t=&tile->detailTris[(pd->triBase+j)*4];
  1141. Vec vtx[3];
  1142. for(int k=0; k<3; ++k)
  1143. {
  1144. C float *vtx_pos;
  1145. if(t[k]<p->vertCount)vtx_pos=&tile->verts[p->verts[t[k]]*3];
  1146. else vtx_pos=&tile->detailVerts[(pd->vertBase+t[k]-p->vertCount)*3];
  1147. vtx[k].set(vtx_pos[0], vtx_pos[1], vtx_pos[2]);
  1148. }
  1149. VI.tri(surface_color, vtx[0], vtx[1], vtx[2]);
  1150. }
  1151. }
  1152. VI.end();
  1153. }
  1154. DrawPolyBoundaries(tile, inner_edge_color, 1.5f, true );
  1155. DrawPolyBoundaries(tile, outer_edge_color, 2.5f, false);
  1156. }
  1157. }
  1158. void PathWorld::draw(Byte surface_color_alpha, Flt y_offset, C Color &outer_edge_color, C Color &inner_edge_color)C
  1159. {
  1160. ConstCast(T).update();
  1161. SetMatrix(Matrix(Vec(0, y_offset, 0)));
  1162. if(C dtNavMesh *cmesh=_mesh)REP(cmesh->getMaxTiles())DrawMeshTile(cmesh->getTile(i), surface_color_alpha, outer_edge_color, inner_edge_color);
  1163. }
  1164. /******************************************************************************/
  1165. }
  1166. /******************************************************************************/