From a7cb1f86e2c75911347f8f88c55e66ed6891ba3e Mon Sep 17 00:00:00 2001 From: Mikko Mononen Date: Mon, 13 Jul 2009 12:06:04 +0000 Subject: [PATCH] Detour: Commented DetourTileNavMesh API. Demo: Fixed and improved few tile navmesh demo issues. --- Detour/Include/DetourStatNavMesh.h | 8 +- .../Bin/Recast.app/Contents/MacOS/Recast | Bin 463400 -> 463812 bytes .../Xcode/Recast.xcodeproj/memon.pbxuser | 1011 ++++++++++++++++- .../Recast.xcodeproj/memon.perspectivev3 | 91 +- RecastDemo/Include/Sample.h | 50 + RecastDemo/Include/Sample_StatMesh.h | 70 ++ RecastDemo/Include/Sample_StatMeshSimple.h | 63 + RecastDemo/Include/Sample_StatMeshTiled.h | 91 ++ RecastDemo/Include/Sample_TileMesh.h | 113 ++ RecastDemo/Source/Sample.cpp | 123 ++ RecastDemo/Source/Sample_StatMesh.cpp | 336 ++++++ RecastDemo/Source/Sample_StatMeshSimple.cpp | 499 ++++++++ RecastDemo/Source/Sample_StatMeshTiled.cpp | 973 ++++++++++++++++ RecastDemo/Source/Sample_TileMesh.cpp | 847 ++++++++++++++ 14 files changed, 4167 insertions(+), 108 deletions(-) create mode 100644 RecastDemo/Include/Sample.h create mode 100644 RecastDemo/Include/Sample_StatMesh.h create mode 100644 RecastDemo/Include/Sample_StatMeshSimple.h create mode 100644 RecastDemo/Include/Sample_StatMeshTiled.h create mode 100644 RecastDemo/Include/Sample_TileMesh.h create mode 100644 RecastDemo/Source/Sample.cpp create mode 100644 RecastDemo/Source/Sample_StatMesh.cpp create mode 100644 RecastDemo/Source/Sample_StatMeshSimple.cpp create mode 100644 RecastDemo/Source/Sample_StatMeshTiled.cpp create mode 100644 RecastDemo/Source/Sample_TileMesh.cpp diff --git a/Detour/Include/DetourStatNavMesh.h b/Detour/Include/DetourStatNavMesh.h index 500fca5..281c165 100755 --- a/Detour/Include/DetourStatNavMesh.h +++ b/Detour/Include/DetourStatNavMesh.h @@ -64,11 +64,11 @@ public: dtStatNavMesh(); ~dtStatNavMesh(); - // Initializes the path finder with path data. + // Initializes the navmesh with data. // Params: - // data - (in) Pointer to path data. - // dataSize - (in) size of the path data. - // ownsData - (in) Flag indicating if the pathfinder should delete the data. + // data - (in) Pointer to navmesh data. + // dataSize - (in) size of the navmesh data. + // ownsData - (in) Flag indicating if the navmesh should own and delete the data. bool init(unsigned char* data, int dataSize, bool ownsData); // Finds the nearest navigation polygon around the center location. diff --git a/RecastDemo/Bin/Recast.app/Contents/MacOS/Recast b/RecastDemo/Bin/Recast.app/Contents/MacOS/Recast index 01c0a3855dfe68eb92fd728d23e9b0b893d35409..7d1971822f68f1a47e0fa681d98f9c8a546c872b 100755 GIT binary patch delta 103556 zcmbrn33!di7e9Vyaw8E*?hO$U*#!|nLaYf1a&5H}O9idgT2x8wnh?S56-!@?eQ#@P zOQWKy1RU2&Sc?8=T{fj}d8oqS97^KrZ_TsvW24%OgxyOlU zuDb<<6Cqs3ln`$}(SMOsfZL7+To+J9r`r*#wDfB3cB%u{9Rt>>X39*j=H-`lL^f2X zTb6yq>p(fa<4dzi>-BT5Xy1v#OPr7oQCEd`C>#PMD%#v>Hs+#N%7r4XyW}6HesPcMqI1puVfl$+ zt~(ki_iNSUw~G|-@F@OGQFhz#%g!7eL+jP!r#dJ->K&^lD>O{;KwTc^P`RN`Zc#-o zlijGkhjWd}zd`mku9>+hu4a9hT25y5$a< zCp-SsYszmi19;7AN{dFpUV}@hC9|Td%9uv+ZaL^n;{EcflG7-(<=v~uqAj=aq-Ky3 z@zm23aPv|12w}v8sRQxM%r$2Qzp`3W4MdZ5RjJary4y4xi+8RniH(1A+iR=f^i?IY z$r}FcW#wd(O+I%oLD0^wmR80reZs*4P9j{pls&s?4Ms}(vRNvxa#0Cu9+I}?SFX$Y zL49Gh63%qw^7Ua-oTFw4rlt`jG0a^D8O_teTuE9o4D(^aABxghxhYZRxG?o!q@fz> z9(UeYYu^B15`{nI^>dI=4Q>Ce8X)lt96ERDiZzIji`!tP`>2EY}T1v#S&A zb6duzELrk-<-?ZM(&EchR9E8{eFbfHT|dcIo$3Ry8VY_?XJtHLXopg&t@j23dds!b zSQL4O$^O504(Y?1;$Wa;62AfjsUp_<+vVR*i7H_B1Bxt_gDGok>$Y=BaJ)|wMHYnH zDoCaZYzUL7f@Bz$K5P_$kFJlju~i42Q%1+vsM`b>>zmqu>o-vXRfP~8I)GXhXQyyK zryPi{!k?T~F2>j3C(kO@_;CLXXrZNYd*D#xbUNRXy62u%+9ZUx9SDT76|^S>y;(s+ zLX`q)BJU%SNhyA3m7NJSe4m^_dDxq>UOS`wkq}-r8->zj53lb_C^mhrI-}HYRb^NP zN-dRTQ^4DyCu_HLOK*VJzfV9Zp?<13&2{XwmOy(pbV3l%7cV}w5tw%+?V)~^shI( z6~Jg)GF5JCS3LriQG&0AIEoM*l)wOfO$iTA*#Z7(nyp?ZNQs2YY|T6l=<7o=hago* z3a!o42$TACjVxH7?jWOuc)m{nn;NE+0HkXFy8+K{L9&A zjtM_2x7vjAiyah~#CQ0=KPe*;`*Ynd%F)CH-d8bGJu&oiE|5sY&3cV253bpx2aMJ8xlNSo?I%B7jFxZc~>`;>N=&&<;(%^fH?`fjE|Ned+|D_T(WPeENc95^`WyO3HYaXw+ ze4Bt|spfibSDc16;xC&h&4>2nC$=dIhW_37^j2u35@|gjDU0bUyi>KJ8s@_?sZH)* zqJ-S&I(hUboQ?nYm94eY@z|P36vsJl2 z+>d*2Ra{2|L^>LPWx6Y-tt|ikPYh%UeYapKG@t;klKNepx<%d`tN@4o%o30j~BYxX6Ys z+@kzD!jG5RqWFw##y4+Px{vhdK3kLzM^@(6&Gd*;H$(MULSPAIDuCb8>QztwZ}8sD z%A!&Je3kb2STZd44&f#%2S4zQYAur*x2vVx>ilo8_h#jz4=VFlU&zPrUnpxo@aM`d7_rQ67Wa$`yX-IOsSeWIF4K$*Hi30$^86|_p8el8nu;dABdQ6W70bLGsa zaDL+R?3bgQSm!63&}X~byeu_>iJwWxt5V1i4N_bR>8U}i8Hb%qfh|8%I*qZoJ%<%& zenYu2#=F|ajWCr}0mCtRpzlmy`6L^U!=fi=Xt$_w}q7|K5exq{u!;g5^ zjY{LOWBIiW%I2}5{JRaxNj#QpP#%t*%6&H|qdyAePuDApKJww?*DG5-dW&CJr#$

