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.
This commit is contained in:
Kawe Mazidjatari 2024-09-20 15:03:30 +02:00
parent 0722def4d8
commit 290f78f8c8
9 changed files with 348 additions and 219 deletions

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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));

View File

@ -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();

View File

@ -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
};

View File

@ -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);
///@}