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
19 #include <cstdio>
20 #define _USE_MATH_DEFINES
21 #include <cmath>
22
23 #include "SDL.h"
24 #include "SDL_opengl.h"
25 #ifdef __APPLE__
26 # include <OpenGL/glu.h>
27 #else
28 # include <GL/glu.h>
29 #endif
30
31 #include <vector>
32 #include <string>
33
34 #include "imgui.h"
35 #include "imguiRenderGL.h"
36
37 #include "Recast.h"
38 #include "RecastDebugDraw.h"
39 #include "InputGeom.h"
40 #include "TestCase.h"
41 #include "Filelist.h"
42 #include "Sample_SoloMesh.h"
43 #include "Sample_TileMesh.h"
44 #include "Sample_TempObstacles.h"
45 #include "Sample_Debug.h"
46
47 #ifdef WIN32
48 # define snprintf _snprintf
49 # define putenv _putenv
50 #endif
51
52 using std::string;
53 using std::vector;
54
55 struct SampleItem
56 {
57 Sample* (*create)();
58 const string name;
59 };
createSolo()60 Sample* createSolo() { return new Sample_SoloMesh(); }
createTile()61 Sample* createTile() { return new Sample_TileMesh(); }
createTempObstacle()62 Sample* createTempObstacle() { return new Sample_TempObstacles(); }
createDebug()63 Sample* createDebug() { return new Sample_Debug(); }
64 static SampleItem g_samples[] =
65 {
66 { createSolo, "Solo Mesh" },
67 { createTile, "Tile Mesh" },
68 { createTempObstacle, "Temp Obstacles" },
69 };
70 static const int g_nsamples = sizeof(g_samples) / sizeof(SampleItem);
71
main(int,char **)72 int main(int /*argc*/, char** /*argv*/)
73 {
74 // Init SDL
75 if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
76 {
77 printf("Could not initialise SDL.\nError: %s\n", SDL_GetError());
78 return -1;
79 }
80
81 // Enable depth buffer.
82 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
83 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
84
85 // Set color channel depth.
86 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
87 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
88 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
89 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
90
91 // 4x MSAA.
92 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
93 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
94
95 SDL_DisplayMode displayMode;
96 SDL_GetCurrentDisplayMode(0, &displayMode);
97
98 bool presentationMode = false;
99 Uint32 flags = SDL_WINDOW_OPENGL;
100 int width;
101 int height;
102 if (presentationMode)
103 {
104 // Create a fullscreen window at the native resolution.
105 width = displayMode.w;
106 height = displayMode.h;
107 flags |= SDL_WINDOW_FULLSCREEN;
108 }
109 else
110 {
111 float aspect = 16.0f / 9.0f;
112 width = rcMin(displayMode.w, (int)(displayMode.h * aspect)) - 80;
113 height = displayMode.h - 80;
114 }
115
116 SDL_Window* window;
117 SDL_Renderer* renderer;
118 int errorCode = SDL_CreateWindowAndRenderer(width, height, flags, &window, &renderer);
119
120 if (errorCode != 0 || !window || !renderer)
121 {
122 printf("Could not initialise SDL opengl\nError: %s\n", SDL_GetError());
123 return -1;
124 }
125
126 SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
127 SDL_GL_CreateContext(window);
128
129 if (!imguiRenderGLInit("DroidSans.ttf"))
130 {
131 printf("Could not init GUI renderer.\n");
132 SDL_Quit();
133 return -1;
134 }
135
136 float t = 0.0f;
137 float timeAcc = 0.0f;
138 Uint32 prevFrameTime = SDL_GetTicks();
139 int mousePos[2] = {0, 0};
140 int origMousePos[2] = {0, 0}; // Used to compute mouse movement totals across frames.
141
142 float cameraEulers[] = {45, -45};
143 float cameraPos[] = {0, 0, 0};
144 float camr = 1000;
145 float origCameraEulers[] = {0, 0}; // Used to compute rotational changes across frames.
146
147 float moveFront = 0.0f, moveBack = 0.0f, moveLeft = 0.0f, moveRight = 0.0f, moveUp = 0.0f, moveDown = 0.0f;
148
149 float scrollZoom = 0;
150 bool rotate = false;
151 bool movedDuringRotate = false;
152 float rayStart[3];
153 float rayEnd[3];
154 bool mouseOverMenu = false;
155
156 bool showMenu = !presentationMode;
157 bool showLog = false;
158 bool showTools = true;
159 bool showLevels = false;
160 bool showSample = false;
161 bool showTestCases = false;
162
163 // Window scroll positions.
164 int propScroll = 0;
165 int logScroll = 0;
166 int toolsScroll = 0;
167
168 string sampleName = "Choose Sample...";
169
170 vector<string> files;
171 const string meshesFolder = "Meshes";
172 string meshName = "Choose Mesh...";
173
174 float markerPosition[3] = {0, 0, 0};
175 bool markerPositionSet = false;
176
177 InputGeom* geom = 0;
178 Sample* sample = 0;
179
180 const string testCasesFolder = "TestCases";
181 TestCase* test = 0;
182
183 BuildContext ctx;
184
185 // Fog.
186 float fogColor[4] = { 0.32f, 0.31f, 0.30f, 1.0f };
187 glEnable(GL_FOG);
188 glFogi(GL_FOG_MODE, GL_LINEAR);
189 glFogf(GL_FOG_START, camr * 0.1f);
190 glFogf(GL_FOG_END, camr * 1.25f);
191 glFogfv(GL_FOG_COLOR, fogColor);
192
193 glEnable(GL_CULL_FACE);
194 glDepthFunc(GL_LEQUAL);
195
196 bool done = false;
197 while(!done)
198 {
199 // Handle input events.
200 int mouseScroll = 0;
201 bool processHitTest = false;
202 bool processHitTestShift = false;
203 SDL_Event event;
204
205 while (SDL_PollEvent(&event))
206 {
207 switch (event.type)
208 {
209 case SDL_KEYDOWN:
210 // Handle any key presses here.
211 if (event.key.keysym.sym == SDLK_ESCAPE)
212 {
213 done = true;
214 }
215 else if (event.key.keysym.sym == SDLK_t)
216 {
217 showLevels = false;
218 showSample = false;
219 showTestCases = true;
220 scanDirectory(testCasesFolder, ".txt", files);
221 }
222 else if (event.key.keysym.sym == SDLK_TAB)
223 {
224 showMenu = !showMenu;
225 }
226 else if (event.key.keysym.sym == SDLK_SPACE)
227 {
228 if (sample)
229 sample->handleToggle();
230 }
231 else if (event.key.keysym.sym == SDLK_1)
232 {
233 if (sample)
234 sample->handleStep();
235 }
236 else if (event.key.keysym.sym == SDLK_9)
237 {
238 if (sample && geom)
239 {
240 string savePath = meshesFolder + "/";
241 BuildSettings settings;
242 memset(&settings, 0, sizeof(settings));
243
244 rcVcopy(settings.navMeshBMin, geom->getNavMeshBoundsMin());
245 rcVcopy(settings.navMeshBMax, geom->getNavMeshBoundsMax());
246
247 sample->collectSettings(settings);
248
249 geom->saveGeomSet(&settings);
250 }
251 }
252 break;
253
254 case SDL_MOUSEWHEEL:
255 if (event.wheel.y < 0)
256 {
257 // wheel down
258 if (mouseOverMenu)
259 {
260 mouseScroll++;
261 }
262 else
263 {
264 scrollZoom += 1.0f;
265 }
266 }
267 else
268 {
269 if (mouseOverMenu)
270 {
271 mouseScroll--;
272 }
273 else
274 {
275 scrollZoom -= 1.0f;
276 }
277 }
278 break;
279 case SDL_MOUSEBUTTONDOWN:
280 if (event.button.button == SDL_BUTTON_RIGHT)
281 {
282 if (!mouseOverMenu)
283 {
284 // Rotate view
285 rotate = true;
286 movedDuringRotate = false;
287 origMousePos[0] = mousePos[0];
288 origMousePos[1] = mousePos[1];
289 origCameraEulers[0] = cameraEulers[0];
290 origCameraEulers[1] = cameraEulers[1];
291 }
292 }
293 break;
294
295 case SDL_MOUSEBUTTONUP:
296 // Handle mouse clicks here.
297 if (event.button.button == SDL_BUTTON_RIGHT)
298 {
299 rotate = false;
300 if (!mouseOverMenu)
301 {
302 if (!movedDuringRotate)
303 {
304 processHitTest = true;
305 processHitTestShift = true;
306 }
307 }
308 }
309 else if (event.button.button == SDL_BUTTON_LEFT)
310 {
311 if (!mouseOverMenu)
312 {
313 processHitTest = true;
314 processHitTestShift = (SDL_GetModState() & KMOD_SHIFT) ? true : false;
315 }
316 }
317
318 break;
319
320 case SDL_MOUSEMOTION:
321 mousePos[0] = event.motion.x;
322 mousePos[1] = height-1 - event.motion.y;
323
324 if (rotate)
325 {
326 int dx = mousePos[0] - origMousePos[0];
327 int dy = mousePos[1] - origMousePos[1];
328 cameraEulers[0] = origCameraEulers[0] - dy * 0.25f;
329 cameraEulers[1] = origCameraEulers[1] + dx * 0.25f;
330 if (dx * dx + dy * dy > 3 * 3)
331 {
332 movedDuringRotate = true;
333 }
334 }
335 break;
336
337 case SDL_QUIT:
338 done = true;
339 break;
340
341 default:
342 break;
343 }
344 }
345
346 unsigned char mouseButtonMask = 0;
347 if (SDL_GetMouseState(0, 0) & SDL_BUTTON_LMASK)
348 mouseButtonMask |= IMGUI_MBUT_LEFT;
349 if (SDL_GetMouseState(0, 0) & SDL_BUTTON_RMASK)
350 mouseButtonMask |= IMGUI_MBUT_RIGHT;
351
352 Uint32 time = SDL_GetTicks();
353 float dt = (time - prevFrameTime) / 1000.0f;
354 prevFrameTime = time;
355
356 t += dt;
357
358 // Hit test mesh.
359 if (processHitTest && geom && sample)
360 {
361 float hitTime;
362 bool hit = geom->raycastMesh(rayStart, rayEnd, hitTime);
363
364 if (hit)
365 {
366 if (SDL_GetModState() & KMOD_CTRL)
367 {
368 // Marker
369 markerPositionSet = true;
370 markerPosition[0] = rayStart[0] + (rayEnd[0] - rayStart[0]) * hitTime;
371 markerPosition[1] = rayStart[1] + (rayEnd[1] - rayStart[1]) * hitTime;
372 markerPosition[2] = rayStart[2] + (rayEnd[2] - rayStart[2]) * hitTime;
373 }
374 else
375 {
376 float pos[3];
377 pos[0] = rayStart[0] + (rayEnd[0] - rayStart[0]) * hitTime;
378 pos[1] = rayStart[1] + (rayEnd[1] - rayStart[1]) * hitTime;
379 pos[2] = rayStart[2] + (rayEnd[2] - rayStart[2]) * hitTime;
380 sample->handleClick(rayStart, pos, processHitTestShift);
381 }
382 }
383 else
384 {
385 if (SDL_GetModState() & KMOD_CTRL)
386 {
387 // Marker
388 markerPositionSet = false;
389 }
390 }
391 }
392
393 // Update sample simulation.
394 const float SIM_RATE = 20;
395 const float DELTA_TIME = 1.0f / SIM_RATE;
396 timeAcc = rcClamp(timeAcc + dt, -1.0f, 1.0f);
397 int simIter = 0;
398 while (timeAcc > DELTA_TIME)
399 {
400 timeAcc -= DELTA_TIME;
401 if (simIter < 5 && sample)
402 {
403 sample->handleUpdate(DELTA_TIME);
404 }
405 simIter++;
406 }
407
408 // Clamp the framerate so that we do not hog all the CPU.
409 const float MIN_FRAME_TIME = 1.0f / 40.0f;
410 if (dt < MIN_FRAME_TIME)
411 {
412 int ms = (int)((MIN_FRAME_TIME - dt) * 1000.0f);
413 if (ms > 10) ms = 10;
414 if (ms >= 0) SDL_Delay(ms);
415 }
416
417 // Set the viewport.
418 glViewport(0, 0, width, height);
419 GLint viewport[4];
420 glGetIntegerv(GL_VIEWPORT, viewport);
421
422 // Clear the screen
423 glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
424 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
425 glEnable(GL_BLEND);
426 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
427 glDisable(GL_TEXTURE_2D);
428 glEnable(GL_DEPTH_TEST);
429
430 // Compute the projection matrix.
431 glMatrixMode(GL_PROJECTION);
432 glLoadIdentity();
433 gluPerspective(50.0f, (float)width/(float)height, 1.0f, camr);
434 GLdouble projectionMatrix[16];
435 glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
436
437 // Compute the modelview matrix.
438 glMatrixMode(GL_MODELVIEW);
439 glLoadIdentity();
440 glRotatef(cameraEulers[0], 1, 0, 0);
441 glRotatef(cameraEulers[1], 0, 1, 0);
442 glTranslatef(-cameraPos[0], -cameraPos[1], -cameraPos[2]);
443 GLdouble modelviewMatrix[16];
444 glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix);
445
446 // Get hit ray position and direction.
447 GLdouble x, y, z;
448 gluUnProject(mousePos[0], mousePos[1], 0.0f, modelviewMatrix, projectionMatrix, viewport, &x, &y, &z);
449 rayStart[0] = (float)x;
450 rayStart[1] = (float)y;
451 rayStart[2] = (float)z;
452 gluUnProject(mousePos[0], mousePos[1], 1.0f, modelviewMatrix, projectionMatrix, viewport, &x, &y, &z);
453 rayEnd[0] = (float)x;
454 rayEnd[1] = (float)y;
455 rayEnd[2] = (float)z;
456
457 // Handle keyboard movement.
458 const Uint8* keystate = SDL_GetKeyboardState(NULL);
459 moveFront = rcClamp(moveFront + dt * 4 * ((keystate[SDL_SCANCODE_W] || keystate[SDL_SCANCODE_UP ]) ? 1 : -1), 0.0f, 1.0f);
460 moveLeft = rcClamp(moveLeft + dt * 4 * ((keystate[SDL_SCANCODE_A] || keystate[SDL_SCANCODE_LEFT ]) ? 1 : -1), 0.0f, 1.0f);
461 moveBack = rcClamp(moveBack + dt * 4 * ((keystate[SDL_SCANCODE_S] || keystate[SDL_SCANCODE_DOWN ]) ? 1 : -1), 0.0f, 1.0f);
462 moveRight = rcClamp(moveRight + dt * 4 * ((keystate[SDL_SCANCODE_D] || keystate[SDL_SCANCODE_RIGHT ]) ? 1 : -1), 0.0f, 1.0f);
463 moveUp = rcClamp(moveUp + dt * 4 * ((keystate[SDL_SCANCODE_Q] || keystate[SDL_SCANCODE_PAGEUP ]) ? 1 : -1), 0.0f, 1.0f);
464 moveDown = rcClamp(moveDown + dt * 4 * ((keystate[SDL_SCANCODE_E] || keystate[SDL_SCANCODE_PAGEDOWN ]) ? 1 : -1), 0.0f, 1.0f);
465
466 float keybSpeed = 22.0f;
467 if (SDL_GetModState() & KMOD_SHIFT)
468 {
469 keybSpeed *= 4.0f;
470 }
471
472 float movex = (moveRight - moveLeft) * keybSpeed * dt;
473 float movey = (moveBack - moveFront) * keybSpeed * dt + scrollZoom * 2.0f;
474 scrollZoom = 0;
475
476 cameraPos[0] += movex * (float)modelviewMatrix[0];
477 cameraPos[1] += movex * (float)modelviewMatrix[4];
478 cameraPos[2] += movex * (float)modelviewMatrix[8];
479
480 cameraPos[0] += movey * (float)modelviewMatrix[2];
481 cameraPos[1] += movey * (float)modelviewMatrix[6];
482 cameraPos[2] += movey * (float)modelviewMatrix[10];
483
484 cameraPos[1] += (moveUp - moveDown) * keybSpeed * dt;
485
486 glEnable(GL_FOG);
487
488 if (sample)
489 sample->handleRender();
490 if (test)
491 test->handleRender();
492
493 glDisable(GL_FOG);
494
495 // Render GUI
496 glDisable(GL_DEPTH_TEST);
497 glMatrixMode(GL_PROJECTION);
498 glLoadIdentity();
499 gluOrtho2D(0, width, 0, height);
500 glMatrixMode(GL_MODELVIEW);
501 glLoadIdentity();
502
503 mouseOverMenu = false;
504
505 imguiBeginFrame(mousePos[0], mousePos[1], mouseButtonMask, mouseScroll);
506
507 if (sample)
508 {
509 sample->handleRenderOverlay((double*)projectionMatrix, (double*)modelviewMatrix, (int*)viewport);
510 }
511 if (test)
512 {
513 if (test->handleRenderOverlay((double*)projectionMatrix, (double*)modelviewMatrix, (int*)viewport))
514 mouseOverMenu = true;
515 }
516
517 // Help text.
518 if (showMenu)
519 {
520 const char msg[] = "W/S/A/D: Move RMB: Rotate";
521 imguiDrawText(280, height-20, IMGUI_ALIGN_LEFT, msg, imguiRGBA(255,255,255,128));
522 }
523
524 if (showMenu)
525 {
526 if (imguiBeginScrollArea("Properties", width-250-10, 10, 250, height-20, &propScroll))
527 mouseOverMenu = true;
528
529 if (imguiCheck("Show Log", showLog))
530 showLog = !showLog;
531 if (imguiCheck("Show Tools", showTools))
532 showTools = !showTools;
533
534 imguiSeparator();
535 imguiLabel("Sample");
536 if (imguiButton(sampleName.c_str()))
537 {
538 if (showSample)
539 {
540 showSample = false;
541 }
542 else
543 {
544 showSample = true;
545 showLevels = false;
546 showTestCases = false;
547 }
548 }
549
550 imguiSeparator();
551 imguiLabel("Input Mesh");
552 if (imguiButton(meshName.c_str()))
553 {
554 if (showLevels)
555 {
556 showLevels = false;
557 }
558 else
559 {
560 showSample = false;
561 showTestCases = false;
562 showLevels = true;
563 scanDirectory(meshesFolder, ".obj", files);
564 scanDirectoryAppend(meshesFolder, ".gset", files);
565 }
566 }
567 if (geom)
568 {
569 char text[64];
570 snprintf(text, 64, "Verts: %.1fk Tris: %.1fk",
571 geom->getMesh()->getVertCount()/1000.0f,
572 geom->getMesh()->getTriCount()/1000.0f);
573 imguiValue(text);
574 }
575 imguiSeparator();
576
577 if (geom && sample)
578 {
579 imguiSeparatorLine();
580
581 sample->handleSettings();
582
583 if (imguiButton("Build"))
584 {
585 ctx.resetLog();
586 if (!sample->handleBuild())
587 {
588 showLog = true;
589 logScroll = 0;
590 }
591 ctx.dumpLog("Build log %s:", meshName.c_str());
592
593 // Clear test.
594 delete test;
595 test = 0;
596 }
597
598 imguiSeparator();
599 }
600
601 if (sample)
602 {
603 imguiSeparatorLine();
604 sample->handleDebugMode();
605 }
606
607 imguiEndScrollArea();
608 }
609
610 // Sample selection dialog.
611 if (showSample)
612 {
613 static int levelScroll = 0;
614 if (imguiBeginScrollArea("Choose Sample", width-10-250-10-200, height-10-250, 200, 250, &levelScroll))
615 mouseOverMenu = true;
616
617 Sample* newSample = 0;
618 for (int i = 0; i < g_nsamples; ++i)
619 {
620 if (imguiItem(g_samples[i].name.c_str()))
621 {
622 newSample = g_samples[i].create();
623 if (newSample)
624 sampleName = g_samples[i].name;
625 }
626 }
627 if (newSample)
628 {
629 delete sample;
630 sample = newSample;
631 sample->setContext(&ctx);
632 if (geom)
633 {
634 sample->handleMeshChanged(geom);
635 }
636 showSample = false;
637 }
638
639 if (geom || sample)
640 {
641 const float* bmin = 0;
642 const float* bmax = 0;
643 if (geom)
644 {
645 bmin = geom->getNavMeshBoundsMin();
646 bmax = geom->getNavMeshBoundsMax();
647 }
648 // Reset camera and fog to match the mesh bounds.
649 if (bmin && bmax)
650 {
651 camr = sqrtf(rcSqr(bmax[0]-bmin[0]) +
652 rcSqr(bmax[1]-bmin[1]) +
653 rcSqr(bmax[2]-bmin[2])) / 2;
654 cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr;
655 cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr;
656 cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr;
657 camr *= 3;
658 }
659 cameraEulers[0] = 45;
660 cameraEulers[1] = -45;
661 glFogf(GL_FOG_START, camr*0.1f);
662 glFogf(GL_FOG_END, camr*1.25f);
663 }
664
665 imguiEndScrollArea();
666 }
667
668 // Level selection dialog.
669 if (showLevels)
670 {
671 static int levelScroll = 0;
672 if (imguiBeginScrollArea("Choose Level", width - 10 - 250 - 10 - 200, height - 10 - 450, 200, 450, &levelScroll))
673 mouseOverMenu = true;
674
675 vector<string>::const_iterator fileIter = files.begin();
676 vector<string>::const_iterator filesEnd = files.end();
677 vector<string>::const_iterator levelToLoad = filesEnd;
678 for (; fileIter != filesEnd; ++fileIter)
679 {
680 if (imguiItem(fileIter->c_str()))
681 {
682 levelToLoad = fileIter;
683 }
684 }
685
686 if (levelToLoad != filesEnd)
687 {
688 meshName = *levelToLoad;
689 showLevels = false;
690
691 delete geom;
692 geom = 0;
693
694 string path = meshesFolder + "/" + meshName;
695
696 geom = new InputGeom;
697 if (!geom->load(&ctx, path))
698 {
699 delete geom;
700 geom = 0;
701
702 // Destroy the sample if it already had geometry loaded, as we've just deleted it!
703 if (sample && sample->getInputGeom())
704 {
705 delete sample;
706 sample = 0;
707 }
708
709 showLog = true;
710 logScroll = 0;
711 ctx.dumpLog("Geom load log %s:", meshName.c_str());
712 }
713 if (sample && geom)
714 {
715 sample->handleMeshChanged(geom);
716 }
717
718 if (geom || sample)
719 {
720 const float* bmin = 0;
721 const float* bmax = 0;
722 if (geom)
723 {
724 bmin = geom->getNavMeshBoundsMin();
725 bmax = geom->getNavMeshBoundsMax();
726 }
727 // Reset camera and fog to match the mesh bounds.
728 if (bmin && bmax)
729 {
730 camr = sqrtf(rcSqr(bmax[0]-bmin[0]) +
731 rcSqr(bmax[1]-bmin[1]) +
732 rcSqr(bmax[2]-bmin[2])) / 2;
733 cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr;
734 cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr;
735 cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr;
736 camr *= 3;
737 }
738 cameraEulers[0] = 45;
739 cameraEulers[1] = -45;
740 glFogf(GL_FOG_START, camr * 0.1f);
741 glFogf(GL_FOG_END, camr * 1.25f);
742 }
743 }
744
745 imguiEndScrollArea();
746
747 }
748
749 // Test cases
750 if (showTestCases)
751 {
752 static int testScroll = 0;
753 if (imguiBeginScrollArea("Choose Test To Run", width-10-250-10-200, height-10-450, 200, 450, &testScroll))
754 mouseOverMenu = true;
755
756 vector<string>::const_iterator fileIter = files.begin();
757 vector<string>::const_iterator filesEnd = files.end();
758 vector<string>::const_iterator testToLoad = filesEnd;
759 for (; fileIter != filesEnd; ++fileIter)
760 {
761 if (imguiItem(fileIter->c_str()))
762 {
763 testToLoad = fileIter;
764 }
765 }
766
767 if (testToLoad != filesEnd)
768 {
769 string path = testCasesFolder + "/" + *testToLoad;
770 test = new TestCase;
771 if (test)
772 {
773 // Load the test.
774 if (!test->load(path))
775 {
776 delete test;
777 test = 0;
778 }
779
780 // Create sample
781 Sample* newSample = 0;
782 for (int i = 0; i < g_nsamples; ++i)
783 {
784 if (g_samples[i].name == test->getSampleName())
785 {
786 newSample = g_samples[i].create();
787 if (newSample)
788 sampleName = g_samples[i].name;
789 }
790 }
791
792 delete sample;
793 sample = newSample;
794
795 if (sample)
796 {
797 sample->setContext(&ctx);
798 showSample = false;
799 }
800
801 // Load geom.
802 meshName = test->getGeomFileName();
803
804
805 path = meshesFolder + "/" + meshName;
806
807 delete geom;
808 geom = new InputGeom;
809 if (!geom || !geom->load(&ctx, path))
810 {
811 delete geom;
812 geom = 0;
813 delete sample;
814 sample = 0;
815 showLog = true;
816 logScroll = 0;
817 ctx.dumpLog("Geom load log %s:", meshName.c_str());
818 }
819 if (sample && geom)
820 {
821 sample->handleMeshChanged(geom);
822 }
823
824 // This will ensure that tile & poly bits are updated in tiled sample.
825 if (sample)
826 sample->handleSettings();
827
828 ctx.resetLog();
829 if (sample && !sample->handleBuild())
830 {
831 ctx.dumpLog("Build log %s:", meshName.c_str());
832 }
833
834 if (geom || sample)
835 {
836 const float* bmin = 0;
837 const float* bmax = 0;
838 if (geom)
839 {
840 bmin = geom->getNavMeshBoundsMin();
841 bmax = geom->getNavMeshBoundsMax();
842 }
843 // Reset camera and fog to match the mesh bounds.
844 if (bmin && bmax)
845 {
846 camr = sqrtf(rcSqr(bmax[0] - bmin[0]) +
847 rcSqr(bmax[1] - bmin[1]) +
848 rcSqr(bmax[2] - bmin[2])) / 2;
849 cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr;
850 cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr;
851 cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr;
852 camr *= 3;
853 }
854 cameraEulers[0] = 45;
855 cameraEulers[1] = -45;
856 glFogf(GL_FOG_START, camr * 0.2f);
857 glFogf(GL_FOG_END, camr * 1.25f);
858 }
859
860 // Do the tests.
861 if (sample)
862 test->doTests(sample->getNavMesh(), sample->getNavMeshQuery());
863 }
864 }
865
866 imguiEndScrollArea();
867 }
868
869
870 // Log
871 if (showLog && showMenu)
872 {
873 if (imguiBeginScrollArea("Log", 250 + 20, 10, width - 300 - 250, 200, &logScroll))
874 mouseOverMenu = true;
875 for (int i = 0; i < ctx.getLogCount(); ++i)
876 imguiLabel(ctx.getLogText(i));
877 imguiEndScrollArea();
878 }
879
880 // Left column tools menu
881 if (!showTestCases && showTools && showMenu) // && geom && sample)
882 {
883 if (imguiBeginScrollArea("Tools", 10, 10, 250, height - 20, &toolsScroll))
884 mouseOverMenu = true;
885
886 if (sample)
887 sample->handleTools();
888
889 imguiEndScrollArea();
890 }
891
892 // Marker
893 if (markerPositionSet && gluProject((GLdouble)markerPosition[0], (GLdouble)markerPosition[1], (GLdouble)markerPosition[2],
894 modelviewMatrix, projectionMatrix, viewport, &x, &y, &z))
895 {
896 // Draw marker circle
897 glLineWidth(5.0f);
898 glColor4ub(240,220,0,196);
899 glBegin(GL_LINE_LOOP);
900 const float r = 25.0f;
901 for (int i = 0; i < 20; ++i)
902 {
903 const float a = (float)i / 20.0f * RC_PI*2;
904 const float fx = (float)x + cosf(a)*r;
905 const float fy = (float)y + sinf(a)*r;
906 glVertex2f(fx,fy);
907 }
908 glEnd();
909 glLineWidth(1.0f);
910 }
911
912 imguiEndFrame();
913 imguiRenderGLDraw();
914
915 glEnable(GL_DEPTH_TEST);
916 SDL_GL_SwapWindow(window);
917 }
918
919 imguiRenderGLDestroy();
920
921 SDL_Quit();
922
923 delete sample;
924 delete geom;
925
926 return 0;
927 }
928