@ue{ePC3<3YUY{QR74l#}XpkH56wir% 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

1a7G4{*(nC=S%C1o+_f2 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

TK?xrk+6uFX>^X+HD9^i^EJ#{|ASgV1z5) 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 N+HYK4FLyzw`aeHG?mGYg diff --git a/RecastDemo/Build/Xcode/Recast.xcodeproj/memon.pbxuser b/RecastDemo/Build/Xcode/Recast.xcodeproj/memon.pbxuser index 052167d..f91b9cb 100644 --- a/RecastDemo/Build/Xcode/Recast.xcodeproj/memon.pbxuser +++ b/RecastDemo/Build/Xcode/Recast.xcodeproj/memon.pbxuser @@ -16,7 +16,7 @@ 8D1107260486CEB800E47090 /* Recast */, ); breakpoints = ( - 6B0249B11003783F00CF7107 /* Sample_TileMesh.cpp:833 */, + 6B0249B11003783F00CF7107 /* Sample_TileMesh.cpp:784 */, 6B0249ED10037C0A00CF7107 /* DetourTileNavMesh.cpp:1 */, 6B0249EF10037C0C00CF7107 /* DetourTileNavMesh.cpp:1 */, 6B024AC01004AB3900CF7107 /* DetourTileNavMesh.cpp:1 */, @@ -610,6 +610,87 @@ 6B555F4E100B4C5800247EA3 /* PBXTextBookmark */ = 6B555F4E100B4C5800247EA3 /* PBXTextBookmark */; 6B555F4F100B4C5800247EA3 /* PBXTextBookmark */ = 6B555F4F100B4C5800247EA3 /* PBXTextBookmark */; 6B555F52100B4CE300247EA3 /* PBXTextBookmark */ = 6B555F52100B4CE300247EA3 /* PBXTextBookmark */; + 6B555F5C100B53F500247EA3 /* PBXTextBookmark */ = 6B555F5C100B53F500247EA3 /* PBXTextBookmark */; + 6B555F5D100B53F500247EA3 /* PBXTextBookmark */ = 6B555F5D100B53F500247EA3 /* PBXTextBookmark */; + 6B555F5E100B53F500247EA3 /* PBXTextBookmark */ = 6B555F5E100B53F500247EA3 /* PBXTextBookmark */; + 6B555F5F100B53F500247EA3 /* PBXTextBookmark */ = 6B555F5F100B53F500247EA3 /* PBXTextBookmark */; + 6B555F60100B53F500247EA3 /* PBXTextBookmark */ = 6B555F60100B53F500247EA3 /* PBXTextBookmark */; + 6B555F61100B53F500247EA3 /* PBXTextBookmark */ = 6B555F61100B53F500247EA3 /* PBXTextBookmark */; + 6B555F62100B53F500247EA3 /* PBXTextBookmark */ = 6B555F62100B53F500247EA3 /* PBXTextBookmark */; + 6B555F63100B53F500247EA3 /* PBXTextBookmark */ = 6B555F63100B53F500247EA3 /* PBXTextBookmark */; + 6B555F64100B53F500247EA3 /* PBXTextBookmark */ = 6B555F64100B53F500247EA3 /* PBXTextBookmark */; + 6B555F65100B53F500247EA3 /* PBXTextBookmark */ = 6B555F65100B53F500247EA3 /* PBXTextBookmark */; + 6B555F66100B53F500247EA3 /* PBXTextBookmark */ = 6B555F66100B53F500247EA3 /* PBXTextBookmark */; + 6B555F67100B53F500247EA3 /* PBXTextBookmark */ = 6B555F67100B53F500247EA3 /* PBXTextBookmark */; + 6B555F68100B53F500247EA3 /* PBXTextBookmark */ = 6B555F68100B53F500247EA3 /* PBXTextBookmark */; + 6B555F69100B53F500247EA3 /* PBXTextBookmark */ = 6B555F69100B53F500247EA3 /* PBXTextBookmark */; + 6B555F6A100B53F500247EA3 /* PBXTextBookmark */ = 6B555F6A100B53F500247EA3 /* PBXTextBookmark */; + 6B555F6B100B53F500247EA3 /* PBXTextBookmark */ = 6B555F6B100B53F500247EA3 /* PBXTextBookmark */; + 6B555F6C100B53F500247EA3 /* PBXTextBookmark */ = 6B555F6C100B53F500247EA3 /* PBXTextBookmark */; + 6B555F6D100B53F500247EA3 /* PBXTextBookmark */ = 6B555F6D100B53F500247EA3 /* PBXTextBookmark */; + 6B555F6E100B53F500247EA3 /* PBXTextBookmark */ = 6B555F6E100B53F500247EA3 /* PBXTextBookmark */; + 6B555F6F100B53F500247EA3 /* PBXTextBookmark */ = 6B555F6F100B53F500247EA3 /* PBXTextBookmark */; + 6B555F70100B53F500247EA3 /* PBXTextBookmark */ = 6B555F70100B53F500247EA3 /* PBXTextBookmark */; + 6B555F71100B53F500247EA3 /* PBXTextBookmark */ = 6B555F71100B53F500247EA3 /* PBXTextBookmark */; + 6B555F72100B53F500247EA3 /* PBXTextBookmark */ = 6B555F72100B53F500247EA3 /* PBXTextBookmark */; + 6B555F73100B53F500247EA3 /* PBXTextBookmark */ = 6B555F73100B53F500247EA3 /* PBXTextBookmark */; + 6B555F74100B541500247EA3 /* PBXTextBookmark */ = 6B555F74100B541500247EA3 /* PBXTextBookmark */; + 6B555F75100B541900247EA3 /* PBXTextBookmark */ = 6B555F75100B541900247EA3 /* PBXTextBookmark */; + 6B555F76100B541900247EA3 /* PBXTextBookmark */ = 6B555F76100B541900247EA3 /* PBXTextBookmark */; + 6B555F77100B54CE00247EA3 /* PBXTextBookmark */ = 6B555F77100B54CE00247EA3 /* PBXTextBookmark */; + 6B555F78100B54CE00247EA3 /* PBXTextBookmark */ = 6B555F78100B54CE00247EA3 /* PBXTextBookmark */; + 6B555F79100B54CE00247EA3 /* PBXTextBookmark */ = 6B555F79100B54CE00247EA3 /* PBXTextBookmark */; + 6B555F7A100B54CE00247EA3 /* PBXTextBookmark */ = 6B555F7A100B54CE00247EA3 /* PBXTextBookmark */; + 6B555F7B100B54CE00247EA3 /* PBXTextBookmark */ = 6B555F7B100B54CE00247EA3 /* PBXTextBookmark */; + 6B555F7C100B54CE00247EA3 /* PBXTextBookmark */ = 6B555F7C100B54CE00247EA3 /* PBXTextBookmark */; + 6B555F82100B557700247EA3 /* PBXTextBookmark */ = 6B555F82100B557700247EA3 /* PBXTextBookmark */; + 6B555F83100B557700247EA3 /* PBXTextBookmark */ = 6B555F83100B557700247EA3 /* PBXTextBookmark */; + 6B555F84100B557700247EA3 /* PBXTextBookmark */ = 6B555F84100B557700247EA3 /* PBXTextBookmark */; + 6B555F85100B557700247EA3 /* PBXTextBookmark */ = 6B555F85100B557700247EA3 /* PBXTextBookmark */; + 6B555F88100B577A00247EA3 /* PBXTextBookmark */ = 6B555F88100B577A00247EA3 /* PBXTextBookmark */; + 6B555F89100B577A00247EA3 /* PBXTextBookmark */ = 6B555F89100B577A00247EA3 /* PBXTextBookmark */; + 6B555F8A100B577A00247EA3 /* PBXTextBookmark */ = 6B555F8A100B577A00247EA3 /* PBXTextBookmark */; + 6B555F8B100B577A00247EA3 /* PBXTextBookmark */ = 6B555F8B100B577A00247EA3 /* PBXTextBookmark */; + 6B555F8C100B577A00247EA3 /* PBXTextBookmark */ = 6B555F8C100B577A00247EA3 /* PBXTextBookmark */; + 6B555F8D100B577A00247EA3 /* PBXTextBookmark */ = 6B555F8D100B577A00247EA3 /* PBXTextBookmark */; + 6B555F8E100B577A00247EA3 /* PBXTextBookmark */ = 6B555F8E100B577A00247EA3 /* PBXTextBookmark */; + 6B555F8F100B577A00247EA3 /* PBXTextBookmark */ = 6B555F8F100B577A00247EA3 /* PBXTextBookmark */; + 6B555F90100B577A00247EA3 /* PBXTextBookmark */ = 6B555F90100B577A00247EA3 /* PBXTextBookmark */; + 6B555F91100B577A00247EA3 /* PBXTextBookmark */ = 6B555F91100B577A00247EA3 /* PBXTextBookmark */; + 6B555F92100B577A00247EA3 /* PBXTextBookmark */ = 6B555F92100B577A00247EA3 /* PBXTextBookmark */; + 6B555F93100B577A00247EA3 /* PBXTextBookmark */ = 6B555F93100B577A00247EA3 /* PBXTextBookmark */; + 6B555F94100B577A00247EA3 /* PBXTextBookmark */ = 6B555F94100B577A00247EA3 /* PBXTextBookmark */; + 6B555F95100B577A00247EA3 /* PBXTextBookmark */ = 6B555F95100B577A00247EA3 /* PBXTextBookmark */; + 6B555F96100B577A00247EA3 /* PBXTextBookmark */ = 6B555F96100B577A00247EA3 /* PBXTextBookmark */; + 6B555F97100B577A00247EA3 /* PBXTextBookmark */ = 6B555F97100B577A00247EA3 /* PBXTextBookmark */; + 6B555F98100B577A00247EA3 /* PBXTextBookmark */ = 6B555F98100B577A00247EA3 /* PBXTextBookmark */; + 6B555F99100B577A00247EA3 /* PBXTextBookmark */ = 6B555F99100B577A00247EA3 /* PBXTextBookmark */; + 6B555F9A100B577A00247EA3 /* PBXTextBookmark */ = 6B555F9A100B577A00247EA3 /* PBXTextBookmark */; + 6B555F9B100B577A00247EA3 /* PBXTextBookmark */ = 6B555F9B100B577A00247EA3 /* PBXTextBookmark */; + 6B555F9E100B57B500247EA3 /* PBXTextBookmark */ = 6B555F9E100B57B500247EA3 /* PBXTextBookmark */; + 6B555F9F100B57B500247EA3 /* PBXTextBookmark */ = 6B555F9F100B57B500247EA3 /* PBXTextBookmark */; + 6B555FA0100B57B500247EA3 /* PBXTextBookmark */ = 6B555FA0100B57B500247EA3 /* PBXTextBookmark */; + 6B555FA1100B57B500247EA3 /* PBXTextBookmark */ = 6B555FA1100B57B500247EA3 /* PBXTextBookmark */; + 6B555FA2100B57CA00247EA3 /* PBXTextBookmark */ = 6B555FA2100B57CA00247EA3 /* PBXTextBookmark */; + 6B555FA3100B57CC00247EA3 /* PBXTextBookmark */ = 6B555FA3100B57CC00247EA3 /* PBXTextBookmark */; + 6B555FA4100B57CC00247EA3 /* PBXTextBookmark */ = 6B555FA4100B57CC00247EA3 /* PBXTextBookmark */; + 6B555FA8100B589200247EA3 /* PBXTextBookmark */ = 6B555FA8100B589200247EA3 /* PBXTextBookmark */; + 6B555FA9100B589200247EA3 /* PBXTextBookmark */ = 6B555FA9100B589200247EA3 /* PBXTextBookmark */; + 6B555FAA100B589200247EA3 /* PBXTextBookmark */ = 6B555FAA100B589200247EA3 /* PBXTextBookmark */; + 6B555FAB100B589200247EA3 /* PBXTextBookmark */ = 6B555FAB100B589200247EA3 /* PBXTextBookmark */; + 6B555FAC100B589200247EA3 /* PBXTextBookmark */ = 6B555FAC100B589200247EA3 /* PBXTextBookmark */; + 6B555FB0100B595C00247EA3 /* PBXTextBookmark */ = 6B555FB0100B595C00247EA3 /* PBXTextBookmark */; + 6B555FB1100B595C00247EA3 /* PBXTextBookmark */ = 6B555FB1100B595C00247EA3 /* PBXTextBookmark */; + 6B555FB2100B595C00247EA3 /* PBXTextBookmark */ = 6B555FB2100B595C00247EA3 /* PBXTextBookmark */; + 6B555FB3100B595C00247EA3 /* PBXTextBookmark */ = 6B555FB3100B595C00247EA3 /* PBXTextBookmark */; + 6B555FB4100B595C00247EA3 /* PBXTextBookmark */ = 6B555FB4100B595C00247EA3 /* PBXTextBookmark */; + 6B555FB5100B595C00247EA3 /* PBXTextBookmark */ = 6B555FB5100B595C00247EA3 /* PBXTextBookmark */; + 6B555FB6100B595C00247EA3 /* PBXTextBookmark */ = 6B555FB6100B595C00247EA3 /* PBXTextBookmark */; + 6B555FB7100B595C00247EA3 /* PBXTextBookmark */ = 6B555FB7100B595C00247EA3 /* PBXTextBookmark */; + 6B555FBA100B598E00247EA3 /* PBXTextBookmark */ = 6B555FBA100B598E00247EA3 /* PBXTextBookmark */; + 6B555FBD100B59B600247EA3 /* PBXTextBookmark */ = 6B555FBD100B59B600247EA3 /* PBXTextBookmark */; + 6B555FBF100B5A0000247EA3 /* PBXTextBookmark */ = 6B555FBF100B5A0000247EA3 /* PBXTextBookmark */; + 6B555FC0100B5A3300247EA3 /* PBXTextBookmark */ = 6B555FC0100B5A3300247EA3 /* PBXTextBookmark */; 6B7707B90FBD66CF00D21BAE = 6B7707B90FBD66CF00D21BAE /* PBXTextBookmark */; 6B7707F00FBD90F100D21BAE = 6B7707F00FBD90F100D21BAE /* PBXTextBookmark */; 6B7707F70FBD90F100D21BAE = 6B7707F70FBD90F100D21BAE /* PBXTextBookmark */; @@ -689,7 +770,7 @@ vrLen = 558; vrLoc = 362; }; - 6B0249B11003783F00CF7107 /* Sample_TileMesh.cpp:833 */ = { + 6B0249B11003783F00CF7107 /* Sample_TileMesh.cpp:784 */ = { isa = PBXFileBreakpoint; actions = ( ); @@ -701,7 +782,7 @@ functionName = "BuilderTiledMesh::toolRecalc()"; hitCount = 0; ignoreCount = 0; - lineNumber = 833; + lineNumber = 784; location = Recast; modificationTime = 268741438.169822; state = 1; @@ -782,7 +863,7 @@ fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; name = "DetourTiledNavMesh.h: 137"; rLen = 0; - rLoc = 5005; + rLoc = 9483; rType = 0; vrLen = 1459; vrLoc = 3572; @@ -1081,7 +1162,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTiledNavMesh.cpp: 1073"; rLen = 0; - rLoc = 27719; + rLoc = 12201; rType = 0; vrLen = 526; vrLoc = 26309; @@ -1161,7 +1242,7 @@ fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; name = "DetourStatNavMesh.h: 176"; rLen = 0; - rLoc = 7183; + rLoc = 7185; rType = 0; vrLen = 1247; vrLoc = 59; @@ -1171,7 +1252,7 @@ fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; name = "DetourTileNavMesh.h: 19"; rLen = 0; - rLoc = 938; + rLoc = 937; rType = 0; vrLen = 1075; vrLoc = 784; @@ -1546,16 +1627,16 @@ }; 6B25B6150FFA62BE004F1BC4 /* Sample_StatMesh.cpp */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {915, 5568}}"; - sepNavSelRange = "{162, 0}"; - sepNavVisRange = "{0, 498}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 5200}}"; + sepNavSelRange = "{7561, 0}"; + sepNavVisRange = "{7958, 728}"; }; }; 6B25B6160FFA62BE004F1BC4 /* Sample_StatMeshSimple.cpp */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {915, 8588}}"; - sepNavSelRange = "{143, 0}"; - sepNavVisRange = "{0, 513}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 8304}}"; + sepNavSelRange = "{4247, 49}"; + sepNavVisRange = "{3933, 719}"; }; }; 6B25B6180FFA62BE004F1BC4 /* main.cpp */ = { @@ -1585,16 +1666,16 @@ }; 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1223, 14524}}"; - sepNavSelRange = "{1062, 0}"; - sepNavVisRange = "{784, 548}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 14464}}"; + sepNavSelRange = "{14722, 0}"; + sepNavVisRange = "{14230, 802}"; }; }; 6B2AEC550FFB89E7005BE9CC /* Sample_StatMeshTiled.cpp */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {950, 15932}}"; - sepNavSelRange = "{143, 0}"; - sepNavVisRange = "{0, 527}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 15520}}"; + sepNavSelRange = "{5369, 89}"; + sepNavVisRange = "{4879, 630}"; }; }; 6B2AEC570FFB89F4005BE9CC /* Sample_StatMeshTiled.h */ = { @@ -1606,16 +1687,16 @@ }; 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {915, 3296}}"; - sepNavSelRange = "{2613, 0}"; - sepNavVisRange = "{6136, 1040}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 4960}}"; + sepNavSelRange = "{4189, 0}"; + sepNavVisRange = "{3741, 1240}"; }; }; 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {915, 20864}}"; - sepNavSelRange = "{24104, 0}"; - sepNavVisRange = "{23722, 893}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 21872}}"; + sepNavSelRange = "{11834, 0}"; + sepNavVisRange = "{11590, 460}"; }; }; 6B2AEC5C0FFB8AB0005BE9CC /* PBXTextBookmark */ = { @@ -2900,9 +2981,9 @@ }; 6B555DB0100B212E00247EA3 /* imguiRenderGL.cpp */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {915, 7024}}"; - sepNavSelRange = "{919, 0}"; - sepNavVisRange = "{0, 1274}"; + sepNavIntBoundsRect = "{{0, 0}, {1219, 7152}}"; + sepNavSelRange = "{8401, 0}"; + sepNavVisRange = "{7552, 396}"; }; }; 6B555DBD100B236A00247EA3 /* PBXTextBookmark */ = { @@ -3953,7 +4034,7 @@ fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; name = "Sample_TileMesh.cpp: 88"; rLen = 0; - rLoc = 2195; + rLoc = 2233; rType = 0; vrLen = 628; vrLoc = 2008; @@ -4091,7 +4172,7 @@ fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; name = "Sample_TileMesh.cpp: 88"; rLen = 0; - rLoc = 2195; + rLoc = 2233; rType = 0; vrLen = 628; vrLoc = 2008; @@ -4291,7 +4372,7 @@ fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; name = "Sample_TileMesh.cpp: 492"; rLen = 0; - rLoc = 13338; + rLoc = 11518; rType = 0; vrLen = 921; vrLoc = 12886; @@ -4371,7 +4452,7 @@ fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; name = "Sample_TileMesh.cpp: 492"; rLen = 0; - rLoc = 13338; + rLoc = 11518; rType = 0; vrLen = 921; vrLoc = 12886; @@ -4751,7 +4832,7 @@ fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; name = "Sample_TileMesh.cpp: 493"; rLen = 0; - rLoc = 13424; + rLoc = 11604; rType = 0; vrLen = 879; vrLoc = 12888; @@ -4881,7 +4962,7 @@ fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; name = "Sample_TileMesh.cpp: 493"; rLen = 0; - rLoc = 13424; + rLoc = 11604; rType = 0; vrLen = 879; vrLoc = 12888; @@ -5501,7 +5582,7 @@ fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; name = "DetourTileNavMesh.h: 70"; rLen = 0; - rLoc = 2716; + rLoc = 2714; rType = 0; vrLen = 620; vrLoc = 2419; @@ -5511,7 +5592,7 @@ fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; name = "DetourStatNavMesh.h: 179"; rLen = 95; - rLoc = 7147; + rLoc = 7149; rType = 0; vrLen = 1013; vrLoc = 6323; @@ -5619,7 +5700,7 @@ fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; name = "DetourTileNavMesh.h: 70"; rLen = 0; - rLoc = 2716; + rLoc = 2714; rType = 0; vrLen = 620; vrLoc = 2419; @@ -5649,7 +5730,7 @@ fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; name = "DetourStatNavMesh.h: 179"; rLen = 95; - rLoc = 7147; + rLoc = 7149; rType = 0; vrLen = 1013; vrLoc = 6323; @@ -5709,7 +5790,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 381"; rLen = 0; - rLoc = 10668; + rLoc = 10352; rType = 0; vrLen = 402; vrLoc = 9892; @@ -5767,7 +5848,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 426"; rLen = 0; - rLoc = 11437; + rLoc = 11589; rType = 0; vrLen = 444; vrLoc = 10615; @@ -5787,7 +5868,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 382"; rLen = 0; - rLoc = 10669; + rLoc = 10353; rType = 0; vrLen = 370; vrLoc = 9892; @@ -5807,7 +5888,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 381"; rLen = 0; - rLoc = 10668; + rLoc = 10352; rType = 0; vrLen = 402; vrLoc = 9892; @@ -5857,7 +5938,7 @@ fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; name = "DetourTileNavMesh.h: 61"; rLen = 0; - rLoc = 2613; + rLoc = 2611; rType = 0; vrLen = 1040; vrLoc = 6136; @@ -5867,7 +5948,7 @@ comments = "error: expected `;' before ')' token"; fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; rLen = 1; - rLoc = 283; + rLoc = 278; rType = 1; }; 6B555F45100B4C5800247EA3 /* PBXTextBookmark */ = { @@ -5885,7 +5966,7 @@ fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; name = "DetourTileNavMesh.h: 61"; rLen = 0; - rLoc = 2613; + rLoc = 2611; rType = 0; vrLen = 576; vrLoc = 2212; @@ -5895,7 +5976,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 376"; rLen = 0; - rLoc = 10580; + rLoc = 10262; rType = 0; vrLen = 693; vrLoc = 9654; @@ -5905,7 +5986,7 @@ fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; name = "DetourTileNavMesh.h: 61"; rLen = 0; - rLoc = 2613; + rLoc = 2611; rType = 0; vrLen = 781; vrLoc = 2058; @@ -5915,7 +5996,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 378"; rLen = 0; - rLoc = 10610; + rLoc = 10292; rType = 0; vrLen = 548; vrLoc = 9654; @@ -5934,8 +6015,8 @@ isa = PBXTextBookmark; fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 923"; - rLen = 222; - rLoc = 23882; + rLen = 0; + rLoc = 12201; rType = 0; vrLen = 926; vrLoc = 22891; @@ -5955,7 +6036,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 922"; rLen = 0; - rLoc = 23848; + rLoc = 12201; rType = 0; vrLen = 926; vrLoc = 22891; @@ -5965,7 +6046,7 @@ fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; name = "DetourTileNavMesh.h: 61"; rLen = 0; - rLoc = 2613; + rLoc = 2611; rType = 0; vrLen = 1040; vrLoc = 6136; @@ -5975,7 +6056,7 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 275"; rLen = 0; - rLoc = 7462; + rLoc = 7347; rType = 0; vrLen = 803; vrLoc = 7247; @@ -5985,11 +6066,815 @@ fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; name = "DetourTileNavMesh.cpp: 943"; rLen = 0; - rLoc = 24104; + rLoc = 12201; rType = 0; vrLen = 893; vrLoc = 23722; }; + 6B555F5C100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; + name = "DetourStatNavMesh.h: 67"; + rLen = 0; + rLoc = 1901; + rType = 0; + vrLen = 1236; + vrLoc = 1851; + }; + 6B555F5D100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 636"; + rLen = 0; + rLoc = 14069; + rType = 0; + vrLen = 712; + vrLoc = 16084; + }; + 6B555F5E100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E070F91113800904EEF /* DetourDebugDraw.cpp */; + name = "DetourDebugDraw.cpp: 394"; + rLen = 0; + rLoc = 10003; + rType = 0; + vrLen = 771; + vrLoc = 9621; + }; + 6B555F5F100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 241"; + rLen = 0; + rLoc = 9819; + rType = 0; + vrLen = 1166; + vrLoc = 9275; + }; + 6B555F60100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + comments = "error: 'getNeighbourTile' was not declared in this scope"; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + rLen = 1; + rLoc = 381; + rType = 1; + }; + 6B555F61100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 349"; + rLen = 0; + rLoc = 8059; + rType = 0; + vrLen = 869; + vrLoc = 8419; + }; + 6B555F62100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 114"; + rLen = 0; + rLoc = 4092; + rType = 0; + vrLen = 981; + vrLoc = 3430; + }; + 6B555F63100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 327"; + rLen = 0; + rLoc = 8926; + rType = 0; + vrLen = 776; + vrLoc = 8618; + }; + 6B555F64100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 116"; + rLen = 0; + rLoc = 4091; + rType = 0; + vrLen = 1052; + vrLoc = 3452; + }; + 6B555F65100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 329"; + rLen = 0; + rLoc = 9009; + rType = 0; + vrLen = 873; + vrLoc = 9008; + }; + 6B555F66100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 127"; + rLen = 0; + rLoc = 4729; + rType = 0; + vrLen = 1086; + vrLoc = 3462; + }; + 6B555F67100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 437"; + rLen = 0; + rLoc = 11982; + rType = 0; + vrLen = 616; + vrLoc = 10849; + }; + 6B555F68100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 127"; + rLen = 0; + rLoc = 4729; + rType = 0; + vrLen = 1086; + vrLoc = 3462; + }; + 6B555F69100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 434"; + rLen = 0; + rLoc = 11941; + rType = 0; + vrLen = 539; + vrLoc = 10824; + }; + 6B555F6A100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 128"; + rLen = 0; + rLoc = 4730; + rType = 0; + vrLen = 1113; + vrLoc = 3462; + }; + 6B555F6B100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 439"; + rLen = 0; + rLoc = 11982; + rType = 0; + vrLen = 581; + vrLoc = 10824; + }; + 6B555F6C100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 144"; + rLen = 0; + rLoc = 5258; + rType = 0; + vrLen = 1338; + vrLoc = 4292; + }; + 6B555F6D100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 92"; + rLen = 0; + rLoc = 3021; + rType = 0; + vrLen = 705; + vrLoc = 2528; + }; + 6B555F6E100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 636"; + rLen = 0; + rLoc = 14069; + rType = 0; + vrLen = 712; + vrLoc = 16084; + }; + 6B555F6F100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E070F91113800904EEF /* DetourDebugDraw.cpp */; + name = "DetourDebugDraw.cpp: 394"; + rLen = 0; + rLoc = 10003; + rType = 0; + vrLen = 771; + vrLoc = 9621; + }; + 6B555F70100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 143"; + rLen = 0; + rLoc = 5219; + rType = 0; + vrLen = 1302; + vrLoc = 4292; + }; + 6B555F71100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 1322"; + rLen = 0; + rLoc = 12201; + rType = 0; + vrLen = 686; + vrLoc = 33504; + }; + 6B555F72100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 241"; + rLen = 0; + rLoc = 9819; + rType = 0; + vrLen = 1166; + vrLoc = 9275; + }; + 6B555F73100B53F500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 370"; + rLen = 0; + rLoc = 10294; + rType = 0; + vrLen = 506; + vrLoc = 9725; + }; + 6B555F74100B541500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 369"; + rLen = 0; + rLoc = 10291; + rType = 0; + vrLen = 535; + vrLoc = 9788; + }; + 6B555F75100B541900247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B555DB0100B212E00247EA3 /* imguiRenderGL.cpp */; + name = "imguiRenderGL.cpp: 360"; + rLen = 0; + rLoc = 8401; + rType = 0; + vrLen = 396; + vrLoc = 7552; + }; + 6B555F76100B541900247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 369"; + rLen = 0; + rLoc = 10291; + rType = 0; + vrLen = 296; + vrLoc = 9863; + }; + 6B555F77100B54CE00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 368"; + rLen = 0; + rLoc = 10262; + rType = 0; + vrLen = 426; + vrLoc = 10066; + }; + 6B555F78100B54CE00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 259"; + rLen = 0; + rLoc = 10547; + rType = 0; + vrLen = 1206; + vrLoc = 9497; + }; + 6B555F79100B54CE00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 368"; + rLen = 0; + rLoc = 10262; + rType = 0; + vrLen = 426; + vrLoc = 10066; + }; + 6B555F7A100B54CE00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 259"; + rLen = 0; + rLoc = 10547; + rType = 0; + vrLen = 1206; + vrLoc = 9497; + }; + 6B555F7B100B54CE00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 368"; + rLen = 0; + rLoc = 10262; + rType = 0; + vrLen = 426; + vrLoc = 10066; + }; + 6B555F7C100B54CE00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 275"; + rLen = 0; + rLoc = 11211; + rType = 0; + vrLen = 1011; + vrLoc = 10070; + }; + 6B555F82100B557700247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 87"; + rLen = 0; + rLoc = 2997; + rType = 0; + vrLen = 680; + vrLoc = 2751; + }; + 6B555F83100B557700247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + comments = "error: 'encodeId' was not declared in this scope"; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + rLen = 0; + rLoc = 490; + rType = 1; + }; + 6B555F84100B557700247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 87"; + rLen = 0; + rLoc = 2997; + rType = 0; + vrLen = 680; + vrLoc = 2751; + }; + 6B555F85100B557700247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 441"; + rLen = 0; + rLoc = 12049; + rType = 0; + vrLen = 650; + vrLoc = 11119; + }; + 6B555F88100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E080F91113800904EEF /* DetourStatNavMesh.cpp */; + name = "DetourStatNavMesh.cpp: 46"; + rLen = 8; + rLoc = 1455; + rType = 0; + vrLen = 650; + vrLoc = 1708; + }; + 6B555F89100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; + name = "DetourStatNavMesh.h: 71"; + rLen = 0; + rLoc = 2105; + rType = 0; + vrLen = 1005; + vrLoc = 1851; + }; + 6B555F8A100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 631"; + rLen = 0; + rLoc = 13994; + rType = 0; + vrLen = 591; + vrLoc = 16086; + }; + 6B555F8B100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 124"; + rLen = 0; + rLoc = 4189; + rType = 0; + vrLen = 1240; + vrLoc = 3741; + }; + 6B555F8C100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 83"; + rLen = 0; + rLoc = 2739; + rType = 0; + vrLen = 551; + vrLoc = 2447; + }; + 6B555F8D100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 441"; + rLen = 0; + rLoc = 12049; + rType = 0; + vrLen = 650; + vrLoc = 11119; + }; + 6B555F8E100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; + name = "DetourStatNavMesh.h: 67"; + rLen = 0; + rLoc = 1901; + rType = 0; + vrLen = 1003; + vrLoc = 1851; + }; + 6B555F8F100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 441"; + rLen = 0; + rLoc = 12049; + rType = 0; + vrLen = 650; + vrLoc = 11119; + }; + 6B555F90100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 83"; + rLen = 0; + rLoc = 2892; + rType = 0; + vrLen = 418; + vrLoc = 2613; + }; + 6B555F91100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; + name = "DetourStatNavMesh.h: 71"; + rLen = 83; + rLoc = 2039; + rType = 0; + vrLen = 997; + vrLoc = 1851; + }; + 6B555F92100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E080F91113800904EEF /* DetourStatNavMesh.cpp */; + name = "DetourStatNavMesh.cpp: 706"; + rLen = 0; + rLoc = 17464; + rType = 0; + vrLen = 1261; + vrLoc = 17259; + }; + 6B555F93100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; + name = "DetourStatNavMesh.h: 73"; + rLen = 0; + rLoc = 2184; + rType = 0; + vrLen = 997; + vrLoc = 1851; + }; + 6B555F94100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E080F91113800904EEF /* DetourStatNavMesh.cpp */; + name = "DetourStatNavMesh.cpp: 46"; + rLen = 8; + rLoc = 1455; + rType = 0; + vrLen = 650; + vrLoc = 1708; + }; + 6B555F95100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 126"; + rLen = 8; + rLoc = 4301; + rType = 0; + vrLen = 1276; + vrLoc = 3705; + }; + 6B555F96100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */; + name = "DetourStatNavMesh.h: 71"; + rLen = 0; + rLoc = 2105; + rType = 0; + vrLen = 1005; + vrLoc = 1851; + }; + 6B555F97100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 124"; + rLen = 0; + rLoc = 4189; + rType = 0; + vrLen = 1240; + vrLoc = 3741; + }; + 6B555F98100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 83"; + rLen = 0; + rLoc = 2739; + rType = 0; + vrLen = 622; + vrLoc = 2376; + }; + 6B555F99100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 631"; + rLen = 0; + rLoc = 13994; + rType = 0; + vrLen = 591; + vrLoc = 16086; + }; + 6B555F9A100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC580FFB8A68005BE9CC /* DetourTileNavMesh.h */; + name = "DetourTileNavMesh.h: 124"; + rLen = 0; + rLoc = 4189; + rType = 0; + vrLen = 1240; + vrLoc = 3741; + }; + 6B555F9B100B577A00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 446"; + rLen = 0; + rLoc = 11713; + rType = 0; + vrLen = 483; + vrLoc = 11267; + }; + 6B555F9E100B57B500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 446"; + rLen = 0; + rLoc = 11713; + rType = 0; + vrLen = 483; + vrLoc = 11267; + }; + 6B555F9F100B57B500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + comments = "error: no matching function for call to 'dtTiledNavMesh::addTileAt(const int&, const int&, unsigned char*&, int&)'"; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + rLen = 1; + rLoc = 549; + rType = 1; + }; + 6B555FA0100B57B500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 446"; + rLen = 0; + rLoc = 11713; + rType = 0; + vrLen = 483; + vrLoc = 11267; + }; + 6B555FA1100B57B500247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 606"; + rLen = 0; + rLoc = 14034; + rType = 0; + vrLen = 627; + vrLoc = 15489; + }; + 6B555FA2100B57CA00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 608"; + rLen = 0; + rLoc = 14069; + rType = 0; + vrLen = 681; + vrLoc = 15489; + }; + 6B555FA3100B57CC00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 380"; + rLen = 0; + rLoc = 10291; + rType = 0; + vrLen = 534; + vrLoc = 9692; + }; + 6B555FA4100B57CC00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 608"; + rLen = 0; + rLoc = 14069; + rType = 0; + vrLen = 431; + vrLoc = 15559; + }; + 6B555FA8100B589200247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 469"; + rLen = 0; + rLoc = 11834; + rType = 0; + vrLen = 460; + vrLoc = 11590; + }; + 6B555FA9100B589200247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 608"; + rLen = 0; + rLoc = 14069; + rType = 0; + vrLen = 681; + vrLoc = 15489; + }; + 6B555FAA100B589200247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 608"; + rLen = 0; + rLoc = 14069; + rType = 0; + vrLen = 681; + vrLoc = 15489; + }; + 6B555FAB100B589200247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; + name = "DetourTileNavMesh.cpp: 469"; + rLen = 0; + rLoc = 11834; + rType = 0; + vrLen = 460; + vrLoc = 11590; + }; + 6B555FAC100B589200247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 605"; + rLen = 0; + rLoc = 13992; + rType = 0; + vrLen = 655; + vrLoc = 15489; + }; + 6B555FB0100B595C00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC550FFB89E7005BE9CC /* Sample_StatMeshTiled.cpp */; + name = "Sample_StatMeshTiled.cpp: 175"; + rLen = 89; + rLoc = 5369; + rType = 0; + vrLen = 630; + vrLoc = 4879; + }; + 6B555FB1100B595C00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B25B6150FFA62BE004F1BC4 /* Sample_StatMesh.cpp */; + name = "Sample_StatMesh.cpp: 303"; + rLen = 0; + rLoc = 7561; + rType = 0; + vrLen = 728; + vrLoc = 7958; + }; + 6B555FB2100B595C00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B25B6160FFA62BE004F1BC4 /* Sample_StatMeshSimple.cpp */; + name = "Sample_StatMeshSimple.cpp: 130"; + rLen = 49; + rLoc = 4247; + rType = 0; + vrLen = 719; + vrLoc = 3933; + }; + 6B555FB3100B595C00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 207"; + rLen = 0; + rLoc = 5427; + rType = 0; + vrLen = 600; + vrLoc = 4992; + }; + 6B555FB4100B595C00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 207"; + rLen = 0; + rLoc = 5427; + rType = 0; + vrLen = 621; + vrLoc = 4992; + }; + 6B555FB5100B595C00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B25B6150FFA62BE004F1BC4 /* Sample_StatMesh.cpp */; + name = "Sample_StatMesh.cpp: 303"; + rLen = 0; + rLoc = 7561; + rType = 0; + vrLen = 728; + vrLoc = 7958; + }; + 6B555FB6100B595C00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B25B6160FFA62BE004F1BC4 /* Sample_StatMeshSimple.cpp */; + name = "Sample_StatMeshSimple.cpp: 130"; + rLen = 49; + rLoc = 4247; + rType = 0; + vrLen = 719; + vrLoc = 3933; + }; + 6B555FB7100B595C00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 210"; + rLen = 0; + rLoc = 5423; + rType = 0; + vrLen = 582; + vrLoc = 4992; + }; + 6B555FBA100B598E00247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 210"; + rLen = 0; + rLoc = 5364; + rType = 0; + vrLen = 601; + vrLoc = 4992; + }; + 6B555FBD100B59B600247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 209"; + rLen = 0; + rLoc = 5285; + rType = 0; + vrLen = 594; + vrLoc = 4992; + }; + 6B555FBF100B5A0000247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 572"; + rLen = 0; + rLoc = 14636; + rType = 0; + vrLen = 719; + vrLoc = 14230; + }; + 6B555FC0100B5A3300247EA3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 6B2AEC520FFB8958005BE9CC /* Sample_TileMesh.cpp */; + name = "Sample_TileMesh.cpp: 575"; + rLen = 0; + rLoc = 14722; + rType = 0; + vrLen = 802; + vrLoc = 14230; + }; 6B7707B90FBD66CF00D21BAE /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 6B137C6D0F7FCBBB00459200 /* MeshLoaderObj.cpp */; @@ -6479,9 +7364,9 @@ }; 6BDD9E050F91112200904EEF /* DetourStatNavMesh.h */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {915, 3424}}"; - sepNavSelRange = "{7147, 95}"; - sepNavVisRange = "{6323, 1013}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 3488}}"; + sepNavSelRange = "{2105, 0}"; + sepNavVisRange = "{1851, 1005}"; }; }; 6BDD9E060F91112200904EEF /* DetourStatNavMeshBuilder.h */ = { @@ -6493,16 +7378,16 @@ }; 6BDD9E070F91113800904EEF /* DetourDebugDraw.cpp */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {915, 6752}}"; - sepNavSelRange = "{4897, 0}"; - sepNavVisRange = "{4634, 519}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 6568}}"; + sepNavSelRange = "{10003, 0}"; + sepNavVisRange = "{9621, 835}"; }; }; 6BDD9E080F91113800904EEF /* DetourStatNavMesh.cpp */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {915, 12688}}"; - sepNavSelRange = "{17533, 0}"; - sepNavVisRange = "{17287, 1201}"; + sepNavIntBoundsRect = "{{0, 0}, {915, 12400}}"; + sepNavSelRange = "{1455, 8}"; + sepNavVisRange = "{1708, 650}"; }; }; 6BDD9E090F91113800904EEF /* DetourStatNavMeshBuilder.cpp */ = { diff --git a/RecastDemo/Build/Xcode/Recast.xcodeproj/memon.perspectivev3 b/RecastDemo/Build/Xcode/Recast.xcodeproj/memon.perspectivev3 index 910458e..9a1016b 100644 --- a/RecastDemo/Build/Xcode/Recast.xcodeproj/memon.perspectivev3 +++ b/RecastDemo/Build/Xcode/Recast.xcodeproj/memon.perspectivev3 @@ -270,6 +270,7 @@ 080E96DDFE201D6D7F000001 6BDD9E030F91110C00904EEF 6B137C7D0F7FCBE800459200 + 6B555DF5100B25FC00247EA3 29B97315FDCFA39411CA2CEA 29B97317FDCFA39411CA2CEA 1C37FBAC04509CD000000102 @@ -278,14 +279,14 @@ PBXSmartGroupTreeModuleOutlineStateSelectionKey - 11 - 3 + 36 + 32 1 0 PBXSmartGroupTreeModuleOutlineStateVisibleRectKey - {{0, 0}, {282, 628}} + {{0, 104}, {282, 628}} PBXTopSmartGroupGIDs @@ -320,7 +321,7 @@ PBXProjectModuleGUID 6B8632A30F78115100E2684A PBXProjectModuleLabel - DetourTileNavMesh.cpp + Sample_TileMesh.cpp PBXSplitModuleInNavigatorKey Split0 @@ -328,11 +329,11 @@ PBXProjectModuleGUID 6B8632A40F78115100E2684A PBXProjectModuleLabel - DetourTileNavMesh.cpp + Sample_TileMesh.cpp _historyCapacity 0 bookmark - 6B555F52100B4CE300247EA3 + 6B555FC0100B5A3300247EA3 history 6BB87E0B0F9DE8A300E33F12 @@ -385,16 +386,16 @@ 6B555F11100B473F00247EA3 6B555F12100B473F00247EA3 6B555F13100B473F00247EA3 - 6B555F15100B473F00247EA3 - 6B555F2A100B499000247EA3 - 6B555F2B100B499000247EA3 - 6B555F2C100B499000247EA3 - 6B555F2D100B499000247EA3 - 6B555F40100B4C5800247EA3 - 6B555F41100B4C5800247EA3 6B555F42100B4C5800247EA3 - 6B555F43100B4C5800247EA3 - 6B555F44100B4C5800247EA3 + 6B555F5E100B53F500247EA3 + 6B555F88100B577A00247EA3 + 6B555F89100B577A00247EA3 + 6B555F8B100B577A00247EA3 + 6B555FA8100B589200247EA3 + 6B555FB0100B595C00247EA3 + 6B555FB1100B595C00247EA3 + 6B555FB2100B595C00247EA3 + 6B555FB3100B595C00247EA3 prevStack @@ -435,7 +436,6 @@ 6B0249051001EABD00CF7107 6B02498D1003751300CF7107 6B024A721004A2FE00CF7107 - 6B024BBB1005DF5700CF7107 6B024BCF1005DFAB00CF7107 6B024C041006098300CF7107 6B024C1310060C7600CF7107 @@ -445,7 +445,6 @@ 6B1186401006945C0018F96F 6B1186411006945C0018F96F 6B1186581006945C0018F96F - 6B1186591006945C0018F96F 6B118672100694C40018F96F 6B555D15100B125300247EA3 6B555D26100B136A00247EA3 @@ -465,7 +464,6 @@ 6B555D50100B175F00247EA3 6B555D53100B175F00247EA3 6B555D54100B175F00247EA3 - 6B555D55100B175F00247EA3 6B555D5D100B17DB00247EA3 6B555D5E100B17DB00247EA3 6B555D5F100B17DB00247EA3 @@ -482,7 +480,6 @@ 6B555D97100B1B6900247EA3 6B555D9E100B1C2400247EA3 6B555DA0100B1C2400247EA3 - 6B555DAB100B1E6500247EA3 6B555DC3100B236A00247EA3 6B555DC4100B236A00247EA3 6B555DC5100B236A00247EA3 @@ -519,7 +516,6 @@ 6B555E09100B285300247EA3 6B555E0A100B285300247EA3 6B555E0B100B285300247EA3 - 6B555E0C100B285300247EA3 6B555E0D100B285300247EA3 6B555E0E100B285300247EA3 6B555E0F100B285300247EA3 @@ -537,7 +533,6 @@ 6B555E47100B311B00247EA3 6B555E48100B311B00247EA3 6B555E49100B311B00247EA3 - 6B555E4A100B311B00247EA3 6B555E4B100B311B00247EA3 6B555E4C100B311B00247EA3 6B555E4D100B311B00247EA3 @@ -554,7 +549,6 @@ 6B555E67100B334900247EA3 6B555E68100B334900247EA3 6B555E69100B334900247EA3 - 6B555E6A100B334900247EA3 6B555E6B100B334900247EA3 6B555E6C100B334900247EA3 6B555E6D100B334900247EA3 @@ -581,7 +575,6 @@ 6B555EA9100B37AB00247EA3 6B555EAA100B37AB00247EA3 6B555EAB100B37AB00247EA3 - 6B555EAC100B37AB00247EA3 6B555EAF100B37AB00247EA3 6B555EB1100B37AB00247EA3 6B555EB2100B37AB00247EA3 @@ -600,29 +593,45 @@ 6B555F1D100B473F00247EA3 6B555F1E100B473F00247EA3 6B555F1F100B473F00247EA3 - 6B555F20100B473F00247EA3 6B555F21100B473F00247EA3 6B555F22100B473F00247EA3 6B555F23100B473F00247EA3 6B555F30100B499000247EA3 6B555F31100B499000247EA3 - 6B555F32100B499000247EA3 6B555F33100B499000247EA3 - 6B555F34100B499000247EA3 6B555F35100B499000247EA3 - 6B555F36100B499000247EA3 6B555F37100B499000247EA3 - 6B555F38100B499000247EA3 6B555F45100B4C5800247EA3 - 6B555F46100B4C5800247EA3 - 6B555F47100B4C5800247EA3 - 6B555F48100B4C5800247EA3 - 6B555F49100B4C5800247EA3 6B555F4A100B4C5800247EA3 - 6B555F4B100B4C5800247EA3 6B555F4C100B4C5800247EA3 - 6B555F4D100B4C5800247EA3 - 6B555F4E100B4C5800247EA3 + 6B555F63100B53F500247EA3 + 6B555F65100B53F500247EA3 + 6B555F67100B53F500247EA3 + 6B555F69100B53F500247EA3 + 6B555F6B100B53F500247EA3 + 6B555F6D100B53F500247EA3 + 6B555F6F100B53F500247EA3 + 6B555F71100B53F500247EA3 + 6B555F79100B54CE00247EA3 + 6B555F7B100B54CE00247EA3 + 6B555F8D100B577A00247EA3 + 6B555F8E100B577A00247EA3 + 6B555F8F100B577A00247EA3 + 6B555F90100B577A00247EA3 + 6B555F91100B577A00247EA3 + 6B555F92100B577A00247EA3 + 6B555F93100B577A00247EA3 + 6B555F94100B577A00247EA3 + 6B555F95100B577A00247EA3 + 6B555F96100B577A00247EA3 + 6B555F97100B577A00247EA3 + 6B555F98100B577A00247EA3 + 6B555F9A100B577A00247EA3 + 6B555FA0100B57B500247EA3 + 6B555FAB100B589200247EA3 + 6B555FB4100B595C00247EA3 + 6B555FB5100B595C00247EA3 + 6B555FB6100B595C00247EA3 SplitCount @@ -636,18 +645,18 @@ GeometryConfiguration Frame - {{0, 0}, {976, 443}} + {{0, 0}, {976, 462}} RubberWindowFrame 0 91 1280 687 0 0 1280 778 Module PBXNavigatorGroup Proportion - 443pt + 462pt Proportion - 198pt + 179pt Tabs @@ -661,7 +670,7 @@ GeometryConfiguration Frame - {{10, 27}, {976, -27}} + {{10, 27}, {976, 85}} Module XCDetailModule @@ -677,7 +686,7 @@ GeometryConfiguration Frame - {{10, 27}, {976, 244}} + {{10, 27}, {976, 196}} Module PBXProjectFindModule @@ -715,7 +724,7 @@ GeometryConfiguration Frame - {{10, 27}, {976, 171}} + {{10, 27}, {976, 152}} RubberWindowFrame 0 91 1280 687 0 0 1280 778 diff --git a/RecastDemo/Include/Sample.h b/RecastDemo/Include/Sample.h new file mode 100644 index 0000000..7706f8a --- /dev/null +++ b/RecastDemo/Include/Sample.h @@ -0,0 +1,50 @@ +#ifndef RECASTSAMPLE_H +#define RECASTSAMPLE_H + + +class Sample +{ +protected: + const float* m_verts; + int m_nverts; + const int* m_tris; + const float* m_trinorms; + int m_ntris; + float m_bmin[3], m_bmax[3]; + + float m_cellSize; + float m_cellHeight; + float m_agentHeight; + float m_agentRadius; + float m_agentMaxClimb; + float m_agentMaxSlope; + float m_regionMinSize; + float m_regionMergeSize; + float m_edgeMaxLen; + float m_edgeMaxError; + float m_vertsPerPoly; + +public: + Sample(); + virtual ~Sample(); + + virtual void handleSettings(); + virtual void handleTools(); + virtual void handleDebugMode(); + + virtual void setToolStartPos(const float* p); + virtual void setToolEndPos(const float* p); + + virtual void handleRender(); + virtual void handleRenderOverlay(double* proj, double* model, int* view); + virtual void handleMeshChanged(const float* verts, int nverts, + const int* tris, const float* trinorms, int ntris, + const float* bmin, const float* bmax); + virtual bool handleBuild(); + + void resetCommonSettings(); + void handleCommonSettings(); +}; + + +#endif // RECASTSAMPLE_H diff --git a/RecastDemo/Include/Sample_StatMesh.h b/RecastDemo/Include/Sample_StatMesh.h new file mode 100644 index 0000000..59e0c70 --- /dev/null +++ b/RecastDemo/Include/Sample_StatMesh.h @@ -0,0 +1,70 @@ +#ifndef RECASTSAMPLESTATMESH_H +#define RECASTSAMPLESTATMESH_H + +#include "Sample.h" +#include "DetourStatNavMesh.h" +#include "Recast.h" +#include "RecastLog.h" + +class Sample_StatMesh : public Sample +{ +protected: + + dtStatNavMesh* m_navMesh; + + enum ToolMode + { + TOOLMODE_PATHFIND, + TOOLMODE_RAYCAST, + TOOLMODE_DISTANCE_TO_WALL, + TOOLMODE_FIND_POLYS_AROUND, + }; + + ToolMode m_toolMode; + + static const int MAX_POLYS = 256; + + dtStatPolyRef m_startRef; + dtStatPolyRef m_endRef; + dtStatPolyRef m_polys[MAX_POLYS]; + dtStatPolyRef m_parent[MAX_POLYS]; + int m_npolys; + float m_straightPath[MAX_POLYS*3]; + int m_nstraightPath; + float m_polyPickExt[3]; + + float m_spos[3]; + float m_epos[3]; + float m_hitPos[3]; + float m_hitNormal[3]; + float m_distanceToWall; + bool m_sposSet; + bool m_eposSet; + + enum ToolRenderFlags + { + NAVMESH_POLYS = 0x01, + NAVMESH_BVTREE = 0x02, + NAVMESH_TOOLS = 0x04, + }; + + void toolCleanup(); + void toolReset(); + void toolRecalc(); + void toolRender(int flags); + void toolRenderOverlay(double* proj, double* model, int* view); + + void drawAgent(const float* pos, float r, float h, float c, const float* col); + + +public: + Sample_StatMesh(); + virtual ~Sample_StatMesh(); + + virtual void handleTools(); + virtual void setToolStartPos(const float* p); + virtual void setToolEndPos(const float* p); +}; + + +#endif // RECASTSAMPLESTATMESH_H diff --git a/RecastDemo/Include/Sample_StatMeshSimple.h b/RecastDemo/Include/Sample_StatMeshSimple.h new file mode 100644 index 0000000..c381df6 --- /dev/null +++ b/RecastDemo/Include/Sample_StatMeshSimple.h @@ -0,0 +1,63 @@ +#ifndef RECASTSAMPLESTATMESHSIMPLE_H +#define RECASTSAMPLESTATMESHSIMPLE_H + +#include "Sample_StatMesh.h" +#include "DetourStatNavMesh.h" +#include "Recast.h" +#include "RecastLog.h" + +class Sample_StatMeshSimple : public Sample_StatMesh +{ +protected: + + bool m_keepInterResults; + rcBuildTimes m_buildTimes; + + unsigned char* m_triflags; + rcHeightfield* m_solid; + rcCompactHeightfield* m_chf; + rcContourSet* m_cset; + rcPolyMesh* m_polyMesh; + rcConfig m_cfg; + + enum DrawMode + { + DRAWMODE_NAVMESH, + DRAWMODE_NAVMESH_TRANS, + DRAWMODE_NAVMESH_BVTREE, + DRAWMODE_NAVMESH_INVIS, + DRAWMODE_MESH, + DRAWMODE_VOXELS, + DRAWMODE_VOXELS_WALKABLE, + DRAWMODE_COMPACT, + DRAWMODE_COMPACT_DISTANCE, + DRAWMODE_COMPACT_REGIONS, + DRAWMODE_REGION_CONNECTIONS, + DRAWMODE_RAW_CONTOURS, + DRAWMODE_BOTH_CONTOURS, + DRAWMODE_CONTOURS, + DRAWMODE_POLYMESH, + MAX_DRAWMODE + }; + + DrawMode m_drawMode; + + void cleanup(); + +public: + Sample_StatMeshSimple(); + virtual ~Sample_StatMeshSimple(); + + virtual void handleSettings(); + virtual void handleDebugMode(); + + virtual void handleRender(); + virtual void handleRenderOverlay(double* proj, double* model, int* view); + virtual void handleMeshChanged(const float* verts, int nverts, + const int* tris, const float* trinorms, int ntris, + const float* bmin, const float* bmax); + virtual bool handleBuild(); +}; + + +#endif // RECASTSAMPLESTATMESHSIMPLE_H diff --git a/RecastDemo/Include/Sample_StatMeshTiled.h b/RecastDemo/Include/Sample_StatMeshTiled.h new file mode 100644 index 0000000..0db0bb0 --- /dev/null +++ b/RecastDemo/Include/Sample_StatMeshTiled.h @@ -0,0 +1,91 @@ +#ifndef RECASTSAMPLESTATMESHTILED_H +#define RECASTSAMPLESTATMESHTILED_H + +#include "Sample_StatMesh.h" +#include "DetourStatNavMesh.h" +#include "Recast.h" +#include "RecastLog.h" +#include "ChunkyTriMesh.h" + +class Sample_StatMeshTiled : public Sample_StatMesh +{ +protected: + + struct Tile + { + inline Tile() : chf(0), cset(0), solid(0), buildTime(0) {} + inline ~Tile() { delete chf; delete cset; delete solid; } + rcCompactHeightfield* chf; + rcHeightfield* solid; + rcContourSet* cset; + int buildTime; + }; + + struct TileSet + { + inline TileSet() : width(0), height(0), tiles(0) {} + inline ~TileSet() { delete [] tiles; } + int width, height; + float bmin[3], bmax[3]; + float cs, ch; + Tile* tiles; + }; + + bool m_measurePerTileTimings; + bool m_keepInterResults; + float m_tileSize; + rcBuildTimes m_buildTimes; + + rcChunkyTriMesh* m_chunkyMesh; + rcPolyMesh* m_polyMesh; + rcConfig m_cfg; + TileSet* m_tileSet; + + static const int MAX_STAT_BUCKETS = 1000; + int m_statPolysPerTile[MAX_STAT_BUCKETS]; + int m_statPolysPerTileSamples; + int m_statTimePerTile[MAX_STAT_BUCKETS]; + int m_statTimePerTileSamples; + + + enum DrawMode + { + DRAWMODE_NAVMESH, + DRAWMODE_NAVMESH_TRANS, + DRAWMODE_NAVMESH_BVTREE, + DRAWMODE_NAVMESH_INVIS, + DRAWMODE_MESH, + DRAWMODE_VOXELS, + DRAWMODE_VOXELS_WALKABLE, + DRAWMODE_COMPACT, + DRAWMODE_COMPACT_DISTANCE, + DRAWMODE_COMPACT_REGIONS, + DRAWMODE_REGION_CONNECTIONS, + DRAWMODE_RAW_CONTOURS, + DRAWMODE_BOTH_CONTOURS, + DRAWMODE_CONTOURS, + DRAWMODE_POLYMESH, + MAX_DRAWMODE + }; + + DrawMode m_drawMode; + + void cleanup(); + +public: + Sample_StatMeshTiled(); + virtual ~Sample_StatMeshTiled(); + + virtual void handleSettings(); + virtual void handleDebugMode(); + + virtual void handleRender(); + virtual void handleRenderOverlay(double* proj, double* model, int* view); + virtual void handleMeshChanged(const float* verts, int nverts, + const int* tris, const float* trinorms, int ntris, + const float* bmin, const float* bmax); + virtual bool handleBuild(); +}; + + +#endif // RECASTSAMPLESTATMESHTILED_H diff --git a/RecastDemo/Include/Sample_TileMesh.h b/RecastDemo/Include/Sample_TileMesh.h new file mode 100644 index 0000000..b957312 --- /dev/null +++ b/RecastDemo/Include/Sample_TileMesh.h @@ -0,0 +1,113 @@ +// +// Copyright (c) 2009 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef RECASTSAMPLETILEDMESH_H +#define RECASTSAMPLETILEDMESH_H + +#include "Sample.h" +#include "DetourTileNavMesh.h" +#include "Recast.h" +#include "RecastLog.h" +#include "ChunkyTriMesh.h" + +class Sample_TileMesh : public Sample +{ +protected: + + bool m_keepInterResults; + rcBuildTimes m_buildTimes; + + dtTiledNavMesh* m_navMesh; + rcChunkyTriMesh* m_chunkyMesh; + unsigned char* m_triflags; + rcHeightfield* m_solid; + rcCompactHeightfield* m_chf; + rcContourSet* m_cset; + rcPolyMesh* m_polyMesh; + rcConfig m_cfg; + + float m_tileSize; + + float m_spos[3]; + float m_epos[3]; + bool m_sposSet; + bool m_eposSet; + + float m_tileCol[4]; + float m_tileBmin[3]; + float m_tileBmax[3]; + float m_tileBuildTime; + float m_tileMemUsage; + int m_tileTriCount; + + enum ToolMode + { + TOOLMODE_CREATE_TILES, + TOOLMODE_PATHFIND, + TOOLMODE_RAYCAST, + TOOLMODE_DISTANCE_TO_WALL, + TOOLMODE_FIND_POLYS_AROUND, + }; + + dtTilePolyRef m_startRef; + dtTilePolyRef m_endRef; + float m_polyPickExt[3]; + + static const int MAX_POLYS = 256; + + dtTilePolyRef m_polys[MAX_POLYS]; + dtTilePolyRef m_parent[MAX_POLYS]; + int m_npolys; + float m_straightPath[MAX_POLYS*3]; + int m_nstraightPath; + float m_hitPos[3]; + float m_hitNormal[3]; + float m_distanceToWall; + + ToolMode m_toolMode; + + void toolRecalc(); + + void buildTile(const float* pos); + void removeTile(const float* pos); + + unsigned char* buildTileMesh(const float* bmin, const float* bmax, int& dataSize); + + void cleanup(); + +public: + Sample_TileMesh(); + virtual ~Sample_TileMesh(); + + virtual void handleSettings(); + virtual void handleTools(); + virtual void handleDebugMode(); + + virtual void setToolStartPos(const float* p); + virtual void setToolEndPos(const float* p); + + virtual void handleRender(); + virtual void handleRenderOverlay(double* proj, double* model, int* view); + virtual void handleMeshChanged(const float* verts, int nverts, + const int* tris, const float* trinorms, int ntris, + const float* bmin, const float* bmax); + virtual bool handleBuild(); +}; + + +#endif // RECASTBUILDERTILEDMESH_H diff --git a/RecastDemo/Source/Sample.cpp b/RecastDemo/Source/Sample.cpp new file mode 100644 index 0000000..1219547 --- /dev/null +++ b/RecastDemo/Source/Sample.cpp @@ -0,0 +1,123 @@ +#define _USE_MATH_DEFINES +#include +#include +#include "Sample.h" +#include "Recast.h" +#include "RecastDebugDraw.h" +#include "imgui.h" + +#ifdef WIN32 +# define snprintf _snprintf +#endif + + +Sample::Sample() : + m_verts(0), m_nverts(0), m_tris(0), m_trinorms(0), m_ntris(0) +{ + resetCommonSettings(); +} + +Sample::~Sample() +{ +} + +void Sample::handleSettings() +{ +} + +void Sample::handleTools() +{ +} + +void Sample::handleDebugMode() +{ +} + +void Sample::handleRender() +{ + if (!m_verts || !m_tris || !m_trinorms) + return; + // Draw mesh + rcDebugDrawMesh(m_verts, m_nverts, m_tris, m_trinorms, m_ntris, 0); + // Draw bounds + float col[4] = {1,1,1,0.5f}; + rcDebugDrawBoxWire(m_bmin[0],m_bmin[1],m_bmin[2], m_bmax[0],m_bmax[1],m_bmax[2], col); +} + +void Sample::handleRenderOverlay(double* proj, double* model, int* view) +{ +} + +void Sample::handleMeshChanged(const float* verts, int nverts, + const int* tris, const float* trinorms, int ntris, + const float* bmin, const float* bmax) +{ + m_verts = verts; + m_nverts = nverts; + m_tris = tris; + m_trinorms = trinorms; + m_ntris = ntris; + vcopy(m_bmin, bmin); + vcopy(m_bmax, bmax); +} + +void Sample::resetCommonSettings() +{ + m_cellSize = 0.3f; + m_cellHeight = 0.2f; + m_agentHeight = 2.0f; + m_agentRadius = 0.6f; + m_agentMaxClimb = 0.9f; + m_agentMaxSlope = 45.0f; + m_regionMinSize = 50; + m_regionMergeSize = 20; + m_edgeMaxLen = 12.0f; + m_edgeMaxError = 1.3f; + m_vertsPerPoly = 6.0f; +} + +void Sample::handleCommonSettings() +{ + imguiLabel("Rasterization"); + imguiSlider("Cell Size", &m_cellSize, 0.1f, 1.0f, 0.01f); + imguiSlider("Cell Height", &m_cellHeight, 0.1f, 1.0f, 0.01f); + + int gw = 0, gh = 0; + rcCalcGridSize(m_bmin, m_bmax, m_cellSize, &gw, &gh); + char text[64]; + snprintf(text, 64, "Voxels %d x %d", gw, gh); + imguiValue(text); + + imguiSeparator(); + imguiLabel("Agent"); + imguiSlider("Height", &m_agentHeight, 0.1f, 5.0f, 0.1f); + imguiSlider("Radius", &m_agentRadius, 0.0f, 5.0f, 0.1f); + imguiSlider("Max Climb", &m_agentMaxClimb, 0.1f, 5.0f, 0.1f); + imguiSlider("Max Slope", &m_agentMaxSlope, 0.0f, 90.0f, 1.0f); + + imguiSeparator(); + imguiLabel("Region"); + imguiSlider("Min Region Size", &m_regionMinSize, 0.0f, 150.0f, 1.0f); + imguiSlider("Merged Region Size", &m_regionMergeSize, 0.0f, 150.0f, 1.0f); + + imguiSeparator(); + imguiLabel("Polygonization"); + imguiSlider("Max Edge Length", &m_edgeMaxLen, 0.0f, 50.0f, 1.0f); + imguiSlider("Max Edge Error", &m_edgeMaxError, 0.1f, 3.0f, 0.1f); + imguiSlider("Verts Per Poly", &m_vertsPerPoly, 3.0f, 12.0f, 1.0f); + + imguiSeparator(); +} + +void Sample::setToolStartPos(const float* p) +{ +} + +void Sample::setToolEndPos(const float* p) +{ +} + +bool Sample::handleBuild() +{ + return true; +} diff --git a/RecastDemo/Source/Sample_StatMesh.cpp b/RecastDemo/Source/Sample_StatMesh.cpp new file mode 100644 index 0000000..4063389 --- /dev/null +++ b/RecastDemo/Source/Sample_StatMesh.cpp @@ -0,0 +1,336 @@ +#define _USE_MATH_DEFINES +#include +#include +#include +#include "SDL.h" +#include "SDL_Opengl.h" +#include "imgui.h" +#include "Sample.h" +#include "Sample_StatMesh.h" +#include "Recast.h" +#include "RecastTimer.h" +#include "RecastDebugDraw.h" +#include "DetourStatNavMesh.h" +#include "DetourStatNavMeshBuilder.h" +#include "DetourDebugDraw.h" + +#ifdef WIN32 +# define snprintf _snprintf +#endif + +Sample_StatMesh::Sample_StatMesh() : + m_navMesh(0), + m_toolMode(TOOLMODE_PATHFIND), + m_sposSet(false), + m_eposSet(false) +{ + toolReset(); + m_polyPickExt[0] = 2; + m_polyPickExt[1] = 4; + m_polyPickExt[2] = 2; +} + +Sample_StatMesh::~Sample_StatMesh() +{ + toolCleanup(); +} + +void Sample_StatMesh::handleTools() +{ + if (imguiCheck("Pathfind", m_toolMode == TOOLMODE_PATHFIND)) + { + m_toolMode = TOOLMODE_PATHFIND; + toolRecalc(); + } + if (imguiCheck("Distance to Wall", m_toolMode == TOOLMODE_DISTANCE_TO_WALL)) + { + m_toolMode = TOOLMODE_DISTANCE_TO_WALL; + toolRecalc(); + } + if (imguiCheck("Raycast", m_toolMode == TOOLMODE_RAYCAST)) + { + m_toolMode = TOOLMODE_RAYCAST; + toolRecalc(); + } + if (imguiCheck("Find Polys Around", m_toolMode == TOOLMODE_FIND_POLYS_AROUND)) + { + m_toolMode = TOOLMODE_FIND_POLYS_AROUND; + toolRecalc(); + } +} + +void Sample_StatMesh::setToolStartPos(const float* p) +{ + m_sposSet = true; + vcopy(m_spos, p); + toolRecalc(); +} + +void Sample_StatMesh::setToolEndPos(const float* p) +{ + m_eposSet = true; + vcopy(m_epos, p); + toolRecalc(); +} + +void Sample_StatMesh::toolCleanup() +{ + delete m_navMesh; + m_navMesh = 0; +} + +void Sample_StatMesh::toolReset() +{ + m_startRef = 0; + m_endRef = 0; + m_npolys = 0; + m_nstraightPath = 0; + memset(m_hitPos, 0, sizeof(m_hitPos)); + memset(m_hitNormal, 0, sizeof(m_hitNormal)); + m_distanceToWall = 0; +} + +void Sample_StatMesh::toolRecalc() +{ + if (!m_navMesh) + return; + + if (m_sposSet) + m_startRef = m_navMesh->findNearestPoly(m_spos, m_polyPickExt); + else + m_startRef = 0; + + if (m_eposSet) + m_endRef = m_navMesh->findNearestPoly(m_epos, m_polyPickExt); + else + m_endRef = 0; + + if (m_toolMode == TOOLMODE_PATHFIND) + { + if (m_sposSet && m_eposSet && m_startRef && m_endRef) + { + m_npolys = m_navMesh->findPath(m_startRef, m_endRef, m_polys, MAX_POLYS); + if (m_npolys) + m_nstraightPath = m_navMesh->findStraightPath(m_spos, m_epos, m_polys, m_npolys, m_straightPath, MAX_POLYS); + } + else + { + m_npolys = 0; + m_nstraightPath = 0; + } + } + else if (m_toolMode == TOOLMODE_RAYCAST) + { + m_nstraightPath = 0; + if (m_sposSet && m_eposSet && m_startRef) + { + float t = 0; + m_npolys = 0; + m_nstraightPath = 2; + m_straightPath[0] = m_spos[0]; + m_straightPath[1] = m_spos[1]; + m_straightPath[2] = m_spos[2]; + m_npolys = m_navMesh->raycast(m_startRef, m_spos, m_epos, t, m_polys, MAX_POLYS); + if (t < 1) + { + m_straightPath[3] = m_spos[0] + (m_epos[0] - m_spos[0]) * t; + m_straightPath[4] = m_spos[1] + (m_epos[1] - m_spos[1]) * t; + m_straightPath[5] = m_spos[2] + (m_epos[2] - m_spos[2]) * t; + } + else + { + m_straightPath[3] = m_epos[0]; + m_straightPath[4] = m_epos[1]; + m_straightPath[5] = m_epos[2]; + } + } + } + else if (m_toolMode == TOOLMODE_DISTANCE_TO_WALL) + { + m_distanceToWall = 0; + if (m_sposSet && m_startRef) + m_distanceToWall = m_navMesh->findDistanceToWall(m_startRef, m_spos, 100.0f, m_hitPos, m_hitNormal); + } + else if (m_toolMode == TOOLMODE_FIND_POLYS_AROUND) + { + if (m_sposSet && m_startRef && m_eposSet) + { + const float dx = m_epos[0] - m_spos[0]; + const float dz = m_epos[2] - m_spos[2]; + float dist = sqrtf(dx*dx + dz*dz); + m_npolys = m_navMesh->findPolysAround(m_startRef, m_spos, dist, m_polys, m_parent, 0, MAX_POLYS); + } + } +} + +static void getPolyCenter(dtStatNavMesh* navMesh, dtStatPolyRef ref, float* center) +{ + const dtStatPoly* p = navMesh->getPolyByRef(ref); + if (!p) return; + center[0] = 0; + center[1] = 0; + center[2] = 0; + for (int i = 0; i < (int)p->nv; ++i) + { + const float* v = navMesh->getVertex(p->v[i]); + center[0] += v[0]; + center[1] += v[1]; + center[2] += v[2]; + } + const float s = 1.0f / p->nv; + center[0] *= s; + center[1] *= s; + center[2] *= s; +} + +void Sample_StatMesh::toolRender(int flags) +{ + if (!m_navMesh) + return; + + static const float startCol[4] = { 0.5f, 0.1f, 0.0f, 0.75f }; + static const float endCol[4] = { 0.2f, 0.4f, 0.0f, 0.75f }; + static const float pathCol[4] = {0,0,0,0.25f}; + + glDepthMask(GL_FALSE); + + if (flags & NAVMESH_POLYS) + dtDebugDrawStatNavMesh(m_navMesh); + + if (flags & NAVMESH_BVTREE) + dtDebugDrawStatNavMeshBVTree(m_navMesh); + + if (flags & NAVMESH_TOOLS) + { + if (m_toolMode == TOOLMODE_PATHFIND) + { + dtDebugDrawStatNavMeshPoly(m_navMesh, m_startRef, startCol); + dtDebugDrawStatNavMeshPoly(m_navMesh, m_endRef, endCol); + + if (m_npolys) + { + for (int i = 1; i < m_npolys-1; ++i) + dtDebugDrawStatNavMeshPoly(m_navMesh, m_polys[i], pathCol); + } + if (m_nstraightPath) + { + glColor4ub(128,16,0,220); + glLineWidth(3.0f); + glBegin(GL_LINE_STRIP); + for (int i = 0; i < m_nstraightPath; ++i) + glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]); + glEnd(); + glLineWidth(1.0f); + glPointSize(4.0f); + glBegin(GL_POINTS); + for (int i = 0; i < m_nstraightPath; ++i) + glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]); + glEnd(); + glPointSize(1.0f); + } + } + else if (m_toolMode == TOOLMODE_RAYCAST) + { + dtDebugDrawStatNavMeshPoly(m_navMesh, m_startRef, startCol); + + if (m_nstraightPath) + { + for (int i = 1; i < m_npolys; ++i) + dtDebugDrawStatNavMeshPoly(m_navMesh, m_polys[i], pathCol); + + glColor4ub(128,16,0,220); + glLineWidth(3.0f); + glBegin(GL_LINE_STRIP); + for (int i = 0; i < m_nstraightPath; ++i) + glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]); + glEnd(); + glLineWidth(1.0f); + glPointSize(4.0f); + glBegin(GL_POINTS); + for (int i = 0; i < m_nstraightPath; ++i) + glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]); + glEnd(); + glPointSize(1.0f); + } + } + else if (m_toolMode == TOOLMODE_DISTANCE_TO_WALL) + { + dtDebugDrawStatNavMeshPoly(m_navMesh, m_startRef, startCol); + const float col[4] = {1,1,1,0.5f}; + rcDebugDrawCylinderWire(m_spos[0]-m_distanceToWall, m_spos[1]+0.02f, m_spos[2]-m_distanceToWall, + m_spos[0]+m_distanceToWall, m_spos[1]+m_agentHeight, m_spos[2]+m_distanceToWall, col); + glLineWidth(3.0f); + glColor4fv(col); + glBegin(GL_LINES); + glVertex3f(m_hitPos[0], m_hitPos[1] + 0.02f, m_hitPos[2]); + glVertex3f(m_hitPos[0], m_hitPos[1] + m_agentHeight, m_hitPos[2]); + glEnd(); + glLineWidth(1.0f); + } + else if (m_toolMode == TOOLMODE_FIND_POLYS_AROUND) + { + glLineWidth(2.0f); + for (int i = 0; i < m_npolys; ++i) + { + dtDebugDrawStatNavMeshPoly(m_navMesh, m_polys[i], pathCol); + if (m_parent[i]) + { + float p0[3], p1[3]; + getPolyCenter(m_navMesh, m_polys[i], p0); + getPolyCenter(m_navMesh, m_parent[i], p1); + glColor4ub(0,0,0,128); + rcDrawArc(p0, p1); + } + } + glLineWidth(1.0f); + + const float dx = m_epos[0] - m_spos[0]; + const float dz = m_epos[2] - m_spos[2]; + float dist = sqrtf(dx*dx + dz*dz); + const float col[4] = {1,1,1,0.5f}; + rcDebugDrawCylinderWire(m_spos[0]-dist, m_spos[1]+0.02f, m_spos[2]-dist, + m_spos[0]+dist, m_spos[1]+m_agentHeight, m_spos[2]+dist, col); + } + } + + glDepthMask(GL_TRUE); +} + +void Sample_StatMesh::toolRenderOverlay(double* proj, double* model, int* view) +{ + GLdouble x, y, z; + + // Draw start and end point labels + if (m_sposSet && gluProject((GLdouble)m_spos[0], (GLdouble)m_spos[1], (GLdouble)m_spos[2], + model, proj, view, &x, &y, &z)) + { + imguiDrawText((int)x, (int)(y-25), IMGUI_ALIGN_CENTER, "Start", imguiRGBA(0,0,0,220)); + } + if (m_eposSet && gluProject((GLdouble)m_epos[0], (GLdouble)m_epos[1], (GLdouble)m_epos[2], + model, proj, view, &x, &y, &z)) + { + imguiDrawText((int)x, (float)(y-25), IMGUI_ALIGN_CENTER, "End", imguiRGBA(0,0,0,220)); + } +} + +void Sample_StatMesh::drawAgent(const float* pos, float r, float h, float c, const float* col) +{ + glDepthMask(GL_FALSE); + + // Agent dimensions. + glLineWidth(2.0f); + rcDebugDrawCylinderWire(pos[0]-r, pos[1]+0.02f, pos[2]-r, pos[0]+r, pos[1]+h, pos[2]+r, col); + glLineWidth(1.0f); + + glColor4ub(0,0,0,196); + glBegin(GL_LINES); + glVertex3f(pos[0], pos[1]-c, pos[2]); + glVertex3f(pos[0], pos[1]+c, pos[2]); + glVertex3f(pos[0]-r/2, pos[1]+0.02f, pos[2]); + glVertex3f(pos[0]+r/2, pos[1]+0.02f, pos[2]); + glVertex3f(pos[0], pos[1]+0.02f, pos[2]-r/2); + glVertex3f(pos[0], pos[1]+0.02f, pos[2]+r/2); + glEnd(); + + glDepthMask(GL_TRUE); +} diff --git a/RecastDemo/Source/Sample_StatMeshSimple.cpp b/RecastDemo/Source/Sample_StatMeshSimple.cpp new file mode 100644 index 0000000..9decb0a --- /dev/null +++ b/RecastDemo/Source/Sample_StatMeshSimple.cpp @@ -0,0 +1,499 @@ +#define _USE_MATH_DEFINES +#include +#include +#include +#include "SDL.h" +#include "SDL_Opengl.h" +#include "imgui.h" +#include "Sample.h" +#include "Sample_StatMeshSimple.h" +#include "Recast.h" +#include "RecastTimer.h" +#include "RecastDebugDraw.h" +#include "DetourStatNavMesh.h" +#include "DetourStatNavMeshBuilder.h" +#include "DetourDebugDraw.h" + +#ifdef WIN32 +# define snprintf _snprintf +#endif + +Sample_StatMeshSimple::Sample_StatMeshSimple() : + m_keepInterResults(false), + m_triflags(0), + m_solid(0), + m_chf(0), + m_cset(0), + m_polyMesh(0), + m_drawMode(DRAWMODE_NAVMESH) +{ +} + +Sample_StatMeshSimple::~Sample_StatMeshSimple() +{ + cleanup(); +} + +void Sample_StatMeshSimple::cleanup() +{ + delete [] m_triflags; + m_triflags = 0; + delete m_solid; + m_solid = 0; + delete m_chf; + m_chf = 0; + delete m_cset; + m_cset = 0; + delete m_polyMesh; + m_polyMesh = 0; + toolCleanup(); +} + +void Sample_StatMeshSimple::handleSettings() +{ + Sample::handleCommonSettings(); + + if (imguiCheck("Keep Itermediate Results", m_keepInterResults)) + m_keepInterResults = !m_keepInterResults; + + imguiSeparator(); +} + +void Sample_StatMeshSimple::handleDebugMode() +{ + // Check which modes are valid. + bool valid[MAX_DRAWMODE]; + for (int i = 0; i < MAX_DRAWMODE; ++i) + valid[i] = false; + + if (m_verts && m_tris) + { + valid[DRAWMODE_NAVMESH] = m_navMesh != 0; + valid[DRAWMODE_NAVMESH_TRANS] = m_navMesh != 0; + valid[DRAWMODE_NAVMESH_BVTREE] = m_navMesh != 0; + valid[DRAWMODE_NAVMESH_INVIS] = m_navMesh != 0; + valid[DRAWMODE_MESH] = true; + valid[DRAWMODE_VOXELS] = m_solid != 0; + valid[DRAWMODE_VOXELS_WALKABLE] = m_solid != 0; + valid[DRAWMODE_COMPACT] = m_chf != 0; + valid[DRAWMODE_COMPACT_DISTANCE] = m_chf != 0; + valid[DRAWMODE_COMPACT_REGIONS] = m_chf != 0; + valid[DRAWMODE_REGION_CONNECTIONS] = m_cset != 0; + valid[DRAWMODE_RAW_CONTOURS] = m_cset != 0; + valid[DRAWMODE_BOTH_CONTOURS] = m_cset != 0; + valid[DRAWMODE_CONTOURS] = m_cset != 0; + valid[DRAWMODE_POLYMESH] = m_polyMesh != 0; + } + + int unavail = 0; + for (int i = 0; i < MAX_DRAWMODE; ++i) + if (!valid[i]) unavail++; + + if (unavail == MAX_DRAWMODE) + return; + + imguiLabel("Draw"); + if (imguiCheck("Input Mesh", m_drawMode == DRAWMODE_MESH, valid[DRAWMODE_MESH])) + m_drawMode = DRAWMODE_MESH; + if (imguiCheck("Navmesh", m_drawMode == DRAWMODE_NAVMESH, valid[DRAWMODE_NAVMESH])) + m_drawMode = DRAWMODE_NAVMESH; + if (imguiCheck("Navmesh Invis", m_drawMode == DRAWMODE_NAVMESH_INVIS, valid[DRAWMODE_NAVMESH_INVIS])) + m_drawMode = DRAWMODE_NAVMESH_INVIS; + if (imguiCheck("Navmesh Trans", m_drawMode == DRAWMODE_NAVMESH_TRANS, valid[DRAWMODE_NAVMESH_TRANS])) + m_drawMode = DRAWMODE_NAVMESH_TRANS; + if (imguiCheck("Navmesh BVTree", m_drawMode == DRAWMODE_NAVMESH_BVTREE, valid[DRAWMODE_NAVMESH_BVTREE])) + m_drawMode = DRAWMODE_NAVMESH_BVTREE; + if (imguiCheck("Voxels", m_drawMode == DRAWMODE_VOXELS, valid[DRAWMODE_VOXELS])) + m_drawMode = DRAWMODE_VOXELS; + if (imguiCheck("Walkable Voxels", m_drawMode == DRAWMODE_VOXELS_WALKABLE, valid[DRAWMODE_VOXELS_WALKABLE])) + m_drawMode = DRAWMODE_VOXELS_WALKABLE; + if (imguiCheck("Compact", m_drawMode == DRAWMODE_COMPACT, valid[DRAWMODE_COMPACT])) + m_drawMode = DRAWMODE_COMPACT; + if (imguiCheck("Compact Distance", m_drawMode == DRAWMODE_COMPACT_DISTANCE, valid[DRAWMODE_COMPACT_DISTANCE])) + m_drawMode = DRAWMODE_COMPACT_DISTANCE; + if (imguiCheck("Compact Regions", m_drawMode == DRAWMODE_COMPACT_REGIONS, valid[DRAWMODE_COMPACT_REGIONS])) + m_drawMode = DRAWMODE_COMPACT_REGIONS; + if (imguiCheck("Region Connections", m_drawMode == DRAWMODE_REGION_CONNECTIONS, valid[DRAWMODE_REGION_CONNECTIONS])) + m_drawMode = DRAWMODE_REGION_CONNECTIONS; + if (imguiCheck("Raw Contours", m_drawMode == DRAWMODE_RAW_CONTOURS, valid[DRAWMODE_RAW_CONTOURS])) + m_drawMode = DRAWMODE_RAW_CONTOURS; + if (imguiCheck("Both Contours", m_drawMode == DRAWMODE_BOTH_CONTOURS, valid[DRAWMODE_BOTH_CONTOURS])) + m_drawMode = DRAWMODE_BOTH_CONTOURS; + if (imguiCheck("Contours", m_drawMode == DRAWMODE_CONTOURS, valid[DRAWMODE_CONTOURS])) + m_drawMode = DRAWMODE_CONTOURS; + if (imguiCheck("Poly Mesh", m_drawMode == DRAWMODE_POLYMESH, valid[DRAWMODE_POLYMESH])) + m_drawMode = DRAWMODE_POLYMESH; + + if (unavail) + { + imguiValue("Tick 'Keep Itermediate Results'"); + imguiValue("to see more debug mode options."); + } +} + +void Sample_StatMeshSimple::handleRender() +{ + if (!m_verts || !m_tris || !m_trinorms) + return; + + float col[4]; + + glEnable(GL_FOG); + glDepthMask(GL_TRUE); + + if (m_drawMode == DRAWMODE_MESH) + { + // Draw mesh + rcDebugDrawMeshSlope(m_verts, m_nverts, m_tris, m_trinorms, m_ntris, m_agentMaxSlope); + } + else if (m_drawMode != DRAWMODE_NAVMESH_TRANS) + { + // Draw mesh + rcDebugDrawMesh(m_verts, m_nverts, m_tris, m_trinorms, m_ntris, 0); + } + + glDisable(GL_FOG); + glDepthMask(GL_FALSE); + + // Draw bounds + col[0] = 1; col[1] = 1; col[2] = 1; col[3] = 0.5f; + rcDebugDrawBoxWire(m_bmin[0],m_bmin[1],m_bmin[2], m_bmax[0],m_bmax[1],m_bmax[2], col); + + if (m_navMesh && + (m_drawMode == DRAWMODE_NAVMESH || + m_drawMode == DRAWMODE_NAVMESH_TRANS || + m_drawMode == DRAWMODE_NAVMESH_BVTREE || + m_drawMode == DRAWMODE_NAVMESH_INVIS)) + { + int flags = NAVMESH_TOOLS; + if (m_drawMode != DRAWMODE_NAVMESH_INVIS) + flags |= NAVMESH_POLYS; + if (m_drawMode == DRAWMODE_NAVMESH_BVTREE) + flags |= NAVMESH_BVTREE; + toolRender(flags); + } + + glDepthMask(GL_TRUE); + + if (m_chf && m_drawMode == DRAWMODE_COMPACT) + rcDebugDrawCompactHeightfieldSolid(*m_chf); + + if (m_chf && m_drawMode == DRAWMODE_COMPACT_DISTANCE) + rcDebugDrawCompactHeightfieldDistance(*m_chf); + if (m_chf && m_drawMode == DRAWMODE_COMPACT_REGIONS) + rcDebugDrawCompactHeightfieldRegions(*m_chf); + if (m_solid && m_drawMode == DRAWMODE_VOXELS) + { + glEnable(GL_FOG); + rcDebugDrawHeightfieldSolid(*m_solid); + glDisable(GL_FOG); + } + if (m_solid && m_drawMode == DRAWMODE_VOXELS_WALKABLE) + { + glEnable(GL_FOG); + rcDebugDrawHeightfieldWalkable(*m_solid); + glDisable(GL_FOG); + } + if (m_cset && m_drawMode == DRAWMODE_RAW_CONTOURS) + { + glDepthMask(GL_FALSE); + rcDebugDrawRawContours(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch); + glDepthMask(GL_TRUE); + } + if (m_cset && m_drawMode == DRAWMODE_BOTH_CONTOURS) + { + glDepthMask(GL_FALSE); + rcDebugDrawRawContours(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch, 0.5f); + rcDebugDrawContours(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch); + glDepthMask(GL_TRUE); + } + if (m_cset && m_drawMode == DRAWMODE_CONTOURS) + { + glDepthMask(GL_FALSE); + rcDebugDrawContours(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch); + glDepthMask(GL_TRUE); + } + if (m_chf && m_cset && m_drawMode == DRAWMODE_REGION_CONNECTIONS) + { + rcDebugDrawCompactHeightfieldRegions(*m_chf); + + glDepthMask(GL_FALSE); + rcDebugDrawRegionConnections(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch); + glDepthMask(GL_TRUE); + } + if (m_polyMesh && m_drawMode == DRAWMODE_POLYMESH) + { + glDepthMask(GL_FALSE); + rcDebugDrawPolyMesh(*m_polyMesh); + glDepthMask(GL_TRUE); + } + + static const float startCol[4] = { 0.5f, 0.1f, 0.0f, 0.75f }; + static const float endCol[4] = { 0.2f, 0.4f, 0.0f, 0.75f }; + if (m_sposSet) + drawAgent(m_spos, m_agentRadius, m_agentHeight, m_agentMaxClimb, startCol); + if (m_eposSet) + drawAgent(m_epos, m_agentRadius, m_agentHeight, m_agentMaxClimb, endCol); + +} + +void Sample_StatMeshSimple::handleRenderOverlay(double* proj, double* model, int* view) +{ + toolRenderOverlay(proj, model, view); +} + +void Sample_StatMeshSimple::handleMeshChanged(const float* verts, int nverts, + const int* tris, const float* trinorms, int ntris, + const float* bmin, const float* bmax) +{ + Sample::handleMeshChanged(verts, nverts, tris, trinorms, ntris, bmin, bmax); + toolCleanup(); + toolReset(); +} + +bool Sample_StatMeshSimple::handleBuild() +{ + if (!m_verts || ! m_tris) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified."); + return false; + } + + cleanup(); + toolCleanup(); + + // Init build configuration from GUI + memset(&m_cfg, 0, sizeof(m_cfg)); + m_cfg.cs = m_cellSize; + m_cfg.ch = m_cellHeight; + m_cfg.walkableSlopeAngle = m_agentMaxSlope; + m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch); + m_cfg.walkableClimb = (int)ceilf(m_agentMaxClimb / m_cfg.ch); + m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs); + m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize); + m_cfg.maxSimplificationError = m_edgeMaxError; + m_cfg.minRegionSize = (int)rcSqr(m_regionMinSize); + m_cfg.mergeRegionSize = (int)rcSqr(m_regionMergeSize); + m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly; + + // Set the area where the navigation will be build. + // Here the bounds of the input mesh are used, but the + // area could be specified by an user defined box, etc. + vcopy(m_cfg.bmin, m_bmin); + vcopy(m_cfg.bmax, m_bmax); + rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); + + // Reset build times gathering. + memset(&m_buildTimes, 0, sizeof(m_buildTimes)); + rcSetBuildTimes(&m_buildTimes); + + // Start the build process. + rcTimeVal totStartTime = rcGetPerformanceTimer(); + + if (rcGetLog()) + { + rcGetLog()->log(RC_LOG_PROGRESS, "Building navigation:"); + rcGetLog()->log(RC_LOG_PROGRESS, " - %d x %d cells", m_cfg.width, m_cfg.height); + rcGetLog()->log(RC_LOG_PROGRESS, " - %.1fK verts, %.1fK tris", m_nverts/1000.0f, m_ntris/1000.0f); + } + + // Allocate voxel heighfield where we rasterize our input data to. + m_solid = new rcHeightfield; + if (!m_solid) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'."); + return false; + } + if (!rcCreateHeightfield(*m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield."); + return false; + } + + // Allocate array that can hold triangle flags. + // If you have multiple meshes you need to process, allocate + // and array which can hold the max number of triangles you need to process. + m_triflags = new unsigned char[m_ntris]; + if (!m_triflags) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'triangleFlags' (%d).", m_ntris); + return false; + } + + + // Find triangles which are walkable based on their slope and rasterize them. + // If your input data is multiple meshes, you can transform them here, calculate + // the flags for each of the meshes and rasterize them. + memset(m_triflags, 0, m_ntris*sizeof(unsigned char)); + rcMarkWalkableTriangles(m_cfg.walkableSlopeAngle, m_verts, m_nverts, m_tris, m_ntris, m_triflags); + rcRasterizeTriangles(m_verts, m_nverts, m_tris, m_triflags, m_ntris, *m_solid); + + if (!m_keepInterResults) + { + delete [] m_triflags; + m_triflags = 0; + } + + // Once all geoemtry 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. + rcFilterLedgeSpans(m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid); + rcFilterWalkableLowHeightSpans(m_cfg.walkableHeight, *m_solid); + + // 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. + m_chf = new rcCompactHeightfield; + if (!m_chf) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'."); + return false; + } + if (!rcBuildCompactHeightfield(m_cfg.walkableHeight, m_cfg.walkableClimb, RC_WALKABLE, *m_solid, *m_chf)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data."); + return false; + } + + if (!m_keepInterResults) + { + delete m_solid; + m_solid = 0; + } + + // Prepare for region partitioning, by calculating distance field along the walkable surface. + if (!rcBuildDistanceField(*m_chf)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not build distance field."); + return false; + } + + // Partition the walkable surface into simple regions without holes. + if (!rcBuildRegions(*m_chf, m_cfg.walkableRadius, m_cfg.borderSize, m_cfg.minRegionSize, m_cfg.mergeRegionSize)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not build regions."); + } + + // Create contours. + m_cset = new rcContourSet; + if (!m_cset) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); + return false; + } + if (!rcBuildContours(*m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not create contours."); + return false; + } + + if (!m_keepInterResults) + { + delete m_chf; + m_chf = 0; + } + + // Build polygon navmesh from the contours. + m_polyMesh = new rcPolyMesh; + if (!m_polyMesh) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'polyMesh'."); + return false; + } + if (!rcBuildPolyMesh(*m_cset, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, m_cfg.maxVertsPerPoly, *m_polyMesh)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); + return false; + } + + if (!m_keepInterResults) + { + delete m_cset; + m_cset = 0; + } + + if (m_cfg.maxVertsPerPoly == DT_STAT_VERTS_PER_POLYGON) + { + unsigned char* navData = 0; + int navDataSize = 0; + if (!dtCreateNavMeshData(m_polyMesh->verts, m_polyMesh->nverts, + m_polyMesh->polys, m_polyMesh->npolys, m_polyMesh->nvp, + m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, &navData, &navDataSize)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "Could not build Detour navmesh."); + return false; + } + + m_navMesh = new dtStatNavMesh; + if (!m_navMesh) + { + delete [] navData; + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "Could not create Detour navmesh"); + return false; + } + + if (!m_navMesh->init(navData, navDataSize, true)) + { + delete [] navData; + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "Could not init Detour navmesh"); + return false; + } + } + + rcTimeVal totEndTime = rcGetPerformanceTimer(); + + // Show performance stats. + if (rcGetLog()) + { + const float pc = 100.0f / rcGetDeltaTimeUsec(totStartTime, totEndTime); + + rcGetLog()->log(RC_LOG_PROGRESS, "Rasterize: %.1fms (%.1f%%)", m_buildTimes.rasterizeTriangles/1000.0f, m_buildTimes.rasterizeTriangles*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Compact: %.1fms (%.1f%%)", m_buildTimes.buildCompact/1000.0f, m_buildTimes.buildCompact*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Border: %.1fms (%.1f%%)", m_buildTimes.filterBorder/1000.0f, m_buildTimes.filterBorder*pc); + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Walkable: %.1fms (%.1f%%)", m_buildTimes.filterWalkable/1000.0f, m_buildTimes.filterWalkable*pc); + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Reachable: %.1fms (%.1f%%)", m_buildTimes.filterMarkReachable/1000.0f, m_buildTimes.filterMarkReachable*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Distancefield: %.1fms (%.1f%%)", m_buildTimes.buildDistanceField/1000.0f, m_buildTimes.buildDistanceField*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - distance: %.1fms (%.1f%%)", m_buildTimes.buildDistanceFieldDist/1000.0f, m_buildTimes.buildDistanceFieldDist*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - blur: %.1fms (%.1f%%)", m_buildTimes.buildDistanceFieldBlur/1000.0f, m_buildTimes.buildDistanceFieldBlur*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Regions: %.1fms (%.1f%%)", m_buildTimes.buildRegions/1000.0f, m_buildTimes.buildRegions*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - watershed: %.1fms (%.1f%%)", m_buildTimes.buildRegionsReg/1000.0f, m_buildTimes.buildRegionsReg*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - expand: %.1fms (%.1f%%)", m_buildTimes.buildRegionsExp/1000.0f, m_buildTimes.buildRegionsExp*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - find catchment basins: %.1fms (%.1f%%)", m_buildTimes.buildRegionsFlood/1000.0f, m_buildTimes.buildRegionsFlood*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - filter: %.1fms (%.1f%%)", m_buildTimes.buildRegionsFilter/1000.0f, m_buildTimes.buildRegionsFilter*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Contours: %.1fms (%.1f%%)", m_buildTimes.buildContours/1000.0f, m_buildTimes.buildContours*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - trace: %.1fms (%.1f%%)", m_buildTimes.buildContoursTrace/1000.0f, m_buildTimes.buildContoursTrace*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - simplify: %.1fms (%.1f%%)", m_buildTimes.buildContoursSimplify/1000.0f, m_buildTimes.buildContoursSimplify*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Fixup contours: %.1fms (%.1f%%)", m_buildTimes.fixupContours/1000.0f, m_buildTimes.fixupContours*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh: %.1fms (%.1f%%)", m_buildTimes.buildPolymesh/1000.0f, m_buildTimes.buildPolymesh*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_polyMesh->nverts, m_polyMesh->npolys); + + rcGetLog()->log(RC_LOG_PROGRESS, "TOTAL: %.1fms", rcGetDeltaTimeUsec(totStartTime, totEndTime)/1000.0f); + } + + toolRecalc(); + + return true; +} diff --git a/RecastDemo/Source/Sample_StatMeshTiled.cpp b/RecastDemo/Source/Sample_StatMeshTiled.cpp new file mode 100644 index 0000000..c245a89 --- /dev/null +++ b/RecastDemo/Source/Sample_StatMeshTiled.cpp @@ -0,0 +1,973 @@ +#define _USE_MATH_DEFINES +#include +#include +#include +#include "SDL.h" +#include "SDL_Opengl.h" +#include "imgui.h" +#include "Sample.h" +#include "Sample_StatMeshTiled.h" +#include "Recast.h" +#include "RecastTimer.h" +#include "RecastDebugDraw.h" +#include "DetourStatNavMesh.h" +#include "DetourStatNavMeshBuilder.h" +#include "DetourDebugDraw.h" + +#ifdef WIN32 +# define snprintf _snprintf +#endif + +Sample_StatMeshTiled::Sample_StatMeshTiled() : + m_keepInterResults(false), + m_measurePerTileTimings(false), + m_tileSize(64), + m_chunkyMesh(0), + m_tileSet(0), + m_polyMesh(0), + m_drawMode(DRAWMODE_NAVMESH), + m_statTimePerTileSamples(0), + m_statPolysPerTileSamples(0) +{ +} + +Sample_StatMeshTiled::~Sample_StatMeshTiled() +{ + cleanup(); +} + +void Sample_StatMeshTiled::cleanup() +{ + delete m_chunkyMesh; + m_chunkyMesh = 0; + delete m_tileSet; + m_tileSet = 0; + delete m_polyMesh; + m_polyMesh = 0; + toolCleanup(); + m_statTimePerTileSamples = 0; + m_statPolysPerTileSamples = 0; +} + +void Sample_StatMeshTiled::handleSettings() +{ + Sample::handleCommonSettings(); + + imguiLabel("Tiling"); + imguiSlider("TileSize", &m_tileSize, 16.0f, 1024.0f, 16.0f); + + char text[64]; + int gw = 0, gh = 0; + rcCalcGridSize(m_bmin, m_bmax, m_cellSize, &gw, &gh); + const int ts = (int)m_tileSize; + const int tw = (gw + ts-1) / ts; + const int th = (gh + ts-1) / ts; + snprintf(text, 64, "Tiles %d x %d", tw, th); + imguiValue(text); + + imguiSeparator(); + if (imguiCheck("Keep Itermediate Results", m_keepInterResults)) + m_keepInterResults = !m_keepInterResults; + if (imguiCheck("Measure Per Tile Timings", m_measurePerTileTimings)) + m_measurePerTileTimings = !m_measurePerTileTimings; + + imguiSeparator(); +} + +void Sample_StatMeshTiled::handleDebugMode() +{ + // Check which modes are valid. + bool valid[MAX_DRAWMODE]; + for (int i = 0; i < MAX_DRAWMODE; ++i) + valid[i] = false; + + bool hasChf = false; + bool hasSolid = false; + bool hasCset = false; + if (m_tileSet) + { + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].solid) hasSolid = true; + if (m_tileSet->tiles[i].chf) hasChf = true; + if (m_tileSet->tiles[i].cset) hasCset = true; + } + } + + if (m_verts && m_tris) + { + valid[DRAWMODE_NAVMESH] = m_navMesh != 0; + valid[DRAWMODE_NAVMESH_TRANS] = m_navMesh != 0; + valid[DRAWMODE_NAVMESH_BVTREE] = m_navMesh != 0; + valid[DRAWMODE_NAVMESH_INVIS] = m_navMesh != 0; + valid[DRAWMODE_MESH] = true; + valid[DRAWMODE_VOXELS] = hasSolid; + valid[DRAWMODE_VOXELS_WALKABLE] = hasSolid; + valid[DRAWMODE_COMPACT] = hasChf; + valid[DRAWMODE_COMPACT_DISTANCE] = hasChf; + valid[DRAWMODE_COMPACT_REGIONS] = hasChf; + valid[DRAWMODE_REGION_CONNECTIONS] = hasCset; + valid[DRAWMODE_RAW_CONTOURS] = hasCset; + valid[DRAWMODE_BOTH_CONTOURS] = hasCset; + valid[DRAWMODE_CONTOURS] = hasCset; + valid[DRAWMODE_POLYMESH] = m_polyMesh != 0; + } + + int unavail = 0; + for (int i = 0; i < MAX_DRAWMODE; ++i) + if (!valid[i]) unavail++; + + if (unavail == MAX_DRAWMODE) + return; + + imguiLabel("Draw"); + if (imguiCheck("Input Mesh", m_drawMode == DRAWMODE_MESH, valid[DRAWMODE_MESH])) + m_drawMode = DRAWMODE_MESH; + if (imguiCheck("Navmesh", m_drawMode == DRAWMODE_NAVMESH, valid[DRAWMODE_NAVMESH])) + m_drawMode = DRAWMODE_NAVMESH; + if (imguiCheck("Navmesh Invis", m_drawMode == DRAWMODE_NAVMESH_INVIS, valid[DRAWMODE_NAVMESH_INVIS])) + m_drawMode = DRAWMODE_NAVMESH_INVIS; + if (imguiCheck("Navmesh Trans", m_drawMode == DRAWMODE_NAVMESH_TRANS, valid[DRAWMODE_NAVMESH_TRANS])) + m_drawMode = DRAWMODE_NAVMESH_TRANS; + if (imguiCheck("Navmesh BVTree", m_drawMode == DRAWMODE_NAVMESH_BVTREE, valid[DRAWMODE_NAVMESH_BVTREE])) + m_drawMode = DRAWMODE_NAVMESH_BVTREE; + if (imguiCheck("Voxels", m_drawMode == DRAWMODE_VOXELS, valid[DRAWMODE_VOXELS])) + m_drawMode = DRAWMODE_VOXELS; + if (imguiCheck("Walkable Voxels", m_drawMode == DRAWMODE_VOXELS_WALKABLE, valid[DRAWMODE_VOXELS_WALKABLE])) + m_drawMode = DRAWMODE_VOXELS_WALKABLE; + if (imguiCheck("Compact", m_drawMode == DRAWMODE_COMPACT, valid[DRAWMODE_COMPACT])) + m_drawMode = DRAWMODE_COMPACT; + if (imguiCheck("Compact Distance", m_drawMode == DRAWMODE_COMPACT_DISTANCE, valid[DRAWMODE_COMPACT_DISTANCE])) + m_drawMode = DRAWMODE_COMPACT_DISTANCE; + if (imguiCheck("Compact Regions", m_drawMode == DRAWMODE_COMPACT_REGIONS, valid[DRAWMODE_COMPACT_REGIONS])) + m_drawMode = DRAWMODE_COMPACT_REGIONS; + if (imguiCheck("Region Connections", m_drawMode == DRAWMODE_REGION_CONNECTIONS, valid[DRAWMODE_REGION_CONNECTIONS])) + m_drawMode = DRAWMODE_REGION_CONNECTIONS; + if (imguiCheck("Raw Contours", m_drawMode == DRAWMODE_RAW_CONTOURS, valid[DRAWMODE_RAW_CONTOURS])) + m_drawMode = DRAWMODE_RAW_CONTOURS; + if (imguiCheck("Both Contours", m_drawMode == DRAWMODE_BOTH_CONTOURS, valid[DRAWMODE_BOTH_CONTOURS])) + m_drawMode = DRAWMODE_BOTH_CONTOURS; + if (imguiCheck("Contours", m_drawMode == DRAWMODE_CONTOURS, valid[DRAWMODE_CONTOURS])) + m_drawMode = DRAWMODE_CONTOURS; + if (imguiCheck("Poly Mesh", m_drawMode == DRAWMODE_POLYMESH, valid[DRAWMODE_POLYMESH])) + m_drawMode = DRAWMODE_POLYMESH; + + if (unavail) + { + imguiValue("Tick 'Keep Itermediate Results'"); + imguiValue("to see more debug mode options."); + } +} + +void Sample_StatMeshTiled::handleRender() +{ + if (!m_verts || !m_tris || !m_trinorms) + return; + + float col[4]; + + glEnable(GL_FOG); + glDepthMask(GL_TRUE); + + if (m_drawMode == DRAWMODE_MESH) + { + // Draw mesh + rcDebugDrawMeshSlope(m_verts, m_nverts, m_tris, m_trinorms, m_ntris, m_agentMaxSlope); + } + else if (m_drawMode != DRAWMODE_NAVMESH_TRANS) + { + // Draw mesh + rcDebugDrawMesh(m_verts, m_nverts, m_tris, m_trinorms, m_ntris, 0); + } + + glDisable(GL_FOG); + glDepthMask(GL_FALSE); + + // Draw bounds + col[0] = 1; col[1] = 1; col[2] = 1; col[3] = 0.5f; + rcDebugDrawBoxWire(m_bmin[0],m_bmin[1],m_bmin[2], m_bmax[0],m_bmax[1],m_bmax[2], col); + + // Tiling grid. + const int ts = (int)m_tileSize; + int gw = 0, gh = 0; + rcCalcGridSize(m_bmin, m_bmax, m_cellSize, &gw, &gh); + int tw = (gw + ts-1) / ts; + int th = (gh + ts-1) / ts; + const float s = ts*m_cellSize; + glBegin(GL_LINES); + glColor4ub(0,0,0,64); + for (int y = 0; y < th; ++y) + { + for (int x = 0; x < tw; ++x) + { + float fx, fy, fz; + fx = m_bmin[0] + x*s; + fy = m_bmin[1]; + fz = m_bmin[2] + y*s; + + glVertex3f(fx,fy,fz); + glVertex3f(fx+s,fy,fz); + glVertex3f(fx,fy,fz); + glVertex3f(fx,fy,fz+s); + + if (x+1 >= tw) + { + glVertex3f(fx+s,fy,fz); + glVertex3f(fx+s,fy,fz+s); + } + if (y+1 >= th) + { + glVertex3f(fx,fy,fz+s); + glVertex3f(fx+s,fy,fz+s); + } + } + } + glEnd(); + + + if (m_navMesh && + (m_drawMode == DRAWMODE_NAVMESH || + m_drawMode == DRAWMODE_NAVMESH_TRANS || + m_drawMode == DRAWMODE_NAVMESH_BVTREE || + m_drawMode == DRAWMODE_NAVMESH_INVIS)) + { + int flags = NAVMESH_TOOLS; + if (m_drawMode != DRAWMODE_NAVMESH_INVIS) + flags |= NAVMESH_POLYS; + if (m_drawMode == DRAWMODE_NAVMESH_BVTREE) + flags |= NAVMESH_BVTREE; + toolRender(flags); + } + + glDepthMask(GL_TRUE); + + if (m_tileSet) + { + if (m_drawMode == DRAWMODE_COMPACT) + { + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].chf) + rcDebugDrawCompactHeightfieldSolid(*m_tileSet->tiles[i].chf); + } + } + + if (m_drawMode == DRAWMODE_COMPACT_DISTANCE) + { + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].chf) + rcDebugDrawCompactHeightfieldDistance(*m_tileSet->tiles[i].chf); + } + } + if (m_drawMode == DRAWMODE_COMPACT_REGIONS) + { + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].chf) + rcDebugDrawCompactHeightfieldRegions(*m_tileSet->tiles[i].chf); + } + } + + if (m_drawMode == DRAWMODE_VOXELS) + { + glEnable(GL_FOG); + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].solid) + rcDebugDrawHeightfieldSolid(*m_tileSet->tiles[i].solid); + } + glDisable(GL_FOG); + } + if (m_drawMode == DRAWMODE_VOXELS_WALKABLE) + { + glEnable(GL_FOG); + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].solid) + rcDebugDrawHeightfieldWalkable(*m_tileSet->tiles[i].solid); + } + glDisable(GL_FOG); + } + if (m_drawMode == DRAWMODE_RAW_CONTOURS) + { + glDepthMask(GL_FALSE); + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].cset) + rcDebugDrawRawContours(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch); + } + glDepthMask(GL_TRUE); + } + if (m_drawMode == DRAWMODE_BOTH_CONTOURS) + { + glDepthMask(GL_FALSE); + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].cset) + { + rcDebugDrawRawContours(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch, 0.5f); + rcDebugDrawContours(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch); + } + } + glDepthMask(GL_TRUE); + } + if (m_drawMode == DRAWMODE_CONTOURS) + { + glDepthMask(GL_FALSE); + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].cset) + rcDebugDrawContours(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch); + } + glDepthMask(GL_TRUE); + } + if (m_drawMode == DRAWMODE_REGION_CONNECTIONS) + { + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].chf) + rcDebugDrawCompactHeightfieldRegions(*m_tileSet->tiles[i].chf); + } + + glDepthMask(GL_FALSE); + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].cset) + rcDebugDrawRegionConnections(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch); + } + glDepthMask(GL_TRUE); + } + if (m_polyMesh && m_drawMode == DRAWMODE_POLYMESH) + { + glDepthMask(GL_FALSE); + rcDebugDrawPolyMesh(*m_polyMesh); + glDepthMask(GL_TRUE); + } + } + + static const float startCol[4] = { 0.5f, 0.1f, 0.0f, 0.75f }; + static const float endCol[4] = { 0.2f, 0.4f, 0.0f, 0.75f }; + if (m_sposSet) + drawAgent(m_spos, m_agentRadius, m_agentHeight, m_agentMaxClimb, startCol); + if (m_eposSet) + drawAgent(m_epos, m_agentRadius, m_agentHeight, m_agentMaxClimb, endCol); + +} + +static float nicenum(float x, int round) +{ + float expv = floorf(log10f(x)); + float f = x / powf(10.0f, expv); + float nf; + if (round) + { + if (f < 1.5f) nf = 1.0f; + else if (f < 3.0f) nf = 2.0f; + else if (f < 7.0f) nf = 5.0f; + else nf = 10.0f; + } + else + { + if (f <= 1.0f) nf = 1.0f; + else if (f <= 2.0f) nf = 2.0f; + else if (f <= 5.0f) nf = 5.0f; + else nf = 10.0f; + } + return nf*powf(10.0f, expv); +} + +static void drawLabels(int x, int y, int w, int h, + int nticks, float vmin, float vmax, const char* unit) +{ + char str[8], temp[32]; + + float range = nicenum(vmax-vmin, 0); + float d = nicenum(range/(float)(nticks-1), 1); + float graphmin = floorf(vmin/d)*d; + float graphmax = ceilf(vmax/d)*d; + int nfrac = (int)-floorf(log10f(d)); + if (nfrac < 0) nfrac = 0; + snprintf(str, 6, "%%.%df %%s", nfrac); + + for (float v = graphmin; v < graphmax+d/2; v += d) + { + float lx = x + (v-vmin)/(vmax-vmin)*w; + if (lx < 0 || lx > w) continue; + snprintf(temp, 20, str, v, unit); + imguiDrawText((int)lx+2, (int)y+2, IMGUI_ALIGN_LEFT, temp, imguiRGBA(255,255,255)); + glColor4ub(0,0,0,64); + glBegin(GL_LINES); + glVertex2f(lx,(float)y); + glVertex2f(lx,(float)(y+h)); + glEnd(); + } +} + +static void drawGraph(const char* name, int x, int y, int w, int h, float sd, + const int* samples, int n, int nsamples, const char* unit) +{ + char text[64]; + int first, last, maxval; + first = 0; + last = n-1; + while (first < n && samples[first] == 0) + first++; + while (last >= 0 && samples[last] == 0) + last--; + if (first == last) + return; + maxval = 1; + for (int i = first; i <= last; ++i) + { + if (samples[i] > maxval) + maxval = samples[i]; + } + const float sx = (float)w / (float)(last-first); + const float sy = (float)h / (float)maxval; + + glBegin(GL_QUADS); + glColor4ub(32,32,32,64); + glVertex2i(x,y); + glVertex2i(x+w,y); + glVertex2i(x+w,y+h); + glVertex2i(x,y+h); + glEnd(); + + glColor4ub(255,255,255,64); + glBegin(GL_LINES); + for (int i = 0; i <= 4; ++i) + { + int yy = y+i*h/4; + glVertex2i(x,yy); + glVertex2i(x+w,yy); + } + glEnd(); + + glColor4ub(0,196,255,255); + glBegin(GL_LINE_STRIP); + for (int i = first; i <= last; ++i) + { + float fx = x + (i-first)*sx; + float fy = y + samples[i]*sy; + glVertex2f(fx,fy); + } + glEnd(); + + snprintf(text,64,"%d", maxval); + imguiDrawText((int)x+w-2, (int)y+h-20, IMGUI_ALIGN_RIGHT, text, imguiRGBA(0,0,0)); + imguiDrawText((int)x+2, (int)y+h-20, IMGUI_ALIGN_LEFT, name, imguiRGBA(255,255,255)); + + drawLabels(x, y, w, h, 10, first*sd, last*sd, unit); +} + +void Sample_StatMeshTiled::handleRenderOverlay(double* proj, double* model, int* view) +{ + toolRenderOverlay(proj, model, view); + + if (m_measurePerTileTimings) + { + if (m_statTimePerTileSamples) + drawGraph("Build Time/Tile", 10, 10, 500, 100, 1.0f, m_statTimePerTile, MAX_STAT_BUCKETS, m_statTimePerTileSamples, "ms"); + + if (m_statPolysPerTileSamples) + drawGraph("Polygons/Tile", 10, 120, 500, 100, 1.0f, m_statPolysPerTile, MAX_STAT_BUCKETS, m_statPolysPerTileSamples, ""); + + int validTiles = 0; + if (m_tileSet) + { + for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i) + { + if (m_tileSet->tiles[i].buildTime > 0) + validTiles++; + } + } + char text[64]; + snprintf(text,64,"Tiles %d\n", validTiles); + imguiDrawText(10, 240, IMGUI_ALIGN_LEFT, text, imguiRGBA(255,255,255)); + } +} + +void Sample_StatMeshTiled::handleMeshChanged(const float* verts, int nverts, + const int* tris, const float* trinorms, int ntris, + const float* bmin, const float* bmax) +{ + Sample::handleMeshChanged(verts, nverts, tris, trinorms, ntris, bmin, bmax); + toolCleanup(); + toolReset(); + m_statTimePerTileSamples = 0; + m_statPolysPerTileSamples = 0; +} + +bool Sample_StatMeshTiled::handleBuild() +{ + if (!m_verts || ! m_tris) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified."); + return false; + } + + if (m_measurePerTileTimings) + { + memset(m_statPolysPerTile, 0, sizeof(m_statPolysPerTile)); + memset(m_statTimePerTile, 0, sizeof(m_statTimePerTile)); + m_statPolysPerTileSamples = 0; + m_statTimePerTileSamples = 0; + } + + cleanup(); + toolCleanup(); + toolReset(); + + // Init build configuration from GUI + memset(&m_cfg, 0, sizeof(m_cfg)); + m_cfg.cs = m_cellSize; + m_cfg.ch = m_cellHeight; + m_cfg.walkableSlopeAngle = m_agentMaxSlope; + m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch); + m_cfg.walkableClimb = (int)ceilf(m_agentMaxClimb / m_cfg.ch); + m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs); + m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize); + m_cfg.maxSimplificationError = m_edgeMaxError; + m_cfg.minRegionSize = (int)rcSqr(m_regionMinSize); + m_cfg.mergeRegionSize = (int)rcSqr(m_regionMergeSize); + m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly; + m_cfg.tileSize = (int)m_tileSize; + m_cfg.borderSize = m_cfg.walkableRadius*2 + 2; // Reserve enough padding. + + // Set the area where the navigation will be build. + // Here the bounds of the input mesh are used, but the + // area could be specified by an user defined box, etc. + vcopy(m_cfg.bmin, m_bmin); + vcopy(m_cfg.bmax, m_bmax); + rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); + + // Reset build times gathering. + memset(&m_buildTimes, 0, sizeof(m_buildTimes)); + rcSetBuildTimes(&m_buildTimes); + + // Start the build process. + rcTimeVal totStartTime = rcGetPerformanceTimer(); + + // Calculate the number of tiles in the output and initialize tiles. + m_tileSet = new TileSet; + if (!m_tileSet) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'tileSet'."); + return false; + } + vcopy(m_tileSet->bmin, m_cfg.bmin); + vcopy(m_tileSet->bmax, m_cfg.bmax); + m_tileSet->cs = m_cfg.cs; + m_tileSet->ch = m_cfg.ch; + m_tileSet->width = (m_cfg.width + m_cfg.tileSize-1) / m_cfg.tileSize; + m_tileSet->height = (m_cfg.height + m_cfg.tileSize-1) / m_cfg.tileSize; + m_tileSet->tiles = new Tile[m_tileSet->height * m_tileSet->width]; + if (!m_tileSet->tiles) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'tileSet->tiles' (%d).", m_tileSet->height * m_tileSet->width); + return false; + } + + // Build chunky trimesh for local polygon queries. + rcTimeVal chunkyStartTime = rcGetPerformanceTimer(); + m_chunkyMesh = new rcChunkyTriMesh; + if (!m_chunkyMesh) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'm_chunkyMesh'."); + return false; + } + if (!rcCreateChunkyTriMesh(m_verts, m_tris, m_ntris, 256, m_chunkyMesh)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Could not build chunky mesh."); + return false; + } + rcTimeVal chunkyEndTime = rcGetPerformanceTimer(); + + if (rcGetLog()) + { + rcGetLog()->log(RC_LOG_PROGRESS, "Building navigation:"); + rcGetLog()->log(RC_LOG_PROGRESS, " - %d x %d cells", m_cfg.width, m_cfg.height); + rcGetLog()->log(RC_LOG_PROGRESS, " - %d x %d tiles", m_tileSet->width, m_tileSet->height); + rcGetLog()->log(RC_LOG_PROGRESS, " - %.1f verts, %.1f tris", m_nverts/1000.0f, m_ntris/1000.0f); + } + + // Initialize per tile config. + rcConfig tileCfg; + memcpy(&tileCfg, &m_cfg, sizeof(rcConfig)); + tileCfg.width = m_cfg.tileSize + m_cfg.borderSize*2; + tileCfg.height = m_cfg.tileSize + m_cfg.borderSize*2; + + // Allocate array that can hold triangle flags for all geom chunks. + unsigned char* triangleFlags = new unsigned char[m_chunkyMesh->maxTrisPerChunk]; + if (!triangleFlags) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'triangleFlags' (%d).", m_chunkyMesh->maxTrisPerChunk); + return false; + } + + rcHeightfield* solid = 0; + rcCompactHeightfield* chf = 0; + rcContourSet* cset = 0; + + for (int y = 0; y < m_tileSet->height; ++y) + { + for (int x = 0; x < m_tileSet->width; ++x) + { + rcTimeVal startTime = rcGetPerformanceTimer(); + + Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width]; + + // Calculate the per tile bounding box. + tileCfg.bmin[0] = m_cfg.bmin[0] + (x*m_cfg.tileSize - m_cfg.borderSize)*m_cfg.cs; + tileCfg.bmin[2] = m_cfg.bmin[2] + (y*m_cfg.tileSize - m_cfg.borderSize)*m_cfg.cs; + tileCfg.bmax[0] = m_cfg.bmin[0] + ((x+1)*m_cfg.tileSize + m_cfg.borderSize)*m_cfg.cs; + tileCfg.bmax[2] = m_cfg.bmin[2] + ((y+1)*m_cfg.tileSize + m_cfg.borderSize)*m_cfg.cs; + + delete solid; + delete chf; + solid = 0; + chf = 0; + + float tbmin[2], tbmax[2]; + tbmin[0] = tileCfg.bmin[0]; + tbmin[1] = tileCfg.bmin[2]; + tbmax[0] = tileCfg.bmax[0]; + tbmax[1] = tileCfg.bmax[2]; + int cid[256];// TODO: Make grow when returning too many items. + const int ncid = rcGetChunksInRect(m_chunkyMesh, tbmin, tbmax, cid, 256); + if (!ncid) + continue; + + solid = new rcHeightfield; + if (!solid) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Out of memory 'solid'.", x, y); + continue; + } + if (!rcCreateHeightfield(*solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not create solid heightfield.", x, y); + continue; + } + + for (int i = 0; i < ncid; ++i) + { + const rcChunkyTriMeshNode& node = m_chunkyMesh->nodes[cid[i]]; + const int* tris = &m_chunkyMesh->tris[node.i*3]; + const int ntris = node.n; + + memset(triangleFlags, 0, ntris*sizeof(unsigned char)); + rcMarkWalkableTriangles(tileCfg.walkableSlopeAngle, + m_verts, m_nverts, tris, ntris, triangleFlags); + + rcRasterizeTriangles(m_verts, m_nverts, tris, triangleFlags, ntris, *solid); + } + + rcFilterLedgeSpans(tileCfg.walkableHeight, tileCfg.walkableClimb, *solid); + + rcFilterWalkableLowHeightSpans(tileCfg.walkableHeight, *solid); + + chf = new rcCompactHeightfield; + if (!chf) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Out of memory 'chf'.", x, y); + continue; + } + if (!rcBuildCompactHeightfield(tileCfg.walkableHeight, tileCfg.walkableClimb, + RC_WALKABLE, *solid, *chf)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not build compact data.", x, y); + continue; + } + + if (!rcBuildDistanceField(*chf)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not build distance fields.", x, y); + continue; + } + + if (!rcBuildRegions(*chf, tileCfg.walkableRadius, tileCfg.borderSize, tileCfg.minRegionSize, tileCfg.mergeRegionSize)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not build regions.", x, y); + continue; + } + + cset = new rcContourSet; + if (!cset) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Out of memory 'cset'.", x, y); + continue; + } + if (!rcBuildContours(*chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *cset)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not create contours.", x, y); + continue; + } + + if (m_keepInterResults) + { + tile.solid = solid; + solid = 0; + tile.chf = chf; + chf = 0; + } + + if (!cset->nconts) + { + delete cset; + cset = 0; + continue; + } + + tile.cset = cset; + // Offset the vertices in the cset. + rcTranslateContours(tile.cset, x*tileCfg.tileSize - tileCfg.borderSize, 0, y*tileCfg.tileSize - tileCfg.borderSize); + + rcTimeVal endTime = rcGetPerformanceTimer(); + tile.buildTime += rcGetDeltaTimeUsec(startTime, endTime); + } + } + + delete [] triangleFlags; + delete solid; + delete chf; + + + // Some extra code to measure some per tile statistics, + // such as build time and how many polygons there are per tile. + if (m_measurePerTileTimings) + { + for (int y = 0; y < m_tileSet->height; ++y) + { + for (int x = 0; x < m_tileSet->width; ++x) + { + Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width]; + + if (!tile.cset) + continue; + + rcTimeVal startTime = rcGetPerformanceTimer(); + rcPolyMesh* polyMesh = new rcPolyMesh; + if (!polyMesh) + continue; + if (rcBuildPolyMesh(*tile.cset, m_cfg.bmin, m_cfg.bmax, + m_cfg.cs, m_cfg.ch, m_cfg.maxVertsPerPoly, *polyMesh)) + { + int bucket = polyMesh->npolys; + if (bucket < 0) bucket = 0; + if (bucket >= MAX_STAT_BUCKETS) bucket = MAX_STAT_BUCKETS-1; + m_statPolysPerTile[bucket]++; + m_statPolysPerTileSamples++; + } + delete polyMesh; + + rcTimeVal endTime = rcGetPerformanceTimer(); + int time = tile.buildTime += rcGetDeltaTimeUsec(startTime, endTime); + + int bucket = (time+500)/1000; + if (bucket < 0) bucket = 0; + if (bucket >= MAX_STAT_BUCKETS) bucket = MAX_STAT_BUCKETS-1; + m_statTimePerTile[bucket]++; + m_statTimePerTileSamples++; + } + } + } + + // Make sure that the vertices along the tile edges match, + // so that they can be later properly stitched together. + for (int y = 0; y < m_tileSet->height; ++y) + { + for (int x = 0; x < m_tileSet->width; ++x) + { + rcTimeVal startTime = rcGetPerformanceTimer(); + if ((x+1) < m_tileSet->width) + { + if (!rcFixupAdjacentContours(m_tileSet->tiles[x + y*m_tileSet->width].cset, + m_tileSet->tiles[x+1 + y*m_tileSet->width].cset, + m_cfg.walkableClimb, (x+1)*m_cfg.tileSize, -1)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not fixup x+1.", x, y); + return false; + } + } + + if ((y+1) < m_tileSet->height) + { + if (!rcFixupAdjacentContours(m_tileSet->tiles[x + y*m_tileSet->width].cset, + m_tileSet->tiles[x + (y+1)*m_tileSet->width].cset, + m_cfg.walkableClimb, -1, (y+1)*m_cfg.tileSize)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not fixup y+1.", x, y); + return false; + } + } + rcTimeVal endTime = rcGetPerformanceTimer(); + m_tileSet->tiles[x+y*m_tileSet->width].buildTime += rcGetDeltaTimeUsec(startTime, endTime); + } + } + + + // Combine contours. + rcContourSet combSet; + + combSet.nconts = 0; + for (int y = 0; y < m_tileSet->height; ++y) + { + for (int x = 0; x < m_tileSet->width; ++x) + { + Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width]; + if (!tile.cset) continue; + combSet.nconts += tile.cset->nconts; + } + } + combSet.conts = new rcContour[combSet.nconts]; + if (!combSet.conts) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'combSet.conts' (%d).", combSet.nconts); + return false; + } + int n = 0; + for (int y = 0; y < m_tileSet->height; ++y) + { + for (int x = 0; x < m_tileSet->width; ++x) + { + Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width]; + if (!tile.cset) continue; + for (int i = 0; i < tile.cset->nconts; ++i) + { + combSet.conts[n].verts = tile.cset->conts[i].verts; + combSet.conts[n].nverts = tile.cset->conts[i].nverts; + combSet.conts[n].reg = tile.cset->conts[i].reg; + n++; + } + } + } + + m_polyMesh = new rcPolyMesh; + if (!m_polyMesh) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'polyMesh'."); + return false; + } + + bool polyRes = rcBuildPolyMesh(combSet, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, m_cfg.maxVertsPerPoly, *m_polyMesh); + + // Remove vertex binding to avoid double deletion. + for (int i = 0; i < combSet.nconts; ++i) + { + combSet.conts[i].verts = 0; + combSet.conts[i].nverts = 0; + } + + if (!polyRes) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Could not triangulate contours."); + return false; + } + + if (!m_keepInterResults) + { + for (int y = 0; y < m_tileSet->height; ++y) + { + for (int x = 0; x < m_tileSet->width; ++x) + { + Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width]; + delete tile.cset; + tile.cset = 0; + } + } + } + + if (m_cfg.maxVertsPerPoly == DT_STAT_VERTS_PER_POLYGON) + { + unsigned char* navData = 0; + int navDataSize = 0; + if (!dtCreateNavMeshData(m_polyMesh->verts, m_polyMesh->nverts, + m_polyMesh->polys, m_polyMesh->npolys, m_polyMesh->nvp, + m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, &navData, &navDataSize)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "Could not build Detour navmesh."); + return false; + } + + m_navMesh = new dtStatNavMesh; + if (!m_navMesh) + { + delete [] navData; + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "Could not create Detour navmesh"); + return false; + } + + if (!m_navMesh->init(navData, navDataSize, true)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "Could not init Detour navmesh"); + return false; + } + } + + rcTimeVal totEndTime = rcGetPerformanceTimer(); + + if (rcGetLog()) + { + const float pc = 100.0f / rcGetDeltaTimeUsec(totStartTime, totEndTime); + + rcGetLog()->log(RC_LOG_PROGRESS, "Chunky Mesh: %.1fms (%.1f%%)", rcGetDeltaTimeUsec(chunkyStartTime, chunkyEndTime)/1000.0f, rcGetDeltaTimeUsec(chunkyStartTime, chunkyEndTime)*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Rasterize: %.1fms (%.1f%%)", m_buildTimes.rasterizeTriangles/1000.0f, m_buildTimes.rasterizeTriangles*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Compact: %.1fms (%.1f%%)", m_buildTimes.buildCompact/1000.0f, m_buildTimes.buildCompact*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Border: %.1fms (%.1f%%)", m_buildTimes.filterBorder/1000.0f, m_buildTimes.filterBorder*pc); + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Walkable: %.1fms (%.1f%%)", m_buildTimes.filterWalkable/1000.0f, m_buildTimes.filterWalkable*pc); + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Reachable: %.1fms (%.1f%%)", m_buildTimes.filterMarkReachable/1000.0f, m_buildTimes.filterMarkReachable*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Distancefield: %.1fms (%.1f%%)", m_buildTimes.buildDistanceField/1000.0f, m_buildTimes.buildDistanceField*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - distance: %.1fms (%.1f%%)", m_buildTimes.buildDistanceFieldDist/1000.0f, m_buildTimes.buildDistanceFieldDist*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - blur: %.1fms (%.1f%%)", m_buildTimes.buildDistanceFieldBlur/1000.0f, m_buildTimes.buildDistanceFieldBlur*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Regions: %.1fms (%.1f%%)", m_buildTimes.buildRegions/1000.0f, m_buildTimes.buildRegions*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - watershed: %.1fms (%.1f%%)", m_buildTimes.buildRegionsReg/1000.0f, m_buildTimes.buildRegionsReg*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - expand: %.1fms (%.1f%%)", m_buildTimes.buildRegionsExp/1000.0f, m_buildTimes.buildRegionsExp*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - find catchment basins: %.1fms (%.1f%%)", m_buildTimes.buildRegionsFlood/1000.0f, m_buildTimes.buildRegionsFlood*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - filter: %.1fms (%.1f%%)", m_buildTimes.buildRegionsFilter/1000.0f, m_buildTimes.buildRegionsFilter*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Contours: %.1fms (%.1f%%)", m_buildTimes.buildContours/1000.0f, m_buildTimes.buildContours*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - trace: %.1fms (%.1f%%)", m_buildTimes.buildContoursTrace/1000.0f, m_buildTimes.buildContoursTrace*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - simplify: %.1fms (%.1f%%)", m_buildTimes.buildContoursSimplify/1000.0f, m_buildTimes.buildContoursSimplify*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Fixup contours: %.1fms (%.1f%%)", m_buildTimes.fixupContours/1000.0f, m_buildTimes.fixupContours*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh: %.1fms (%.1f%%)", m_buildTimes.buildPolymesh/1000.0f, m_buildTimes.buildPolymesh*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_polyMesh->nverts, m_polyMesh->npolys); + + rcGetLog()->log(RC_LOG_PROGRESS, "TOTAL: %.1fms", rcGetDeltaTimeUsec(totStartTime, totEndTime)/1000.0f); + } + + toolRecalc(); + + return true; +} diff --git a/RecastDemo/Source/Sample_TileMesh.cpp b/RecastDemo/Source/Sample_TileMesh.cpp new file mode 100644 index 0000000..5f23106 --- /dev/null +++ b/RecastDemo/Source/Sample_TileMesh.cpp @@ -0,0 +1,847 @@ +// +// Copyright (c) 2009 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#define _USE_MATH_DEFINES +#include +#include +#include +#include "SDL.h" +#include "SDL_Opengl.h" +#include "imgui.h" +#include "Sample.h" +#include "Sample_TileMesh.h" +#include "Recast.h" +#include "RecastTimer.h" +#include "RecastDebugDraw.h" +#include "DetourTileNavMesh.h" +#include "DetourTileNavMeshBuilder.h" +#include "DetourDebugDraw.h" + +#ifdef WIN32 +# define snprintf _snprintf +#endif + +Sample_TileMesh::Sample_TileMesh() : + m_tileSize(32), + m_navMesh(0), + m_chunkyMesh(0), + m_keepInterResults(true), + m_tileBuildTime(0), + m_tileMemUsage(0), + m_triflags(0), + m_solid(0), + m_chf(0), + m_cset(0), + m_polyMesh(0), + m_tileTriCount(0), + m_toolMode(TOOLMODE_CREATE_TILES), + m_startRef(0), + m_endRef(0), + m_npolys(0), + m_nstraightPath(0), + m_sposSet(false), + m_eposSet(false) +{ + resetCommonSettings(); + memset(m_tileBmin, 0, sizeof(m_tileBmin)); + memset(m_tileBmax, 0, sizeof(m_tileBmax)); + m_polyPickExt[0] = 2; + m_polyPickExt[1] = 4; + m_polyPickExt[2] = 2; +} + +Sample_TileMesh::~Sample_TileMesh() +{ + cleanup(); + delete m_navMesh; + delete m_chunkyMesh; +} + +void Sample_TileMesh::cleanup() +{ + delete [] m_triflags; + m_triflags = 0; + delete m_solid; + m_solid = 0; + delete m_chf; + m_chf = 0; + delete m_cset; + m_cset = 0; + delete m_polyMesh; + m_polyMesh = 0; +} + +void Sample_TileMesh::handleSettings() +{ + Sample::handleCommonSettings(); + + imguiLabel("Tiling"); + imguiSlider("TileSize", &m_tileSize, 16.0f, 1024.0f, 16.0f); + + char text[64]; + int gw = 0, gh = 0; + rcCalcGridSize(m_bmin, m_bmax, m_cellSize, &gw, &gh); + const int ts = (int)m_tileSize; + const int tw = (gw + ts-1) / ts; + const int th = (gh + ts-1) / ts; + snprintf(text, 64, "Tiles %d x %d", tw, th); + imguiValue(text); +} + +void Sample_TileMesh::toolRecalc() +{ + m_startRef = 0; + if (m_sposSet) + m_startRef = m_navMesh->findNearestPoly(m_spos, m_polyPickExt); + + m_endRef = 0; + if (m_eposSet) + m_endRef = m_navMesh->findNearestPoly(m_epos, m_polyPickExt); + + if (m_toolMode == TOOLMODE_PATHFIND) + { + if (m_sposSet && m_eposSet && m_startRef && m_endRef) + { + m_npolys = m_navMesh->findPath(m_startRef, m_endRef, m_polys, MAX_POLYS); + if (m_npolys) + m_nstraightPath = m_navMesh->findStraightPath(m_spos, m_epos, m_polys, m_npolys, m_straightPath, MAX_POLYS); + } + else + { + m_npolys = 0; + m_nstraightPath = 0; + } + } + else if (m_toolMode == TOOLMODE_RAYCAST) + { + m_nstraightPath = 0; + if (m_sposSet && m_eposSet && m_startRef) + { + float t = 0; + m_npolys = 0; + m_nstraightPath = 2; + m_straightPath[0] = m_spos[0]; + m_straightPath[1] = m_spos[1]; + m_straightPath[2] = m_spos[2]; + m_npolys = m_navMesh->raycast(m_startRef, m_spos, m_epos, t, m_polys, MAX_POLYS); + if (m_npolys && t < 1) + { + m_straightPath[3] = m_spos[0] + (m_epos[0] - m_spos[0]) * t; + m_straightPath[4] = m_spos[1] + (m_epos[1] - m_spos[1]) * t; + m_straightPath[5] = m_spos[2] + (m_epos[2] - m_spos[2]) * t; + } + else + { + m_straightPath[3] = m_epos[0]; + m_straightPath[4] = m_epos[1]; + m_straightPath[5] = m_epos[2]; + } + } + } + else if (m_toolMode == TOOLMODE_DISTANCE_TO_WALL) + { + m_distanceToWall = 0; + if (m_sposSet && m_startRef) + m_distanceToWall = m_navMesh->findDistanceToWall(m_startRef, m_spos, 100.0f, m_hitPos, m_hitNormal); + } + else if (m_toolMode == TOOLMODE_FIND_POLYS_AROUND) + { + if (m_sposSet && m_startRef && m_eposSet) + { + const float dx = m_epos[0] - m_spos[0]; + const float dz = m_epos[2] - m_spos[2]; + float dist = sqrtf(dx*dx + dz*dz); + m_npolys = m_navMesh->findPolysAround(m_startRef, m_spos, dist, m_polys, m_parent, 0, MAX_POLYS); + } + } + +} + +void Sample_TileMesh::handleTools() +{ + if (imguiCheck("Create Tiles", m_toolMode == TOOLMODE_CREATE_TILES)) + { + m_toolMode = TOOLMODE_CREATE_TILES; + toolRecalc(); + } + if (imguiCheck("Pathfind", m_toolMode == TOOLMODE_PATHFIND)) + { + m_toolMode = TOOLMODE_PATHFIND; + toolRecalc(); + } + if (imguiCheck("Distance to Wall", m_toolMode == TOOLMODE_DISTANCE_TO_WALL)) + { + m_toolMode = TOOLMODE_DISTANCE_TO_WALL; + toolRecalc(); + } + if (imguiCheck("Raycast", m_toolMode == TOOLMODE_RAYCAST)) + { + m_toolMode = TOOLMODE_RAYCAST; + toolRecalc(); + } + if (imguiCheck("Find Polys Around", m_toolMode == TOOLMODE_FIND_POLYS_AROUND)) + { + m_toolMode = TOOLMODE_FIND_POLYS_AROUND; + toolRecalc(); + } +} + +void Sample_TileMesh::handleDebugMode() +{ + if (m_navMesh) + { + imguiValue("Navmesh ready."); + imguiValue("Use 'Create Tiles' tool to experiment."); + imguiValue("LMB: (Re)Create tiles."); + imguiValue("LMB+SHIFT: Remove tiles."); + } + +} + +static void getPolyCenter(dtTiledNavMesh* navMesh, dtTilePolyRef ref, float* center) +{ + const dtTilePoly* p = navMesh->getPolyByRef(ref); + if (!p) return; + const float* verts = navMesh->getPolyVertsByRef(ref); + center[0] = 0; + center[1] = 0; + center[2] = 0; + for (int i = 0; i < (int)p->nv; ++i) + { + const float* v = &verts[p->v[i]*3]; + center[0] += v[0]; + center[1] += v[1]; + center[2] += v[2]; + } + const float s = 1.0f / p->nv; + center[0] *= s; + center[1] *= s; + center[2] *= s; +} + +void Sample_TileMesh::handleRender() +{ + if (!m_verts || !m_tris || !m_trinorms) + return; + + // Draw mesh + if (m_navMesh) + rcDebugDrawMesh(m_verts, m_nverts, m_tris, m_trinorms, m_ntris, 0); + else + rcDebugDrawMeshSlope(m_verts, m_nverts, m_tris, m_trinorms, m_ntris, m_agentMaxSlope); + + glDepthMask(GL_FALSE); + + // Draw bounds + float col[4] = {1,1,1,0.5f}; + rcDebugDrawBoxWire(m_bmin[0],m_bmin[1],m_bmin[2], m_bmax[0],m_bmax[1],m_bmax[2], col); + + // Tiling grid. + const int ts = (int)m_tileSize; + int gw = 0, gh = 0; + rcCalcGridSize(m_bmin, m_bmax, m_cellSize, &gw, &gh); + int tw = (gw + ts-1) / ts; + int th = (gh + ts-1) / ts; + const float s = ts*m_cellSize; + glBegin(GL_LINES); + glColor4ub(0,0,0,64); + for (int y = 0; y < th; ++y) + { + for (int x = 0; x < tw; ++x) + { + float fx, fy, fz; + fx = m_bmin[0] + x*s; + fy = m_bmin[1]; + fz = m_bmin[2] + y*s; + + glVertex3f(fx,fy,fz); + glVertex3f(fx+s,fy,fz); + glVertex3f(fx,fy,fz); + glVertex3f(fx,fy,fz+s); + + if (x+1 >= tw) + { + glVertex3f(fx+s,fy,fz); + glVertex3f(fx+s,fy,fz+s); + } + if (y+1 >= th) + { + glVertex3f(fx,fy,fz+s); + glVertex3f(fx+s,fy,fz+s); + } + } + } + glEnd(); + + // Draw active tile + rcDebugDrawBoxWire(m_tileBmin[0],m_tileBmin[1],m_tileBmin[2], m_tileBmax[0],m_tileBmax[1],m_tileBmax[2], m_tileCol); + + if (m_navMesh) + dtDebugDrawTiledNavMesh(m_navMesh); + + if (m_sposSet) + { + const float s = 0.5f; + glColor4ub(128,0,0,255); + glBegin(GL_LINES); + glVertex3f(m_spos[0]-s,m_spos[1],m_spos[2]); + glVertex3f(m_spos[0]+s,m_spos[1],m_spos[2]); + glVertex3f(m_spos[0],m_spos[1]-s,m_spos[2]); + glVertex3f(m_spos[0],m_spos[1]+s,m_spos[2]); + glVertex3f(m_spos[0],m_spos[1],m_spos[2]-s); + glVertex3f(m_spos[0],m_spos[1],m_spos[2]+s); + glEnd(); + } + if (m_eposSet) + { + const float s = 0.5f; + glColor4ub(0,128,0,255); + glBegin(GL_LINES); + glVertex3f(m_epos[0]-s,m_epos[1],m_epos[2]); + glVertex3f(m_epos[0]+s,m_epos[1],m_epos[2]); + glVertex3f(m_epos[0],m_epos[1]-s,m_epos[2]); + glVertex3f(m_epos[0],m_epos[1]+s,m_epos[2]); + glVertex3f(m_epos[0],m_epos[1],m_epos[2]-s); + glVertex3f(m_epos[0],m_epos[1],m_epos[2]+s); + glEnd(); + } + + static const float startCol[4] = { 0.5f, 0.1f, 0.0f, 0.75f }; + static const float endCol[4] = { 0.2f, 0.4f, 0.0f, 0.75f }; + static const float pathCol[4] = {0,0,0,0.25f}; + + if (m_toolMode == TOOLMODE_PATHFIND) + { + dtDebugDrawTiledNavMeshPoly(m_navMesh, m_startRef, startCol); + dtDebugDrawTiledNavMeshPoly(m_navMesh, m_endRef, endCol); + + if (m_npolys) + { + for (int i = 1; i < m_npolys-1; ++i) + dtDebugDrawTiledNavMeshPoly(m_navMesh, m_polys[i], pathCol); + } + if (m_nstraightPath) + { + glColor4ub(128,16,0,220); + glLineWidth(3.0f); + glBegin(GL_LINE_STRIP); + for (int i = 0; i < m_nstraightPath; ++i) + glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]); + glEnd(); + glLineWidth(1.0f); + glPointSize(4.0f); + glBegin(GL_POINTS); + for (int i = 0; i < m_nstraightPath; ++i) + glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]); + glEnd(); + glPointSize(1.0f); + } + } + else if (m_toolMode == TOOLMODE_RAYCAST) + { + dtDebugDrawTiledNavMeshPoly(m_navMesh, m_startRef, startCol); + + if (m_nstraightPath) + { + for (int i = 1; i < m_npolys; ++i) + dtDebugDrawTiledNavMeshPoly(m_navMesh, m_polys[i], pathCol); + + glColor4ub(128,16,0,220); + glLineWidth(3.0f); + glBegin(GL_LINE_STRIP); + for (int i = 0; i < m_nstraightPath; ++i) + glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]); + glEnd(); + glLineWidth(1.0f); + glPointSize(4.0f); + glBegin(GL_POINTS); + for (int i = 0; i < m_nstraightPath; ++i) + glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]); + glEnd(); + glPointSize(1.0f); + } + } + else if (m_toolMode == TOOLMODE_DISTANCE_TO_WALL) + { + dtDebugDrawTiledNavMeshPoly(m_navMesh, m_startRef, startCol); + const float col[4] = {1,1,1,0.5f}; + rcDebugDrawCylinderWire(m_spos[0]-m_distanceToWall, m_spos[1]+0.02f, m_spos[2]-m_distanceToWall, + m_spos[0]+m_distanceToWall, m_spos[1]+m_agentHeight, m_spos[2]+m_distanceToWall, col); + glLineWidth(3.0f); + glColor4fv(col); + glBegin(GL_LINES); + glVertex3f(m_hitPos[0], m_hitPos[1] + 0.02f, m_hitPos[2]); + glVertex3f(m_hitPos[0], m_hitPos[1] + m_agentHeight, m_hitPos[2]); + glEnd(); + glLineWidth(1.0f); + } + else if (m_toolMode == TOOLMODE_FIND_POLYS_AROUND) + { + glLineWidth(2.0f); + for (int i = 0; i < m_npolys; ++i) + { + dtDebugDrawTiledNavMeshPoly(m_navMesh, m_polys[i], pathCol); + if (m_parent[i]) + { + float p0[3], p1[3]; + getPolyCenter(m_navMesh, m_polys[i], p0); + getPolyCenter(m_navMesh, m_parent[i], p1); + glColor4ub(0,0,0,128); + rcDrawArc(p0, p1); + } + } + glLineWidth(1.0f); + + const float dx = m_epos[0] - m_spos[0]; + const float dz = m_epos[2] - m_spos[2]; + float dist = sqrtf(dx*dx + dz*dz); + const float col[4] = {1,1,1,0.5f}; + rcDebugDrawCylinderWire(m_spos[0]-dist, m_spos[1]+0.02f, m_spos[2]-dist, + m_spos[0]+dist, m_spos[1]+m_agentHeight, m_spos[2]+dist, col); + } + + glDepthMask(GL_TRUE); + +} + +void Sample_TileMesh::handleRenderOverlay(double* proj, double* model, int* view) +{ + GLdouble x, y, z; + + // Draw start and end point labels + if (m_tileBuildTime > 0.0f && gluProject((GLdouble)(m_tileBmin[0]+m_tileBmax[0])/2, (GLdouble)(m_tileBmin[1]+m_tileBmax[1])/2, (GLdouble)(m_tileBmin[2]+m_tileBmax[2])/2, + model, proj, view, &x, &y, &z)) + { + char text[32]; + snprintf(text,32,"%.3fms / %dTris / %.1fkB", m_tileBuildTime, m_tileTriCount, m_tileMemUsage); + imguiDrawText((int)x, (int)y-25, IMGUI_ALIGN_CENTER, text, imguiRGBA(0,0,0,220)); + } +} + +void Sample_TileMesh::handleMeshChanged(const float* verts, int nverts, + const int* tris, const float* trinorms, int ntris, + const float* bmin, const float* bmax) +{ + m_verts = verts; + m_nverts = nverts; + m_tris = tris; + m_trinorms = trinorms; + m_ntris = ntris; + vcopy(m_bmin, bmin); + vcopy(m_bmax, bmax); + + delete m_chunkyMesh; + m_chunkyMesh = 0; + delete m_navMesh; + m_navMesh = 0; + cleanup(); +} + +void Sample_TileMesh::setToolStartPos(const float* p) +{ + m_sposSet = true; + vcopy(m_spos, p); + + if (m_toolMode == TOOLMODE_CREATE_TILES) + removeTile(m_spos); + else + toolRecalc(); +} + +void Sample_TileMesh::setToolEndPos(const float* p) +{ + if (!m_navMesh) + return; + + m_eposSet = true; + vcopy(m_epos, p); + + if (m_toolMode == TOOLMODE_CREATE_TILES) + buildTile(m_epos); + else + toolRecalc(); +} + +bool Sample_TileMesh::handleBuild() +{ + if (!m_verts || !m_tris) + { + printf("No verts or tris\n"); + return false; + } + + delete m_navMesh; + m_navMesh = new dtTiledNavMesh; + if (!m_navMesh) + { + printf("Could not allocate navmehs\n"); + return false; + } + if (!m_navMesh->init(m_bmin, m_tileSize*m_cellSize, m_agentMaxClimb*m_cellHeight)) + { + printf("Could not init navmesh\n"); + return false; + } + + // Build chunky mesh. + delete m_chunkyMesh; + m_chunkyMesh = new rcChunkyTriMesh; + if (!m_chunkyMesh) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'm_chunkyMesh'."); + return false; + } + if (!rcCreateChunkyTriMesh(m_verts, m_tris, m_ntris, 256, m_chunkyMesh)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Could not build chunky mesh."); + return false; + } + + return true; +} + +void Sample_TileMesh::buildTile(const float* pos) +{ + if (!m_navMesh) + return; + + const float ts = m_tileSize*m_cellSize; + const int tx = (int)floorf((pos[0]-m_bmin[0]) / ts); + const int ty = (int)floorf((pos[2]-m_bmin[2]) / ts); + if (tx < 0 || ty < 0) + return; + + m_tileBmin[0] = m_bmin[0] + tx*ts; + m_tileBmin[1] = m_bmin[1]; + m_tileBmin[2] = m_bmin[2] + ty*ts; + + m_tileBmax[0] = m_bmin[0] + (tx+1)*ts; + m_tileBmax[1] = m_bmax[1]; + m_tileBmax[2] = m_bmin[2] + (ty+1)*ts; + + m_tileCol[0] = 0.3f; m_tileCol[1] = 0.8f; m_tileCol[2] = 0; m_tileCol[3] = 1; + + int dataSize = 0; + unsigned char* data = buildTileMesh(m_tileBmin, m_tileBmax, dataSize); + + if (data) + { + // Remove any previous data (navmesh owns and deletes the data). + m_navMesh->removeTileAt(tx,ty,0,0); + + // Let the navmesh own the data. + if (!m_navMesh->addTileAt(tx,ty,data,dataSize,true)) + delete [] data; + } +} + +void Sample_TileMesh::removeTile(const float* pos) +{ + if (!m_navMesh) + return; + + const float ts = m_tileSize*m_cellSize; + const int tx = (int)floorf((pos[0]-m_bmin[0]) / ts); + const int ty = (int)floorf((pos[2]-m_bmin[2]) / ts); + + m_tileBmin[0] = m_bmin[0] + tx*ts; + m_tileBmin[1] = m_bmin[1]; + m_tileBmin[2] = m_bmin[2] + ty*ts; + + m_tileBmax[0] = m_bmin[0] + (tx+1)*ts; + m_tileBmax[1] = m_bmax[1]; + m_tileBmax[2] = m_bmin[2] + (ty+1)*ts; + + m_tileCol[0] = 0.8f; m_tileCol[1] = 0.1f; m_tileCol[2] = 0; m_tileCol[3] = 1; + + unsigned char* rdata = 0; + int rdataSize = 0; + if (m_navMesh->removeTileAt(tx,ty,&rdata,&rdataSize)) + delete [] rdata; +} + +unsigned char* Sample_TileMesh::buildTileMesh(const float* bmin, const float* bmax, int& dataSize) +{ + if (!m_verts || ! m_tris) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified."); + return 0; + } + + cleanup(); + + // Init build configuration from GUI + memset(&m_cfg, 0, sizeof(m_cfg)); + m_cfg.cs = m_cellSize; + m_cfg.ch = m_cellHeight; + m_cfg.walkableSlopeAngle = m_agentMaxSlope; + m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch); + m_cfg.walkableClimb = (int)ceilf(m_agentMaxClimb / m_cfg.ch); + m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs); + m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize); + m_cfg.maxSimplificationError = m_edgeMaxError; + m_cfg.minRegionSize = (int)rcSqr(m_regionMinSize); + m_cfg.mergeRegionSize = (int)rcSqr(m_regionMergeSize); + m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly; + m_cfg.tileSize = (int)m_tileSize; + m_cfg.borderSize = m_cfg.walkableRadius*2 + 2; // Reserve enough padding. + m_cfg.width = m_cfg.tileSize + m_cfg.borderSize*2; + m_cfg.height = m_cfg.tileSize + m_cfg.borderSize*2; + +/* if (m_cfg.maxVertsPerPoly == DT_VERTS_PER_POLYGON) + m_drawMode = DRAWMODE_NAVMESH; + else + m_drawMode = DRAWMODE_POLYMESH;*/ + + vcopy(m_cfg.bmin, bmin); + vcopy(m_cfg.bmax, bmax); + m_cfg.bmin[0] -= m_cfg.borderSize*m_cfg.cs; + m_cfg.bmin[2] -= m_cfg.borderSize*m_cfg.cs; + m_cfg.bmax[0] += m_cfg.borderSize*m_cfg.cs; + m_cfg.bmax[2] += m_cfg.borderSize*m_cfg.cs; + + // Reset build times gathering. + memset(&m_buildTimes, 0, sizeof(m_buildTimes)); + rcSetBuildTimes(&m_buildTimes); + + // Start the build process. + rcTimeVal totStartTime = rcGetPerformanceTimer(); + + if (rcGetLog()) + { + rcGetLog()->log(RC_LOG_PROGRESS, "Building navigation:"); + rcGetLog()->log(RC_LOG_PROGRESS, " - %d x %d cells", m_cfg.width, m_cfg.height); + rcGetLog()->log(RC_LOG_PROGRESS, " - %.1fK verts, %.1fK tris", m_nverts/1000.0f, m_ntris/1000.0f); + } + + // Allocate voxel heighfield where we rasterize our input data to. + m_solid = new rcHeightfield; + if (!m_solid) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'."); + return 0; + } + if (!rcCreateHeightfield(*m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield."); + return 0; + } + + // Allocate array that can hold triangle flags. + // If you have multiple meshes you need to process, allocate + // and array which can hold the max number of triangles you need to process. + m_triflags = new unsigned char[m_chunkyMesh->maxTrisPerChunk]; + if (!m_triflags) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'triangleFlags' (%d).", m_chunkyMesh->maxTrisPerChunk); + return 0; + } + + + float tbmin[2], tbmax[2]; + tbmin[0] = m_cfg.bmin[0]; + tbmin[1] = m_cfg.bmin[2]; + tbmax[0] = m_cfg.bmax[0]; + tbmax[1] = m_cfg.bmax[2]; + int cid[256];// TODO: Make grow when returning too many items. + const int ncid = rcGetChunksInRect(m_chunkyMesh, tbmin, tbmax, cid, 256); + if (!ncid) + return 0; + + m_tileTriCount = 0; + + for (int i = 0; i < ncid; ++i) + { + const rcChunkyTriMeshNode& node = m_chunkyMesh->nodes[cid[i]]; + const int* tris = &m_chunkyMesh->tris[node.i*3]; + const int ntris = node.n; + + m_tileTriCount += ntris; + + memset(m_triflags, 0, ntris*sizeof(unsigned char)); + rcMarkWalkableTriangles(m_cfg.walkableSlopeAngle, + m_verts, m_nverts, tris, ntris, m_triflags); + + rcRasterizeTriangles(m_verts, m_nverts, tris, m_triflags, ntris, *m_solid); + } + + if (!m_keepInterResults) + { + delete [] m_triflags; + m_triflags = 0; + } + + // Once all geoemtry 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. + rcFilterLedgeSpans(m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid); + rcFilterWalkableLowHeightSpans(m_cfg.walkableHeight, *m_solid); + + // 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. + m_chf = new rcCompactHeightfield; + if (!m_chf) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'."); + return 0; + } + if (!rcBuildCompactHeightfield(m_cfg.walkableHeight, m_cfg.walkableClimb, RC_WALKABLE, *m_solid, *m_chf)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data."); + return 0; + } + + if (!m_keepInterResults) + { + delete m_solid; + m_solid = 0; + } + + // Prepare for region partitioning, by calculating distance field along the walkable surface. + if (!rcBuildDistanceField(*m_chf)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not build distance field."); + return 0; + } + + // Partition the walkable surface into simple regions without holes. + if (!rcBuildRegions(*m_chf, m_cfg.walkableRadius, m_cfg.borderSize, m_cfg.minRegionSize, m_cfg.mergeRegionSize)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not build regions."); + return 0; + } + + // Create contours. + m_cset = new rcContourSet; + if (!m_cset) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); + return 0; + } + if (!rcBuildContours(*m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not create contours."); + return 0; + } + + if (!m_keepInterResults) + { + delete m_chf; + m_chf = 0; + } + + // Build polygon navmesh from the contours. + m_polyMesh = new rcPolyMesh; + if (!m_polyMesh) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'polyMesh'."); + return 0; + } + if (!rcBuildPolyMesh(*m_cset, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, m_cfg.maxVertsPerPoly, *m_polyMesh)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); + return 0; + } + + if (!m_keepInterResults) + { + delete m_cset; + m_cset = 0; + } + + unsigned char* navData = 0; + int navDataSize = 0; + if (m_cfg.maxVertsPerPoly == DT_TILE_VERTS_PER_POLYGON) + { + // Remove padding from the polymesh data. + for (int i = 0; i < m_polyMesh->nverts; ++i) + { + unsigned short* v = &m_polyMesh->verts[i*3]; + v[0] -= (unsigned short)m_cfg.borderSize; + v[2] -= (unsigned short)m_cfg.borderSize; + } + + if (!dtCreateNavMeshTileData(m_polyMesh->verts, m_polyMesh->nverts, + m_polyMesh->polys, m_polyMesh->npolys, m_polyMesh->nvp, + bmin, bmax, m_cfg.cs, m_cfg.ch, m_cfg.tileSize, m_cfg.walkableClimb, &navData, &navDataSize)) + { + if (rcGetLog()) + rcGetLog()->log(RC_LOG_ERROR, "Could not build Detour navmesh."); + return 0; + } + } + m_tileMemUsage = navDataSize/1024.0f; + + rcTimeVal totEndTime = rcGetPerformanceTimer(); + + // Show performance stats. + if (rcGetLog()) + { + const float pc = 100.0f / rcGetDeltaTimeUsec(totStartTime, totEndTime); + + rcGetLog()->log(RC_LOG_PROGRESS, "Rasterize: %.1fms (%.1f%%)", m_buildTimes.rasterizeTriangles/1000.0f, m_buildTimes.rasterizeTriangles*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Compact: %.1fms (%.1f%%)", m_buildTimes.buildCompact/1000.0f, m_buildTimes.buildCompact*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Border: %.1fms (%.1f%%)", m_buildTimes.filterBorder/1000.0f, m_buildTimes.filterBorder*pc); + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Walkable: %.1fms (%.1f%%)", m_buildTimes.filterWalkable/1000.0f, m_buildTimes.filterWalkable*pc); + rcGetLog()->log(RC_LOG_PROGRESS, "Filter Reachable: %.1fms (%.1f%%)", m_buildTimes.filterMarkReachable/1000.0f, m_buildTimes.filterMarkReachable*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Distancefield: %.1fms (%.1f%%)", m_buildTimes.buildDistanceField/1000.0f, m_buildTimes.buildDistanceField*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - distance: %.1fms (%.1f%%)", m_buildTimes.buildDistanceFieldDist/1000.0f, m_buildTimes.buildDistanceFieldDist*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - blur: %.1fms (%.1f%%)", m_buildTimes.buildDistanceFieldBlur/1000.0f, m_buildTimes.buildDistanceFieldBlur*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Regions: %.1fms (%.1f%%)", m_buildTimes.buildRegions/1000.0f, m_buildTimes.buildRegions*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - watershed: %.1fms (%.1f%%)", m_buildTimes.buildRegionsReg/1000.0f, m_buildTimes.buildRegionsReg*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - expand: %.1fms (%.1f%%)", m_buildTimes.buildRegionsExp/1000.0f, m_buildTimes.buildRegionsExp*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - find catchment basins: %.1fms (%.1f%%)", m_buildTimes.buildRegionsFlood/1000.0f, m_buildTimes.buildRegionsFlood*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - filter: %.1fms (%.1f%%)", m_buildTimes.buildRegionsFilter/1000.0f, m_buildTimes.buildRegionsFilter*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Contours: %.1fms (%.1f%%)", m_buildTimes.buildContours/1000.0f, m_buildTimes.buildContours*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - trace: %.1fms (%.1f%%)", m_buildTimes.buildContoursTrace/1000.0f, m_buildTimes.buildContoursTrace*pc); + rcGetLog()->log(RC_LOG_PROGRESS, " - simplify: %.1fms (%.1f%%)", m_buildTimes.buildContoursSimplify/1000.0f, m_buildTimes.buildContoursSimplify*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Fixup contours: %.1fms (%.1f%%)", m_buildTimes.fixupContours/1000.0f, m_buildTimes.fixupContours*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh: %.1fms (%.1f%%)", m_buildTimes.buildPolymesh/1000.0f, m_buildTimes.buildPolymesh*pc); + + rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_polyMesh->nverts, m_polyMesh->npolys); + + rcGetLog()->log(RC_LOG_PROGRESS, "TOTAL: %.1fms", rcGetDeltaTimeUsec(totStartTime, totEndTime)/1000.0f); + } + + m_tileBuildTime = rcGetDeltaTimeUsec(totStartTime, totEndTime)/1000.0f; + + dataSize = navDataSize; + return navData; +}