/** @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; }
crowd->update(0.5, 0); // The agent should not have moved dtVcopy(agt1NewPos, crowd->getAgent(ag.id)->position); CHECK(dtVequal(agt1NewPos, posAgt1)); // This time we affect the behavior with the right size CHECK(pipeline->setBehaviors(&behavior, 1)); crowd->update(0.5, 0); dtVcopy(agt1NewPos, crowd->getAgent(ag.id)->position); // A behavior has been affected to the pipeline, the agent should have moved CHECK(!dtVequal(agt1NewPos, posAgt1)); } SECTION("Using a container", "Using a container to store behaviors, set them to the pipeline and then destroy the container. The behaviors should still be in the pipeline") { dtCollisionAvoidance* ca = dtCollisionAvoidance::allocate(crowd->getAgentCount()); ca->init(); dtCollisionAvoidanceParams* params = ca->getBehaviorParams(ag.id); if (params) { params->debug = 0; params->velBias = 0.4f; params->weightDesVel = 2.0f;
void PathInfo::updateNextPosition() { float x, y, z; getStartPosition(x, y, z); float startPos[3] = {y, z, x}; getEndPosition(x, y, z); float endPos[3] = {y, z, x}; float* pathPoints = new float[MAX_PATH_LENGTH*3]; int pointCount = m_navMesh->findStraightPath( startPos, // start position endPos, // end position m_pathPolyRefs, // current path m_length, // lenth of current path pathPoints, // [out] path corner points 0, // [out] flags 0, // [out] shortened path PATHFIND TODO: see if this is usable (IE, doesn't leave gaps in path) MAX_PATH_LENGTH); // maximum number of points/polygons to use // TODO: imitate PATHFIND_ITER code from RecastDemo so that terrain following is smoother if(pointCount == 0) { delete [] pathPoints; // only happens if pass bad data to findStraightPath or navmesh is broken sLog.outDebug("%u's UpdateNextPosition failed: 0 length path", m_sourceObject->GetGUID()); shortcut(); return; } int ix, iy, iz; if(pointCount == 1) { ix = 2; iy = 0; iz = 1; } else { ix = 5; iy = 3; iz = 4; } setNextPosition(pathPoints[ix], pathPoints[iy], pathPoints[iz]); // if startPosition == nextPosition, NPC will never advance to destination if(isSamePoint(m_startPosition, m_nextPosition) && !isSamePoint(m_nextPosition, m_endPosition)) setNextPosition(pathPoints[ix + 3], pathPoints[iy + 3], pathPoints[iz + 3]); delete [] m_pathPoints; m_pathPoints = pathPoints; m_type = PathType(m_type | PATHFIND_NORMAL); // if end position from findStraightPath isn't the // place we want to go, there is likely no path endPos[0] = pathPoints[(pointCount-1)*3+2]; endPos[1] = pathPoints[(pointCount-1)*3]; endPos[2] = pathPoints[(pointCount-1)*3+1]; if(!dtVequal(endPos, m_endPosition)) m_type = PathType(m_type | PATHFIND_NOPATH); }