Path: blob/master/thirdparty/rvo2/rvo2_3d/Agent3d.cpp
9903 views
/*1* Agent.cpp2* RVO2-3D Library3*4* Copyright 2008 University of North Carolina at Chapel Hill5*6* Licensed under the Apache License, Version 2.0 (the "License");7* you may not use this file except in compliance with the License.8* You may obtain a copy of the License at9*10* https://www.apache.org/licenses/LICENSE-2.011*12* Unless required by applicable law or agreed to in writing, software13* distributed under the License is distributed on an "AS IS" BASIS,14* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.15* See the License for the specific language governing permissions and16* limitations under the License.17*18* Please send all bug reports to <[email protected]>.19*20* The authors may be contacted via:21*22* Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha23* Dept. of Computer Science24* 201 S. Columbia St.25* Frederick P. Brooks, Jr. Computer Science Bldg.26* Chapel Hill, N.C. 27599-317527* United States of America28*29* <https://gamma.cs.unc.edu/RVO2/>30*/3132#include "Agent3d.h"3334#include <cmath>35#include <algorithm>3637#include "Definitions.h"38#include "KdTree3d.h"3940namespace RVO3D {41/**42* \brief A sufficiently small positive number.43*/44const float RVO3D_EPSILON = 0.00001f;4546/**47* \brief Defines a directed line.48*/49class Line3D {50public:51/**52* \brief The direction of the directed line.53*/54Vector3 direction;5556/**57* \brief A point on the directed line.58*/59Vector3 point;60};6162/**63* \brief Solves a one-dimensional linear program on a specified line subject to linear constraints defined by planes and a spherical constraint.64* \param planes Planes defining the linear constraints.65* \param planeNo The plane on which the line lies.66* \param line The line on which the 1-d linear program is solved67* \param radius The radius of the spherical constraint.68* \param optVelocity The optimization velocity.69* \param directionOpt True if the direction should be optimized.70* \param result A reference to the result of the linear program.71* \return True if successful.72*/73bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line3D &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result);7475/**76* \brief Solves a two-dimensional linear program on a specified plane subject to linear constraints defined by planes and a spherical constraint.77* \param planes Planes defining the linear constraints.78* \param planeNo The plane on which the 2-d linear program is solved79* \param radius The radius of the spherical constraint.80* \param optVelocity The optimization velocity.81* \param directionOpt True if the direction should be optimized.82* \param result A reference to the result of the linear program.83* \return True if successful.84*/85bool linearProgram2(const std::vector<Plane> &planes, size_t planeNo, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result);8687/**88* \brief Solves a three-dimensional linear program subject to linear constraints defined by planes and a spherical constraint.89* \param planes Planes defining the linear constraints.90* \param radius The radius of the spherical constraint.91* \param optVelocity The optimization velocity.92* \param directionOpt True if the direction should be optimized.93* \param result A reference to the result of the linear program.94* \return The number of the plane it fails on, and the number of planes if successful.95*/96size_t linearProgram3(const std::vector<Plane> &planes, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result);9798/**99* \brief Solves a four-dimensional linear program subject to linear constraints defined by planes and a spherical constraint.100* \param planes Planes defining the linear constraints.101* \param beginPlane The plane on which the 3-d linear program failed.102* \param radius The radius of the spherical constraint.103* \param result A reference to the result of the linear program.104*/105void linearProgram4(const std::vector<Plane> &planes, size_t beginPlane, float radius, Vector3 &result);106107Agent3D::Agent3D() : id_(0), maxNeighbors_(0), maxSpeed_(0.0f), neighborDist_(0.0f), radius_(0.0f), timeHorizon_(0.0f) { }108109void Agent3D::computeNeighbors(RVOSimulator3D *sim_)110{111agentNeighbors_.clear();112113if (maxNeighbors_ > 0) {114sim_->kdTree_->computeAgentNeighbors(this, neighborDist_ * neighborDist_);115}116}117118void Agent3D::computeNewVelocity(RVOSimulator3D *sim_)119{120orcaPlanes_.clear();121122const float invTimeHorizon = 1.0f / timeHorizon_;123124/* Create agent ORCA planes. */125for (size_t i = 0; i < agentNeighbors_.size(); ++i) {126const Agent3D *const other = agentNeighbors_[i].second;127128//const float timeHorizon_mod = (avoidance_priority_ - other->avoidance_priority_ + 1.0f) * 0.5f;129//const float invTimeHorizon = (1.0f / timeHorizon_) * timeHorizon_mod;130131const Vector3 relativePosition = other->position_ - position_;132const Vector3 relativeVelocity = velocity_ - other->velocity_;133const float distSq = absSq(relativePosition);134const float combinedRadius = radius_ + other->radius_;135const float combinedRadiusSq = sqr(combinedRadius);136137Plane plane;138Vector3 u;139140if (distSq > combinedRadiusSq) {141/* No collision. */142const Vector3 w = relativeVelocity - invTimeHorizon * relativePosition;143/* Vector from cutoff center to relative velocity. */144const float wLengthSq = absSq(w);145146const float dotProduct = w * relativePosition;147148if (dotProduct < 0.0f && sqr(dotProduct) > combinedRadiusSq * wLengthSq) {149/* Project on cut-off circle. */150const float wLength = std::sqrt(wLengthSq);151const Vector3 unitW = w / wLength;152153plane.normal = unitW;154u = (combinedRadius * invTimeHorizon - wLength) * unitW;155}156else {157/* Project on cone. */158const float a = distSq;159const float b = relativePosition * relativeVelocity;160const float c = absSq(relativeVelocity) - absSq(cross(relativePosition, relativeVelocity)) / (distSq - combinedRadiusSq);161const float t = (b + std::sqrt(sqr(b) - a * c)) / a;162const Vector3 w = relativeVelocity - t * relativePosition;163const float wLength = abs(w);164const Vector3 unitW = w / wLength;165166plane.normal = unitW;167u = (combinedRadius * t - wLength) * unitW;168}169}170else {171/* Collision. */172const float invTimeStep = 1.0f / sim_->timeStep_;173const Vector3 w = relativeVelocity - invTimeStep * relativePosition;174const float wLength = abs(w);175const Vector3 unitW = w / wLength;176177plane.normal = unitW;178u = (combinedRadius * invTimeStep - wLength) * unitW;179}180181plane.point = velocity_ + 0.5f * u;182orcaPlanes_.push_back(plane);183}184185const size_t planeFail = linearProgram3(orcaPlanes_, maxSpeed_, prefVelocity_, false, newVelocity_);186187if (planeFail < orcaPlanes_.size()) {188linearProgram4(orcaPlanes_, planeFail, maxSpeed_, newVelocity_);189}190}191192void Agent3D::insertAgentNeighbor(const Agent3D *agent, float &rangeSq)193{194// no point processing same agent195if (this == agent) {196return;197}198// ignore other agent if layers/mask bitmasks have no matching bit199if ((avoidance_mask_ & agent->avoidance_layers_) == 0) {200return;201}202203if (avoidance_priority_ > agent->avoidance_priority_) {204return;205}206207const float distSq = absSq(position_ - agent->position_);208209if (distSq < rangeSq) {210if (agentNeighbors_.size() < maxNeighbors_) {211agentNeighbors_.push_back(std::make_pair(distSq, agent));212}213214size_t i = agentNeighbors_.size() - 1;215216while (i != 0 && distSq < agentNeighbors_[i - 1].first) {217agentNeighbors_[i] = agentNeighbors_[i - 1];218--i;219}220221agentNeighbors_[i] = std::make_pair(distSq, agent);222223if (agentNeighbors_.size() == maxNeighbors_) {224rangeSq = agentNeighbors_.back().first;225}226}227}228229void Agent3D::update(RVOSimulator3D *sim_)230{231velocity_ = newVelocity_;232position_ += velocity_ * sim_->timeStep_;233}234235bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line3D &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result)236{237const float dotProduct = line.point * line.direction;238const float discriminant = sqr(dotProduct) + sqr(radius) - absSq(line.point);239240if (discriminant < 0.0f) {241/* Max speed sphere fully invalidates line. */242return false;243}244245const float sqrtDiscriminant = std::sqrt(discriminant);246float tLeft = -dotProduct - sqrtDiscriminant;247float tRight = -dotProduct + sqrtDiscriminant;248249for (size_t i = 0; i < planeNo; ++i) {250const float numerator = (planes[i].point - line.point) * planes[i].normal;251const float denominator = line.direction * planes[i].normal;252253if (sqr(denominator) <= RVO3D_EPSILON) {254/* Lines3D line is (almost) parallel to plane i. */255if (numerator > 0.0f) {256return false;257}258else {259continue;260}261}262263const float t = numerator / denominator;264265if (denominator >= 0.0f) {266/* Plane i bounds line on the left. */267tLeft = std::max(tLeft, t);268}269else {270/* Plane i bounds line on the right. */271tRight = std::min(tRight, t);272}273274if (tLeft > tRight) {275return false;276}277}278279if (directionOpt) {280/* Optimize direction. */281if (optVelocity * line.direction > 0.0f) {282/* Take right extreme. */283result = line.point + tRight * line.direction;284}285else {286/* Take left extreme. */287result = line.point + tLeft * line.direction;288}289}290else {291/* Optimize closest point. */292const float t = line.direction * (optVelocity - line.point);293294if (t < tLeft) {295result = line.point + tLeft * line.direction;296}297else if (t > tRight) {298result = line.point + tRight * line.direction;299}300else {301result = line.point + t * line.direction;302}303}304305return true;306}307308bool linearProgram2(const std::vector<Plane> &planes, size_t planeNo, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result)309{310const float planeDist = planes[planeNo].point * planes[planeNo].normal;311const float planeDistSq = sqr(planeDist);312const float radiusSq = sqr(radius);313314if (planeDistSq > radiusSq) {315/* Max speed sphere fully invalidates plane planeNo. */316return false;317}318319const float planeRadiusSq = radiusSq - planeDistSq;320321const Vector3 planeCenter = planeDist * planes[planeNo].normal;322323if (directionOpt) {324/* Project direction optVelocity on plane planeNo. */325const Vector3 planeOptVelocity = optVelocity - (optVelocity * planes[planeNo].normal) * planes[planeNo].normal;326const float planeOptVelocityLengthSq = absSq(planeOptVelocity);327328if (planeOptVelocityLengthSq <= RVO3D_EPSILON) {329result = planeCenter;330}331else {332result = planeCenter + std::sqrt(planeRadiusSq / planeOptVelocityLengthSq) * planeOptVelocity;333}334}335else {336/* Project point optVelocity on plane planeNo. */337result = optVelocity + ((planes[planeNo].point - optVelocity) * planes[planeNo].normal) * planes[planeNo].normal;338339/* If outside planeCircle, project on planeCircle. */340if (absSq(result) > radiusSq) {341const Vector3 planeResult = result - planeCenter;342const float planeResultLengthSq = absSq(planeResult);343result = planeCenter + std::sqrt(planeRadiusSq / planeResultLengthSq) * planeResult;344}345}346347for (size_t i = 0; i < planeNo; ++i) {348if (planes[i].normal * (planes[i].point - result) > 0.0f) {349/* Result does not satisfy constraint i. Compute new optimal result. */350/* Compute intersection line of plane i and plane planeNo. */351Vector3 crossProduct = cross(planes[i].normal, planes[planeNo].normal);352353if (absSq(crossProduct) <= RVO3D_EPSILON) {354/* Planes planeNo and i are (almost) parallel, and plane i fully invalidates plane planeNo. */355return false;356}357358Line3D line;359line.direction = normalize(crossProduct);360const Vector3 lineNormal = cross(line.direction, planes[planeNo].normal);361line.point = planes[planeNo].point + (((planes[i].point - planes[planeNo].point) * planes[i].normal) / (lineNormal * planes[i].normal)) * lineNormal;362363if (!linearProgram1(planes, i, line, radius, optVelocity, directionOpt, result)) {364return false;365}366}367}368369return true;370}371372size_t linearProgram3(const std::vector<Plane> &planes, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result)373{374if (directionOpt) {375/* Optimize direction. Note that the optimization velocity is of unit length in this case. */376result = optVelocity * radius;377}378else if (absSq(optVelocity) > sqr(radius)) {379/* Optimize closest point and outside circle. */380result = normalize(optVelocity) * radius;381}382else {383/* Optimize closest point and inside circle. */384result = optVelocity;385}386387for (size_t i = 0; i < planes.size(); ++i) {388if (planes[i].normal * (planes[i].point - result) > 0.0f) {389/* Result does not satisfy constraint i. Compute new optimal result. */390const Vector3 tempResult = result;391392if (!linearProgram2(planes, i, radius, optVelocity, directionOpt, result)) {393result = tempResult;394return i;395}396}397}398399return planes.size();400}401402void linearProgram4(const std::vector<Plane> &planes, size_t beginPlane, float radius, Vector3 &result)403{404float distance = 0.0f;405406for (size_t i = beginPlane; i < planes.size(); ++i) {407if (planes[i].normal * (planes[i].point - result) > distance) {408/* Result does not satisfy constraint of plane i. */409std::vector<Plane> projPlanes;410411for (size_t j = 0; j < i; ++j) {412Plane plane;413414const Vector3 crossProduct = cross(planes[j].normal, planes[i].normal);415416if (absSq(crossProduct) <= RVO3D_EPSILON) {417/* Plane i and plane j are (almost) parallel. */418if (planes[i].normal * planes[j].normal > 0.0f) {419/* Plane i and plane j point in the same direction. */420continue;421}422else {423/* Plane i and plane j point in opposite direction. */424plane.point = 0.5f * (planes[i].point + planes[j].point);425}426}427else {428/* Plane.point is point on line of intersection between plane i and plane j. */429const Vector3 lineNormal = cross(crossProduct, planes[i].normal);430plane.point = planes[i].point + (((planes[j].point - planes[i].point) * planes[j].normal) / (lineNormal * planes[j].normal)) * lineNormal;431}432433plane.normal = normalize(planes[j].normal - planes[i].normal);434projPlanes.push_back(plane);435}436437const Vector3 tempResult = result;438439if (linearProgram3(projPlanes, radius, planes[i].normal, true, result) < projPlanes.size()) {440/* This should in principle not happen. The result is by definition already in the feasible region of this linear program. If it fails, it is due to small floating point error, and the current result is kept. */441result = tempResult;442}443444distance = planes[i].normal * (planes[i].point - result);445}446}447}448}449450451