minor changes to floorplan
fixed some compile issues worked on nav-meshes added some tests
This commit is contained in:
@@ -6,10 +6,14 @@
|
||||
|
||||
#include "NavMesh.h"
|
||||
#include "NavMeshTriangle.h"
|
||||
#include "NavMeshFactoryListener.h"
|
||||
#include "NavMeshType.h"
|
||||
#include "NavMeshSettings.h"
|
||||
|
||||
#include "../lib/gpc/gpc.cpp.h"
|
||||
#include "../lib/Recast/Recast.h"
|
||||
|
||||
|
||||
namespace NM {
|
||||
|
||||
|
||||
@@ -119,11 +123,7 @@ namespace NM {
|
||||
|
||||
|
||||
|
||||
enum SamplePartitionType {
|
||||
SAMPLE_PARTITION_WATERSHED,
|
||||
SAMPLE_PARTITION_MONOTONE,
|
||||
SAMPLE_PARTITION_LAYERS,
|
||||
};
|
||||
|
||||
|
||||
struct TriangleIn {
|
||||
Point3 p1;
|
||||
@@ -150,86 +150,172 @@ namespace NM {
|
||||
|
||||
};
|
||||
|
||||
#define NMF_STEPS 8
|
||||
|
||||
template <typename Tria> class NavMeshFactory {
|
||||
|
||||
private:
|
||||
|
||||
float maxQuality_m = 0.20f; // 25cm elements are the smallest to-be-detected
|
||||
|
||||
NavMesh<Tria>* dst = nullptr;
|
||||
|
||||
const NavMeshSettings& settings;
|
||||
|
||||
std::vector<TriangleIn> triangles;
|
||||
|
||||
public:
|
||||
|
||||
NavMeshFactory(NavMesh<Tria>* dst) : dst(dst) {
|
||||
NavMeshFactory(NavMesh<Tria>* dst, const NavMeshSettings& settings) : dst(dst), settings(settings) {
|
||||
|
||||
}
|
||||
|
||||
void build(Floorplan::IndoorMap* map) {
|
||||
void build(Floorplan::IndoorMap* map, NavMeshFactoryListener* listener = nullptr) {
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor("preparing");}
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor(NMF_STEPS, 0);}
|
||||
const BBox3 bbox = FloorplanHelper::getBBox(map);
|
||||
for (const Floorplan::Floor* floor : map->floors) {
|
||||
add(floor);
|
||||
}
|
||||
fire(bbox);
|
||||
fire(bbox, listener);
|
||||
}
|
||||
|
||||
/** get the smallest obstacle size that can be detected */
|
||||
float getMaxQuality_m() const {
|
||||
return maxQuality_m;
|
||||
}
|
||||
// /** get the smallest obstacle size that can be detected */
|
||||
// float getMaxQuality_m() const {
|
||||
// return maxQuality_m;
|
||||
// }
|
||||
|
||||
private:
|
||||
|
||||
/** add one floor */
|
||||
void add(const Floorplan::Floor* floor) {
|
||||
|
||||
NavMeshPoly nmPoly(floor->atHeight);
|
||||
if (!floor->enabled) {return;}
|
||||
|
||||
// NavMeshPoly nmPoly(floor->atHeight);
|
||||
|
||||
// for (Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
// if (poly->method == Floorplan::OutlineMethod::ADD) {
|
||||
// nmPoly.add(poly->poly);
|
||||
// }
|
||||
// }
|
||||
|
||||
// for (Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
// if (poly->method == Floorplan::OutlineMethod::REMOVE) {
|
||||
// nmPoly.remove(poly->poly);
|
||||
// }
|
||||
// }
|
||||
|
||||
// for (Floorplan::FloorObstacle* obs : floor->obstacles) {
|
||||
// Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obs);
|
||||
// if (line != nullptr) {
|
||||
// nmPoly.remove(getPolygon(line));
|
||||
// }
|
||||
// }
|
||||
|
||||
// std::vector<std::vector<Point3>> tmp = nmPoly.get();
|
||||
// for (const std::vector<Point3>& tria : tmp) {
|
||||
// const TriangleIn t(tria[0], tria[1], tria[2], 1; // TODO outdoor
|
||||
// triangles.push_back(t);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// we need this strange loop, as we need to distinguish between indoor and outdoor regions/polygons
|
||||
// adding all "add" polygons first and removing "remove" polygons / obstacles afterwards is more performant
|
||||
// but does not allow for tagging the "add" polygons (indoor/outdoor/...)
|
||||
// thats why we have to tread each "add" polygon on its own (and remove all potential elements from it)
|
||||
for (Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
|
||||
// if this is a to-be-added polygon, add it
|
||||
if (poly->method == Floorplan::OutlineMethod::ADD) {
|
||||
|
||||
NavMeshPoly nmPoly(floor->atHeight);
|
||||
nmPoly.add(poly->poly);
|
||||
}
|
||||
}
|
||||
|
||||
for (Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
if (poly->method == Floorplan::OutlineMethod::REMOVE) {
|
||||
nmPoly.remove(poly->poly);
|
||||
}
|
||||
}
|
||||
// get all other polygons of this floor, that are tagged as "remove" and remove them (many will be outside of the added polygon)
|
||||
for (Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
if (poly->method == Floorplan::OutlineMethod::REMOVE) {
|
||||
nmPoly.remove(poly->poly);
|
||||
}
|
||||
}
|
||||
|
||||
for (Floorplan::FloorObstacle* obs : floor->obstacles) {
|
||||
Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obs);
|
||||
if (line != nullptr) {
|
||||
nmPoly.remove(getPolygon(line));
|
||||
}
|
||||
}
|
||||
// get all obstacles of this floor and remove them from the polygon as well (many will be outside of the added polygon)
|
||||
for (Floorplan::FloorObstacle* obs : floor->obstacles) {
|
||||
Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obs);
|
||||
if (line != nullptr) {
|
||||
nmPoly.remove(getPolygon(line));
|
||||
}
|
||||
}
|
||||
|
||||
// construct and add
|
||||
std::vector<std::vector<Point3>> tmp = nmPoly.get();
|
||||
int type = poly->outdoor ? (int) NavMeshType::FLOOR_OUTDOOR : (int) NavMeshType::FLOOR_INDOOR;
|
||||
for (const std::vector<Point3>& tria : tmp) {
|
||||
const TriangleIn t(tria[0], tria[1], tria[2], type);
|
||||
triangles.push_back(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::vector<Point3>> tmp = nmPoly.get();
|
||||
for (const std::vector<Point3>& tria : tmp) {
|
||||
const TriangleIn t(tria[0], tria[1], tria[2], 1); // TODO outdoor
|
||||
triangles.push_back(t);
|
||||
}
|
||||
|
||||
// add all stairs
|
||||
// those must be DIRECTLY connected to the ending floor (stair's ending edge connected to an edge of the floor)
|
||||
// otherwise the stair ends UNDER a floor polygon and is thus not added (higher polygons always win)
|
||||
for (const Floorplan::Stair* stair : floor->stairs) {
|
||||
const std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(stair->getParts(), floor);
|
||||
const std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(stair->getParts(), floor); // slightly grow to ensure connection?!
|
||||
for (const Floorplan::Quad3& quad : quads) {
|
||||
const TriangleIn t1(quad.p1, quad.p2, quad.p3, 2); // TODO type
|
||||
const TriangleIn t2(quad.p1, quad.p3, quad.p4, 2);
|
||||
|
||||
// stair has two options: either leveled parts (no steps) and skewed parts (steps)
|
||||
// as those affect the pedestrian's step-length, we tag them differently
|
||||
const int type = quad.isLeveled() ? (int) NavMeshType::STAIR_LEVELED : (int) NavMeshType::STAIR_SKEWED;
|
||||
const TriangleIn t1(quad.p1, quad.p2, quad.p3, type);
|
||||
const TriangleIn t2(quad.p1, quad.p3, quad.p4, type);
|
||||
triangles.push_back(t1);
|
||||
triangles.push_back(t2);
|
||||
|
||||
// sanity check. should never happen. just to be ultra sure
|
||||
const Point3 norm1 = cross((t1.p2-t1.p1), (t1.p3-t1.p1));
|
||||
const Point3 norm2 = cross((t2.p2-t2.p1), (t2.p3-t2.p1));
|
||||
Assert::isTrue(norm1.z > 0, "detected invalid culling for stair-quad. normal points downwards");
|
||||
Assert::isTrue(norm2.z > 0, "detected invalid culling for stair-quad. normal points downwards");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// finally create additional triangles for the doors to tag doors differently (tagging also seems to improve the triangulation result)
|
||||
// note: door-regions are already walkable as doors are NOT removed from the outline
|
||||
// however: adding them again here seems to work.. triangles at the end of the list seem to overwrite (tagging) previous ones -> fine
|
||||
{
|
||||
|
||||
// add (overlay) all doors for tagging them within the plan
|
||||
NavMeshPoly nmDoors(floor->atHeight);
|
||||
for (Floorplan::FloorObstacle* obs : floor->obstacles) {
|
||||
Floorplan::FloorObstacleDoor* door = dynamic_cast<Floorplan::FloorObstacleDoor*>(obs);
|
||||
if (door != nullptr) {
|
||||
nmDoors.add(getPolygon(door));
|
||||
}
|
||||
}
|
||||
|
||||
// construct and add triangles
|
||||
std::vector<std::vector<Point3>> tmp = nmDoors.get();
|
||||
for (const std::vector<Point3>& tria : tmp) {
|
||||
const TriangleIn t(tria[0], tria[1], tria[2], (int) NavMeshType::DOOR);
|
||||
triangles.push_back(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool fire(BBox3 bbox) {
|
||||
bool fire(BBox3 bbox, NavMeshFactoryListener* listener) {
|
||||
|
||||
std::vector<int> tData;
|
||||
std::vector<float> vData;
|
||||
std::vector<uint8_t> typeData;
|
||||
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor("building polygons");}
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor(NMF_STEPS, 1);}
|
||||
|
||||
// floor outlines
|
||||
for (const TriangleIn& t : triangles) {
|
||||
|
||||
@@ -282,37 +368,37 @@ namespace NM {
|
||||
rcPolyMeshDetail* m_dmesh;
|
||||
rcContext* m_ctx = new rcContext();
|
||||
|
||||
float m_cellSize = maxQuality_m/2.0f; //0.3f; // ensure quality is enough to fit maxQuality_m
|
||||
float m_cellHeight = maxQuality_m/2.0f; //0.2f;
|
||||
float m_agentHeight = 2.0f;
|
||||
float m_agentRadius = 0.2f;//0.6f;
|
||||
float m_agentMaxClimb = maxQuality_m; // 0.9f; // prevent jumping onto stairs from the side of the stair. setting this below 2xgrid-size will fail!
|
||||
float m_agentMaxSlope = 45.0f; // elevator???
|
||||
float m_regionMinSize = 2;//8;
|
||||
float m_regionMergeSize = 20;
|
||||
float m_edgeMaxLen = 10.0f; // maximal size for one triangle. too high = too many samples when walking!
|
||||
float m_edgeMaxError = 1.1f; //1.3f; // higher values allow joining some small triangles
|
||||
float m_vertsPerPoly = 3;//6.0f;
|
||||
float m_detailSampleDist = 6.0f;
|
||||
float m_detailSampleMaxError = 1.0f;//1.0f;
|
||||
int m_partitionType = SAMPLE_PARTITION_WATERSHED; // SAMPLE_PARTITION_WATERSHED SAMPLE_PARTITION_MONOTONE SAMPLE_PARTITION_LAYERS
|
||||
// float m_cellSize = maxQuality_m/2.0f; //0.3f; // ensure quality is enough to fit maxQuality_m
|
||||
// float m_cellHeight = maxQuality_m/2.0f; //0.2f;
|
||||
// float m_agentHeight = 1.8f;
|
||||
// float m_agentRadius = 0.2f;//0.6f;
|
||||
// float m_agentMaxClimb = maxQuality_m; // 0.9f; // prevent jumping onto stairs from the side of the stair. setting this below 2xgrid-size will fail!
|
||||
// float m_agentMaxSlope = 45.0f; // elevator???
|
||||
// float m_regionMinSize = 2;//8;
|
||||
// float m_regionMergeSize = 20;
|
||||
// float m_edgeMaxLen = 10.0f; // maximal size for one triangle. too high = too many samples when walking!
|
||||
// float m_edgeMaxError = 1.1f; //1.3f; // higher values allow joining some small triangles
|
||||
// float m_vertsPerPoly = 3;//6.0f;
|
||||
// float m_detailSampleDist = 6.0f;
|
||||
// float m_detailSampleMaxError = 1.0f;//1.0f;
|
||||
// int m_partitionType = SAMPLE_PARTITION_WATERSHED; // SAMPLE_PARTITION_WATERSHED SAMPLE_PARTITION_MONOTONE SAMPLE_PARTITION_LAYERS
|
||||
|
||||
|
||||
// Init build configuration from GUI
|
||||
memset(&m_cfg, 0, sizeof(m_cfg));
|
||||
m_cfg.cs = m_cellSize;
|
||||
m_cfg.ch = m_cellHeight;
|
||||
m_cfg.walkableSlopeAngle = m_agentMaxSlope;
|
||||
m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch);
|
||||
m_cfg.walkableClimb = (int)floorf(m_agentMaxClimb / m_cfg.ch);
|
||||
m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs);
|
||||
m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize);
|
||||
m_cfg.maxSimplificationError = m_edgeMaxError;
|
||||
m_cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size
|
||||
m_cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size
|
||||
m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
|
||||
m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist;
|
||||
m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError;
|
||||
m_cfg.cs = settings.getCellSizeXY();
|
||||
m_cfg.ch = settings.getCellSizeZ();
|
||||
m_cfg.walkableSlopeAngle = settings.agentMaxSlope;
|
||||
m_cfg.walkableHeight = (int)ceilf(settings.agentHeight / m_cfg.ch);
|
||||
m_cfg.walkableClimb = (int)floorf(settings.getMaxClimb() / m_cfg.ch);
|
||||
m_cfg.walkableRadius = (int)ceilf(settings.agentRadius / m_cfg.cs);
|
||||
m_cfg.maxEdgeLen = (int)(settings.edgeMaxLen / settings.getCellSizeXY());
|
||||
m_cfg.maxSimplificationError = settings.edgeMaxError;
|
||||
m_cfg.minRegionArea = (int)rcSqr(settings.regionMinSize); // Note: area = size*size
|
||||
m_cfg.mergeRegionArea = (int)rcSqr(settings.regionMergeSize); // Note: area = size*size
|
||||
m_cfg.maxVertsPerPoly = settings.vertsPerPoly;
|
||||
m_cfg.detailSampleDist = settings.detailSampleDist < 0.9f ? 0 : settings.getCellSizeXY() * settings.detailSampleDist;
|
||||
m_cfg.detailSampleMaxError = settings.getCellSizeZ() * settings.detailSampleMaxError;
|
||||
|
||||
float bmin[3] = {bbox.getMin().x, bbox.getMin().z, bbox.getMin().y};
|
||||
float bmax[3] = {bbox.getMax().x, bbox.getMax().z, bbox.getMax().y};// x/z swapped?
|
||||
@@ -338,15 +424,16 @@ namespace NM {
|
||||
// Step 2. Rasterize input polygon soup.
|
||||
//
|
||||
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor("rasterizing polygons");}
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor(NMF_STEPS, 2);}
|
||||
|
||||
// Allocate voxel heightfield where we rasterize our input data to.
|
||||
m_solid = rcAllocHeightfield();
|
||||
if (!m_solid)
|
||||
{
|
||||
if (!m_solid) {
|
||||
m_ctx->log(RC_LOG_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))
|
||||
{
|
||||
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_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield.");
|
||||
return false;
|
||||
}
|
||||
@@ -366,8 +453,7 @@ namespace NM {
|
||||
// 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, verts, nverts, tris, ntris, m_triareas);
|
||||
if (!rcRasterizeTriangles(m_ctx, verts, nverts, tris, m_triareas, ntris, *m_solid, m_cfg.walkableClimb))
|
||||
{
|
||||
if (!rcRasterizeTriangles(m_ctx, verts, nverts, tris, m_triareas, ntris, *m_solid, m_cfg.walkableClimb)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not rasterize triangles.");
|
||||
return false;
|
||||
}
|
||||
@@ -377,18 +463,9 @@ namespace NM {
|
||||
bool m_filterLedgeSpans = false;
|
||||
bool m_filterWalkableLowHeightSpans = false;
|
||||
|
||||
// std::vector!
|
||||
// if (!m_keepInterResults)
|
||||
// {
|
||||
// delete [] m_triareas;
|
||||
// m_triareas = 0;
|
||||
// }
|
||||
|
||||
//
|
||||
// Step 3. Filter walkables surfaces.
|
||||
//
|
||||
|
||||
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor("filtering");}
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor(NMF_STEPS, 3);}
|
||||
|
||||
// Once all geoemtry is rasterized, we do initial pass of filtering to
|
||||
// remove unwanted overhangs caused by the conservative rasterization
|
||||
@@ -401,30 +478,27 @@ namespace NM {
|
||||
rcFilterWalkableLowHeightSpans(m_ctx, m_cfg.walkableHeight, *m_solid);
|
||||
|
||||
|
||||
//
|
||||
// Step 4. Partition walkable surface to simple regions.
|
||||
//
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor("partitioning");}
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor(NMF_STEPS, 4);}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (!m_chf) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf))
|
||||
{
|
||||
if (!rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_keepInterResults)
|
||||
{
|
||||
//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))
|
||||
@@ -465,103 +539,107 @@ namespace NM {
|
||||
// if you have large open areas with small obstacles (not a problem if you use tiles)
|
||||
// * good choice to use for tiled navmesh with medium and small sized tiles
|
||||
|
||||
if (m_partitionType == SAMPLE_PARTITION_WATERSHED)
|
||||
{
|
||||
|
||||
switch (settings.partitionType) {
|
||||
|
||||
case SamplePartitionType::SAMPLE_PARTITION_WATERSHED:
|
||||
|
||||
// Prepare for region partitioning, by calculating distance field along the walkable surface.
|
||||
if (!rcBuildDistanceField(m_ctx, *m_chf))
|
||||
{
|
||||
if (!rcBuildDistanceField(m_ctx, *m_chf)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build distance field.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Partition the walkable surface into simple regions without holes.
|
||||
if (!rcBuildRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea))
|
||||
{
|
||||
if (!rcBuildRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build watershed regions.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_partitionType == SAMPLE_PARTITION_MONOTONE)
|
||||
{
|
||||
break;
|
||||
|
||||
case SamplePartitionType::SAMPLE_PARTITION_MONOTONE:
|
||||
|
||||
// Partition the walkable surface into simple regions without holes.
|
||||
// Monotone partitioning does not need distancefield.
|
||||
if (!rcBuildRegionsMonotone(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea))
|
||||
{
|
||||
if (!rcBuildRegionsMonotone(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build monotone regions.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // SAMPLE_PARTITION_LAYERS
|
||||
{
|
||||
|
||||
break;
|
||||
|
||||
case SamplePartitionType::SAMPLE_PARTITION_LAYERS:
|
||||
|
||||
// Partition the walkable surface into simple regions without holes.
|
||||
if (!rcBuildLayerRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea))
|
||||
{
|
||||
if (!rcBuildLayerRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build layer regions.");
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
throw Exception("unsupported SamplePartitionType");
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Step 5. Trace and simplify region contours.
|
||||
//
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor("tracing");}
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor(NMF_STEPS, 5);}
|
||||
|
||||
// Create contours.
|
||||
m_cset = rcAllocContourSet();
|
||||
if (!m_cset)
|
||||
{
|
||||
if (!m_cset) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcBuildContours(m_ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset))
|
||||
{
|
||||
if (!rcBuildContours(m_ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create contours.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Step 6. Build polygons mesh from contours.
|
||||
//
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor("building triangles");}
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor(NMF_STEPS, 6);}
|
||||
|
||||
// Build polygon navmesh from the contours.
|
||||
m_pmesh = rcAllocPolyMesh();
|
||||
if (!m_pmesh)
|
||||
{
|
||||
if (!m_pmesh) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh))
|
||||
{
|
||||
if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Step 7. Create detail mesh which allows to access approximate height on each polygon.
|
||||
//
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor("building details");}
|
||||
if (listener) {listener->onNavMeshBuildUpdateMajor(NMF_STEPS, 7);}
|
||||
|
||||
m_dmesh = rcAllocPolyMeshDetail();
|
||||
if (!m_dmesh)
|
||||
{
|
||||
if (!m_dmesh) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmdtl'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh))
|
||||
{
|
||||
if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) {
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build detail mesh.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_keepInterResults)
|
||||
{
|
||||
//if (!m_keepInterResults) {
|
||||
rcFreeCompactHeightfield(m_chf);
|
||||
m_chf = 0;
|
||||
rcFreeContourSet(m_cset);
|
||||
m_cset = 0;
|
||||
}
|
||||
//}
|
||||
|
||||
|
||||
std::vector<TriangleOut> res;
|
||||
// std::vector<TriangleOut> res;
|
||||
|
||||
const float* orig = m_pmesh->bmin;
|
||||
|
||||
@@ -628,8 +706,7 @@ namespace NM {
|
||||
|
||||
/** as line-obstacles have a thickness, we need 4 lines for the intersection test! */
|
||||
Floorplan::Polygon2 getPolygon(const Floorplan::FloorObstacleLine* line) const {
|
||||
//const Line2 base(line->from*100, line->to*100);
|
||||
const float thickness_m = std::max(line->thickness_m, maxQuality_m); // wall's thickness (make thin walls big enough to be detected)
|
||||
const float thickness_m = std::max(line->thickness_m, settings.maxQuality_m); // wall's thickness (make thin walls big enough to be detected)
|
||||
const Point2 dir = (line->to - line->from); // obstacle's direction
|
||||
const Point2 perp = dir.perpendicular().normalized(); // perpendicular direction (90 degree)
|
||||
const Point2 p1 = line->from + perp * thickness_m/2; // start-up
|
||||
@@ -644,6 +721,23 @@ namespace NM {
|
||||
return res;
|
||||
}
|
||||
|
||||
/** as line-obstacles have a thickness, we need 4 lines for the intersection test! */
|
||||
Floorplan::Polygon2 getPolygon(const Floorplan::FloorObstacleDoor* door) const {
|
||||
const float thickness_m = std::max(0.3f, settings.maxQuality_m); // wall's thickness (make thin walls big enough to be detected)
|
||||
const Point2 dir = (door->to - door->from); // obstacle's direction
|
||||
const Point2 perp = dir.perpendicular().normalized(); // perpendicular direction (90 degree)
|
||||
const Point2 p1 = door->from + perp * thickness_m/2; // start-up
|
||||
const Point2 p2 = door->from - perp * thickness_m/2; // start-down
|
||||
const Point2 p3 = door->to + perp * thickness_m/2; // end-up
|
||||
const Point2 p4 = door->to - perp * thickness_m/2; // end-down
|
||||
Floorplan::Polygon2 res;
|
||||
res.points.push_back(p1);
|
||||
res.points.push_back(p2);
|
||||
res.points.push_back(p4);
|
||||
res.points.push_back(p3);
|
||||
return res;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user