Recast Detour revisit

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
thanhle
Posts: 325
Joined: Wed Jun 12, 2013 8:09 am

Recast Detour revisit

Post by thanhle »

Code adopted from one of the old member.
I added in the detour part to complete it.

You have to download recast detour library from GitHub. Recompile it, then link those lib to your project. Ignore the SDL errors.
https://github.com/memononen/recastnavigation

<<<How to use of recast_util.h>>>>

I should rename it to recast_detour_util.h. Since it included the detour navmesh function. I also add the return path function in there.
I leave this as an exercise for you.

recast_util.h

Code: Select all

 
#pragma once
#ifndef _RECAST_UTIL_H_
#define _RECAST_UTIL_H_
#include <iostream>
#include <vector>
 
#include <stdio.h>
#include <cstddef>
#include <math.h>
#include <irrlicht.h>
#include <Recast.h>
#include <DetourNavMeshQuery.h>
#include "SampleInterfaces.h"
#include <DetourNavMeshBuilder.h>
#include <DetourNavMesh.h>
#include "InputGeom.h"
#include "ChunkyTriMesh.h"
 
#ifndef RECAST_UTIL_PROPERTIES
#define RECAST_UTIL_PROPERTIES
#endif
 
    /// Tool types.
    enum SampleToolType
    {
        TOOL_NONE = 0,
        TOOL_TILE_EDIT,
        TOOL_TILE_HIGHLIGHT,
        TOOL_TEMP_OBSTACLE,
        TOOL_NAVMESH_TESTER,
        TOOL_NAVMESH_PRUNE,
        TOOL_OFFMESH_CONNECTION,
        TOOL_CONVEX_VOLUME,
        TOOL_CROWD,
        MAX_TOOLS
    };
 
    /// These are just sample areas to use consistent values across the samples.
    /// The use should specify these base on his needs.
    enum SamplePolyAreas
    {
        SAMPLE_POLYAREA_GROUND,
        SAMPLE_POLYAREA_WATER,
        SAMPLE_POLYAREA_ROAD,
        SAMPLE_POLYAREA_DOOR,
        SAMPLE_POLYAREA_GRASS,
        SAMPLE_POLYAREA_JUMP,
    };
    enum SamplePolyFlags
    {
        SAMPLE_POLYFLAGS_WALK = 0x01,       // Ability to walk (ground, grass, road)
        SAMPLE_POLYFLAGS_SWIM = 0x02,       // Ability to swim (water).
        SAMPLE_POLYFLAGS_DOOR = 0x04,       // Ability to move through doors.
        SAMPLE_POLYFLAGS_JUMP = 0x08,       // Ability to jump.
        SAMPLE_POLYFLAGS_DISABLED = 0x10,       // Disabled polygon
        SAMPLE_POLYFLAGS_ALL = 0xffff   // All abilities.
    };
 
 
 
    class RecastUtilM
    {
        public:
            RecastUtilM()/* : verts(0),
                tris(0),
 
                m_ctx(0),
 
                m_keepInterResults(true),
                m_totalBuildTimeMs(0),
                m_triareas(0),
                m_solid(0),
                m_chf(0),
                m_cset(0),
                m_pmesh(0),
                m_dmesh(0)*/
            {
                verts = 0;
                tris = 0;
 
                m_ctx = 0;
                m_keepInterResults = true;
                m_totalBuildTimeMs = 0;
                m_triareas = 0;
 
                m_solid = 0;
                m_chf = 0;
                m_cset = 0;
                m_pmesh = 0;
                m_dmesh = 0;
                BuildContext *ctx;
 
                m_ctx = new BuildContext();
            
                //this->setContext(ctx);
                this->resetCommonSettings();
            };
            ~RecastUtilM()
            {
                cleanup();
            };
 
        
    private:
        float* verts;
        int* tris;
 
        float m_cellSize;
        float m_cellHeight;
        float m_agentHeight;
        float m_agentRadius;
        float m_agentMaxClimb;
        float m_agentMaxSlope;
        float m_regionMinSize;
        float m_regionMergeSize;
        bool m_monotonePartitioning;
        float m_edgeMaxLen;
        float m_edgeMaxError;
        float m_vertsPerPoly;
        float m_detailSampleDist;
        float m_detailSampleMaxError;
 
        BuildContext* m_ctx;
 
 
        bool m_keepInterResults;
        float m_totalBuildTimeMs;
 
        unsigned char* m_triareas;
        rcHeightfield* m_solid;
        rcCompactHeightfield* m_chf;
        rcContourSet* m_cset;
        rcPolyMesh* m_pmesh;
        rcConfig m_cfg;
        rcPolyMeshDetail* m_dmesh;
        void cleanup();
        
    protected:
         InputGeom* m_geom;
         dtNavMeshQuery* m_navQuery;
         dtNavMesh* m_navMesh;
        
    public:
    
        void setContext(BuildContext* ctx) { m_ctx = ctx; }
    
        //virtual ~RecastUtil();
 
         bool handleBuild(irr::scene::IMeshBuffer* buffer);
         void resetCommonSettings();
         bool getMeshBufferData
            (
            irr::scene::IMeshBuffer* buffer,
            std::vector<float>& verts, int& nverts,
            std::vector<int>& tris, int& ntris
            );
 
         bool setupIrrSMeshFromRecastDetailMesh(irr::scene::SMesh* smesh);
         void showHeightFieldInfo(const rcHeightfield& hf);
         bool getMeshDataFromPolyMeshDetail
            (
            rcPolyMeshDetail* dmesh,
            std::vector<float>& vertsOut, int& nvertsOut,
            std::vector<int>& trisOut, int& ntrisOut
            );
 
         bool setMeshBufferData
            (
            irr::scene::SMeshBuffer& buffer,
            const std::vector<float>& verts, int& nverts,
            const std::vector<int>& tris, int& ntris
            );
 
         std::vector<irr::core::vector3df> returnPath(irr::core::vector3df pStart, irr::core::vector3df pEnd);
    };
 
