dtPolyRef PathGenerator::GetPolyByLocation(float const* point, float* distance) const { // first we check the current path // if the current path doesn't contain the current poly, // we need to use the expensive navMesh.findNearestPoly dtPolyRef polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, distance); if (polyRef != INVALID_POLYREF) return polyRef; // we don't have it in our old path // try to get it by findNearestPoly() // first try with low search box float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f}; if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF) { *distance = dtVdist(closestPoint, point); return polyRef; } // still nothing .. // try with bigger search box // Note that the extent should not overlap more than 128 polygons in the navmesh (see dtNavMeshQuery::findNearestPoly) extents[1] = 50.0f; if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF) { *distance = dtVdist(closestPoint, point); return polyRef; } return INVALID_POLYREF; }
dtPolyRef PathInfo::getPolyByLocation(const float* point, float *distance) { // first we check the current path // if the current path doesn't contain the current poly, // we need to use the expensive navMesh.findNearestPoly dtPolyRef polyRef = getPathPolyByPosition(m_pathPolyRefs, m_polyLength, point, distance); if(polyRef != INVALID_POLYREF) return polyRef; // we don't have it in our old path // try to get it by findNearestPoly() // first try with low search box float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area dtQueryFilter filter = createFilter(); float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f}; dtStatus result = m_navMeshQuery->findNearestPoly(point, extents, &filter, &polyRef, closestPoint); if(DT_SUCCESS == result && polyRef != INVALID_POLYREF) { *distance = dtVdist(closestPoint, point); return polyRef; } // still nothing .. // try with bigger search box extents[1] = 200.0f; result = m_navMeshQuery->findNearestPoly(point, extents, &filter, &polyRef, closestPoint); if(DT_SUCCESS == result && polyRef != INVALID_POLYREF) { *distance = dtVdist(closestPoint, point); return polyRef; } return INVALID_POLYREF; }
float MQ2NavigationPlugin::GetNavigationPathLength(const glm::vec3& pos) { float result = -1.f; NavigationPath path(Get<NavMeshLoader>()->GetNavMesh()); path.FindPath(pos); //WriteChatf("MQ2Nav::GetPathLength - num points: %d", numPoints); int numPoints = path.GetPathSize(); if (numPoints > 0) { result = 0; for (int i = 0; i < numPoints - 1; ++i) { const float* first = path.GetRawPosition(i); const float* second = path.GetRawPosition(i + 1); float segment = dtVdist(first, second); result += segment; //WriteChatf("MQ2Nav::GetPathLength - segment #%d length: %f - total: %f", i, segment, result); } } return result; }
dtOffMeshConnection* dtCrowdQuery::getOffMeshConnection(unsigned id, float dist) const { // Check validity of the ID if (id >= m_maxAgents || dist < 0.f) return 0; const dtCrowdAgent ag = *getAgent(id); dtPolyRef agentPolyRef; float nearest[3]; // Get the polygon reference the agent is on m_navMeshQuery->findNearestPoly(ag.position, m_ext, &m_filter, &agentPolyRef, nearest); if (!agentPolyRef) return 0; dtPoly poly; dtMeshTile tile; const dtPoly* ptrPoly = &poly; const dtMeshTile* ptrTile = &tile; // Get the tile the agent is on if (dtStatusFailed(m_navMeshQuery->getAttachedNavMesh()->getTileAndPolyByRef(agentPolyRef, (const dtMeshTile**)(&ptrTile), (const dtPoly**)(&ptrPoly)))) return 0; if (ptrTile->header->offMeshConCount <= 0) return 0; unsigned nbOffMeshConnections = ptrTile->header->offMeshConCount; dtOffMeshConnection* offMeshConnections = ptrTile->offMeshCons; // For every offMesh connections located in this tile for (unsigned i = 0; i < nbOffMeshConnections; ++i) { dtPolyRef polysIDs[256]; int nbPolys = 0; dtOffMeshConnection& offMeshCo = offMeshConnections[i]; // Find every polygons reached by the offMesh connection and its radius if (dtStatusFailed(m_navMeshQuery->findPolysAroundCircle(agentPolyRef, offMeshCo.pos, offMeshCo.rad, &m_filter, polysIDs, 0, 0, &nbPolys, 256))) continue; if (nbPolys <= 0) continue; // For every polygons touched by the offMesh connection, we check if one of them is the one the agent is on. // If so, we check the distance from the agent to the center of the offMesh connection (taking into account the extra distance) for (int j = 0; j < nbPolys; ++j) if (polysIDs[j] == agentPolyRef) if (dtVdist(ag.position, offMeshCo.pos) < offMeshCo.rad + dist) return &offMeshCo; } return 0; }
void dtSeekBehavior::applyForce(const dtCrowdAgent* oldAgent, dtCrowdAgent* newAgent, float* force, float dt) { float tmpForce[] = {0, 0, 0}; float newVelocity[] = {0, 0, 0}; const dtCrowdAgent* target = m_agentParams[oldAgent->id]->targetID; const float distance = m_agentParams[oldAgent->id]->seekDistance; // Adapting the force to the dt and the previous velocity dtVscale(tmpForce, force, dt); dtVadd(newVelocity, oldAgent->vel, tmpForce); float currentSpeed = dtVlen(oldAgent->vel); // Required distance to reach nil speed according to the acceleration and current speed float slowDist = currentSpeed * (currentSpeed - 0) / oldAgent->params.maxAcceleration; float distToObj = dtVdist(oldAgent->npos, target->npos) - oldAgent->params.radius - target->params.radius - distance; // If we have reached the target, we stop if (distToObj <= EPSILON) { dtVset(newVelocity, 0, 0, 0); newAgent->desiredSpeed = 0.f; } // If the have to slow down else if (distToObj < slowDist) { float slowDownRadius = distToObj / slowDist; dtVscale(newVelocity, newVelocity, slowDownRadius); newAgent->desiredSpeed = dtVlen(newVelocity); } // Else, we move as fast as possible else newAgent->desiredSpeed = oldAgent->params.maxSpeed; // Check for maximal speed dtVclamp(newVelocity, dtVlen(newVelocity), oldAgent->params.maxSpeed); dtVcopy(newAgent->dvel, newVelocity); }
void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const { const dtMeshTile* tile = 0; const dtPoly* poly = 0; getTileAndPolyByRefUnsafe(ref, &tile, &poly); // Off-mesh connections don't have detail polygons. if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) { const float* v0 = &tile->verts[poly->verts[0]*3]; const float* v1 = &tile->verts[poly->verts[1]*3]; const float d0 = dtVdist(pos, v0); const float d1 = dtVdist(pos, v1); const float u = d0 / (d0+d1); dtVlerp(closest, v0, v1, u); if (posOverPoly) *posOverPoly = false; return; } const unsigned int ip = (unsigned int)(poly - tile->polys); const dtPolyDetail* pd = &tile->detailMeshes[ip]; // Clamp point to be inside the polygon. float verts[DT_VERTS_PER_POLYGON*3]; float edged[DT_VERTS_PER_POLYGON]; float edget[DT_VERTS_PER_POLYGON]; const int nv = poly->vertCount; for (int i = 0; i < nv; ++i) dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]); dtVcopy(closest, pos); if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget)) { // Point is outside the polygon, dtClamp to nearest edge. float dmin = FLT_MAX; int imin = -1; for (int i = 0; i < nv; ++i) { if (edged[i] < dmin) { dmin = edged[i]; imin = i; } } const float* va = &verts[imin*3]; const float* vb = &verts[((imin+1)%nv)*3]; dtVlerp(closest, va, vb, edget[imin]); if (posOverPoly) *posOverPoly = false; } else { if (posOverPoly) *posOverPoly = true; } // Find height at the location. for (int j = 0; j < pd->triCount; ++j) { const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4]; const float* v[3]; for (int k = 0; k < 3; ++k) { if (t[k] < poly->vertCount) v[k] = &tile->verts[poly->verts[t[k]]*3]; else v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3]; } float h; if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h)) { closest[1] = h; break; } } }
bool TestCase::handleRenderOverlay(double* proj, double* model, int* view) { GLdouble x, y, z; char text[64], subtext[64]; int n = 0; static const float LABEL_DIST = 1.0f; for (Test* iter = m_tests; iter; iter = iter->next) { float pt[3], dir[3]; if (iter->nstraight) { dtVcopy(pt, &iter->straight[3]); if (dtVdist(pt, iter->spos) > LABEL_DIST) { dtVsub(dir, pt, iter->spos); dtVnormalize(dir); dtVmad(pt, iter->spos, dir, LABEL_DIST); } pt[1]+=0.5f; } else { dtVsub(dir, iter->epos, iter->spos); dtVnormalize(dir); dtVmad(pt, iter->spos, dir, LABEL_DIST); pt[1]+=0.5f; } if (gluProject((GLdouble)pt[0], (GLdouble)pt[1], (GLdouble)pt[2], model, proj, view, &x, &y, &z)) { snprintf(text, 64, "Path %d\n", n); unsigned int col = imguiRGBA(0,0,0,128); if (iter->expand) col = imguiRGBA(255,192,0,220); imguiDrawText((int)x, (int)(y-25), IMGUI_ALIGN_CENTER, text, col); } n++; } static int resScroll = 0; bool mouseOverMenu = imguiBeginScrollArea("Test Results", 10, view[3] - 10 - 350, 200, 350, &resScroll); // mouseOverMenu = true; n = 0; for (Test* iter = m_tests; iter; iter = iter->next) { const int total = iter->findNearestPolyTime + iter->findPathTime + iter->findStraightPathTime; snprintf(subtext, 64, "%.4f ms", (float)total/1000.0f); snprintf(text, 64, "Path %d", n); if (imguiCollapse(text, subtext, iter->expand)) iter->expand = !iter->expand; if (iter->expand) { snprintf(text, 64, "Poly: %.4f ms", (float)iter->findNearestPolyTime/1000.0f); imguiValue(text); snprintf(text, 64, "Path: %.4f ms", (float)iter->findPathTime/1000.0f); imguiValue(text); snprintf(text, 64, "Straight: %.4f ms", (float)iter->findStraightPathTime/1000.0f); imguiValue(text); imguiSeparator(); } n++; } imguiEndScrollArea(); return mouseOverMenu; }
/** @par This is the function used to plan local movement within the corridor. One or more corners can be detected in order to plan movement. It performs essentially the same function as #dtNavMeshQuery::findStraightPath. Due to internal optimizations, the maximum number of corners returned will be (@p maxCorners - 1) For example: If the buffers are sized to hold 10 corners, the function will never return more than 9 corners. So if 10 corners are needed, the buffers should be sized for 11 corners. If the target is within range, it will be the last corner and have a polygon reference id of zero. */ int dtPathCorridor::findCorners(float* cornerVerts, unsigned char* cornerFlags, dtPolyRef* cornerPolys, const int maxCorners, dtNavMeshQuery* navquery, const dtQueryFilter* filter, float radius) { dtAssert(m_path); dtAssert(m_npath); static const float MIN_TARGET_DIST = 0.01f; dtQueryResult result; navquery->findStraightPath(m_pos, m_target, m_path, m_npath, result); int ncorners = dtMin(result.size(), maxCorners); result.copyRefs(cornerPolys, maxCorners); result.copyFlags(cornerFlags, maxCorners); result.copyPos(cornerVerts, maxCorners); // Prune points in the beginning of the path which are too close. while (ncorners) { if ((cornerFlags[0] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) || dtVdist2DSqr(&cornerVerts[0], m_pos) > dtSqr(MIN_TARGET_DIST)) break; ncorners--; if (ncorners) { memmove(cornerFlags, cornerFlags+1, sizeof(unsigned char)*ncorners); memmove(cornerPolys, cornerPolys+1, sizeof(dtPolyRef)*ncorners); memmove(cornerVerts, cornerVerts+3, sizeof(float)*3*ncorners); } } // Prune points after an off-mesh connection. for (int i = 0; i < ncorners; ++i) { if (cornerFlags[i] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) { ncorners = i+1; break; } } // [UE4] Offset path points from corners const dtNavMesh* nav = navquery->getAttachedNavMesh(); float v1[3], v2[3], dir[3]; for (int i = 0; i < ncorners - 1; i++) { int fromIdx = 0; while (m_path[fromIdx] != cornerPolys[i] && fromIdx < m_npath) { fromIdx++; } if (m_path[fromIdx] != cornerPolys[i] || fromIdx == 0) continue; const dtMeshTile* tile0 = 0; const dtMeshTile* tile1 = 0; const dtPoly* poly0 = 0; const dtPoly* poly1 = 0; nav->getTileAndPolyByRefUnsafe(m_path[fromIdx - 1], &tile0, &poly0); nav->getTileAndPolyByRefUnsafe(m_path[fromIdx], &tile1, &poly1); if (tile0 != tile1) continue; float* corner = &cornerVerts[i * 3]; unsigned char dummyT1, dummyT2; navquery->getPortalPoints(m_path[fromIdx - 1], m_path[fromIdx], v1, v2, dummyT1, dummyT2); const float edgeLen = dtVdist(v1, v2); if (edgeLen > 0.001f) { const float edgeOffset = dtMin(radius, edgeLen * 0.75f) / edgeLen; if (dtVequal(corner, v1)) { dtVsub(dir, v2, v1); dtVmad(corner, corner, dir, edgeOffset); } else { dtVsub(dir, v1, v2); dtVmad(corner, corner, dir, edgeOffset); } } } return ncorners; }
void PathInfo::BuildPolyPath(PathNode startPos, PathNode endPos) { // *** getting start/end poly logic *** float distToStartPoly, distToEndPoly; // first we check the current path // if the current path doesn't contain the current poly, // we need to use the expensive navMesh.findNearestPoly dtPolyRef startPoly = getPathPolyByPosition(m_pathPolyRefs, m_polyLength, startPos, &distToStartPoly); dtPolyRef endPoly = getPathPolyByPosition(m_pathPolyRefs, m_polyLength, endPos, &distToEndPoly); float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x}; float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x}; // we don't have it in our old path // try to get it by findNearestPoly() // use huge vertical range here if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF) { float extents[VERTEX_SIZE] = {3.f, 200.f, 3.f}; // bounds of poly search area dtQueryFilter filter = createFilter(); float closestPoint[VERTEX_SIZE]; if (startPoly == INVALID_POLYREF) { if (DT_SUCCESS == m_navMeshQuery->findNearestPoly(startPoint, extents, &filter, &startPoly, closestPoint)) distToStartPoly = dtVdist(closestPoint, startPoint); } if (endPoly == INVALID_POLYREF) { if (DT_SUCCESS == m_navMeshQuery->findNearestPoly(endPoint, extents, &filter, &endPoly, closestPoint)) distToEndPoly = dtVdist(closestPoint, endPoint); } } // we have a hole in our mesh // make shortcut path and mark it as NOPATH ( with flying exception ) // its up to caller how he will use this info if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF) { PATH_DEBUG("++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n"); BuildShortcut(); m_type = (m_sourceUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_sourceUnit)->IsLevitating()) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; return; } // we may need a better number here bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); if (farFromPoly) { PATH_DEBUG("++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly); bool buildShotrcut = false; if (m_sourceUnit->GetTypeId() == TYPEID_UNIT) { Creature* owner = (Creature*)m_sourceUnit; if (m_sourceUnit->GetBaseMap()->IsUnderWater(endPos.x, endPos.y, endPos.z)) { PATH_DEBUG("++ BuildPolyPath :: underWater case\n"); if (owner->canSwim()) buildShotrcut = true; } else { PATH_DEBUG("++ BuildPolyPath :: flying case\n"); if (owner->IsLevitating()) buildShotrcut = true; } } if (buildShotrcut) { BuildShortcut(); m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); return; } else { float closestPoint[VERTEX_SIZE]; // we may want to use closestPointOnPolyBoundary instead if (DT_SUCCESS == m_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint)) { dtVcopy(endPoint, closestPoint); setActualEndPosition(PathNode(endPoint[2],endPoint[0],endPoint[1])); } m_type = PATHFIND_INCOMPLETE; } } // *** poly path generating logic *** // start and end are on same polygon // just need to move in straight line if (startPoly == endPoly) { PATH_DEBUG("++ BuildPolyPath :: (startPoly == endPoly)\n"); BuildShortcut(); m_pathPolyRefs = new dtPolyRef[1]; m_pathPolyRefs[0] = startPoly; m_polyLength = 1; m_type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL; PATH_DEBUG("++ BuildPolyPath :: path type %d\n", m_type); return; } // look for startPoly/endPoly in current path // TODO: we can merge it with getPathPolyByPosition() loop bool startPolyFound = false; bool endPolyFound = false; uint32 pathStartIndex, pathEndIndex; if (m_polyLength) { for (pathStartIndex = 0; pathStartIndex < m_polyLength; ++pathStartIndex) { if (m_pathPolyRefs[pathStartIndex] == startPoly) { startPolyFound = true; break; } for (pathEndIndex = m_polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex) { if (m_pathPolyRefs[pathEndIndex] == endPoly) { endPolyFound = true; break; } } } } if (startPolyFound && endPolyFound) { PATH_DEBUG("++ BuildPolyPath :: (startPolyFound && endPolyFound)\n"); // we moved along the path and the target did not move out of our old poly-path // our path is a simple subpath case, we have all the data we need // just "cut" it out m_polyLength = pathEndIndex - pathStartIndex + 1; dtPolyRef* newPolyRefs = new dtPolyRef[m_polyLength]; memcpy(newPolyRefs, m_pathPolyRefs+pathStartIndex, m_polyLength*sizeof(dtPolyRef)); delete [] m_pathPolyRefs; m_pathPolyRefs = newPolyRefs; } else if (startPolyFound && !endPolyFound) { PATH_DEBUG("++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n"); // we are moving on the old path but target moved out // so we have atleast part of poly-path ready m_polyLength -= pathStartIndex; // try to adjust the suffix of the path instead of recalculating entire length // at given interval the target cannot get too far from its last location // thus we have less poly to cover // sub-path of optimal path is optimal // take ~80% of the original length // TODO : play with the values here uint32 prefixPolyLength = uint32(m_polyLength*0.8f + 0.5f); dtPolyRef prefixPathPolys[MAX_PATH_LENGTH]; memcpy(prefixPathPolys, m_pathPolyRefs+pathStartIndex, prefixPolyLength*sizeof(dtPolyRef)); dtPolyRef suffixStartPoly = prefixPathPolys[prefixPolyLength-1]; // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data float suffixEndPoint[VERTEX_SIZE]; if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)) { // suffixStartPoly is invalid somehow, or the navmesh is broken => error state sLog->outError("%u's Path Build failed: invalid polyRef in path", m_sourceUnit->GetGUID()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } // generate suffix dtQueryFilter filter = createFilter(); dtPolyRef suffixPathPolys[MAX_PATH_LENGTH]; uint32 suffixPolyLength = 0; dtStatus dtResult = m_navMeshQuery->findPath( suffixStartPoly, // start polygon endPoly, // end polygon suffixEndPoint, // start position endPoint, // end position &filter, // polygon search filter suffixPathPolys, // [out] path (int*)&suffixPolyLength, MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path if (!suffixPolyLength || dtResult != DT_SUCCESS) { // this is probably an error state, but we'll leave it // and hopefully recover on the next Update // we still need to copy our preffix sLog->outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUID()); } PATH_DEBUG("++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n",m_polyLength, prefixPolyLength, suffixPolyLength); // new path = prefix + suffix - overlap m_polyLength = prefixPolyLength + suffixPolyLength - 1; delete [] m_pathPolyRefs; m_pathPolyRefs = new dtPolyRef[m_polyLength]; // copy the part of the old path we keep - prefix memcpy(m_pathPolyRefs, prefixPathPolys, prefixPolyLength*sizeof(dtPolyRef)); // copy the newly created suffix - skip first poly, we have it at prefix end if (suffixPathPolys) memcpy(m_pathPolyRefs+prefixPolyLength, suffixPathPolys+1, (suffixPolyLength-1)*sizeof(dtPolyRef)); } else { PATH_DEBUG("++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n"); // either we have no path at all -> first run // or something went really wrong -> we aren't moving along the path to the target // just generate new path // free and invalidate old path data clear(); dtQueryFilter filter = createFilter(); // use special filter so we use proper terrain types dtPolyRef pathPolys[MAX_PATH_LENGTH]; m_polyLength = 0; dtStatus dtResult = m_navMeshQuery->findPath( startPoly, // start polygon endPoly, // end polygon startPoint, // start position endPoint, // end position &filter, // polygon search filter pathPolys, // [out] path (int*)&m_polyLength, MAX_PATH_LENGTH); // max number of polygons in output path if (!m_polyLength || dtResult != DT_SUCCESS) { // only happens if we passed bad data to findPath(), or navmesh is messed up sLog->outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUID()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } m_pathPolyRefs = new dtPolyRef[m_polyLength]; memcpy(m_pathPolyRefs, pathPolys, m_polyLength*sizeof(dtPolyRef)); } // by now we know what type of path we can get if (m_pathPolyRefs[m_polyLength - 1] == endPoly && !(m_type & PATHFIND_INCOMPLETE)) m_type = PATHFIND_NORMAL; else m_type = PATHFIND_INCOMPLETE; // generate the point-path out of our up-to-date poly-path BuildPointPath(startPoint, endPoint); }