void OffMeshConnectionTool::handleClick(const float* /*s*/, const float* p, bool shift) { if (!m_sample) return; InputGeom* geom = m_sample->getInputGeom(); if (!geom) return; if (shift) { // Delete // Find nearest link end-point float nearestDist = FLT_MAX; int nearestIndex = -1; const float* verts = geom->getOffMeshConnectionVerts(); for (int i = 0; i < geom->getOffMeshConnectionCount()*2; ++i) { const float* v = &verts[i*3]; float d = rcVdistSqr(p, v); if (d < nearestDist) { nearestDist = d; nearestIndex = i/2; // Each link has two vertices. } } // If end point close enough, delete it. if (nearestIndex != -1 && sqrtf(nearestDist) < m_sample->getAgentRadius()) { geom->deleteOffMeshConnection(nearestIndex); } } else { // Create if (!m_hitPosSet) { rcVcopy(m_hitPos, p); m_hitPosSet = true; } else { const unsigned char area = SAMPLE_POLYAREA_JUMP; const unsigned short flags = SAMPLE_POLYFLAGS_JUMP; geom->addOffMeshConnection(m_hitPos, p, m_sample->getAgentRadius(), m_bidir ? 1 : 0, area, flags); m_hitPosSet = false; float* v = new float[6]; rcVcopy(&v[0], m_hitPos); rcVcopy(&v[3], p); printf("572 29,29 (%.3f %.3f %.3f) (%.3f %.3f %.3f) 5.0f", v[0],v[1], v[2],v[3],v[4],v[5]); } } }
void OffMeshConnectionTool::handleRender() { DebugDrawGL dd; const float s = m_sample->getAgentRadius(); if (m_hitPosSet) duDebugDrawCross(&dd, m_hitPos[0],m_hitPos[1]+0.1f,m_hitPos[2], s, duRGBA(0,0,0,128), 2.0f); InputGeom* geom = m_sample->getInputGeom(); if (geom) geom->drawOffMeshConnections(&dd, true); }
virtual void process(struct dtNavMeshCreateParams* params, navAreaMask* areaMasks) { // Update poly flags from areas. /*for (int i = 0; i < params->polyCount; ++i) { if (polyAreas[i] == DT_TILECACHE_WALKABLE_AREA) polyAreas[i] = SAMPLE_POLYAREA_GROUND; if (polyAreas[i] == SAMPLE_POLYAREA_GROUND || polyAreas[i] == SAMPLE_POLYAREA_GRASS || polyAreas[i] == SAMPLE_POLYAREA_ROAD) { polyFlags[i] = AREAFLAGS_WALK; } else if (polyAreas[i] == SAMPLE_POLYAREA_WATER) { polyFlags[i] = AREAFLAGS_SWIM; } else if (polyAreas[i] == SAMPLE_POLYAREA_DOOR) { polyFlags[i] = AREAFLAGS_WALK | AREAFLAGS_DOOR; } }*/ // Pass in off-mesh connections. if (m_geom) { params->offMeshConVerts = m_geom->getOffMeshConnectionVerts(); params->offMeshConRad = m_geom->getOffMeshConnectionRads(); params->offMeshConDir = m_geom->getOffMeshConnectionDirs(); params->offMeshConAreaFlags = m_geom->getOffMeshConnectionAreaMask(); params->offMeshConUserID = m_geom->getOffMeshConnectionId(); params->offMeshConCount = m_geom->getOffMeshConnectionCount(); } }
int main(int /*argc*/, char** /*argv*/) { // Init SDL if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { printf("Could not initialise SDL\n"); return -1; } // Center window char env[] = "SDL_VIDEO_CENTERED=1"; putenv(env); // Init OpenGL SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); //#ifndef WIN32 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); //#endif const SDL_VideoInfo* vi = SDL_GetVideoInfo(); bool presentationMode = false; int width, height; SDL_Surface* screen = 0; if (presentationMode) { width = 1700; height = 1000; screen = SDL_SetVideoMode(width, height, 0, SDL_OPENGL|SDL_FULLSCREEN); } else { width = 1700; height = 1000; screen = SDL_SetVideoMode(width, height, 0, SDL_OPENGL); } if (!screen) { printf("Could not initialise SDL opengl\n"); return -1; } glEnable(GL_MULTISAMPLE); SDL_WM_SetCaption("Recast Demo", 0); if (!imguiRenderGLInit("DroidSans.ttf")) { printf("Could not init GUI renderer.\n"); SDL_Quit(); return -1; } float t = 0.0f; float timeAcc = 0.0f; Uint32 lastTime = SDL_GetTicks(); int mx = 0, my = 0; float rx = 45; float ry = -45; float moveW = 0, moveS = 0, moveA = 0, moveD = 0; float camx = 0, camy = 0, camz = 0, camr = 1000; float origrx = 0, origry = 0; int origx = 0, origy = 0; float scrollZoom = 0; bool rotate = false; bool movedDuringRotate = false; float rays[3], raye[3]; bool mouseOverMenu = false; bool showMenu = !presentationMode; bool showLog = false; bool showTools = true; bool showLevels = false; bool showSample = false; bool showTestCases = false; int propScroll = 0; int logScroll = 0; int toolsScroll = 0; char sampleName[64] = "Choose Sample..."; FileList files; char meshName[128] = "Choose Mesh..."; float mpos[3] = {0,0,0}; bool mposSet = false; SlideShow slideShow; slideShow.init("slides/"); InputGeom* geom = 0; Sample* sample = 0; TestCase* test = 0; BuildContext ctx; glEnable(GL_CULL_FACE); float fogCol[4] = { 0.32f, 0.31f, 0.30f, 1.0f }; glEnable(GL_FOG); glFogi(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_START, camr*0.1f); glFogf(GL_FOG_END, camr*1.25f); glFogfv(GL_FOG_COLOR, fogCol); glDepthFunc(GL_LEQUAL); bool done = false; while(!done) { // Handle input events. int mscroll = 0; bool processHitTest = false; bool processHitTestShift = false; SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_KEYDOWN: // Handle any key presses here. if (event.key.keysym.sym == SDLK_ESCAPE) { done = true; } else if (event.key.keysym.sym == SDLK_t) { showLevels = false; showSample = false; showTestCases = true; scanDirectory("Tests", ".txt", files); } else if (event.key.keysym.sym == SDLK_TAB) { showMenu = !showMenu; } else if (event.key.keysym.sym == SDLK_SPACE) { if (sample) sample->handleToggle(); } else if (event.key.keysym.sym == SDLK_1) { if (sample) sample->handleStep(); } else if (event.key.keysym.sym == SDLK_9) { if (geom) geom->save("geomset.txt"); } else if (event.key.keysym.sym == SDLK_0) { delete geom; geom = new InputGeom; if (!geom || !geom->load(&ctx, "geomset.txt")) { delete geom; geom = 0; showLog = true; logScroll = 0; ctx.dumpLog("Geom load log %s:", meshName); } if (sample && geom) { sample->handleMeshChanged(geom); } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; if (sample) { bmin = sample->getBoundsMin(); bmax = sample->getBoundsMax(); } else if (geom) { bmin = geom->getMeshBoundsMin(); bmax = geom->getMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) { camr = sqrtf(rcSqr(bmax[0]-bmin[0]) + rcSqr(bmax[1]-bmin[1]) + rcSqr(bmax[2]-bmin[2])) / 2; camx = (bmax[0] + bmin[0]) / 2 + camr; camy = (bmax[1] + bmin[1]) / 2 + camr; camz = (bmax[2] + bmin[2]) / 2 + camr; camr *= 3; } rx = 45; ry = -45; glFogf(GL_FOG_START, camr*0.2f); glFogf(GL_FOG_END, camr*1.25f); } } else if (event.key.keysym.sym == SDLK_RIGHT) { slideShow.nextSlide(); } else if (event.key.keysym.sym == SDLK_LEFT) { slideShow.prevSlide(); } break; case SDL_MOUSEBUTTONDOWN: if (event.button.button == SDL_BUTTON_RIGHT) { if (!mouseOverMenu) { // Rotate view rotate = true; movedDuringRotate = false; origx = mx; origy = my; origrx = rx; origry = ry; } } else if (event.button.button == SDL_BUTTON_WHEELUP) { if (mouseOverMenu) mscroll--; else scrollZoom -= 1.0f; } else if (event.button.button == SDL_BUTTON_WHEELDOWN) { if (mouseOverMenu) mscroll++; else scrollZoom += 1.0f; } break; case SDL_MOUSEBUTTONUP: // Handle mouse clicks here. if (event.button.button == SDL_BUTTON_RIGHT) { rotate = false; if (!mouseOverMenu) { if (!movedDuringRotate) { processHitTest = true; processHitTestShift = true; } } } else if (event.button.button == SDL_BUTTON_LEFT) { if (!mouseOverMenu) { processHitTest = true; processHitTestShift = (SDL_GetModState() & KMOD_SHIFT) ? true : false; } } break; case SDL_MOUSEMOTION: mx = event.motion.x; my = height-1 - event.motion.y; if (rotate) { int dx = mx - origx; int dy = my - origy; rx = origrx - dy*0.25f; ry = origry + dx*0.25f; if (dx*dx+dy*dy > 3*3) movedDuringRotate = true; } break; case SDL_QUIT: done = true; break; default: break; } } unsigned char mbut = 0; if (SDL_GetMouseState(0,0) & SDL_BUTTON_LMASK) mbut |= IMGUI_MBUT_LEFT; if (SDL_GetMouseState(0,0) & SDL_BUTTON_RMASK) mbut |= IMGUI_MBUT_RIGHT; Uint32 time = SDL_GetTicks(); float dt = (time - lastTime) / 1000.0f; lastTime = time; t += dt; // Hit test mesh. if (processHitTest && geom && sample) { float hitt; bool hit = geom->raycastMesh(rays, raye, hitt); if (hit) { if (SDL_GetModState() & KMOD_CTRL) { // Marker mposSet = true; mpos[0] = rays[0] + (raye[0] - rays[0])*hitt; mpos[1] = rays[1] + (raye[1] - rays[1])*hitt; mpos[2] = rays[2] + (raye[2] - rays[2])*hitt; } else { float pos[3]; pos[0] = rays[0] + (raye[0] - rays[0])*hitt; pos[1] = rays[1] + (raye[1] - rays[1])*hitt; pos[2] = rays[2] + (raye[2] - rays[2])*hitt; sample->handleClick(rays, pos, processHitTestShift); } } else { if (SDL_GetModState() & KMOD_CTRL) { // Marker mposSet = false; } } } // Update sample simulation. const float SIM_RATE = 20; const float DELTA_TIME = 1.0f/SIM_RATE; timeAcc = rcClamp(timeAcc+dt, -1.0f, 1.0f); int simIter = 0; while (timeAcc > DELTA_TIME) { timeAcc -= DELTA_TIME; if (simIter < 5) { if (sample) sample->handleUpdate(DELTA_TIME); } simIter++; } // Clamp the framerate so that we do not hog all the CPU. const float MIN_FRAME_TIME = 1.0f/40.0f; if (dt < MIN_FRAME_TIME) { int ms = (int)((MIN_FRAME_TIME - dt)*1000.0f); if (ms > 10) ms = 10; if (ms >= 0) SDL_Delay(ms); } // Update and render glViewport(0, 0, width, height); glClearColor(0.3f, 0.3f, 0.32f, 1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_TEXTURE_2D); // Render 3d glEnable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(50.0f, (float)width/(float)height, 1.0f, camr); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(rx,1,0,0); glRotatef(ry,0,1,0); glTranslatef(-camx, -camy, -camz); // Get hit ray position and direction. GLdouble proj[16]; GLdouble model[16]; GLint view[4]; glGetDoublev(GL_PROJECTION_MATRIX, proj); glGetDoublev(GL_MODELVIEW_MATRIX, model); glGetIntegerv(GL_VIEWPORT, view); GLdouble x, y, z; gluUnProject(mx, my, 0.0f, model, proj, view, &x, &y, &z); rays[0] = (float)x; rays[1] = (float)y; rays[2] = (float)z; gluUnProject(mx, my, 1.0f, model, proj, view, &x, &y, &z); raye[0] = (float)x; raye[1] = (float)y; raye[2] = (float)z; // Handle keyboard movement. Uint8* keystate = SDL_GetKeyState(NULL); moveW = rcClamp(moveW + dt * 4 * (keystate[SDLK_w] ? 1 : -1), 0.0f, 1.0f); moveS = rcClamp(moveS + dt * 4 * (keystate[SDLK_s] ? 1 : -1), 0.0f, 1.0f); moveA = rcClamp(moveA + dt * 4 * (keystate[SDLK_a] ? 1 : -1), 0.0f, 1.0f); moveD = rcClamp(moveD + dt * 4 * (keystate[SDLK_d] ? 1 : -1), 0.0f, 1.0f); float keybSpeed = 22.0f; if (SDL_GetModState() & KMOD_SHIFT) keybSpeed *= 4.0f; float movex = (moveD - moveA) * keybSpeed * dt; float movey = (moveS - moveW) * keybSpeed * dt; movey += scrollZoom * 2.0f; scrollZoom = 0; camx += movex * (float)model[0]; camy += movex * (float)model[4]; camz += movex * (float)model[8]; camx += movey * (float)model[2]; camy += movey * (float)model[6]; camz += movey * (float)model[10]; glEnable(GL_FOG); if (sample) sample->handleRender(); if (test) test->handleRender(); glDisable(GL_FOG); // Render GUI glDisable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, width, 0, height); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); mouseOverMenu = false; imguiBeginFrame(mx,my,mbut,mscroll); if (sample) { sample->handleRenderOverlay((double*)proj, (double*)model, (int*)view); } if (test) { if (test->handleRenderOverlay((double*)proj, (double*)model, (int*)view)) mouseOverMenu = true; } // Help text. if (showMenu) { const char msg[] = "W/S/A/D: Move RMB: Rotate"; imguiDrawText(280, height-20, IMGUI_ALIGN_LEFT, msg, imguiRGBA(255,255,255,128)); } if (showMenu) { if (imguiBeginScrollArea("Properties", width-250-10, 10, 250, height-20, &propScroll)) mouseOverMenu = true; if (imguiCheck("Show Log", showLog)) showLog = !showLog; if (imguiCheck("Show Tools", showTools)) showTools = !showTools; imguiSeparator(); imguiLabel("Sample"); if (imguiButton(sampleName)) { if (showSample) { showSample = false; } else { showSample = true; showLevels = false; showTestCases = false; } } imguiSeparator(); imguiLabel("Input Mesh"); if (imguiButton(meshName)) { if (showLevels) { showLevels = false; } else { showSample = false; showTestCases = false; showLevels = true; scanDirectory("Meshes", ".obj", files); } } if (geom) { char text[64]; snprintf(text, 64, "Verts: %.1fk Tris: %.1fk", geom->getMesh()->getVertCount()/1000.0f, geom->getMesh()->getTriCount()/1000.0f); imguiValue(text); } imguiSeparator(); if (geom && sample) { imguiSeparatorLine(); sample->handleSettings(); if (imguiButton("Build")) { ctx.resetLog(); if (!sample->handleBuild()) { showLog = true; logScroll = 0; } ctx.dumpLog("Build log %s:", meshName); // Clear test. delete test; test = 0; } imguiSeparator(); } if (sample) { imguiSeparatorLine(); sample->handleDebugMode(); } imguiEndScrollArea(); } // Sample selection dialog. if (showSample) { static int levelScroll = 0; if (imguiBeginScrollArea("Choose Sample", width-10-250-10-200, height-10-250, 200, 250, &levelScroll)) mouseOverMenu = true; Sample* newSample = 0; for (int i = 0; i < g_nsamples; ++i) { if (imguiItem(g_samples[i].name)) { newSample = g_samples[i].create(); if (newSample) strcpy(sampleName, g_samples[i].name); } } if (newSample) { delete sample; sample = newSample; sample->setContext(&ctx); if (geom && sample) { sample->handleMeshChanged(geom); } showSample = false; } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; if (sample) { bmin = sample->getBoundsMin(); bmax = sample->getBoundsMax(); } else if (geom) { bmin = geom->getMeshBoundsMin(); bmax = geom->getMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) { camr = sqrtf(rcSqr(bmax[0]-bmin[0]) + rcSqr(bmax[1]-bmin[1]) + rcSqr(bmax[2]-bmin[2])) / 2; camx = (bmax[0] + bmin[0]) / 2 + camr; camy = (bmax[1] + bmin[1]) / 2 + camr; camz = (bmax[2] + bmin[2]) / 2 + camr; camr *= 3; } rx = 45; ry = -45; glFogf(GL_FOG_START, camr*0.1f); glFogf(GL_FOG_END, camr*1.25f); } imguiEndScrollArea(); } // Level selection dialog. if (showLevels) { static int levelScroll = 0; if (imguiBeginScrollArea("Choose Level", width-10-250-10-200, height-10-450, 200, 450, &levelScroll)) mouseOverMenu = true; int levelToLoad = -1; for (int i = 0; i < files.size; ++i) { if (imguiItem(files.files[i])) levelToLoad = i; } if (levelToLoad != -1) { strncpy(meshName, files.files[levelToLoad], sizeof(meshName)); meshName[sizeof(meshName)-1] = '\0'; showLevels = false; delete geom; geom = 0; char path[256]; strcpy(path, "Meshes/"); strcat(path, meshName); geom = new InputGeom; if (!geom || !geom->loadMesh(&ctx, path)) { delete geom; geom = 0; showLog = true; logScroll = 0; ctx.dumpLog("Geom load log %s:", meshName); } if (sample && geom) { sample->handleMeshChanged(geom); } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; if (sample) { bmin = sample->getBoundsMin(); bmax = sample->getBoundsMax(); } else if (geom) { bmin = geom->getMeshBoundsMin(); bmax = geom->getMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) { camr = sqrtf(rcSqr(bmax[0]-bmin[0]) + rcSqr(bmax[1]-bmin[1]) + rcSqr(bmax[2]-bmin[2])) / 2; camx = (bmax[0] + bmin[0]) / 2 + camr; camy = (bmax[1] + bmin[1]) / 2 + camr; camz = (bmax[2] + bmin[2]) / 2 + camr; camr *= 3; } rx = 45; ry = -45; glFogf(GL_FOG_START, camr*0.1f); glFogf(GL_FOG_END, camr*1.25f); } } imguiEndScrollArea(); } // Test cases if (showTestCases) { static int testScroll = 0; if (imguiBeginScrollArea("Choose Test To Run", width-10-250-10-200, height-10-450, 200, 450, &testScroll)) mouseOverMenu = true; int testToLoad = -1; for (int i = 0; i < files.size; ++i) { if (imguiItem(files.files[i])) testToLoad = i; } if (testToLoad != -1) { char path[256]; strcpy(path, "Tests/"); strcat(path, files.files[testToLoad]); test = new TestCase; if (test) { // Load the test. if (!test->load(path)) { delete test; test = 0; } // Create sample Sample* newSample = 0; for (int i = 0; i < g_nsamples; ++i) { if (strcmp(g_samples[i].name, test->getSampleName()) == 0) { newSample = g_samples[i].create(); if (newSample) strcpy(sampleName, g_samples[i].name); } } if (newSample) { delete sample; sample = newSample; sample->setContext(&ctx); showSample = false; } // Load geom. strcpy(meshName, test->getGeomFileName()); meshName[sizeof(meshName)-1] = '\0'; delete geom; geom = 0; strcpy(path, "Meshes/"); strcat(path, meshName); geom = new InputGeom; if (!geom || !geom->loadMesh(&ctx, path)) { delete geom; geom = 0; showLog = true; logScroll = 0; ctx.dumpLog("Geom load log %s:", meshName); } if (sample && geom) { sample->handleMeshChanged(geom); } // This will ensure that tile & poly bits are updated in tiled sample. if (sample) sample->handleSettings(); ctx.resetLog(); if (sample && !sample->handleBuild()) { ctx.dumpLog("Build log %s:", meshName); } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; if (sample) { bmin = sample->getBoundsMin(); bmax = sample->getBoundsMax(); } else if (geom) { bmin = geom->getMeshBoundsMin(); bmax = geom->getMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) { camr = sqrtf(rcSqr(bmax[0]-bmin[0]) + rcSqr(bmax[1]-bmin[1]) + rcSqr(bmax[2]-bmin[2])) / 2; camx = (bmax[0] + bmin[0]) / 2 + camr; camy = (bmax[1] + bmin[1]) / 2 + camr; camz = (bmax[2] + bmin[2]) / 2 + camr; camr *= 3; } rx = 45; ry = -45; glFogf(GL_FOG_START, camr*0.2f); glFogf(GL_FOG_END, camr*1.25f); } // Do the tests. if (sample) test->doTests(sample->getNavMesh(), sample->getNavMeshQuery()); } } imguiEndScrollArea(); } // Log if (showLog && showMenu) { if (imguiBeginScrollArea("Log", 250+20, 10, width - 300 - 250, 200, &logScroll)) mouseOverMenu = true; for (int i = 0; i < ctx.getLogCount(); ++i) imguiLabel(ctx.getLogText(i)); imguiEndScrollArea(); } // Tools if (!showTestCases && showTools && showMenu) // && geom && sample) { if (imguiBeginScrollArea("Tools", 10, 10, 250, height-20, &toolsScroll)) mouseOverMenu = true; if (sample) sample->handleTools(); imguiEndScrollArea(); } slideShow.updateAndDraw(dt, (float)width, (float)height); // Marker if (mposSet && gluProject((GLdouble)mpos[0], (GLdouble)mpos[1], (GLdouble)mpos[2], model, proj, view, &x, &y, &z)) { // Draw marker circle glLineWidth(5.0f); glColor4ub(240,220,0,196); glBegin(GL_LINE_LOOP); const float r = 25.0f; for (int i = 0; i < 20; ++i) { const float a = (float)i / 20.0f * RC_PI*2; const float fx = (float)x + cosf(a)*r; const float fy = (float)y + sinf(a)*r; glVertex2f(fx,fy); } glEnd(); glLineWidth(1.0f); } imguiEndFrame(); imguiRenderGLDraw(); glEnable(GL_DEPTH_TEST); SDL_GL_SwapBuffers(); } imguiRenderGLDestroy(); SDL_Quit(); delete sample; delete geom; return 0; }
void ConvexVolumeTool::handleClick(const float* /*s*/, const float* p, bool shift) { if (!m_sample) return; InputGeom* geom = m_sample->getInputGeom(); if (!geom) return; if (shift) { // Delete int nearestIndex = -1; const ConvexVolume* vols = geom->getConvexVolumes(); for (int i = 0; i < geom->getConvexVolumeCount(); ++i) { if (pointInPoly(vols[i].nverts, vols[i].verts, p) && p[1] >= vols[i].hmin && p[1] <= vols[i].hmax) { nearestIndex = i; } } // If end point close enough, delete it. if (nearestIndex != -1) { geom->deleteConvexVolume(nearestIndex); } } else { // Create // If clicked on that last pt, create the shape. if (m_npts && rcVdistSqr(p, &m_pts[(m_npts-1)*3]) < rcSqr(0.2f)) { if (m_nhull > 2) { // Create shape. float verts[MAX_PTS*3]; for (int i = 0; i < m_nhull; ++i) rcVcopy(&verts[i*3], &m_pts[m_hull[i]*3]); float minh = FLT_MAX, maxh = 0; for (int i = 0; i < m_nhull; ++i) minh = rcMin(minh, verts[i*3+1]); minh -= m_boxDescent; maxh = minh + m_boxHeight; geom->addConvexVolume(verts, m_nhull, minh, maxh, (unsigned char)m_areaType); } m_npts = 0; m_nhull = 0; } else { // Add new point if (m_npts < MAX_PTS) { rcVcopy(&m_pts[m_npts*3], p); m_npts++; // Update hull. if (m_npts > 1) m_nhull = convexhull(m_pts, m_npts, m_hull); else m_nhull = 0; } } } }
int main(int /*argc*/, char** /*argv*/) { // Init SDL if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { printf("Could not initialise SDL.\nError: %s\n", SDL_GetError()); return -1; } // Enable depth buffer. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); // Set color channel depth. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); // 4x MSAA. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); SDL_DisplayMode displayMode; SDL_GetCurrentDisplayMode(0, &displayMode); bool presentationMode = false; Uint32 flags = SDL_WINDOW_OPENGL; int width; int height; if (presentationMode) { // Create a fullscreen window at the native resolution. width = displayMode.w; height = displayMode.h; flags |= SDL_WINDOW_FULLSCREEN; } else { float aspect = 16.0f / 9.0f; width = rcMin(displayMode.w, (int)(displayMode.h * aspect)) - 80; height = displayMode.h - 80; } SDL_Window* window; SDL_Renderer* renderer; int errorCode = SDL_CreateWindowAndRenderer(width, height, flags, &window, &renderer); if (errorCode != 0 || !window || !renderer) { printf("Could not initialise SDL opengl\nError: %s\n", SDL_GetError()); return -1; } SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); SDL_GL_CreateContext(window); if (!imguiRenderGLInit("DroidSans.ttf")) { printf("Could not init GUI renderer.\n"); SDL_Quit(); return -1; } float t = 0.0f; float timeAcc = 0.0f; Uint32 prevFrameTime = SDL_GetTicks(); int mousePos[2] = {0, 0}; float origMousePos[2] = {0, 0}; // Used to compute mouse movement totals across frames. float cameraEulers[] = {45, -45}; float cameraPos[] = {0, 0, 0}; float camr = 1000; float origCameraEulers[] = {0, 0}; // Used to compute rotational changes across frames. float moveW = 0, moveS = 0, moveA = 0, moveD = 0; float scrollZoom = 0; bool rotate = false; bool movedDuringRotate = false; float rayStart[3]; float rayEnd[3]; bool mouseOverMenu = false; bool showMenu = !presentationMode; bool showLog = false; bool showTools = true; bool showLevels = false; bool showSample = false; bool showTestCases = false; // Window scroll positions. int propScroll = 0; int logScroll = 0; int toolsScroll = 0; char sampleName[64] = "Choose Sample..."; vector<string> files; char meshName[128] = "Choose Mesh..."; float markerPosition[3] = {0, 0, 0}; bool markerPositionSet = false; SlideShow slideShow; slideShow.init("slides/"); InputGeom* geom = 0; Sample* sample = 0; TestCase* test = 0; BuildContext ctx; // Fog. float fogColor[4] = { 0.32f, 0.31f, 0.30f, 1.0f }; glEnable(GL_FOG); glFogi(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_START, camr * 0.1f); glFogf(GL_FOG_END, camr * 1.25f); glFogfv(GL_FOG_COLOR, fogColor); glEnable(GL_CULL_FACE); glDepthFunc(GL_LEQUAL); bool done = false; while(!done) { // Handle input events. int mouseScroll = 0; bool processHitTest = false; bool processHitTestShift = false; SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_KEYDOWN: // Handle any key presses here. if (event.key.keysym.sym == SDLK_ESCAPE) { done = true; } else if (event.key.keysym.sym == SDLK_t) { showLevels = false; showSample = false; showTestCases = true; scanDirectory("TestCases", ".txt", files); } else if (event.key.keysym.sym == SDLK_TAB) { showMenu = !showMenu; } else if (event.key.keysym.sym == SDLK_SPACE) { if (sample) sample->handleToggle(); } else if (event.key.keysym.sym == SDLK_1) { if (sample) sample->handleStep(); } else if (event.key.keysym.sym == SDLK_9) { if (geom) geom->save("geomset.txt"); } else if (event.key.keysym.sym == SDLK_0) { delete geom; geom = new InputGeom; if (!geom || !geom->load(&ctx, "geomset.txt")) { delete geom; geom = 0; showLog = true; logScroll = 0; ctx.dumpLog("Geom load log %s:", meshName); } if (sample && geom) { sample->handleMeshChanged(geom); } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; if (sample) { bmin = sample->getBoundsMin(); bmax = sample->getBoundsMax(); } else if (geom) { bmin = geom->getMeshBoundsMin(); bmax = geom->getMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) { camr = sqrtf(rcSqr(bmax[0] - bmin[0]) + rcSqr(bmax[1] - bmin[1]) + rcSqr(bmax[2] - bmin[2])) / 2; cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr; cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr; cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr; camr *= 3; } cameraEulers[0] = 45; cameraEulers[1] = -45; glFogf(GL_FOG_START, camr * 0.2f); glFogf(GL_FOG_END, camr * 1.25f); } } else if (event.key.keysym.sym == SDLK_RIGHT) { slideShow.nextSlide(); } else if (event.key.keysym.sym == SDLK_LEFT) { slideShow.prevSlide(); } break; case SDL_MOUSEWHEEL: if (event.wheel.y < 0) { // wheel down if (mouseOverMenu) { mouseScroll++; } else { scrollZoom += 1.0f; } } else { if (mouseOverMenu) { mouseScroll--; } else { scrollZoom -= 1.0f; } } break; case SDL_MOUSEBUTTONDOWN: if (event.button.button == SDL_BUTTON_RIGHT) { if (!mouseOverMenu) { // Rotate view rotate = true; movedDuringRotate = false; origMousePos[0] = mousePos[0]; origMousePos[1] = mousePos[1]; origCameraEulers[0] = cameraEulers[0]; origCameraEulers[1] = cameraEulers[1]; } } break; case SDL_MOUSEBUTTONUP: // Handle mouse clicks here. if (event.button.button == SDL_BUTTON_RIGHT) { rotate = false; if (!mouseOverMenu) { if (!movedDuringRotate) { processHitTest = true; processHitTestShift = true; } } } else if (event.button.button == SDL_BUTTON_LEFT) { if (!mouseOverMenu) { processHitTest = true; processHitTestShift = (SDL_GetModState() & KMOD_SHIFT) ? true : false; } } break; case SDL_MOUSEMOTION: mousePos[0] = event.motion.x; mousePos[1] = height-1 - event.motion.y; if (rotate) { int dx = mousePos[0] - origMousePos[0]; int dy = mousePos[1] - origMousePos[1]; cameraEulers[0] = origCameraEulers[0] - dy * 0.25f; cameraEulers[1] = origCameraEulers[1] + dx * 0.25f; if (dx * dx + dy * dy > 3 * 3) { movedDuringRotate = true; } } break; case SDL_QUIT: done = true; break; default: break; } } unsigned char mouseButtonMask = 0; if (SDL_GetMouseState(0, 0) & SDL_BUTTON_LMASK) mouseButtonMask |= IMGUI_MBUT_LEFT; if (SDL_GetMouseState(0, 0) & SDL_BUTTON_RMASK) mouseButtonMask |= IMGUI_MBUT_RIGHT; Uint32 time = SDL_GetTicks(); float dt = (time - prevFrameTime) / 1000.0f; prevFrameTime = time; t += dt; // Hit test mesh. if (processHitTest && geom && sample) { float hitTime; bool hit = geom->raycastMesh(rayStart, rayEnd, hitTime); if (hit) { if (SDL_GetModState() & KMOD_CTRL) { // Marker markerPositionSet = true; markerPosition[0] = rayStart[0] + (rayEnd[0] - rayStart[0]) * hitTime; markerPosition[1] = rayStart[1] + (rayEnd[1] - rayStart[1]) * hitTime; markerPosition[2] = rayStart[2] + (rayEnd[2] - rayStart[2]) * hitTime; } else { float pos[3]; pos[0] = rayStart[0] + (rayEnd[0] - rayStart[0]) * hitTime; pos[1] = rayStart[1] + (rayEnd[1] - rayStart[1]) * hitTime; pos[2] = rayStart[2] + (rayEnd[2] - rayStart[2]) * hitTime; sample->handleClick(rayStart, pos, processHitTestShift); } } else { if (SDL_GetModState() & KMOD_CTRL) { // Marker markerPositionSet = false; } } } // Update sample simulation. const float SIM_RATE = 20; const float DELTA_TIME = 1.0f / SIM_RATE; timeAcc = rcClamp(timeAcc + dt, -1.0f, 1.0f); int simIter = 0; while (timeAcc > DELTA_TIME) { timeAcc -= DELTA_TIME; if (simIter < 5 && sample) { sample->handleUpdate(DELTA_TIME); } simIter++; } // Clamp the framerate so that we do not hog all the CPU. const float MIN_FRAME_TIME = 1.0f / 40.0f; if (dt < MIN_FRAME_TIME) { int ms = (int)((MIN_FRAME_TIME - dt) * 1000.0f); if (ms > 10) ms = 10; if (ms >= 0) SDL_Delay(ms); } // Set the viewport. glViewport(0, 0, width, height); GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); // Clear the screen glClearColor(0.3f, 0.3f, 0.32f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); // Compute the projection matrix. glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(50.0f, (float)width/(float)height, 1.0f, camr); GLdouble projectionMatrix[16]; glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); // Compute the modelview matrix. glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(cameraEulers[0], 1, 0, 0); glRotatef(cameraEulers[1], 0, 1, 0); glTranslatef(-cameraPos[0], -cameraPos[1], -cameraPos[2]); GLdouble modelviewMatrix[16]; glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix); // Get hit ray position and direction. GLdouble x, y, z; gluUnProject(mousePos[0], mousePos[1], 0.0f, modelviewMatrix, projectionMatrix, viewport, &x, &y, &z); rayStart[0] = (float)x; rayStart[1] = (float)y; rayStart[2] = (float)z; gluUnProject(mousePos[0], mousePos[1], 1.0f, modelviewMatrix, projectionMatrix, viewport, &x, &y, &z); rayEnd[0] = (float)x; rayEnd[1] = (float)y; rayEnd[2] = (float)z; // Handle keyboard movement. const Uint8* keystate = SDL_GetKeyboardState(NULL); moveW = rcClamp(moveW + dt * 4 * (keystate[SDL_SCANCODE_W] ? 1 : -1), 0.0f, 1.0f); moveA = rcClamp(moveA + dt * 4 * (keystate[SDL_SCANCODE_A] ? 1 : -1), 0.0f, 1.0f); moveS = rcClamp(moveS + dt * 4 * (keystate[SDL_SCANCODE_S] ? 1 : -1), 0.0f, 1.0f); moveD = rcClamp(moveD + dt * 4 * (keystate[SDL_SCANCODE_D] ? 1 : -1), 0.0f, 1.0f); float keybSpeed = 22.0f; if (SDL_GetModState() & KMOD_SHIFT) { keybSpeed *= 4.0f; } float movex = (moveD - moveA) * keybSpeed * dt; float movey = (moveS - moveW) * keybSpeed * dt + scrollZoom * 2.0f; scrollZoom = 0; cameraPos[0] += movex * (float)modelviewMatrix[0]; cameraPos[1] += movex * (float)modelviewMatrix[4]; cameraPos[2] += movex * (float)modelviewMatrix[8]; cameraPos[0] += movey * (float)modelviewMatrix[2]; cameraPos[1] += movey * (float)modelviewMatrix[6]; cameraPos[2] += movey * (float)modelviewMatrix[10]; glEnable(GL_FOG); if (sample) sample->handleRender(); if (test) test->handleRender(); glDisable(GL_FOG); // Render GUI glDisable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, width, 0, height); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); mouseOverMenu = false; imguiBeginFrame(mousePos[0], mousePos[1], mouseButtonMask, mouseScroll); if (sample) { sample->handleRenderOverlay((double*)projectionMatrix, (double*)modelviewMatrix, (int*)viewport); } if (test) { if (test->handleRenderOverlay((double*)projectionMatrix, (double*)modelviewMatrix, (int*)viewport)) mouseOverMenu = true; } // Help text. if (showMenu) { const char msg[] = "W/S/A/D: Move RMB: Rotate"; imguiDrawText(280, height-20, IMGUI_ALIGN_LEFT, msg, imguiRGBA(255,255,255,128)); } if (showMenu) { if (imguiBeginScrollArea("Properties", width-250-10, 10, 250, height-20, &propScroll)) mouseOverMenu = true; if (imguiCheck("Show Log", showLog)) showLog = !showLog; if (imguiCheck("Show Tools", showTools)) showTools = !showTools; imguiSeparator(); imguiLabel("Sample"); if (imguiButton(sampleName)) { if (showSample) { showSample = false; } else { showSample = true; showLevels = false; showTestCases = false; } } imguiSeparator(); imguiLabel("Input Mesh"); if (imguiButton(meshName)) { if (showLevels) { showLevels = false; } else { showSample = false; showTestCases = false; showLevels = true; scanDirectory("Meshes", ".obj", files); } } if (geom) { char text[64]; snprintf(text, 64, "Verts: %.1fk Tris: %.1fk", geom->getMesh()->getVertCount()/1000.0f, geom->getMesh()->getTriCount()/1000.0f); imguiValue(text); } imguiSeparator(); if (geom && sample) { imguiSeparatorLine(); sample->handleSettings(); if (imguiButton("Build")) { ctx.resetLog(); if (!sample->handleBuild()) { showLog = true; logScroll = 0; } ctx.dumpLog("Build log %s:", meshName); // Clear test. delete test; test = 0; } imguiSeparator(); } if (sample) { imguiSeparatorLine(); sample->handleDebugMode(); } imguiEndScrollArea(); } // Sample selection dialog. if (showSample) { static int levelScroll = 0; if (imguiBeginScrollArea("Choose Sample", width-10-250-10-200, height-10-250, 200, 250, &levelScroll)) mouseOverMenu = true; Sample* newSample = 0; for (int i = 0; i < g_nsamples; ++i) { if (imguiItem(g_samples[i].name)) { newSample = g_samples[i].create(); if (newSample) strcpy(sampleName, g_samples[i].name); } } if (newSample) { delete sample; sample = newSample; sample->setContext(&ctx); if (geom && sample) { sample->handleMeshChanged(geom); } showSample = false; } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; if (sample) { bmin = sample->getBoundsMin(); bmax = sample->getBoundsMax(); } else if (geom) { bmin = geom->getMeshBoundsMin(); bmax = geom->getMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) { camr = sqrtf(rcSqr(bmax[0]-bmin[0]) + rcSqr(bmax[1]-bmin[1]) + rcSqr(bmax[2]-bmin[2])) / 2; cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr; cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr; cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr; camr *= 3; } cameraEulers[0] = 45; cameraEulers[1] = -45; glFogf(GL_FOG_START, camr*0.1f); glFogf(GL_FOG_END, camr*1.25f); } imguiEndScrollArea(); } // Level selection dialog. if (showLevels) { static int levelScroll = 0; if (imguiBeginScrollArea("Choose Level", width - 10 - 250 - 10 - 200, height - 10 - 450, 200, 450, &levelScroll)) mouseOverMenu = true; vector<string>::const_iterator fileIter = files.begin(); vector<string>::const_iterator filesEnd = files.end(); vector<string>::const_iterator levelToLoad = filesEnd; for (; fileIter != filesEnd; ++fileIter) { if (imguiItem(fileIter->c_str())) { levelToLoad = fileIter; } } if (levelToLoad != filesEnd) { strncpy(meshName, levelToLoad->c_str(), sizeof(meshName)); meshName[sizeof(meshName)-1] = '\0'; showLevels = false; delete geom; geom = 0; char path[256]; strcpy(path, "Meshes/"); strcat(path, meshName); geom = new InputGeom; if (!geom || !geom->loadMesh(&ctx, path)) { delete geom; geom = 0; showLog = true; logScroll = 0; ctx.dumpLog("Geom load log %s:", meshName); } if (sample && geom) { sample->handleMeshChanged(geom); } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; if (sample) { bmin = sample->getBoundsMin(); bmax = sample->getBoundsMax(); } else if (geom) { bmin = geom->getMeshBoundsMin(); bmax = geom->getMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) { camr = sqrtf(rcSqr(bmax[0]-bmin[0]) + rcSqr(bmax[1]-bmin[1]) + rcSqr(bmax[2]-bmin[2])) / 2; cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr; cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr; cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr; camr *= 3; } cameraEulers[0] = 45; cameraEulers[1] = -45; glFogf(GL_FOG_START, camr * 0.1f); glFogf(GL_FOG_END, camr * 1.25f); } } imguiEndScrollArea(); } // Test cases if (showTestCases) { static int testScroll = 0; if (imguiBeginScrollArea("Choose Test To Run", width-10-250-10-200, height-10-450, 200, 450, &testScroll)) mouseOverMenu = true; vector<string>::const_iterator fileIter = files.begin(); vector<string>::const_iterator filesEnd = files.end(); vector<string>::const_iterator testToLoad = filesEnd; for (; fileIter != filesEnd; ++fileIter) { if (imguiItem(fileIter->c_str())) { testToLoad = fileIter; } } if (testToLoad != filesEnd) { char path[256]; strcpy(path, "TestCases/"); strcat(path, testToLoad->c_str()); test = new TestCase; if (test) { // Load the test. if (!test->load(path)) { delete test; test = 0; } // Create sample Sample* newSample = 0; for (int i = 0; i < g_nsamples; ++i) { if (strcmp(g_samples[i].name, test->getSampleName()) == 0) { newSample = g_samples[i].create(); if (newSample) strcpy(sampleName, g_samples[i].name); } } if (newSample) { delete sample; sample = newSample; sample->setContext(&ctx); showSample = false; } // Load geom. strcpy(meshName, test->getGeomFileName()); meshName[sizeof(meshName)-1] = '\0'; delete geom; geom = 0; strcpy(path, "Meshes/"); strcat(path, meshName); geom = new InputGeom; if (!geom || !geom->loadMesh(&ctx, path)) { delete geom; geom = 0; showLog = true; logScroll = 0; ctx.dumpLog("Geom load log %s:", meshName); } if (sample && geom) { sample->handleMeshChanged(geom); } // This will ensure that tile & poly bits are updated in tiled sample. if (sample) sample->handleSettings(); ctx.resetLog(); if (sample && !sample->handleBuild()) { ctx.dumpLog("Build log %s:", meshName); } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; if (sample) { bmin = sample->getBoundsMin(); bmax = sample->getBoundsMax(); } else if (geom) { bmin = geom->getMeshBoundsMin(); bmax = geom->getMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) { camr = sqrtf(rcSqr(bmax[0] - bmin[0]) + rcSqr(bmax[1] - bmin[1]) + rcSqr(bmax[2] - bmin[2])) / 2; cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr; cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr; cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr; camr *= 3; } cameraEulers[0] = 45; cameraEulers[1] = -45; glFogf(GL_FOG_START, camr * 0.2f); glFogf(GL_FOG_END, camr * 1.25f); } // Do the tests. if (sample) test->doTests(sample->getNavMesh(), sample->getNavMeshQuery()); } } imguiEndScrollArea(); } // Log if (showLog && showMenu) { if (imguiBeginScrollArea("Log", 250 + 20, 10, width - 300 - 250, 200, &logScroll)) mouseOverMenu = true; for (int i = 0; i < ctx.getLogCount(); ++i) imguiLabel(ctx.getLogText(i)); imguiEndScrollArea(); } // Left column tools menu if (!showTestCases && showTools && showMenu) // && geom && sample) { if (imguiBeginScrollArea("Tools", 10, 10, 250, height - 20, &toolsScroll)) mouseOverMenu = true; if (sample) sample->handleTools(); imguiEndScrollArea(); } slideShow.updateAndDraw(dt, (float)width, (float)height); // Marker if (markerPositionSet && gluProject((GLdouble)markerPosition[0], (GLdouble)markerPosition[1], (GLdouble)markerPosition[2], modelviewMatrix, projectionMatrix, viewport, &x, &y, &z)) { // Draw marker circle glLineWidth(5.0f); glColor4ub(240,220,0,196); glBegin(GL_LINE_LOOP); const float r = 25.0f; for (int i = 0; i < 20; ++i) { const float a = (float)i / 20.0f * RC_PI*2; const float fx = (float)x + cosf(a)*r; const float fy = (float)y + sinf(a)*r; glVertex2f(fx,fy); } glEnd(); glLineWidth(1.0f); } imguiEndFrame(); imguiRenderGLDraw(); glEnable(GL_DEPTH_TEST); SDL_GL_SwapWindow(window); } imguiRenderGLDestroy(); SDL_Quit(); delete sample; delete geom; return 0; }
int main() { rcAllocSetCustom(allocCustom<rcAllocHint>, freeCustom); dtAllocSetCustom(allocCustom<dtAllocHint>, freeCustom); BuildContext ctx; InputGeom geom; geom.load(&ctx, "./geomset.txt"); ArMeshDataBuilder builder(&ctx, &geom, meshProcess); builder.setAgentMaxClimb(1.8f); ArMeshDataPtr data = builder.build(); if (!data) { ctx.log(RC_LOG_ERROR, "Build ArMeshData failed."); return -1; } ctx.log(RC_LOG_PROGRESS, "Total build time: %d ms", ctx.getAccumulatedTime(RC_TIMER_TOTAL)); if (!save(&ctx, data)) { return -1; } FILE* in = fopen("./all_tiles_navmesh.bin", "rb"); if (!in) { ctx.log(RC_LOG_ERROR, "Open all_tiles_navmesh.bin failed."); return -1; } ArMeshDataFileReader reader(in); if (!reader.serialize(*data)) { ctx.log(RC_LOG_ERROR, "Read ArMeshData failed."); fclose(in); return -1; } fclose(in); ArMesh mesh; ArMeshImporter importer(mesh); if (!importer.serialize(*data)) { ctx.log(RC_LOG_ERROR, "Import ArMeshData failed."); return -1; } ArQuery query; if (dtStatusFailed(query.init(&mesh, 2048))) { ctx.log(RC_LOG_ERROR, "Init ArQuery failed."); return -1; } float sp[3]; printf("start: "); scanf("%f%f%f", sp, sp + 1, sp + 2); float ep[3]; printf("end: "); scanf("%f%f%f", ep, ep + 1, ep + 2); float ext[3] = {2, 4, 2}; dtQueryFilter filter; filter.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); filter.setAreaCost(SAMPLE_POLYAREA_WATER, 10.0f); filter.setAreaCost(SAMPLE_POLYAREA_ROAD, 1.0f); filter.setAreaCost(SAMPLE_POLYAREA_DOOR, 1.0f); filter.setAreaCost(SAMPLE_POLYAREA_GRASS, 2.0f); filter.setAreaCost(SAMPLE_POLYAREA_JUMP, 1.5f); dtPolyRef sr; query.backend()->findNearestPoly(sp, ext, &filter, &sr, 0); dtPolyRef er; query.backend()->findNearestPoly(ep, ext, &filter, &er, 0); dtPolyRef polys[256]; int npolys; query.backend()->findPath(sr, er, sp, ep, &filter, polys, &npolys, 256); float path[256 * 3]; dtPolyRef pathPolys[256]; unsigned char pathFlags[256]; int pathLen; query.backend()->findStraightPath(sp, ep, polys, npolys, path, pathFlags, pathPolys, &pathLen, 256, 0); printf("path:\n%d\n", pathLen); for (int i = 0; i < pathLen; ++i) { printf("%f %f %f\n", path[i * 3], path[i * 3 + 1], path[i * 3 + 2]); } if (!exportMesh(&ctx, mesh)) { return -1; } return 0; }
bool OgreRecast::NavMeshBuild(InputGeom* input) { // TODO: clean up unused variables m_pLog->logMessage("NavMeshBuild Start"); // // Step 1. Initialize build config. // // Reset build times gathering. m_ctx->resetTimers(); // Start the build process. m_ctx->startTimer(RC_TIMER_TOTAL); // // Step 2. Rasterize input polygon soup. // InputGeom *inputGeom = input; rcVcopy(m_cfg.bmin, inputGeom->getMeshBoundsMin()); rcVcopy(m_cfg.bmax, inputGeom->getMeshBoundsMax()); rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); int nverts = inputGeom->getVertCount(); int ntris = inputGeom->getTriCount(); Ogre::Vector3 min; FloatAToOgreVect3(inputGeom->getMeshBoundsMin(), min); Ogre::Vector3 max; FloatAToOgreVect3(inputGeom->getMeshBoundsMax(), max); //Ogre::LogManager::getSingletonPtr()->logMessage("Bounds: "+Ogre::StringConverter::toString(min) + " "+ Ogre::StringConverter::toString(max)); m_pLog->logMessage("Building navigation:"); m_pLog->logMessage(" - " + Ogre::StringConverter::toString(m_cfg.width) + " x " + Ogre::StringConverter::toString(m_cfg.height) + " cells"); m_pLog->logMessage(" - " + Ogre::StringConverter::toString(nverts/1000.0f) + " K verts, " + Ogre::StringConverter::toString(ntris/1000.0f) + " K tris"); // Allocate voxel heightfield where we rasterize our input data to. m_solid = rcAllocHeightfield(); if (!m_solid) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'solid'."); return false; } if (!rcCreateHeightfield(m_ctx, *m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch)) { m_pLog->logMessage("ERROR: buildNavigation: Could not create solid heightfield. Possibly it requires too much memory, try setting a higher cellSize and cellHeight value."); return false; } // Allocate array that can hold triangle area types. // If you have multiple meshes you need to process, allocate // an array which can hold the max number of triangles you need to process. m_triareas = new unsigned char[ntris]; if (!m_triareas) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'm_triareas' ("+Ogre::StringConverter::toString(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 are type for each of the meshes and rasterize them. memset(m_triareas, 0, ntris*sizeof(unsigned char)); rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, inputGeom->getVerts(), inputGeom->getVertCount(), inputGeom->getTris(), inputGeom->getTriCount(), m_triareas); rcRasterizeTriangles(m_ctx, inputGeom->getVerts(), inputGeom->getVertCount(), inputGeom->getTris(), m_triareas, inputGeom->getTriCount(), *m_solid, m_cfg.walkableClimb); if (!m_keepInterResults) { delete [] m_triareas; m_triareas = 0; } // // Step 3. Filter walkables surfaces. // // 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. rcFilterLowHangingWalkableObstacles(m_ctx, m_cfg.walkableClimb, *m_solid); rcFilterLedgeSpans(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid); rcFilterWalkableLowHeightSpans(m_ctx, m_cfg.walkableHeight, *m_solid); // // Step 4. Partition walkable surface to simple regions. // // 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 = rcAllocCompactHeightfield(); if (!m_chf) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'chf'."); return false; } if (!rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf)) { m_pLog->logMessage("ERROR: buildNavigation: Could not build compact data."); return false; } if (!m_keepInterResults) { rcFreeHeightField(m_solid); m_solid = 0; } // Erode the walkable area by agent radius. if (!rcErodeWalkableArea(m_ctx, m_cfg.walkableRadius, *m_chf)) { m_pLog->logMessage("ERROR: buildNavigation: Could not erode walkable areas."); return false; } // TODO implement // (Optional) Mark areas. //const ConvexVolume* vols = m_geom->getConvexVolumes(); //for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) // rcMarkConvexPolyArea(m_ctx, vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf); // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!rcBuildDistanceField(m_ctx, *m_chf)) { m_pLog->logMessage("ERROR: buildNavigation: Could not build distance field."); return false; } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_pLog->logMessage("ERROR: buildNavigation: Could not build regions."); return false; } // // Step 5. Trace and simplify region contours. // // Create contours. m_cset = rcAllocContourSet(); if (!m_cset) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'cset'."); return false; } if (!rcBuildContours(m_ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) { m_pLog->logMessage("ERROR: buildNavigation: Could not create contours."); return false; } if (m_cset->nconts == 0) { // In case of errors see: http://groups.google.com/group/recastnavigation/browse_thread/thread/a6fbd509859a12c8 // You should probably tweak the parameters m_pLog->logMessage("ERROR: No contours created (Recast)!"); } // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. m_pmesh = rcAllocPolyMesh(); if (!m_pmesh) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'pmesh'."); return false; } if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) { // Try modifying the parameters. I experienced this error when setting agentMaxClimb too high. m_pLog->logMessage("ERROR: buildNavigation: Could not triangulate contours."); return false; } // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // m_dmesh = rcAllocPolyMeshDetail(); if (!m_dmesh) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'pmdtl'."); return false; } if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) { m_pLog->logMessage("ERROR: buildNavigation: Could not build detail mesh."); return false; } if (!m_keepInterResults) { rcFreeCompactHeightfield(m_chf); m_chf = 0; rcFreeContourSet(m_cset); m_cset = 0; } // At this point the navigation mesh data is ready, you can access it from m_pmesh. // See duDebugDrawPolyMesh or dtCreateNavMeshData as examples how to access the data. // // (Optional) Step 8. Create Detour data from Recast poly mesh. // // The GUI may allow more max points per polygon than Detour can handle. // Only build the detour navmesh if we do not exceed the limit. if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON) { m_pLog->logMessage("Detour 1000"); unsigned char* navData = 0; int navDataSize = 0; // Update poly flags from areas. for (int i = 0; i < m_pmesh->npolys; ++i) { if (m_pmesh->areas[i] == RC_WALKABLE_AREA) { m_pmesh->areas[i] = SAMPLE_POLYAREA_GROUND; m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK; } } // Set navmesh params dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); params.verts = m_pmesh->verts; params.vertCount = m_pmesh->nverts; params.polys = m_pmesh->polys; params.polyAreas = m_pmesh->areas; params.polyFlags = m_pmesh->flags; params.polyCount = m_pmesh->npolys; params.nvp = m_pmesh->nvp; params.detailMeshes = m_dmesh->meshes; params.detailVerts = m_dmesh->verts; params.detailVertsCount = m_dmesh->nverts; params.detailTris = m_dmesh->tris; params.detailTriCount = m_dmesh->ntris; // no off mesh connections yet m_offMeshConCount=0 ; params.offMeshConVerts = m_offMeshConVerts ; params.offMeshConRad = m_offMeshConRads ; params.offMeshConDir = m_offMeshConDirs ; params.offMeshConAreas = m_offMeshConAreas ; params.offMeshConFlags = m_offMeshConFlags ; params.offMeshConUserID = m_offMeshConId ; params.offMeshConCount = m_offMeshConCount ; params.walkableHeight = m_agentHeight; params.walkableRadius = m_agentRadius; params.walkableClimb = m_agentMaxClimb; rcVcopy(params.bmin, m_pmesh->bmin); rcVcopy(params.bmax, m_pmesh->bmax); params.cs = m_cfg.cs; params.ch = m_cfg.ch; m_pLog->logMessage("Detour 2000"); if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { m_pLog->logMessage("ERROR: Could not build Detour navmesh."); return false; } m_pLog->logMessage("Detour 3000"); m_navMesh = dtAllocNavMesh(); if (!m_navMesh) { dtFree(navData); m_pLog->logMessage("ERROR: Could not create Detour navmesh"); return false; } m_pLog->logMessage("Detour 4000"); dtStatus status; status = m_navMesh->init(navData, navDataSize, DT_TILE_FREE_DATA); if (dtStatusFailed(status)) { dtFree(navData); m_pLog->logMessage("ERROR: Could not init Detour navmesh"); return false; } m_pLog->logMessage("Detour 5000"); m_navQuery = dtAllocNavMeshQuery(); status = m_navQuery->init(m_navMesh, 2048); m_pLog->logMessage("Detour 5500"); if (dtStatusFailed(status)) { m_pLog->logMessage("ERROR: Could not init Detour navmesh query"); return false; } m_pLog->logMessage("Detour 6000"); } m_ctx->stopTimer(RC_TIMER_TOTAL); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cleanup stuff we don't need // delete [] rc_verts ; // delete [] rc_tris ; // delete [] rc_trinorms ; //CreateRecastPolyMesh(*m_pmesh) ; // Debug render it m_pLog->logMessage("NavMeshBuild End"); return true; }
int __cdecl buildMeshFromFile(int userId, const char* inputFilename, const char* navmeshFilename, BuildMeshCallback callback, int numCores) { WCellBuildContext ctx; dtNavMesh* mesh = NULL; if (navmeshFilename) { // load navmesh from file mesh = loadMesh(navmeshFilename); } if (!mesh) { InputGeom geom; dtNavMeshQuery* navMeshQuery; // no navmesh has been loaded // Load input mesh if (!geom.loadMesh(&ctx, inputFilename)) { return 0; } // build nav-mesh mesh = buildMesh(&geom, &ctx, numCores); if (!mesh) { // building failed return 0; } if (navmeshFilename) { // save to file saveMesh(navmeshFilename, mesh); } } //int tileCount = 0; int vertCount = 0; int polyCount = 0; int totalPolyVertCount = 0; float* allVerts; unsigned char* polyVertCounts; unsigned int* polyVerts; unsigned int* polyNeighbors; unsigned short* polyFlags; unsigned char* polyAreasAndTypes; // count sizes and offsets unsigned int* tilePolyIndexOffsets = new unsigned int[mesh->getMaxTiles()]; int maxTiles = mesh->getMaxTiles(); //int maxTiles = 1; for (int i = 0; i < maxTiles; ++i) { const dtMeshTile* tile = ((const dtNavMesh*)mesh)->getTile(i); if (!tile || !tile->header || !tile->dataSize) continue; //tileCount++; tilePolyIndexOffsets[i] = polyCount; polyCount += tile->header->polyCount; vertCount += tile->header->vertCount; for (int j = 0; j < tile->header->polyCount; j++) { totalPolyVertCount += tile->polys[j].vertCount; } } // allocate arrays allVerts = new float[vertCount * 3]; polyVertCounts = new unsigned char[polyCount]; polyVerts = new unsigned int[totalPolyVertCount]; polyNeighbors = new unsigned int[totalPolyVertCount]; polyFlags = new unsigned short[polyCount]; polyAreasAndTypes = new unsigned char[polyCount]; int p = 0; int v = 0; int polyIndexOffset = 0; // copy data // iterate tiles for (int i = 0; i < maxTiles; ++i) { const dtMeshTile* tile = ((const dtNavMesh*)mesh)->getTile(i); if (!tile || !tile->header || !tile->dataSize) continue; memcpy(&allVerts[v], tile->verts, tile->header->vertCount * sizeof(float) * 3); int vc = v/3; // iterate polygons for (int j = 0; j < tile->header->polyCount; j++) { int absolutePolyIndex = p+j; dtPoly& poly = tile->polys[j]; polyVertCounts[absolutePolyIndex] = poly.vertCount; // iterate polygon vertices and edges for (int k = 0; k < poly.vertCount; k++) { int absolutePolyVertIndex = polyIndexOffset + k; polyVerts[absolutePolyVertIndex] = vc + poly.verts[k]; // find absolute index of neighbor poly unsigned short neiRef = poly.neis[k]; unsigned int idx = (unsigned int)-1; if (neiRef & DT_EXT_LINK) { // Tile border edge -> find linked polygon for (unsigned int l = poly.firstLink; l != DT_NULL_LINK; l = tile->links[l].next) { const dtLink* link = &tile->links[l]; if (link->ref != 0 && link->edge == k) { // lookup linked neighbor tile and poly unsigned int salt, neigTile, neiPoly; mesh->decodePolyId(link->ref, salt, neigTile, neiPoly); // absolute poly index idx = tilePolyIndexOffsets[neigTile] + neiPoly; //const dtMeshTile* t = ((const dtNavMesh*)mesh)->getTile(neigTile); assert(neigTile < maxTiles-1 || idx < tilePolyIndexOffsets[neigTile+1]); //idx = (unsigned int)-1; } } } else if (neiRef) { // Tile-internal edge idx = tilePolyIndexOffsets[i] + (unsigned int)(neiRef - 1); } assert((int)idx == -1 || idx < polyCount); polyNeighbors[absolutePolyVertIndex] = idx; } polyIndexOffset += poly.vertCount; } v += tile->header->vertCount * 3; p += tile->header->polyCount; } assert(v/3 == vertCount); printf("Preparing navmesh with %d vertices...\n", vertCount); callback( userId, vertCount * 3, polyCount, allVerts, totalPolyVertCount, polyVertCounts, polyVerts, polyNeighbors, polyFlags, polyAreasAndTypes ); delete[] allVerts; delete[] polyVertCounts; delete[] polyVerts; delete[] polyNeighbors; delete[] polyFlags; delete[] polyAreasAndTypes; delete[] tilePolyIndexOffsets; CleanupAfterBuild(); return 1; }
void NavMesh::LoadMesh(const char* filepath) { InputGeom* geom = new InputGeom(); geom->loadMesh(filepath); SetMesh(geom); }