#endif // _RECAST_UTIL_H_
 
 
Now to the c++ file
//recast_util.cpp

Code: Select all

#include "recast_util.h"
using namespace std;
using namespace irr;
using namespace irr::core;
 
    void RecastUtilM::cleanup()
    {
        if (verts) { delete[] verts; verts = 0; }
        if (tris) { delete[] tris; tris = 0; }
 
        delete[] m_triareas;
        m_triareas = 0;
        rcFreeHeightField(m_solid);
        m_solid = 0;
        rcFreeCompactHeightfield(m_chf);
        m_chf = 0;
        rcFreeContourSet(m_cset);
        m_cset = 0;
        rcFreePolyMesh(m_pmesh);
        m_pmesh = 0;
        rcFreePolyMeshDetail(m_dmesh);
        m_dmesh = 0;
 
        //dtFreeNavMesh(m_navMesh);
        //m_navMesh = 0;
        //dtFreeNavMeshQuery(m_navQuery);
        //m_navQuery = 0;
    }
 
    bool RecastUtilM::handleBuild(scene::IMeshBuffer* buffer)
    {
        cleanup();
 
        std::vector<float> vs;
        std::vector<int> ts;
        int nverts;
        int ntris;
 
        if (!this->getMeshBufferData(buffer, vs, nverts, ts, ntris))
        {
            printf("getMeshBufferData() failed!\n");
            return false;
        }
 
        // populate verts and tris:
 
        verts = new float[nverts];
        for (int n = 0; n < nverts; ++n)
        {
            verts[n] = vs[n];
            //printf("verts[%i]=%g\n", n, verts[n]);
        }
 
        tris = new int[ntris * 3];
        for (int n = 0; n < ntris; ++n)
        {
            tris[n * 3] = ts[n * 3];
            tris[n * 3 + 1] = ts[n * 3 + 1];
            tris[n * 3 + 2] = ts[n * 3 + 2];
            //printf("tris[%i]=(%i,%i,%i)\n", n, tris[n*3], tris[n*3+1], tris[n*3+2]);
        }
        printf("vs.size()=%i,nverts=%i\n", vs.size(), nverts);
        printf("ts.size()=%i,ntris=%i\n", ts.size(), ntris);
        float bmin[3];
        float bmax[3];
 
        //rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
        rcCalcBounds(verts, nverts / 3, bmin, bmax);
 
        //
        // Step 1. Initialize build config.
        //
 
        // 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;
 
        // Set the area where the navigation will be build.
        // Here the bounds of the input mesh are used, but the
        // area could be specified by an user defined box, etc.
        rcVcopy(m_cfg.bmin, bmin);
        rcVcopy(m_cfg.bmax, bmax);
        rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height);
 
        // Reset build times gathering.
        m_ctx->resetTimers();
 
        // Start the build process. 
        m_ctx->startTimer(RC_TIMER_TOTAL);
 
        m_ctx->log(RC_LOG_PROGRESS, "Building navigation:");
        m_ctx->log(RC_LOG_PROGRESS, " - %d x %d cells", m_cfg.width, m_cfg.height);
        m_ctx->log(RC_LOG_PROGRESS, " - %.1fK verts, %.1fK tris", nverts / 1000.0f, ntris / 1000.0f);
 
        //
        // Step 2. Rasterize input polygon soup.
        //
 
        // Allocate voxel heightfield where we rasterize our input data to.
        m_solid = rcAllocHeightfield();
        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))
        {
            m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield.");
            return false;
        }
 
        // Allocate array that can hold triangle area types.
        // If you have multiple meshes you need to process, allocate
        // and array which can hold the max number of triangles you need to process.
        m_triareas = new unsigned char[ntris];
        if (!m_triareas)
        {
            m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (%d).", 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, verts, nverts, tris, ntris, m_triareas);
        rcRasterizeTriangles(m_ctx, verts, nverts, tris, m_triareas, ntris, *m_solid, m_cfg.walkableClimb);
 
        this->showHeightFieldInfo(*m_solid); //<-----------------------------------------------------------------------------------------
 
        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_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))
        {
            m_ctx->log(RC_LOG_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_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not erode.");
            return false;
        }
 
        // (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);
 
        if (m_monotonePartitioning)
        {
            // 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))
            {
                m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build regions.");
                return false;
            }
        }
        else
        {
            // Prepare for region partitioning, by calculating distance field along the walkable surface.
            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))
            {
                m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build regions.");
                return false;
            }
        }
 
        //
        // Step 5. Trace and simplify region contours.
        //
 
        // Create contours.
        m_cset = rcAllocContourSet();
        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))
        {
            m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create contours.");
            return false;
        }
        printf("m_cset->nconts=%i\n", m_cset->nconts);
        //
        // Step 6. Build polygons mesh from contours.
        //
 
        // Build polygon navmesh from the contours.
        m_pmesh = rcAllocPolyMesh();
        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))
        {
            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.
        //
 
        m_dmesh = rcAllocPolyMeshDetail();
        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))
        {
            m_ctx->log(RC_LOG_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.
        //
        // ...
 
        //
        // (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)
        {
            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;
 
                if (m_pmesh->areas[i] == SAMPLE_POLYAREA_GROUND ||
                    m_pmesh->areas[i] == SAMPLE_POLYAREA_GRASS ||
                    m_pmesh->areas[i] == SAMPLE_POLYAREA_ROAD)
                {
                    m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK;
                }
                else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_WATER)
                {
                    m_pmesh->flags[i] = SAMPLE_POLYFLAGS_SWIM;
                }
                else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_DOOR)
                {
                    m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR;
                }
            }
 
        //  InputGeom* geom = new InputGeom;
            
 
            //m_geom->raycastMesh()
            dtNavMeshCreateParams params;
            memset(&params, 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;
            /*params.offMeshConVerts = m_geom->getOffMeshConnectionVerts();
            params.offMeshConRad = m_geom->getOffMeshConnectionRads();
            params.offMeshConDir = m_geom->getOffMeshConnectionDirs();
            params.offMeshConAreas = m_geom->getOffMeshConnectionAreas();
            params.offMeshConFlags = m_geom->getOffMeshConnectionFlags();
            params.offMeshConUserID = m_geom->getOffMeshConnectionId();
            params.offMeshConCount = m_geom->getOffMeshConnectionCount();*/
 
            params.offMeshConAreas = 0;
            params.offMeshConCount = 0;
            params.offMeshConDir = 0;
            params.offMeshConFlags = 0;
            params.offMeshConRad = 0;
            params.offMeshConUserID = 0;
            params.offMeshConVerts = 0;
 
            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;
            params.buildBvTree = true;
 
            if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
            {
                m_ctx->log(RC_LOG_ERROR, "Could not build Detour navmesh.");
                return false;
            }
 
            m_navMesh = dtAllocNavMesh();
            if (!m_navMesh)
            {
                dtFree(navData);
                m_ctx->log(RC_LOG_ERROR, "Could not create Detour navmesh");
                return false;
            }
 
 
            dtStatus status;
 
            status = m_navMesh->init(navData, navDataSize, DT_TILE_FREE_DATA);
            if (dtStatusFailed(status))
            {
                dtFree(navData);
                m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh");
                return false;
            }
 
            m_navQuery = dtAllocNavMeshQuery();
            status = m_navQuery->init(m_navMesh, 2048);
            if (dtStatusFailed(status))
            {
                m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh query");
                return false;
            }
 
        }
 
 
            m_ctx->stopTimer(RC_TIMER_TOTAL);
 
            // Show performance stats.
            duLogBuildTimes(*m_ctx, m_ctx->getAccumulatedTime(RC_TIMER_TOTAL));
            m_ctx->log(RC_LOG_PROGRESS, ">> Polymesh: %d vertices  %d polygons", m_pmesh->nverts, m_pmesh->npolys);
 
            m_totalBuildTimeMs = m_ctx->getAccumulatedTime(RC_TIMER_TOTAL) / 1000.0f;
 
            // irr part:
            printf("RC_LOG_PROGRESS >> Polymesh: %d vertices  %d polygons\n", m_pmesh->nverts, m_pmesh->npolys);
            return true;
        
    }
 
    void RecastUtilM::resetCommonSettings()
    {
        m_cellSize = 4.0f; // 0.3f; // 0.3f;
        m_cellHeight =  0.2f;
        m_agentHeight = 2.0f;
        m_agentRadius = 0.6f;
        m_agentMaxClimb = 0.9f;
        m_agentMaxSlope = 45.0f;
        m_regionMinSize = 8;
        m_regionMergeSize = 20;
        m_monotonePartitioning = false;
        m_edgeMaxLen = 12.0f;
        m_edgeMaxError = 1.3f;
        m_vertsPerPoly = 6.0f;
        m_detailSampleDist = 6.0f;
        m_detailSampleMaxError = 1.0f;
    }
 
 
    bool RecastUtilM::getMeshBufferData
        (
        scene::IMeshBuffer* buffer,
        std::vector<float>& verts, int& nverts,
        std::vector<int>& tris, int& ntris
        )
    {
        nverts = buffer->getVertexCount() * 3;
        printf("nverts=%i\n", nverts);
 
        ntris = buffer->getIndexCount() / 3;
        printf("ntris=%i\n", ntris);
 
        if ((nverts <= 0) || (ntris <= 0))
        {
            printf("(nverts <= 0) || (ntris <= 0)\n");
            return false;
        }
 
        for (int i = 0; i < int(buffer->getVertexCount()); ++i)
        {
            core::vector3df pos = buffer->getPosition(i);
            verts.push_back(pos.X);
            verts.push_back(pos.Y);
            verts.push_back(pos.Z);
        }
 
        u16* indices = buffer->getIndices();
        //for (int i = 0; i < int(buffer.Indices.size()); ++i)
        for (int i = 0; i < ntris; ++i)
        {
            // some verts may contain nan (invalid values)??:
            /***
            bool ok = true;
            for (int j = 0; j < 3; ++j)
            {
            if (isnan(verts[buffer.Indices[i*3+j]]))
            {
            ok = false;
            break;
            }
            }
            if (!ok)
            continue;
            ***/
 
            tris.push_back(indices[i * 3]);
            tris.push_back(indices[i * 3 + 1]);
            tris.push_back(indices[i * 3 + 2]);
        }
        ntris = int(tris.size() / 3);
 
        // replace nan with 0 in verts:
        //for (int n = 0; n < int(verts.size()); ++n)
        //if (isnan(verts[n]))
        //verts[n] = 0.0f;
        return true;
    }
 
 
    bool RecastUtilM::setupIrrSMeshFromRecastDetailMesh(scene::SMesh* smesh)
    {
        rcPolyMeshDetail* dmesh = m_dmesh;
 
        if ((!smesh) || (!dmesh))
            return false;
 
        scene::SMeshBuffer* buffer = new scene::SMeshBuffer();
 
        std::vector<float> vertsOut;
        int nvertsOut = 0;
 
        std::vector<int> trisOut;
        int ntrisOut = 0;
 
        bool ok = getMeshDataFromPolyMeshDetail
            (
            dmesh,
            vertsOut, nvertsOut,
            trisOut, ntrisOut
            );
 
        if (!ok)
        {
            printf("setupSMesh(): getMeshDataFromPolyMeshDetail() failed!\n");
            buffer->drop();
            return false;
        }
 
        printf("setupSMesh(): vertsOut.size()=%i\n", vertsOut.size());
 
        ok = setMeshBufferData// std::vector version! 
            (
            *buffer, vertsOut, nvertsOut, trisOut, ntrisOut
            );
        if (!ok)
        {
            printf("setupSMesh(): setMeshBufferData() failed!\n");
 
            buffer->drop();
            return false;
        }
        // this is important:
        // if not done, scene may disappear sometimes!
        // recalculate bounding box
        for (u32 n = 0; n < buffer->getVertexCount(); ++n)
        {
            buffer->BoundingBox.addInternalPoint(buffer->Vertices[n].Pos);
        }
 
        smesh->addMeshBuffer(buffer);
        smesh->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_VERTEX_AND_INDEX);
        smesh->recalculateBoundingBox();
 
        buffer->drop();
        return true;
 
    }
       
    void RecastUtilM::showHeightFieldInfo(const rcHeightfield& hf)
    {
        int w = hf.width;
        int h = hf.height;
        const float* bmin = hf.bmin;
        const float* bmax = hf.bmax;
        float cs = hf.cs;
        float ch = hf.ch;
 
        printf ( "rcHeightfield hf: w=%i,h=%i,bmin=(%f,%f,%f),bmax=(%f,%f,%f),cs=%f,ch=%f\n",  w, h, bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2], cs, ch);
    }
   
