|
|
@ -19,9 +19,39 @@ |
|
|
|
|
|
|
|
#include "ant.hpp"
|
|
|
|
#include "colony.hpp"
|
|
|
|
#include "pheromone.hpp"
|
|
|
|
#include "pheromone-matrix.hpp"
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
float FRAMES_PER_SECOND = 60; |
|
|
|
float TIMESTEP = 1.0f / FRAMES_PER_SECOND; |
|
|
|
float ANT_LENGTH = 0.5f; // 0.5 cm, head to abdomen (not including legs / antennae)
|
|
|
|
float ANT_COLLISION_RADIUS = ANT_LENGTH * 1.25f; |
|
|
|
float RECEPTOR_RADIUS = 0.4f; |
|
|
|
float RECEPTOR_SEPARATION = 0.882f; |
|
|
|
float RECEPTOR_DISTANCE = 0.588f; |
|
|
|
float MOUTH_DISTANCE = 0.2646f; |
|
|
|
float BITE_RADIUS = 0.0294f; |
|
|
|
float FOOD_PARTICLE_RADIUS = 0.1176f; |
|
|
|
float MAX_RECEPTOR_NOISE = 0.05f; // essentially an epsilon
|
|
|
|
float MAX_EXCITEMENT = 1.0f; |
|
|
|
float MAX_PHEROMONE_TURNING_ANGLE = glm::radians(8.5f); |
|
|
|
float MIN_WALK_TIME = 0.5f; // seconds
|
|
|
|
float MAX_WALK_TIME = 8.0f; // seconds
|
|
|
|
float MIN_REST_TIME = 0.15f; |
|
|
|
float MAX_REST_TIME = 0.7f; |
|
|
|
float MIN_CHEW_TIME = 0.25f; |
|
|
|
float MAX_CHEW_TIME = 0.5f; |
|
|
|
float DEEXCITEMENT_FACTOR = 0.999f; // This should probably always be less than the evaporation factor
|
|
|
|
float CALM_FACTOR = 0.995f; |
|
|
|
float MAX_WALK_FORCE = 1.5; |
|
|
|
float MAX_PANIC_FORCE = 0.1029f; |
|
|
|
float MAX_WALK_SPEED = 3.0f; // cm/s
|
|
|
|
float MAX_PANIC_SPEED = 8.82f; // cm/s
|
|
|
|
float PANIC_RADIUS = 7.35f; |
|
|
|
float WANDER_CIRCLE_DISTANCE = 0.441f; |
|
|
|
float WANDER_CIRCLE_RADIUS = 0.0294f; |
|
|
|
float MAX_WANDER_ANGLE = 0.15f; |
|
|
|
|
|
|
|
inline float fwrap(float angle, float limit) |
|
|
|
{ |
|
|
|
return angle - std::floor(angle / limit) * limit; |
|
|
@ -41,6 +71,11 @@ Ant::Ant(Colony* colony): |
|
|
|
modelInstance.setPose(pose); |
|
|
|
|
|
|
|
animationTime = frand(0.0f, 60.0f); |
|
|
|
|
|
|
|
velocity = Vector3(0); |
|
|
|
acceleration = Vector3(0); |
|
|
|
wanderDirection = getForward(); |
|
|
|
excitement = MAX_EXCITEMENT; |
|
|
|
} |
|
|
|
|
|
|
|
Ant::~Ant() |
|
|
@ -52,7 +87,7 @@ void Ant::animate() |
|
|
|
{ |
|
|
|
colony->getTripodGaitAnimation()->animate(pose, animationTime); |
|
|
|
pose->concatenate(); |
|
|
|
animationTime = fwrap(animationTime + 2.0f, colony->getTripodGaitAnimation()->getEndTime()); |
|
|
|
animationTime = fwrap(animationTime + 4.0f, colony->getTripodGaitAnimation()->getEndTime()); |
|
|
|
} |
|
|
|
|
|
|
|
void Ant::suspend(const Vector3& suspensionPoint, const Quaternion& suspensionRotation) |
|
|
@ -100,57 +135,77 @@ void Ant::update(float dt) |
|
|
|
|
|
|
|
animate(); |
|
|
|
|
|
|
|
// Calculate positions of receptors
|
|
|
|
receptorL = getPosition() + getForward() * RECEPTOR_DISTANCE; |
|
|
|
receptorR = receptorL; |
|
|
|
receptorL -= getRight() * RECEPTOR_SEPARATION * 0.5f; |
|
|
|
receptorR += getRight() * RECEPTOR_SEPARATION * 0.5f; |
|
|
|
|
|
|
|
// Steering
|
|
|
|
if (state == Ant::State::WANDER) |
|
|
|
{ |
|
|
|
setWanderCircleDistance(4.0f); |
|
|
|
setWanderCircleRadius(0.3f); |
|
|
|
setWanderRate(glm::radians(90.0f)); |
|
|
|
setSeparationRadius(0.5f); |
|
|
|
setMaxSpeed(0.025f); |
|
|
|
//setWanderCircleDistance(4.0f);
|
|
|
|
//setWanderCircleRadius(0.3f);
|
|
|
|
//setWanderRate(glm::radians(90.0f));
|
|
|
|
//setSeparationRadius(0.5f);
|
|
|
|
//setMaxSpeed(0.025f);
|
|
|
|
|
|
|
|
// Calculate wander force
|
|
|
|
Vector3 wanderForce = wander(dt); |
|
|
|
Vector3 wanderForce = wander() * 1.5f; |
|
|
|
Vector3 followForce = follow() * 3.0f; |
|
|
|
|
|
|
|
// Setup containment probes
|
|
|
|
Vector3 leftProbe = getForward() * probeForwardOffset - getRight() * probeLateralOffset; |
|
|
|
Vector3 rightProbe = getForward() * probeForwardOffset + getRight() * probeLateralOffset; |
|
|
|
//Vector3 leftProbe = getForward() * probeForwardOffset - getRight() * probeLateralOffset;
|
|
|
|
//Vector3 rightProbe = getForward() * probeForwardOffset + getRight() * probeLateralOffset;
|
|
|
|
|
|
|
|
// Calculate containment force
|
|
|
|
Vector3 containmentForce = containment(leftProbe) + containment(rightProbe); |
|
|
|
//Vector3 containmentForce = containment(leftProbe) + containment(rightProbe);
|
|
|
|
|
|
|
|
// Determine neighbors
|
|
|
|
float neighborhoodSize = 2.0f; |
|
|
|
AABB neighborhoodAABB(getPosition() - Vector3(neighborhoodSize * 0.5f), getPosition() + Vector3(neighborhoodSize * 0.5f)); |
|
|
|
std::list<Agent*> neighbors; |
|
|
|
colony->queryAnts(neighborhoodAABB, &neighbors); |
|
|
|
//float neighborhoodSize = 2.0f;
|
|
|
|
//AABB neighborhoodAABB(getPosition() - Vector3(neighborhoodSize * 0.5f), getPosition() + Vector3(neighborhoodSize * 0.5f));
|
|
|
|
//std::list<Agent*> neighbors;
|
|
|
|
//colony->queryAnts(neighborhoodAABB, &neighbors);
|
|
|
|
|
|
|
|
// Calculate separation force
|
|
|
|
Vector3 separationForce = separation(neighbors); |
|
|
|
//Vector3 separationForce = separation(neighbors);
|
|
|
|
|
|
|
|
applyForce(wanderForce); |
|
|
|
applyForce(followForce); |
|
|
|
|
|
|
|
float maxSpeed = MAX_WALK_SPEED * TIMESTEP; |
|
|
|
|
|
|
|
// Limit acceleration
|
|
|
|
float accelerationMagnitudeSquared = glm::dot(acceleration, acceleration); |
|
|
|
if (accelerationMagnitudeSquared > MAX_WALK_FORCE * MAX_WALK_FORCE) |
|
|
|
{ |
|
|
|
acceleration = glm::normalize(acceleration) * MAX_WALK_FORCE; |
|
|
|
} |
|
|
|
|
|
|
|
// Accelerate
|
|
|
|
velocity += acceleration; |
|
|
|
|
|
|
|
// Limit speed
|
|
|
|
float speedSquared = glm::dot(velocity, velocity); |
|
|
|
if (speedSquared > maxSpeed * maxSpeed) |
|
|
|
{ |
|
|
|
velocity = glm::normalize(velocity) * maxSpeed; |
|
|
|
} |
|
|
|
|
|
|
|
// Calculate velocity
|
|
|
|
Vector3 velocity = getVelocity(); |
|
|
|
velocity += wanderForce; |
|
|
|
velocity += containmentForce * 0.0025f; |
|
|
|
velocity += separationForce * 0.01f; |
|
|
|
velocity = limit(velocity, 0.025f); |
|
|
|
setVelocity(velocity); |
|
|
|
Vector3 direction = glm::normalize(velocity); |
|
|
|
setOrientation(direction, getUp()); |
|
|
|
|
|
|
|
setOrientation(glm::normalize(velocity), getUp()); |
|
|
|
// Deposit pheromones
|
|
|
|
Vector2 position2D = Vector2(getPosition().x, getPosition().z); |
|
|
|
colony->getHomingMatrix()->deposit(position2D, excitement); |
|
|
|
excitement *= DEEXCITEMENT_FACTOR; |
|
|
|
|
|
|
|
// Move ant
|
|
|
|
move(velocity); |
|
|
|
} |
|
|
|
else if (state == Ant::State::IDLE) |
|
|
|
{ |
|
|
|
Vector3 leftProbe = getForward() * probeForwardOffset - getRight() * probeLateralOffset; |
|
|
|
Vector3 rightProbe = getForward() * probeForwardOffset + getRight() * probeLateralOffset; |
|
|
|
Vector3 containmentForce = containment(leftProbe) + containment(rightProbe); |
|
|
|
Vector3 velocity = Vector3(0.0f); |
|
|
|
velocity += containmentForce; |
|
|
|
velocity = limit(velocity, 0.025f); |
|
|
|
setVelocity(velocity); |
|
|
|
//setOrientation(glm::normalize(velocity), getUp());
|
|
|
|
velocity = Vector3(0.0f); |
|
|
|
|
|
|
|
// Move ant
|
|
|
|
move(velocity); |
|
|
@ -181,6 +236,7 @@ void Ant::update(float dt) |
|
|
|
*/ |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
Vector3 Ant::forage(const Vector3& leftReceptor, const Vector3& rightReceptor) |
|
|
|
{ |
|
|
|
float leftSignal = 0.0f; |
|
|
@ -233,8 +289,74 @@ Vector3 Ant::forage(const Vector3& leftReceptor, const Vector3& rightReceptor) |
|
|
|
|
|
|
|
return Vector3(0.0f); |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
void Ant::setState(Ant::State state) |
|
|
|
{ |
|
|
|
this->state = state; |
|
|
|
} |
|
|
|
|
|
|
|
Vector3 Ant::seek(const Vector3& target) |
|
|
|
{ |
|
|
|
Vector3 steer(0.0f); |
|
|
|
Vector3 difference = target - getPosition(); |
|
|
|
|
|
|
|
float distanceSquared = glm::dot(difference, difference); |
|
|
|
if (distanceSquared > 0.0f) |
|
|
|
{ |
|
|
|
float maxForce = MAX_WALK_FORCE; |
|
|
|
|
|
|
|
steer = glm::normalize(difference) * maxForce - velocity; |
|
|
|
} |
|
|
|
|
|
|
|
return steer; |
|
|
|
} |
|
|
|
|
|
|
|
Vector3 Ant::flee(const Vector3& target) |
|
|
|
{ |
|
|
|
return -seek(target); |
|
|
|
} |
|
|
|
|
|
|
|
Vector3 Ant::wander() |
|
|
|
{ |
|
|
|
// Determine center of wander circle
|
|
|
|
Vector3 center = getPosition() + getForward() * WANDER_CIRCLE_DISTANCE; |
|
|
|
|
|
|
|
// Calculate wander target
|
|
|
|
Vector3 target = center + wanderDirection * WANDER_CIRCLE_RADIUS; |
|
|
|
|
|
|
|
// Rotate wander direction by a random displacement angle
|
|
|
|
float displacement = frand(-MAX_WANDER_ANGLE, MAX_WANDER_ANGLE); |
|
|
|
wanderDirection = glm::normalize(glm::angleAxis(displacement, getUp()) * wanderDirection); |
|
|
|
|
|
|
|
return seek(target); |
|
|
|
} |
|
|
|
|
|
|
|
Vector3 Ant::follow() |
|
|
|
{ |
|
|
|
const PheromoneMatrix* pheromoneMatrix = colony->getRecruitmentMatrix(); |
|
|
|
|
|
|
|
Vector2 receptorL2D = Vector2(receptorL.x, receptorL.z); |
|
|
|
Vector2 receptorR2D = Vector2(receptorR.x, receptorR.z); |
|
|
|
|
|
|
|
float signalL = pheromoneMatrix->query(receptorL2D, RECEPTOR_RADIUS); |
|
|
|
signalL += frand(0.0f, MAX_RECEPTOR_NOISE); |
|
|
|
|
|
|
|
float signalR = pheromoneMatrix->query(receptorR2D, RECEPTOR_RADIUS); |
|
|
|
signalR += frand(0.0f, MAX_RECEPTOR_NOISE); |
|
|
|
|
|
|
|
if (signalL + signalR > 0.0f) |
|
|
|
{ |
|
|
|
float angle = -MAX_PHEROMONE_TURNING_ANGLE * ((signalL - signalR) / (signalL + signalR)); |
|
|
|
|
|
|
|
Vector3 steer = glm::normalize(glm::angleAxis(angle, getUp()) * getForward()); |
|
|
|
return steer; |
|
|
|
} |
|
|
|
|
|
|
|
return Vector3(0.0f); |
|
|
|
} |
|
|
|
|
|
|
|
void Ant::applyForce(const Vector3& force) |
|
|
|
{ |
|
|
|
acceleration += force; |
|
|
|
} |