From 290f78f8c8e31b4f385b21c4d9e0c976c76351fc Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:03:30 +0200 Subject: [PATCH] Recast: fully implement cylindric and AABB shape volumes Cylindric and AABB volumes can now also be used to mark out area's on the NavMesh. The raycast tests now also take the new shapes into account. User can now also add shapes through the ShapeVolumeTool (previously ConvexVolumeTool). The geomset format has changed slightly: - identifier 'o' now maps to off-mesh links (previously 'c'). - identifier 'p' now maps to convex polygon volumes (previously 'v'). - identifier 'c' maps to cylinder volumes. - identifier 'b' maps to box volumes. --- src/naveditor/ConvexVolumeTool.cpp | 235 +++++++++++++++-------- src/naveditor/Editor_Common.cpp | 2 + src/naveditor/Editor_SoloMesh.cpp | 8 +- src/naveditor/Editor_TempObstacles.cpp | 47 +---- src/naveditor/Editor_TileMesh.cpp | 22 ++- src/naveditor/InputGeom.cpp | 207 +++++++++++++------- src/naveditor/include/ConvexVolumeTool.h | 20 +- src/naveditor/include/Editor.h | 2 +- src/naveditor/include/InputGeom.h | 24 ++- 9 files changed, 348 insertions(+), 219 deletions(-) diff --git a/src/naveditor/ConvexVolumeTool.cpp b/src/naveditor/ConvexVolumeTool.cpp index 3edf84d5..efdcdabb 100644 --- a/src/naveditor/ConvexVolumeTool.cpp +++ b/src/naveditor/ConvexVolumeTool.cpp @@ -75,36 +75,76 @@ static int convexhull(const float* pts, int npts, int* out) } -ConvexVolumeTool::ConvexVolumeTool() : +ShapeVolumeTool::ShapeVolumeTool() : m_editor(0), + m_selectedPrimitive(VOLUME_CONVEX), m_areaType(RC_NULL_AREA), m_polyFlags(0), - m_polyOffset(0.0f), - m_boxHeight(650.0f), - m_boxDescent(150.0f), + m_boxDescent(32), + m_boxAscent(32), + m_cylinderRadius(64), + m_cylinderHeight(128), + m_convexOffset(0.0f), + m_convexHeight(650.0f), + m_convexDescent(150.0f), m_npts(0), m_nhull(0) { } -void ConvexVolumeTool::init(Editor* editor) +void ShapeVolumeTool::init(Editor* editor) { m_editor = editor; } -void ConvexVolumeTool::reset() +void ShapeVolumeTool::reset() { m_npts = 0; m_nhull = 0; } -void ConvexVolumeTool::handleMenu() +static const char* s_primitiveNames[] = { + "Box", + "Cylinder", + "Convex" +}; + +void ShapeVolumeTool::handleMenu() { + if (ImGui::BeginCombo("Primitive", s_primitiveNames[m_selectedPrimitive])) + { + for (int i = 0; i < V_ARRAYSIZE(s_primitiveNames); i++) + { + if (ImGui::Selectable(s_primitiveNames[i], i == m_selectedPrimitive)) + { + m_selectedPrimitive = i; + + m_npts = 0; + m_nhull = 0; + } + } + + ImGui::EndCombo(); + } + ImGui::PushItemWidth(120.f); - ImGui::SliderFloat("Shape Height", &m_boxHeight, 0.1f, MAX_COORD_FLOAT); - ImGui::SliderFloat("Shape Descent", &m_boxDescent, 0.1f, MAX_COORD_FLOAT); - ImGui::SliderFloat("Poly Offset", &m_polyOffset, 0.0f, MAX_COORD_FLOAT/2); + switch (m_selectedPrimitive) + { + case VOLUME_BOX: + ImGui::SliderFloat("Box Descent", &m_boxDescent, 0.1f, 4000); + ImGui::SliderFloat("Box Ascent", &m_boxAscent, 0.1f, 4000); + break; + case VOLUME_CYLINDER: + ImGui::SliderFloat("Cylinder Radius", &m_cylinderRadius, 0.1f, 4000); + ImGui::SliderFloat("Cylinder Height", &m_cylinderHeight, 0.1f, 4000); + break; + case VOLUME_CONVEX: + ImGui::SliderFloat("Convex Height", &m_convexHeight, 0.1f, 4000); + ImGui::SliderFloat("Convex Descent", &m_convexDescent, 0.1f, 4000); + ImGui::SliderFloat("Convex Offset", &m_convexOffset, 0.0f, 2000); + break; + } ImGui::PopItemWidth(); @@ -148,7 +188,7 @@ void ConvexVolumeTool::handleMenu() } } -void ConvexVolumeTool::handleClick(const float* /*s*/, const float* p, bool shift) +void ShapeVolumeTool::handleClick(const float* /*s*/, const float* p, bool shift) { if (!m_editor) return; InputGeom* geom = m_editor->getInputGeom(); @@ -158,7 +198,7 @@ void ConvexVolumeTool::handleClick(const float* /*s*/, const float* p, bool shif { // Delete int nearestIndex = -1; - const ConvexVolume* vols = geom->getConvexVolumes(); + const ShapeVolume* vols = geom->getConvexVolumes(); for (int i = 0; i < geom->getConvexVolumeCount(); ++i) { if (rdPointInPolygon(p, vols[i].verts, vols[i].nverts) && @@ -173,77 +213,106 @@ void ConvexVolumeTool::handleClick(const float* /*s*/, const float* p, bool shif geom->deleteConvexVolume(nearestIndex); } } - else + else // Create { - // Create - - // If clicked on that last pt, create the shape. - if (m_npts && rdVdistSqr(p, &m_pts[(m_npts-1)*3]) < rdSqr(0.2f)) + switch (m_selectedPrimitive) { - if (m_nhull > 2) - { - // Create shape. - float verts[MAX_PTS*3]; - for (int i = 0; i < m_nhull; ++i) - rdVcopy(&verts[i*3], &m_pts[m_hull[i]*3]); - - float minh = FLT_MAX, maxh = 0; - for (int i = 0; i < m_nhull; ++i) - minh = rdMin(minh, verts[i*3+2]); - minh -= m_boxDescent; - maxh = minh + m_boxHeight; + case VOLUME_BOX: + rdVcopy(&m_pts[m_npts*3], p); + m_npts++; - if (m_polyOffset > 0.01f) - { - float offset[MAX_PTS*2*3]; - int noffset = rcOffsetPoly(verts, m_nhull, m_polyOffset, offset, MAX_PTS*2); - if (noffset > 0) - geom->addConvexVolume(offset, noffset, minh, maxh, (unsigned short)m_polyFlags, (unsigned char)m_areaType); - } - else - { - geom->addConvexVolume(verts, m_nhull, minh, maxh, (unsigned short)m_polyFlags, (unsigned char)m_areaType); - } + if (m_npts > 1) + { + float* bmin = &m_pts[0*3]; + float* bmax = &m_pts[1*3]; + + if (bmin[0] > bmax[0]) + rdSwap(bmin[0], bmax[0]); + if (bmin[1] > bmax[1]) + rdSwap(bmin[1], bmax[1]); + if (bmin[2] > bmax[2]) + rdSwap(bmin[2], bmax[2]); + + bmin[2] -= m_boxDescent; + bmax[2] += m_boxAscent; + + geom->addBoxVolume(&m_pts[0*3], &m_pts[1*3], (unsigned short)m_polyFlags, (unsigned char)m_areaType); + + m_npts = 0; + m_nhull = 0; } - - m_npts = 0; - m_nhull = 0; + break; + case VOLUME_CYLINDER: + geom->addCylinderVolume(p, m_cylinderRadius, m_cylinderHeight, (unsigned short)m_polyFlags, (unsigned char)m_areaType); + break; + case VOLUME_CONVEX: + // If clicked on that last pt, create the shape. + if (m_npts && rdVdistSqr(p, &m_pts[(m_npts-1)*3]) < rdSqr(0.2f)) + { + if (m_nhull > 2) + { + // Create shape. + float verts[MAX_PTS*3]; + for (int i = 0; i < m_nhull; ++i) + rdVcopy(&verts[i*3], &m_pts[m_hull[i]*3]); + + float minh = FLT_MAX, maxh = 0; + for (int i = 0; i < m_nhull; ++i) + minh = rdMin(minh, verts[i*3+2]); + minh -= m_convexDescent; + maxh = minh + m_convexHeight; + + if (m_convexOffset > 0.01f) + { + float offset[MAX_PTS*2*3]; + const int noffset = rcOffsetPoly(verts, m_nhull, m_convexOffset, offset, MAX_PTS*2); + if (noffset > 0) + geom->addConvexVolume(offset, noffset, minh, maxh, (unsigned short)m_polyFlags, (unsigned char)m_areaType); + } + else + { + geom->addConvexVolume(verts, m_nhull, minh, maxh, (unsigned short)m_polyFlags, (unsigned char)m_areaType); + } + } + + m_npts = 0; + m_nhull = 0; + } + else + { + // Add new point + if (m_npts < MAX_PTS) + { + rdVcopy(&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; + } + } + break; } - else - { - // Add new point - if (m_npts < MAX_PTS) - { - rdVcopy(&m_pts[m_npts*3], p); - const float* f = &m_pts[m_npts * 3]; - - printf("<%f, %f, %f>\n", f[0], f[1], f[2]); - - m_npts++; - // Update hull. - if (m_npts > 1) - m_nhull = convexhull(m_pts, m_npts, m_hull); - else - m_nhull = 0; - } - } } - + + printf("<%f, %f, %f>\n", p[0], p[1], p[2]); } -void ConvexVolumeTool::handleToggle() +void ShapeVolumeTool::handleToggle() { } -void ConvexVolumeTool::handleStep() +void ShapeVolumeTool::handleStep() { } -void ConvexVolumeTool::handleUpdate(const float /*dt*/) +void ShapeVolumeTool::handleUpdate(const float /*dt*/) { } -void ConvexVolumeTool::handleRender() +void ShapeVolumeTool::handleRender() { duDebugDraw& dd = m_editor->getDebugDraw(); const float* drawOffset = m_editor->getDetourDrawOffset(); @@ -252,8 +321,8 @@ void ConvexVolumeTool::handleRender() float minh = FLT_MAX, maxh = 0; for (int i = 0; i < m_npts; ++i) minh = rdMin(minh, m_pts[i*3+2]); - minh -= m_boxDescent; - maxh = minh + m_boxHeight; + minh -= m_convexDescent; + maxh = minh + m_convexHeight; dd.begin(DU_DRAW_POINTS, 4.0f, drawOffset); for (int i = 0; i < m_npts; ++i) @@ -261,26 +330,30 @@ void ConvexVolumeTool::handleRender() unsigned int col = duRGBA(255,255,255,255); if (i == m_npts-1) col = duRGBA(240,32,16,255); - dd.vertex(m_pts[i*3+0],m_pts[i*3+1],m_pts[i*3+2]+0.1f, col);//Needs to be flipped (y = z). + + dd.vertex(m_pts[i*3+0],m_pts[i*3+1],m_pts[i*3+2]+0.1f, col); } dd.end(); - dd.begin(DU_DRAW_LINES, 2.0f, drawOffset); - for (int i = 0, j = m_nhull-1; i < m_nhull; j = i++) + if (m_selectedPrimitive == VOLUME_CONVEX) { - const float* vi = &m_pts[m_hull[j]*3]; - const float* vj = &m_pts[m_hull[i]*3]; - dd.vertex(vj[0],vj[1],minh, duRGBA(255, 255, 255, 64)); - dd.vertex(vi[0],vi[1],minh, duRGBA(255, 255, 255, 64)); - dd.vertex(vj[0],vj[1],maxh, duRGBA(255, 255, 255, 64)); - dd.vertex(vi[0],vi[1],maxh, duRGBA(255, 255, 255, 64)); - dd.vertex(vj[0],vj[1],minh, duRGBA(255, 255, 255, 64)); - dd.vertex(vj[0],vj[1],maxh, duRGBA(255,255,255,64)); + dd.begin(DU_DRAW_LINES, 2.0f, drawOffset); + for (int i = 0, j = m_nhull-1; i < m_nhull; j = i++) + { + const float* vi = &m_pts[m_hull[j]*3]; + const float* vj = &m_pts[m_hull[i]*3]; + dd.vertex(vj[0],vj[1],minh, duRGBA(255,255,255,64)); + dd.vertex(vi[0],vi[1],minh, duRGBA(255,255,255,64)); + dd.vertex(vj[0],vj[1],maxh, duRGBA(255,255,255,64)); + dd.vertex(vi[0],vi[1],maxh, duRGBA(255,255,255,64)); + dd.vertex(vj[0],vj[1],minh, duRGBA(255,255,255,64)); + dd.vertex(vj[0],vj[1],maxh, duRGBA(255,255,255,64)); + } + dd.end(); } - dd.end(); } -void ConvexVolumeTool::handleRenderOverlay(double* /*proj*/, double* /*model*/, int* /*view*/) +void ShapeVolumeTool::handleRenderOverlay(double* /*proj*/, double* /*model*/, int* /*view*/) { // Tool help if (!m_npts) diff --git a/src/naveditor/Editor_Common.cpp b/src/naveditor/Editor_Common.cpp index b8edd49f..6d3e2698 100644 --- a/src/naveditor/Editor_Common.cpp +++ b/src/naveditor/Editor_Common.cpp @@ -382,6 +382,7 @@ void Editor_StaticTileMeshCommon::renderTileMeshData() // TODO: also add flags for this m_geom->drawBoxVolumes(&m_dd, recastDrawOffset); + m_geom->drawCylinderVolumes(&m_dd, recastDrawOffset); m_geom->drawConvexVolumes(&m_dd, recastDrawOffset); // NOTE: commented out because this already gets rendered when the off-mesh @@ -579,6 +580,7 @@ void Editor_DynamicTileMeshCommon::renderTileMeshData() // TODO: also add flags for this m_geom->drawBoxVolumes(&m_dd, recastDrawOffset); + m_geom->drawCylinderVolumes(&m_dd, recastDrawOffset); m_geom->drawConvexVolumes(&m_dd, recastDrawOffset); // NOTE: commented out because this already gets rendered when the off-mesh diff --git a/src/naveditor/Editor_SoloMesh.cpp b/src/naveditor/Editor_SoloMesh.cpp index 510d5749..a3dc339b 100644 --- a/src/naveditor/Editor_SoloMesh.cpp +++ b/src/naveditor/Editor_SoloMesh.cpp @@ -92,10 +92,10 @@ void Editor_SoloMesh::handleTools() setTool(new OffMeshConnectionTool); } - enabled = type == TOOL_CONVEX_VOLUME; - if (ImGui::Checkbox("Create Convex Volumes", &enabled)) + enabled = type == TOOL_SHAPE_VOLUME; + if (ImGui::Checkbox("Create Shape Volumes", &enabled)) { - setTool(new ConvexVolumeTool); + setTool(new ShapeVolumeTool); } enabled = type == TOOL_CROWD; @@ -299,7 +299,7 @@ bool Editor_SoloMesh::handleBuild() } // (Optional) Mark areas. - const ConvexVolume* vols = m_geom->getConvexVolumes(); + const ShapeVolume* 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 short)vols[i].flags, (unsigned char)vols[i].area, *m_chf); diff --git a/src/naveditor/Editor_TempObstacles.cpp b/src/naveditor/Editor_TempObstacles.cpp index 7a52de2b..ad7ad9e7 100644 --- a/src/naveditor/Editor_TempObstacles.cpp +++ b/src/naveditor/Editor_TempObstacles.cpp @@ -38,43 +38,6 @@ static const int EXPECTED_LAYERS_PER_TILE = 4; -static bool isectSegAABB(const float* sp, const float* sq, - const float* amin, const float* amax, - float& tmin, float& tmax) -{ - float d[3]; - rdVsub(d, sq, sp); - tmin = 0; // set to -FLT_MAX to get first hit on line - tmax = FLT_MAX; // set to max distance ray can travel (for segment) - - // For all three slabs - for (int i = 0; i < 3; i++) - { - if (fabsf(d[i]) < RD_EPS) - { - // Ray is parallel to slab. No hit if origin not within slab - if (sp[i] < amin[i] || sp[i] > amax[i]) - return false; - } - else - { - // Compute intersection t value of ray with near and far plane of slab - const float ood = 1.0f / d[i]; - float t1 = (amin[i] - sp[i]) * ood; - float t2 = (amax[i] - sp[i]) * ood; - // Make t1 be intersection with near plane, t2 with far plane - if (t1 > t2) rdSwap(t1, t2); - // Compute the intersection of slab intersections intervals - if (t1 > tmin) tmin = t1; - if (t2 < tmax) tmax = t2; - // Exit with no collision as soon as slab intersection becomes empty - if (tmin > tmax) return false; - } - } - - return true; -} - static int calcLayerBufferSize(const int gridWidth, const int gridHeight) { const int headerSize = rdAlign4(sizeof(dtTileCacheLayerHeader)); @@ -397,7 +360,7 @@ int Editor_TempObstacles::rasterizeTileLayers( } // (Optional) Mark areas. - const ConvexVolume* vols = m_geom->getConvexVolumes(); + const ShapeVolume* vols = m_geom->getConvexVolumes(); for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) { rcMarkConvexPolyArea(m_ctx, vols[i].verts, vols[i].nverts, @@ -610,7 +573,7 @@ dtObstacleRef hitTestObstacle(const dtTileCache* tc, const float* sp, const floa float bmin[3], bmax[3], t0,t1; tc->getObstacleBounds(ob, bmin,bmax); - if (isectSegAABB(sp,sq, bmin,bmax, t0,t1)) + if (rdIntersectSegmentAABB(sp,sq, bmin,bmax, t0,t1)) { if (t0 < tmin) { @@ -892,10 +855,10 @@ void Editor_TempObstacles::handleTools() setTool(new OffMeshConnectionTool); } - enabled = type == TOOL_CONVEX_VOLUME; - if (ImGui::Checkbox("Create Convex Volumes", &enabled)) + enabled = type == TOOL_SHAPE_VOLUME; + if (ImGui::Checkbox("Create Shape Volumes", &enabled)) { - setTool(new ConvexVolumeTool); + setTool(new ShapeVolumeTool); } enabled = type == TOOL_CROWD; diff --git a/src/naveditor/Editor_TileMesh.cpp b/src/naveditor/Editor_TileMesh.cpp index 563758a6..07efe4a5 100644 --- a/src/naveditor/Editor_TileMesh.cpp +++ b/src/naveditor/Editor_TileMesh.cpp @@ -513,10 +513,10 @@ void Editor_TileMesh::handleTools() setTool(new OffMeshConnectionTool); } - isEnabled = type == TOOL_CONVEX_VOLUME; - if (ImGui::Checkbox("Create Convex Volumes", &isEnabled)) + isEnabled = type == TOOL_SHAPE_VOLUME; + if (ImGui::Checkbox("Create Shape Volumes", &isEnabled)) { - setTool(new ConvexVolumeTool); + setTool(new ShapeVolumeTool); } isEnabled = type == TOOL_CROWD; @@ -1103,15 +1103,23 @@ unsigned char* Editor_TileMesh::buildTileMesh(const int tx, const int ty, const } // (Optional) Mark areas. - const ConvexVolume* vols = m_geom->getConvexVolumes(); + const ShapeVolume* vols = m_geom->getConvexVolumes(); for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) { - const ConvexVolume& vol = vols[i]; + const ShapeVolume& vol = vols[i]; - if (vol.bbox) + switch (vol.type) + { + case VOLUME_BOX: rcMarkBoxArea(m_ctx, &vol.verts[0], &vol.verts[3], vol.flags, vol.area, *m_chf); - else + break; + case VOLUME_CYLINDER: + rcMarkCylinderArea(m_ctx, &vol.verts[0], vol.verts[3], vol.verts[4], vol.flags, vol.area, *m_chf); + break; + case VOLUME_CONVEX: rcMarkConvexPolyArea(m_ctx, vol.verts, vol.nverts, vol.hmin, vol.hmax, vol.flags, vol.area, *m_chf); + break; + } } diff --git a/src/naveditor/InputGeom.cpp b/src/naveditor/InputGeom.cpp index 82d33fa1..99365420 100644 --- a/src/naveditor/InputGeom.cpp +++ b/src/naveditor/InputGeom.cpp @@ -278,7 +278,7 @@ bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath) } } } - else if (row[0] == 'c') + else if (row[0] == 'o') { // Off-mesh connection if (m_offMeshConCount < MAX_OFFMESH_CONNECTIONS) @@ -311,21 +311,35 @@ bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath) // Box volumes if (m_volumeCount < MAX_VOLUMES) { - ConvexVolume* vol = &m_volumes[m_volumeCount++]; + ShapeVolume* vol = &m_volumes[m_volumeCount++]; sscanf(row+1, "%hu %hhu %f %f %f %f %f %f", &vol->flags, &vol->area, &vol->verts[0], &vol->verts[1], &vol->verts[2], &vol->verts[3], &vol->verts[4], &vol->verts[5]); - vol->bbox = true; + vol->type = VOLUME_BOX; } } - else if (row[0] == 'v') + else if (row[0] == 'c') { - // Convex volumes + // Cylinder volumes if (m_volumeCount < MAX_VOLUMES) { - ConvexVolume* vol = &m_volumes[m_volumeCount++]; + ShapeVolume* vol = &m_volumes[m_volumeCount++]; + + sscanf(row + 1, "%hu %hhu %f %f %f %f %f", &vol->flags, &vol->area, + &vol->verts[0], &vol->verts[1], &vol->verts[2], + &vol->verts[3], &vol->verts[4]); + + vol->type = VOLUME_CYLINDER; + } + } + else if (row[0] == 'p') + { + // Convex polygon volumes + if (m_volumeCount < MAX_VOLUMES) + { + ShapeVolume* vol = &m_volumes[m_volumeCount++]; sscanf(row+1, "%d %hu %hhu %f %f", &vol->nverts, &vol->flags, &vol->area, &vol->hmin, &vol->hmax); for (int i = 0; i < vol->nverts; ++i) { @@ -334,7 +348,7 @@ bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath) sscanf(row, "%f %f %f", &vol->verts[i*3+0], &vol->verts[i*3+1], &vol->verts[i*3+2]); } - vol->bbox = false; + vol->type = VOLUME_CONVEX; } } else if (row[0] == 's') @@ -456,7 +470,7 @@ bool InputGeom::saveGeomSet(const BuildSettings* settings) const int order = m_offMeshConOrders[i]; const int area = m_offMeshConAreas[i]; const int flags = m_offMeshConFlags[i]; - fprintf(fp, "c %f %f %f %f %f %f %f %f %f %f %f %d %d %d %d %d\n", + fprintf(fp, "o %f %f %f %f %f %f %f %f %f %f %f %d %d %d %d %d\n", verts[0], verts[1], verts[2], verts[3], verts[4], verts[5], refs[0], refs[1], refs[2], @@ -468,18 +482,24 @@ bool InputGeom::saveGeomSet(const BuildSettings* settings) // Convex volumes for (int i = 0; i < m_volumeCount; ++i) { - ConvexVolume* vol = &m_volumes[i]; - if (vol->bbox) + ShapeVolume* vol = &m_volumes[i]; + + switch (vol->type) { + case VOLUME_BOX: fprintf(fp, "b %hu %hhu %f %f %f %f %f %f\n", vol->flags, vol->area, vol->verts[0], vol->verts[1], vol->verts[2], vol->verts[3], vol->verts[4], vol->verts[5]); - } - else - { - fprintf(fp, "v %d %hu %hhu %f %f\n", vol->nverts, vol->flags, vol->area, vol->hmin, vol->hmax); - for (int j = 0; j < vol->nverts; ++j) - fprintf(fp, "%f %f %f\n", vol->verts[j*3+0], vol->verts[j*3+1], vol->verts[j*3+2]); + break; + case VOLUME_CYLINDER: + fprintf(fp, "c %hu %hhu %f %f %f %f %f\n", vol->flags, vol->area, + vol->verts[0], vol->verts[1], vol->verts[2], + vol->verts[3], vol->verts[4]); + break; + case VOLUME_CONVEX: + fprintf(fp, "p %d %hu %hhu %f %f\n", vol->nverts, vol->flags, vol->area, vol->hmin, vol->hmax); + for (int j = 0; j < vol->nverts; ++j) + fprintf(fp, "%f %f %f\n", vol->verts[j*3+0], vol->verts[j*3+1], vol->verts[j*3+2]); } } @@ -488,45 +508,11 @@ bool InputGeom::saveGeomSet(const BuildSettings* settings) return true; } -static bool isectSegAABB(const float* sp, const float* sq, - const float* amin, const float* amax, - float& tmin, float& tmax) -{ - float d[3]; - d[0] = sq[0] - sp[0]; - d[1] = sq[1] - sp[1]; - d[2] = sq[2] - sp[2]; - tmin = 0.0; - tmax = 1.0f; - - for (int i = 0; i < 3; i++) - { - if (rdMathFabsf(d[i]) < RD_EPS) - { - if (sp[i] < amin[i] || sp[i] > amax[i]) - return false; - } - else - { - const float ood = 1.0f / d[i]; - float t1 = (amin[i] - sp[i]) * ood; - float t2 = (amax[i] - sp[i]) * ood; - if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; } - if (t1 > tmin) tmin = t1; - if (t2 < tmax) tmax = t2; - if (tmin > tmax) return false; - } - } - - return true; -} - - bool InputGeom::raycastMesh(const float* src, const float* dst, float* tmin) const { // Prune hit ray. float btmin, btmax; - if (!isectSegAABB(src, dst, m_meshBMin, m_meshBMax, btmin, btmax)) + if (!rdIntersectSegmentAABB(src, dst, m_meshBMin, m_meshBMax, btmin, btmax)) return false; float p[2], q[2]; p[0] = src[0] + (dst[0]-src[0])*btmin; @@ -549,21 +535,40 @@ bool InputGeom::raycastMesh(const float* src, const float* dst, float* tmin) con for (int i = 0; i < nvol; i++) { - const ConvexVolume& vol = m_volumes[i]; + const ShapeVolume& vol = m_volumes[i]; if (vol.area != RC_NULL_AREA) continue; // Clip brushes only. - if ((src[2] >= vol.hmin && src[2] <= vol.hmax) || - (dst[2] >= vol.hmin && dst[2] <= vol.hmax)) + if (vol.type == VOLUME_BOX) { - if (rdIntersectSegmentPoly2D(src, dst, vol.verts, vol.nverts, - tsmin, tsmax, segMin, segMax)) + if (rdIntersectSegmentAABB(src, dst, &vol.verts[0], &vol.verts[3], tsmin, tsmax)) { hit = true; break; } } + else if (vol.type == VOLUME_CYLINDER) + { + if (rdIntersectSegmentCylinder(src, dst, &vol.verts[0], vol.verts[3], vol.verts[4], tsmin, tsmax)) + { + hit = true; + break; + } + } + else if (vol.type == VOLUME_CONVEX) + { + if ((src[2] >= vol.hmin && src[2] <= vol.hmax) || + (dst[2] >= vol.hmin && dst[2] <= vol.hmax)) + { + if (rdIntersectSegmentPoly2D(src, dst, vol.verts, vol.nverts, + tsmin, tsmax, segMin, segMax)) + { + hit = true; + break; + } + } + } } if (hit) @@ -693,18 +698,46 @@ void InputGeom::drawOffMeshConnections(duDebugDraw* dd, const float* offset, boo dd->depthMask(true); } +void InputGeom::addBoxVolume(const float* bmin, const float* bmax, + unsigned short flags, unsigned char area) +{ + if (m_volumeCount >= MAX_VOLUMES) return; + ShapeVolume* vol = &m_volumes[m_volumeCount++]; + memset(vol, 0, sizeof(ShapeVolume)); + rdVcopy(&vol->verts[0], bmin); + rdVcopy(&vol->verts[3], bmax); + vol->flags = flags; + vol->area = area; + vol->type = VOLUME_BOX; +} + +void InputGeom::addCylinderVolume(const float* pos, const float radius, + const float height, unsigned short flags, unsigned char area) +{ + if (m_volumeCount >= MAX_VOLUMES) return; + ShapeVolume* vol = &m_volumes[m_volumeCount++]; + memset(vol, 0, sizeof(ShapeVolume)); + rdVcopy(vol->verts, pos); + vol->verts[3] = radius; + vol->verts[4] = height; + vol->flags = flags; + vol->area = area; + vol->type = VOLUME_CYLINDER; +} + void InputGeom::addConvexVolume(const float* verts, const int nverts, const float minh, const float maxh, unsigned short flags, unsigned char area) { if (m_volumeCount >= MAX_VOLUMES) return; - ConvexVolume* vol = &m_volumes[m_volumeCount++]; - memset(vol, 0, sizeof(ConvexVolume)); + ShapeVolume* vol = &m_volumes[m_volumeCount++]; + memset(vol, 0, sizeof(ShapeVolume)); memcpy(vol->verts, verts, sizeof(float)*3*nverts); vol->hmin = minh; vol->hmax = maxh; vol->nverts = nverts; vol->flags = flags; vol->area = area; + vol->type = VOLUME_CONVEX; } void InputGeom::deleteConvexVolume(int i) @@ -717,16 +750,16 @@ void InputGeom::drawBoxVolumes(struct duDebugDraw* dd, const float* offset, bool { for (int i = 0; i < m_volumeCount; ++i) { - const ConvexVolume* vol = &m_volumes[i]; + const ShapeVolume* vol = &m_volumes[i]; - if (!vol->bbox) + if (vol->type != VOLUME_BOX) continue; const unsigned int faceCol = vol->area == RC_NULL_AREA - ? duRGBA(255, 0, 0, 128) // Use red for visibility (null acts as deletion). - : duTransCol(dd->areaToCol(vol->area), 64); + ? duRGBA(255, 0, 0, 168) // Use red for visibility (null acts as deletion). + : duTransCol(dd->areaToCol(vol->area), 168); - unsigned int fcol[6] = { faceCol, 0, faceCol, faceCol, faceCol, faceCol }; + unsigned int fcol[6] = { faceCol, faceCol, faceCol, faceCol, faceCol, faceCol }; duDebugDrawBox(dd, vol->verts[0],vol->verts[1],vol->verts[2], @@ -744,23 +777,53 @@ void InputGeom::drawBoxVolumes(struct duDebugDraw* dd, const float* offset, bool } } +void InputGeom::drawCylinderVolumes(struct duDebugDraw* dd, const float* offset, bool /*hilight*/) +{ + for (int i = 0; i < m_volumeCount; ++i) + { + const ShapeVolume* vol = &m_volumes[i]; + + if (vol->type != VOLUME_CYLINDER) + continue; + + const unsigned int faceCol = vol->area == RC_NULL_AREA + ? duRGBA(255, 0, 0, 168) // Use red for visibility (null acts as deletion). + : duTransCol(dd->areaToCol(vol->area), 168); + + const float radius = vol->verts[3]; + const float height = vol->verts[4]; + + duDebugDrawCylinder(dd, + vol->verts[0]-radius,vol->verts[1]-radius,vol->verts[2]+0.1f, + vol->verts[0]+radius,vol->verts[1]+radius,vol->verts[2]+height, faceCol, offset); + + const unsigned int wireCol = vol->area == RC_NULL_AREA + ? duRGBA(255, 0, 0, 220) + : duTransCol(dd->areaToCol(vol->area), 220); + + duDebugDrawCylinderWire(dd, + vol->verts[0]-radius,vol->verts[1]-radius,vol->verts[2]+0.1f, + vol->verts[0]+radius,vol->verts[1]+radius,vol->verts[2]+height, wireCol, 2.0f, offset); + } +} + void InputGeom::drawConvexVolumes(struct duDebugDraw* dd, const float* offset, bool /*hilight*/) { dd->begin(DU_DRAW_TRIS, 1.0f, offset); for (int i = 0; i < m_volumeCount; ++i) { - const ConvexVolume* vol = &m_volumes[i]; + const ShapeVolume* vol = &m_volumes[i]; - if (vol->bbox) + if (vol->type != VOLUME_CONVEX) continue; unsigned int col; if (vol->area == RC_NULL_AREA) - col = duRGBA(255, 0, 0, 128); // Use red for visibility (null acts as deletion). + col = duRGBA(255, 0, 0, 168); // Use red for visibility (null acts as deletion). else - col = duTransCol(dd->areaToCol(vol->area), 64); + col = duTransCol(dd->areaToCol(vol->area), 168); for (int j = 0, k = vol->nverts-1; j < vol->nverts; k = j++) { @@ -786,9 +849,9 @@ void InputGeom::drawConvexVolumes(struct duDebugDraw* dd, const float* offset, b dd->begin(DU_DRAW_LINES, 2.0f, offset); for (int i = 0; i < m_volumeCount; ++i) { - const ConvexVolume* vol = &m_volumes[i]; + const ShapeVolume* vol = &m_volumes[i]; - if (vol->bbox) + if (vol->type != VOLUME_CONVEX) continue; unsigned int col; @@ -815,15 +878,15 @@ void InputGeom::drawConvexVolumes(struct duDebugDraw* dd, const float* offset, b dd->begin(DU_DRAW_POINTS, 3.0f, offset); for (int i = 0; i < m_volumeCount; ++i) { - const ConvexVolume* vol = &m_volumes[i]; + const ShapeVolume* vol = &m_volumes[i]; - if (vol->bbox) + if (vol->type != VOLUME_CONVEX) continue; unsigned int col; if (vol->area == RC_NULL_AREA) - col = duRGBA(255, 0, 0, 220); + col = duDarkenCol(duRGBA(255, 0, 0, 220)); else col = duDarkenCol(duTransCol(dd->areaToCol(vol->area), 220)); diff --git a/src/naveditor/include/ConvexVolumeTool.h b/src/naveditor/include/ConvexVolumeTool.h index bb021db3..108fd370 100644 --- a/src/naveditor/include/ConvexVolumeTool.h +++ b/src/naveditor/include/ConvexVolumeTool.h @@ -21,16 +21,24 @@ #include "NavEditor/Include/Editor.h" -// Tool to create convex volumess for InputGeom +// Tool to create shape volumes for InputGeom -class ConvexVolumeTool : public EditorTool +class ShapeVolumeTool : public EditorTool { Editor* m_editor; + int m_selectedPrimitive; int m_areaType; int m_polyFlags; - float m_polyOffset; - float m_boxHeight; + float m_boxDescent; + float m_boxAscent; + + float m_cylinderRadius; + float m_cylinderHeight; + + float m_convexOffset; + float m_convexHeight; + float m_convexDescent; static const int MAX_PTS = 12; float m_pts[MAX_PTS*3]; @@ -39,9 +47,9 @@ class ConvexVolumeTool : public EditorTool int m_nhull; public: - ConvexVolumeTool(); + ShapeVolumeTool(); - virtual int type() { return TOOL_CONVEX_VOLUME; } + virtual int type() { return TOOL_SHAPE_VOLUME; } virtual void init(Editor* editor); virtual void reset(); virtual void handleMenu(); diff --git a/src/naveditor/include/Editor.h b/src/naveditor/include/Editor.h index 33e3dd40..0096ed70 100644 --- a/src/naveditor/include/Editor.h +++ b/src/naveditor/include/Editor.h @@ -116,7 +116,7 @@ enum EditorToolType TOOL_NAVMESH_PRUNE, TOOL_OFFMESH_CONNECTION, TOOL_TRAVERSE_LINK, - TOOL_CONVEX_VOLUME, + TOOL_SHAPE_VOLUME, TOOL_CROWD, MAX_TOOLS }; diff --git a/src/naveditor/include/InputGeom.h b/src/naveditor/include/InputGeom.h index f3f45bf6..411d7161 100644 --- a/src/naveditor/include/InputGeom.h +++ b/src/naveditor/include/InputGeom.h @@ -22,15 +22,22 @@ #include "NavEditor/Include/ChunkyTriMesh.h" #include "NavEditor/Include/MeshLoaderObj.h" -static const int MAX_CONVEXVOL_PTS = 12; -struct ConvexVolume +enum VolumeType : unsigned char { - float verts[MAX_CONVEXVOL_PTS*3]; + VOLUME_BOX, + VOLUME_CYLINDER, + VOLUME_CONVEX +}; + +static const int MAX_SHAPEVOL_PTS = 12; +struct ShapeVolume +{ + float verts[MAX_SHAPEVOL_PTS*3]; float hmin, hmax; int nverts; unsigned short flags; unsigned char area; - bool bbox; + unsigned char type; }; struct BuildSettings @@ -104,7 +111,7 @@ class InputGeom /// @name Convex Volumes. ///@{ static const int MAX_VOLUMES = 256; - ConvexVolume m_volumes[MAX_VOLUMES]; + ShapeVolume m_volumes[MAX_VOLUMES]; int m_volumeCount; ///@} @@ -160,11 +167,16 @@ public: /// @name Box Volumes. ///@{ int getConvexVolumeCount() const { return m_volumeCount; } - const ConvexVolume* getConvexVolumes() const { return m_volumes; } + const ShapeVolume* getConvexVolumes() const { return m_volumes; } + void addBoxVolume(const float* bmin, const float* bmax, + unsigned short flags, unsigned char area); + void addCylinderVolume(const float* pos, const float radius, + const float height, unsigned short flags, unsigned char area); void addConvexVolume(const float* verts, const int nverts, const float minh, const float maxh, unsigned short flags, unsigned char area); void deleteConvexVolume(int i); void drawBoxVolumes(struct duDebugDraw* dd, const float* offset, bool hilight = false); + void drawCylinderVolumes(struct duDebugDraw* dd, const float* offset, bool hilight = false); void drawConvexVolumes(struct duDebugDraw* dd, const float* offset, bool hilight = false); ///@}