C++ code is too long so forum parser can't parse it futher. Below is the additional functions in the recast_util.cpp file.
more for recast_util.cpp...

Code: Select all

 
bool RecastUtilM::getMeshDataFromPolyMeshDetail ( rcPolyMeshDetail* dmesh,  std::vector<float>& vertsOut, int& nvertsOut,   std::vector<int>& trisOut, int& ntrisOut)
    {
        printf("getMeshDataFromPolyMeshDetail(): dmesh->nmeshes=%i\n", dmesh->nmeshes);
        if (dmesh->nmeshes == 0)
        {
            printf("getMeshDataFromPolyMeshDetail(): dmesh->nmeshes == 0\n");
            return false;
        }
 
        nvertsOut = dmesh->nverts * 3;
        printf("getMeshDataFromPolyMeshDetail: nvertsOut=%i\n", nvertsOut);
 
        ntrisOut = dmesh->ntris;
        printf("getMeshDataFromPolyMeshDetail: ntrisOut=%i\n", ntrisOut);
 
        //const unsigned char* tris = dmesh->tris;
        const float* verts = dmesh->verts;
 
        for (int n = 0; n < nvertsOut; ++n)
        {
            //printf("getMeshDataFromPolyMeshDetail: tris[n*4+k]=%i\n", tris[n*4+k]);
            vertsOut.push_back(verts[n]);
        }
 
 
        //FIXME:
        int tri_offset = 0;
        int old_nverts = 0;
        for (int p = 0; p < dmesh->nmeshes; ++p)
        {
            unsigned int* m = &(dmesh->meshes[p * 4]);
            const unsigned short bverts = m[0]; // `b' means "beginning of"!!
            const unsigned short nverts = m[1];
            const unsigned short btris = m[2];
            const unsigned short ntris = m[3];
            //const float* verts = &(dmesh->verts[bverts*3]);
            const unsigned char* tris = &(dmesh->tris[btris * 4]);
            //printf("meshnum=%i, bverts=%i, btris=%i\n", p, bverts, btris);
            //ntrisOut += int(ntris);
 
            tri_offset += old_nverts;
 
            for (int n = 0; n < ntris; ++n)
            { 
                for (int k = 0; k < 3; ++k)
                {
 
                    int tri = tris[n * 4 + k] + tri_offset; 
                    trisOut.push_back(tri); 
                    //printf("getMeshDataFromPolyMeshDetail: tri=%i, index=%i\n", n, tri);
                    //vertsOut.push_back(verts[tri*3 + 0]);
                    //vertsOut.push_back(verts[tri*3 + 1]);
                    //vertsOut.push_back(verts[tri*3 + 2]);
                }
            } 
            old_nverts = nverts;
        }
       // printf("getMeshDataFromPolyMeshDetail(): vertsOut.size()=%i\n", vertsOut.size());
 
        return true;
 
    }
 
    bool RecastUtilM::setMeshBufferData ( scene::SMeshBuffer& buffer, const std::vector<float>& verts, int& nverts, const std::vector<int>& tris, int& ntris)
    {
        //TODO:
        // err handling:
        if (verts.empty() || tris.empty())
        {
            return false;
        }
 
        buffer.Vertices.set_used(nverts / 3);
        buffer.Indices.set_used(ntris * 3); // ntris triangles!
 
 
        //video::SColor color(255, 255, 255, 255);
        //video::SColor color(128, 255, 255, 255);
 
        //S3DVertex => pos, normal, color, texcoords
 
        for (int i = 0; i < nverts; i += 3)
        {
            float x = verts[i];
            float y = verts[i + 1];
            float z = verts[i + 2];
 
            buffer.Vertices[i / 3] = video::S3DVertex
                (
                x, y, z,
                0.0f, 1.0f, 0.0f,
                //0.0f, 0.0f, 0.0f,
                0x80FF0000,
                //0xFFFFFFFF,
                0.0f, 0.0f
                );
        }
 
 
        for (int i = 0; i < ntris * 3; ++i)
        {
            int index = tris[i];
            buffer.Indices[i] = index;
        }
 
 
        buffer.getMaterial().Lighting = false;
        buffer.getMaterial().BackfaceCulling = false;
        buffer.getMaterial().Wireframe = true;
        buffer.getMaterial().Thickness = 2.0f;
        //buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
        buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
        //buffer.getMaterial().MaterialType = video::EMT_SOLID;
        buffer.recalculateBoundingBox();
 
        return true;
    }
 
    //Detour stuff
    vector<irr::core::vector3df>  RecastUtilM::returnPath(core::vector3df pStart, core::vector3df pEnd)
    {
        std::vector<irr::core::vector3df> lstPoints;
        if (m_navQuery)
        {
            if (m_navMesh == 0)
            {
                return  lstPoints;
            }
        
            dtQueryFilter m_filter;
            dtPolyRef m_startRef;
            dtPolyRef m_endRef;
            
            const int MAX_POLYS = 256;
            dtPolyRef m_polys[MAX_POLYS];
            dtPolyRef returnedPath[MAX_POLYS];
            float m_straightPath[MAX_POLYS * 3];
            int numStraightPaths;
            float  m_spos[3] = { pStart.X, pStart.Y, pStart.Z };
            float  m_epos[3] = { pEnd.X, pEnd.Y, pEnd.Z };
            float m_polyPickExt[3];
            m_polyPickExt[0] = 2;
            m_polyPickExt[1] = 4;
            m_polyPickExt[2] = 2;
            
 
            m_navQuery->findNearestPoly(m_spos, m_polyPickExt, &m_filter, &m_startRef, 0);
 
            if (m_startRef == 0)
            {
                return lstPoints;
 
            }
            m_navQuery->findNearestPoly(m_epos, m_polyPickExt, &m_filter, &m_endRef, 0);
 
            if (m_endRef == 0)
            {
                return lstPoints;
 
            }
            dtStatus findStatus = DT_FAILURE;
            int pathCount;
            
            findStatus = m_navQuery->findPath(m_startRef, m_endRef, m_spos, m_epos, &m_filter, returnedPath, &pathCount, MAX_POLYS);
            
            
        
            if (pathCount > 0)
            {
                findStatus = m_navQuery->findStraightPath(m_spos, m_epos, returnedPath,
                    pathCount, m_straightPath, 0, 0, &numStraightPaths, MAX_POLYS);
                
                for (int i = 0; i < numStraightPaths; ++i)
                {
                    vector3df cpos(m_straightPath[i * 3], m_straightPath[i * 3 + 1] + 0.25,
                        m_straightPath[i * 3 + 2]);
                    
                    lstPoints.push_back(cpos);
                    //path->AddNode(node);
                }
 
                
            }
            
        }
        return lstPoints;
    }
 
  
