TestCase.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. //
  2. // Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
  3. //
  4. // This software is provided 'as-is', without any express or implied
  5. // warranty. In no event will the authors be held liable for any damages
  6. // arising from the use of this software.
  7. // Permission is granted to anyone to use this software for any purpose,
  8. // including commercial applications, and to alter it and redistribute it
  9. // freely, subject to the following restrictions:
  10. // 1. The origin of this software must not be misrepresented; you must not
  11. // claim that you wrote the original software. If you use this software
  12. // in a product, an acknowledgment in the product documentation would be
  13. // appreciated but is not required.
  14. // 2. Altered source versions must be plainly marked as such, and must not be
  15. // misrepresented as being the original software.
  16. // 3. This notice may not be removed or altered from any source distribution.
  17. //
  18. #include <stdio.h>
  19. #include <ctype.h>
  20. #include <string.h>
  21. #include <math.h>
  22. #include "TestCase.h"
  23. #include "DetourNavMesh.h"
  24. #include "DetourNavMeshQuery.h"
  25. #include "DetourCommon.h"
  26. #include "SDL.h"
  27. #include "SDL_opengl.h"
  28. #ifdef __APPLE__
  29. # include <OpenGL/glu.h>
  30. #else
  31. # include <GL/glu.h>
  32. #endif
  33. #include "imgui.h"
  34. #include "PerfTimer.h"
  35. #ifdef WIN32
  36. #define snprintf _snprintf
  37. #endif
  38. TestCase::TestCase() :
  39. m_tests(0)
  40. {
  41. }
  42. TestCase::~TestCase()
  43. {
  44. Test* iter = m_tests;
  45. while (iter)
  46. {
  47. Test* next = iter->next;
  48. delete iter;
  49. iter = next;
  50. }
  51. }
  52. static char* parseRow(char* buf, char* bufEnd, char* row, int len)
  53. {
  54. bool start = true;
  55. bool done = false;
  56. int n = 0;
  57. while (!done && buf < bufEnd)
  58. {
  59. char c = *buf;
  60. buf++;
  61. // multirow
  62. switch (c)
  63. {
  64. case '\n':
  65. if (start) break;
  66. done = true;
  67. break;
  68. case '\r':
  69. break;
  70. case '\t':
  71. case ' ':
  72. if (start) break;
  73. // else falls through
  74. default:
  75. start = false;
  76. row[n++] = c;
  77. if (n >= len-1)
  78. done = true;
  79. break;
  80. }
  81. }
  82. row[n] = '\0';
  83. return buf;
  84. }
  85. static void copyName(std::string& dst, const char* src)
  86. {
  87. // Skip white spaces
  88. while (*src && isspace(*src))
  89. src++;
  90. dst = src;
  91. }
  92. bool TestCase::load(const std::string& filePath)
  93. {
  94. char* buf = 0;
  95. FILE* fp = fopen(filePath.c_str(), "rb");
  96. if (!fp)
  97. return false;
  98. if (fseek(fp, 0, SEEK_END) != 0)
  99. {
  100. fclose(fp);
  101. return false;
  102. }
  103. long bufSize = ftell(fp);
  104. if (bufSize < 0)
  105. {
  106. fclose(fp);
  107. return false;
  108. }
  109. if (fseek(fp, 0, SEEK_SET) != 0)
  110. {
  111. fclose(fp);
  112. return false;
  113. }
  114. buf = new char[bufSize];
  115. if (!buf)
  116. {
  117. fclose(fp);
  118. return false;
  119. }
  120. size_t readLen = fread(buf, bufSize, 1, fp);
  121. fclose(fp);
  122. if (readLen != 1)
  123. {
  124. delete[] buf;
  125. return false;
  126. }
  127. char* src = buf;
  128. char* srcEnd = buf + bufSize;
  129. char row[512];
  130. while (src < srcEnd)
  131. {
  132. // Parse one row
  133. row[0] = '\0';
  134. src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
  135. if (row[0] == 's')
  136. {
  137. // Sample name.
  138. copyName(m_sampleName, row+1);
  139. }
  140. else if (row[0] == 'f')
  141. {
  142. // File name.
  143. copyName(m_geomFileName, row+1);
  144. }
  145. else if (row[0] == 'p' && row[1] == 'f')
  146. {
  147. // Pathfind test.
  148. Test* test = new Test;
  149. memset(test, 0, sizeof(Test));
  150. test->type = TEST_PATHFIND;
  151. test->expand = false;
  152. test->next = m_tests;
  153. m_tests = test;
  154. sscanf(row+2, "%f %f %f %f %f %f %hx %hx",
  155. &test->spos[0], &test->spos[1], &test->spos[2],
  156. &test->epos[0], &test->epos[1], &test->epos[2],
  157. &test->includeFlags, &test->excludeFlags);
  158. }
  159. else if (row[0] == 'r' && row[1] == 'c')
  160. {
  161. // Pathfind test.
  162. Test* test = new Test;
  163. memset(test, 0, sizeof(Test));
  164. test->type = TEST_RAYCAST;
  165. test->expand = false;
  166. test->next = m_tests;
  167. m_tests = test;
  168. sscanf(row+2, "%f %f %f %f %f %f %hx %hx",
  169. &test->spos[0], &test->spos[1], &test->spos[2],
  170. &test->epos[0], &test->epos[1], &test->epos[2],
  171. &test->includeFlags, &test->excludeFlags);
  172. }
  173. }
  174. delete [] buf;
  175. return true;
  176. }
  177. void TestCase::resetTimes()
  178. {
  179. for (Test* iter = m_tests; iter; iter = iter->next)
  180. {
  181. iter->findNearestPolyTime = 0;
  182. iter->findPathTime = 0;
  183. iter->findStraightPathTime = 0;
  184. }
  185. }
  186. void TestCase::doTests(dtNavMesh* navmesh, dtNavMeshQuery* navquery)
  187. {
  188. if (!navmesh || !navquery)
  189. return;
  190. resetTimes();
  191. static const int MAX_POLYS = 256;
  192. dtPolyRef polys[MAX_POLYS];
  193. float straight[MAX_POLYS*3];
  194. const float polyPickExt[3] = {2,4,2};
  195. for (Test* iter = m_tests; iter; iter = iter->next)
  196. {
  197. delete [] iter->polys;
  198. iter->polys = 0;
  199. iter->npolys = 0;
  200. delete [] iter->straight;
  201. iter->straight = 0;
  202. iter->nstraight = 0;
  203. dtQueryFilter filter;
  204. filter.setIncludeFlags(iter->includeFlags);
  205. filter.setExcludeFlags(iter->excludeFlags);
  206. // Find start points
  207. TimeVal findNearestPolyStart = getPerfTime();
  208. dtPolyRef startRef, endRef;
  209. navquery->findNearestPoly(iter->spos, polyPickExt, &filter, &startRef, iter->nspos);
  210. navquery->findNearestPoly(iter->epos, polyPickExt, &filter, &endRef, iter->nepos);
  211. TimeVal findNearestPolyEnd = getPerfTime();
  212. iter->findNearestPolyTime += getPerfTimeUsec(findNearestPolyEnd - findNearestPolyStart);
  213. if (!startRef || ! endRef)
  214. continue;
  215. if (iter->type == TEST_PATHFIND)
  216. {
  217. // Find path
  218. TimeVal findPathStart = getPerfTime();
  219. navquery->findPath(startRef, endRef, iter->spos, iter->epos, &filter, polys, &iter->npolys, MAX_POLYS);
  220. TimeVal findPathEnd = getPerfTime();
  221. iter->findPathTime += getPerfTimeUsec(findPathEnd - findPathStart);
  222. // Find straight path
  223. if (iter->npolys)
  224. {
  225. TimeVal findStraightPathStart = getPerfTime();
  226. navquery->findStraightPath(iter->spos, iter->epos, polys, iter->npolys,
  227. straight, 0, 0, &iter->nstraight, MAX_POLYS);
  228. TimeVal findStraightPathEnd = getPerfTime();
  229. iter->findStraightPathTime += getPerfTimeUsec(findStraightPathEnd - findStraightPathStart);
  230. }
  231. // Copy results
  232. if (iter->npolys)
  233. {
  234. iter->polys = new dtPolyRef[iter->npolys];
  235. memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
  236. }
  237. if (iter->nstraight)
  238. {
  239. iter->straight = new float[iter->nstraight*3];
  240. memcpy(iter->straight, straight, sizeof(float)*3*iter->nstraight);
  241. }
  242. }
  243. else if (iter->type == TEST_RAYCAST)
  244. {
  245. float t = 0;
  246. float hitNormal[3], hitPos[3];
  247. iter->straight = new float[2*3];
  248. iter->nstraight = 2;
  249. iter->straight[0] = iter->spos[0];
  250. iter->straight[1] = iter->spos[1];
  251. iter->straight[2] = iter->spos[2];
  252. TimeVal findPathStart = getPerfTime();
  253. navquery->raycast(startRef, iter->spos, iter->epos, &filter, &t, hitNormal, polys, &iter->npolys, MAX_POLYS);
  254. TimeVal findPathEnd = getPerfTime();
  255. iter->findPathTime += getPerfTimeUsec(findPathEnd - findPathStart);
  256. if (t > 1)
  257. {
  258. // No hit
  259. dtVcopy(hitPos, iter->epos);
  260. }
  261. else
  262. {
  263. // Hit
  264. dtVlerp(hitPos, iter->spos, iter->epos, t);
  265. }
  266. // Adjust height.
  267. if (iter->npolys > 0)
  268. {
  269. float h = 0;
  270. navquery->getPolyHeight(polys[iter->npolys-1], hitPos, &h);
  271. hitPos[1] = h;
  272. }
  273. dtVcopy(&iter->straight[3], hitPos);
  274. if (iter->npolys)
  275. {
  276. iter->polys = new dtPolyRef[iter->npolys];
  277. memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
  278. }
  279. }
  280. }
  281. printf("Test Results:\n");
  282. int n = 0;
  283. for (Test* iter = m_tests; iter; iter = iter->next)
  284. {
  285. const int total = iter->findNearestPolyTime + iter->findPathTime + iter->findStraightPathTime;
  286. printf(" - Path %02d: %.4f ms\n", n, (float)total/1000.0f);
  287. printf(" - poly: %.4f ms\n", (float)iter->findNearestPolyTime/1000.0f);
  288. printf(" - path: %.4f ms\n", (float)iter->findPathTime/1000.0f);
  289. printf(" - straight: %.4f ms\n", (float)iter->findStraightPathTime/1000.0f);
  290. n++;
  291. }
  292. }
  293. void TestCase::handleRender()
  294. {
  295. glLineWidth(2.0f);
  296. glBegin(GL_LINES);
  297. for (Test* iter = m_tests; iter; iter = iter->next)
  298. {
  299. float dir[3];
  300. dtVsub(dir, iter->epos, iter->spos);
  301. dtVnormalize(dir);
  302. glColor4ub(128,25,0,192);
  303. glVertex3f(iter->spos[0],iter->spos[1]-0.3f,iter->spos[2]);
  304. glVertex3f(iter->spos[0],iter->spos[1]+0.3f,iter->spos[2]);
  305. glVertex3f(iter->spos[0],iter->spos[1]+0.3f,iter->spos[2]);
  306. glVertex3f(iter->spos[0]+dir[0]*0.3f,iter->spos[1]+0.3f+dir[1]*0.3f,iter->spos[2]+dir[2]*0.3f);
  307. glColor4ub(51,102,0,129);
  308. glVertex3f(iter->epos[0],iter->epos[1]-0.3f,iter->epos[2]);
  309. glVertex3f(iter->epos[0],iter->epos[1]+0.3f,iter->epos[2]);
  310. if (iter->expand)
  311. {
  312. const float s = 0.1f;
  313. glColor4ub(255,32,0,128);
  314. glVertex3f(iter->spos[0]-s,iter->spos[1],iter->spos[2]);
  315. glVertex3f(iter->spos[0]+s,iter->spos[1],iter->spos[2]);
  316. glVertex3f(iter->spos[0],iter->spos[1],iter->spos[2]-s);
  317. glVertex3f(iter->spos[0],iter->spos[1],iter->spos[2]+s);
  318. glColor4ub(255,192,0,255);
  319. glVertex3f(iter->nspos[0]-s,iter->nspos[1],iter->nspos[2]);
  320. glVertex3f(iter->nspos[0]+s,iter->nspos[1],iter->nspos[2]);
  321. glVertex3f(iter->nspos[0],iter->nspos[1],iter->nspos[2]-s);
  322. glVertex3f(iter->nspos[0],iter->nspos[1],iter->nspos[2]+s);
  323. glColor4ub(255,32,0,128);
  324. glVertex3f(iter->epos[0]-s,iter->epos[1],iter->epos[2]);
  325. glVertex3f(iter->epos[0]+s,iter->epos[1],iter->epos[2]);
  326. glVertex3f(iter->epos[0],iter->epos[1],iter->epos[2]-s);
  327. glVertex3f(iter->epos[0],iter->epos[1],iter->epos[2]+s);
  328. glColor4ub(255,192,0,255);
  329. glVertex3f(iter->nepos[0]-s,iter->nepos[1],iter->nepos[2]);
  330. glVertex3f(iter->nepos[0]+s,iter->nepos[1],iter->nepos[2]);
  331. glVertex3f(iter->nepos[0],iter->nepos[1],iter->nepos[2]-s);
  332. glVertex3f(iter->nepos[0],iter->nepos[1],iter->nepos[2]+s);
  333. }
  334. if (iter->expand)
  335. glColor4ub(255,192,0,255);
  336. else
  337. glColor4ub(0,0,0,64);
  338. for (int i = 0; i < iter->nstraight-1; ++i)
  339. {
  340. glVertex3f(iter->straight[i*3+0],iter->straight[i*3+1]+0.3f,iter->straight[i*3+2]);
  341. glVertex3f(iter->straight[(i+1)*3+0],iter->straight[(i+1)*3+1]+0.3f,iter->straight[(i+1)*3+2]);
  342. }
  343. }
  344. glEnd();
  345. glLineWidth(1.0f);
  346. }
  347. bool TestCase::handleRenderOverlay(double* proj, double* model, int* view)
  348. {
  349. GLdouble x, y, z;
  350. char text[64], subtext[64];
  351. int n = 0;
  352. static const float LABEL_DIST = 1.0f;
  353. for (Test* iter = m_tests; iter; iter = iter->next)
  354. {
  355. float pt[3], dir[3];
  356. if (iter->nstraight)
  357. {
  358. dtVcopy(pt, &iter->straight[3]);
  359. if (dtVdist(pt, iter->spos) > LABEL_DIST)
  360. {
  361. dtVsub(dir, pt, iter->spos);
  362. dtVnormalize(dir);
  363. dtVmad(pt, iter->spos, dir, LABEL_DIST);
  364. }
  365. pt[1]+=0.5f;
  366. }
  367. else
  368. {
  369. dtVsub(dir, iter->epos, iter->spos);
  370. dtVnormalize(dir);
  371. dtVmad(pt, iter->spos, dir, LABEL_DIST);
  372. pt[1]+=0.5f;
  373. }
  374. if (gluProject((GLdouble)pt[0], (GLdouble)pt[1], (GLdouble)pt[2],
  375. model, proj, view, &x, &y, &z))
  376. {
  377. snprintf(text, 64, "Path %d\n", n);
  378. unsigned int col = imguiRGBA(0,0,0,128);
  379. if (iter->expand)
  380. col = imguiRGBA(255,192,0,220);
  381. imguiDrawText((int)x, (int)(y-25), IMGUI_ALIGN_CENTER, text, col);
  382. }
  383. n++;
  384. }
  385. static int resScroll = 0;
  386. bool mouseOverMenu = imguiBeginScrollArea("Test Results", 10, view[3] - 10 - 350, 200, 350, &resScroll);
  387. // mouseOverMenu = true;
  388. n = 0;
  389. for (Test* iter = m_tests; iter; iter = iter->next)
  390. {
  391. const int total = iter->findNearestPolyTime + iter->findPathTime + iter->findStraightPathTime;
  392. snprintf(subtext, 64, "%.4f ms", (float)total/1000.0f);
  393. snprintf(text, 64, "Path %d", n);
  394. if (imguiCollapse(text, subtext, iter->expand))
  395. iter->expand = !iter->expand;
  396. if (iter->expand)
  397. {
  398. snprintf(text, 64, "Poly: %.4f ms", (float)iter->findNearestPolyTime/1000.0f);
  399. imguiValue(text);
  400. snprintf(text, 64, "Path: %.4f ms", (float)iter->findPathTime/1000.0f);
  401. imguiValue(text);
  402. snprintf(text, 64, "Straight: %.4f ms", (float)iter->findStraightPathTime/1000.0f);
  403. imguiValue(text);
  404. imguiSeparator();
  405. }
  406. n++;
  407. }
  408. imguiEndScrollArea();
  409. return mouseOverMenu;
  410. }