returnPath takes start position and end position and return a vector or list of straight points.
RecastUtilM::returnPath(core::vector3df pStart, core::vector3df pEnd)

To use it:

Code: Select all

                         irr::scene::IAnimatedMesh *terrain_model = smgr->addHillPlaneMesh("groundPlane", // Name of the scenenode
                          tileSize, // Tile size
                          tileCount, // Tile count
                           0, // Material
                          20.0f, // Hill height
                           core::dimension2d<f32>(0.0f, 1.0f), // countHills
                           core::dimension2d<f32>(1.0f, 1.0f)); ;// textureRepeatCount
             
                      terrain_node = smgr->addAnimatedMeshSceneNode(terrain_model);
    
                   scene::IMeshBuffer *terbuffer = terrain_node->getMesh()->getMeshBuffer(0);
                
 
                                if (terbuffer)
                 {
                      recast = new RecastUtilM();
                      if (recast->handleBuild(terbuffer))
                      {
                         scene::SMesh* smesh = new scene::SMesh();
                         if (!recast->setupIrrSMeshFromRecastDetailMesh(smesh))
                         {
                             printf("recast->setupIrrSMeshFromRecastDetailMesh(smesh): FAILED!\n");
                         }
                         else
                         {
                            scene::ISceneNode *naviNode = smgr->addOctTreeSceneNode(smesh);
                            naviNode->setName("Terrain");
                            naviNode->setDebugDataVisible(scene::EDS_MESH_WIRE_OVERLAY); 
                         }
                         smesh->drop();
                      }
                  }
 
//On your event input positions
vector<irr::core::vector3df> lstPoints = recast->returnPath(vector3df_Start, vector3df_End);
I haven't test on many map accept for the addHillPlaneMesh.

Note: The setting I used below is for a large mesh. Usually the setting for m_cellSize is 0.3f instead of 4.0f.

void RecastUtilM::resetCommonSettings()
{
m_cellSize = 4.0f; // 0.3f;
m_cellHeight = 0.2f;
m_agentHeight = 2.0f;
m_agentRadius = 0.6f;
m_agentMaxClimb = 0.9f;
m_agentMaxSlope = 45.0f;
m_regionMinSize = 8;
m_regionMergeSize = 20;
m_monotonePartitioning = false;
m_edgeMaxLen = 12.0f;
m_edgeMaxError = 1.3f;
m_vertsPerPoly = 6.0f;
m_detailSampleDist = 6.0f;
m_detailSampleMaxError = 1.0f;
}

Images
Image

Routing 5 ninjas.
Image

Path planning 600 animated ninjas.
Image

On both images link red triangulation is the navmesh, blue line is the path generated.

**Updated to make code display. It turns out that this forum limited the length for the code tag. So I have cut the C++ soure file into two junks as above.
Goodluck!
Last edited by thanhle on Thu Jan 16, 2014 4:13 pm, edited 4 times in total.
The_Glitch
Competition winner
Posts: 523
Joined: Tue Jan 15, 2013 6:36 pm

Re: Recast Detour revisit

Post by The_Glitch »

What exactly is recast? Is it a path finding library? I've seen it before.
thanhle
Posts: 325
Joined: Wed Jun 12, 2013 8:09 am

Re: Recast Detour revisit

Post by thanhle »

Recast use a number of algorithms to generate navigation mesh for a given irrlicht mesh.

Detour uses A* algorithm for path planning so you can move your character around.

The code above allows you to achieve that given input position and the desired position.

Regards
thanh
The_Glitch
Competition winner
Posts: 523
Joined: Tue Jan 15, 2013 6:36 pm

Re: Recast Detour revisit

Post by The_Glitch »

Oh sounds really useful I might check into this.
pop37corn
Posts: 10
Joined: Sat Apr 12, 2014 4:54 pm

Re: Recast Detour revisit

Post by pop37corn »

Hi,

I know it's an old thread, but I built the libraries but I keep on getting this error:

Code: Select all

 
1>IrrFrameWork.obj : error LNK2001: unresolved external symbol "public: __thiscall BuildContext::BuildContext(void)" (??0BuildContext@@QAE@XZ)
Any ideas?

Thanks,

popcorn
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Recast Detour revisit

Post by mongoose7 »

I'm guessing you compiled the shim above but didn't include the Recast Detour library.
pop37corn
Posts: 10
Joined: Sat Apr 12, 2014 4:54 pm

Re: Recast Detour revisit

Post by pop37corn »

mongoose7 wrote:I'm guessing you compiled the shim above but didn't include the Recast Detour library.
I did in fact compile and include the library. I have no idea whats wrong. Been working at this for days. :cry:
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Recast Detour revisit

Post by mongoose7 »

OK, then. grep the code for "BuildContext' to see which file it is in.
thanhle
Posts: 325
Joined: Wed Jun 12, 2013 8:09 am

Re: Recast Detour revisit

Post by thanhle »

Hi,
Build context is in the SampleInterfaces files, which included with the recast detour library. It's their sample files.

You would need to include the header file SampleInterfaces.h and cpp file.

Remove or strip down the functions you don't need inside the SampleInterfaces.cpp.

See my include files. Make sure you include some of those header files.

Regards,
Thanh
kklouzal
Posts: 343
Joined: Sun Mar 28, 2010 8:14 pm
Location: USA - Arizona

Re: Recast Detour revisit

Post by kklouzal »

Does this use the TileCache feature?
Dream Big Or Go Home.
Help Me Help You.
thanhle
Posts: 325
Joined: Wed Jun 12, 2013 8:09 am

Re: Recast Detour revisit

Post by thanhle »

Sorry it doesn't do that at the moment as I haven't look at the tilecache tutorial.

It needs a meshbuffer to construct the navmesh. If you join meshbuffer it will still work.

So if you cleverly extract the region of interest into a mesh buffer (maybe spawning a background thread to do that), then recompute to obtain the new navmesh. Once the navmesh have been generated, maybe have some sort of event saying that the new navmesh can be used so your character can use the updated navmesh for moving.

You could also store multiple navmeshes to files and somehow indexing them.

That is just my idea. I don't have the need to do that yet.

Regards
Thanh
kklouzal
Posts: 343
Joined: Sun Mar 28, 2010 8:14 pm
Location: USA - Arizona

Re: Recast Detour revisit

Post by kklouzal »

Thank you for the amazing job so far Thanhle, hopefully an update to incorporate the TileCache will come soon since at that point the nav mesh can be dynamically changed at runtime without recomputing the entire thing.

Keep up the good work! :)
Dream Big Or Go Home.
Help Me Help You.
Post Reply