|
|
- #include "tool.hpp"
- #include "ant.hpp"
- #include "colony.hpp"
- #include "navmesh.hpp"
- #include "pheromone-matrix.hpp"
- #include "../camera-controller.hpp"
- #include "../configuration.hpp"
- #include <iostream>
- #include <list>
-
- Tool::Tool():
- active(false),
- pick(0.0f),
- cameraController(nullptr)
- {
- modelInstance.setActive(active);
- }
-
- Tool::~Tool()
- {}
-
- void Tool::setActive(bool active)
- {
- this->active = active;
- if (!active)
- {
- modelInstance.setActive(active);
- }
- }
-
- void Tool::setCameraController(const SurfaceCameraController* cameraController)
- {
- this->cameraController = cameraController;
- }
-
- Forceps::Forceps(const Model* model)
- {
- // Allocate pose and initialize to bind pose
- pose = new Pose(model->getSkeleton());
- pose->reset();
- pose->concatenate();
-
- // Setup model instance
- modelInstance.setModel(model);
- modelInstance.setPose(pose);
-
- // Find pinch animation
- pinchAnimation = model->getSkeleton()->getAnimation("pinch");
- if (!pinchAnimation)
- {
- std::cerr << "Forceps pinch animation not found" << std::endl;
- }
-
- // Find release animation
- releaseAnimation = model->getSkeleton()->getAnimation("release");
- if (!releaseAnimation)
- {
- std::cerr << "Forceps release animation not found" << std::endl;
- }
-
- hoverDistance = 1.0f;
-
- // Setup timing
- float descentDuration = 0.125f;
- float ascentDuration = 0.125f;
- float descentFrameCount = descentDuration / (1.0f / 60.0f);
- animationTimeStep = pinchAnimation->getEndTime() / descentFrameCount;
-
- // Allocate tweener and and setup tweens
- tweener = new Tweener();
- descentTween = new Tween<float>(EaseFunction::OUT_CUBIC, 0.0f, descentDuration, hoverDistance, -hoverDistance);
- ascentTween = new Tween<float>(EaseFunction::IN_CUBIC, 0.0f, ascentDuration, 0.0f, hoverDistance);
- descentTween->setEndCallback(std::bind(&TweenBase::start, ascentTween));
- tweener->addTween(descentTween);
- tweener->addTween(ascentTween);
-
- // Setup initial state
- state = Forceps::State::RELEASED;
- animationTime = 0.0f;
- colony = nullptr;
- targetedAnt = nullptr;
- suspendedAnt = nullptr;
- cameraController = nullptr;
- pick = Vector3(0.0f);
- }
-
- Forceps::~Forceps()
- {
- delete pose;
- delete descentTween;
- delete ascentTween;
- delete tweener;
- }
-
- void Forceps::update(float dt)
- {
- modelInstance.setActive(active);
-
- // Update tweener
- tweener->update(dt);
-
- // Determine distance from pick point
- float forcepsDistance = hoverDistance;
- if (!ascentTween->isStopped())
- {
- forcepsDistance = ascentTween->getTweenValue();
- }
- else if (!descentTween->isStopped())
- {
- forcepsDistance = descentTween->getTweenValue();
- }
-
- Quaternion alignment = glm::angleAxis(cameraController->getAzimuth(), Vector3(0, 1, 0));
- Quaternion tilt = glm::angleAxis(glm::radians(15.0f), Vector3(0, 0, -1));
- Quaternion rotation = glm::normalize(alignment * tilt);
- Vector3 translation = pick + rotation * Vector3(0, forcepsDistance, 0);
-
- // Set tool position
- modelInstance.setTranslation(translation);
- modelInstance.setRotation(rotation);
-
- if (state == Forceps::State::RELEASED)
- {
-
- }
- else if (state == Forceps::State::RELEASING)
- {
- // Perform release animation
- releaseAnimation->animate(pose, animationTime);
- pose->concatenate();
-
- // If release animation is finished
- if (animationTime >= releaseAnimation->getEndTime())
- {
- // Changed to released state
- state = Forceps::State::RELEASED;
- }
- }
- else if (state == Forceps::State::PINCHED)
- {
- if (!ascentTween->isStopped())
- {
- // Calculate interpolation factor
- float interpolationFactor = (ascentTween->getTweenValue() - ascentTween->getStartValue()) / ascentTween->getDeltaValue();
-
- // Form tilt quaternion
- //Quaternion tilt = glm::angleAxis(glm::radians(15.0f), Vector3(0, 0, -1));
- tilt = glm::angleAxis(glm::radians(15.0f), Vector3(0, 0, -1));
-
- // Project camera forward onto XZ plane
- Vector3 cameraForwardXZ = cameraController->getCamera()->getForward();
- cameraForwardXZ.y = 0.0f;
- cameraForwardXZ = glm::normalize(cameraForwardXZ);
-
- // Form alignment quaternion
- //Quaternion alignment = glm::rotation(Vector3(0, 0, -1), cameraForwardXZ);
- alignment = glm::angleAxis(cameraController->getAzimuth(), Vector3(0, 1, 0));
-
- // Calculate target rotation at the top of the ascentTween
- rotationTop = glm::normalize(alignment * tilt);
-
- // Interpolate between bottom and top rotations
- Quaternion interpolatedRotation = glm::normalize(glm::slerp(rotationBottom, rotationTop, interpolationFactor));
-
- // Set target translation at the top of the ascent
- translationTop = pick + rotationTop * Vector3(0, hoverDistance, 0);
-
- // Interpolate between bottom and top translations
- Vector3 interpolatedTranslation = glm::lerp(translationBottom, translationTop, interpolationFactor);
-
- // Update model instance transform
- modelInstance.setTranslation(interpolatedTranslation);
- modelInstance.setRotation(interpolatedRotation);
- }
-
- if (suspendedAnt != nullptr)
- {
- // Project forceps forward vector onto XZ plane
- Vector3 forward = glm::normalize(modelInstance.getRotation() * Vector3(0, 0, -1));
- forward.y = 0.0f;
- forward = glm::normalize(forward);
-
- // Calculate suspension quaternion
- Quaternion suspensionRotation = glm::normalize(glm::rotation(Vector3(0, 0, -1), ((flipRotation) ? -forward : forward)));
-
- // Suspend ant
- suspendedAnt->suspend(modelInstance.getTranslation(), suspensionRotation);
- }
- }
- else if (state == Forceps::State::PINCHING)
- {
- // Perform pinch animation
- pinchAnimation->animate(pose, animationTime);
- pose->concatenate();
-
- // Rotate to align forceps with ant
- if (targetedAnt != nullptr)
- {
- // Calculate interpolation factor
- float interpolationFactor = (descentTween->getTweenValue() - descentTween->getStartValue()) / descentTween->getDeltaValue();
-
- // Set target translation at the bottom of the descent
- translationBottom = targetedAnt->getPosition();
-
- // Interpolate between top and bottom translations
- Vector3 interpolatedTranslation = glm::lerp(translationTop, translationBottom, interpolationFactor);
-
- // Project camera forward onto XZ plane
- Vector3 cameraForwardXZ = cameraController->getCamera()->getForward();
- cameraForwardXZ.y = 0.0f;
- cameraForwardXZ = glm::normalize(cameraForwardXZ);
-
- // Form tilt quaternion
- tilt = glm::angleAxis(glm::radians(15.0f), -cameraForwardXZ);
-
- // Project ant forward onto XZ plane
- Vector3 antForwardXZ = targetedAnt->getForward();
- antForwardXZ.y = 0.0f;
- antForwardXZ = glm::normalize(antForwardXZ);
-
- // Form alignment quaternion
- alignment = glm::rotation(Vector3(0, 0, -1), (flipRotation) ? antForwardXZ : -antForwardXZ);
-
- // Calculate target rotation at the bottom of the descent
- rotationBottom = glm::normalize(tilt * alignment);
-
- // Interpolate between top and bottom rotations
- Quaternion interpolatedRotation = glm::normalize(glm::slerp(rotationTop, rotationBottom, interpolationFactor));
-
- // Update model instance transform
- modelInstance.setTranslation(interpolatedTranslation);
- modelInstance.setRotation(interpolatedRotation);
- }
-
- // If pinch animation is finished
- if (animationTime >= pinchAnimation->getEndTime() && descentTween->isStopped())
- {
- // If an ant was targeted
- if (targetedAnt != nullptr)
- {
- // Suspend targeted ant
- suspendedAnt = targetedAnt;
- suspendedAnt->setState(Ant::State::SUSPENDED);
- //suspendedAnt->suspend(modelInstance.getTranslation());
- targetedAnt = nullptr;
- }
-
- // Change to pinched state
- state = Forceps::State::PINCHED;
- }
- }
-
- // Increment animation time
- animationTime += animationTimeStep;
- }
-
- void Forceps::setColony(Colony* colony)
- {
- this->colony = colony;
- }
-
- void Forceps::setNavmesh(Navmesh* navmesh)
- {
- this->navmesh = navmesh;
- }
-
- void Forceps::pinch()
- {
- // Change state to pinching
- state = Forceps::State::PINCHING;
- animationTime = 0.0f;
-
- if (colony != nullptr)
- {
- // Target nearest ant in pinching radius
- Sphere pinchingBounds = Sphere(pick, 0.35f);
-
- // Build a list of ants which intersect the pinching bounds
- std::list<Agent*> ants;
- colony->queryAnts(pinchingBounds, &ants);
-
- // Target ant closest to the center of the pinching bounds
- float closestDistance = std::numeric_limits<float>::infinity();
- for (Agent* agent: ants)
- {
- Ant* ant = static_cast<Ant*>(agent);
-
- Vector3 difference = ant->getPosition() - pinchingBounds.getCenter();
- float distanceSquared = glm::dot(difference, difference);
- if (distanceSquared < closestDistance)
- {
- closestDistance = distanceSquared;
- targetedAnt = ant;
- }
- }
-
- if (targetedAnt != nullptr)
- {
- // Start descent tweener
- descentTween->start();
-
- // Save translation & rotation
- translationTop = modelInstance.getTranslation();
- rotationTop = modelInstance.getRotation();
-
- // Project ant forward onto XZ plane
- Vector3 antForwardXZ = targetedAnt->getForward();
- antForwardXZ.y = 0.0f;
- antForwardXZ = glm::normalize(antForwardXZ);
-
- // Project camera forward onto XZ plane
- Vector3 cameraForwardXZ = cameraController->getCamera()->getForward();
- cameraForwardXZ.y = 0.0f;
- cameraForwardXZ = glm::normalize(cameraForwardXZ);
-
- // Find angle between ant and camera on XZ plane
- float angle = std::acos(glm::dot(cameraForwardXZ, antForwardXZ));
-
- // Determine direction to rotate
- flipRotation = (angle > glm::radians(90.0f));
- }
- }
- }
-
- void Forceps::release()
- {
- // Change state to releasing
- state = Forceps::State::RELEASING;
- animationTime = 0.0f;
- targetedAnt = nullptr;
-
- if (suspendedAnt != nullptr)
- {
- Ray pickingRay;
- pickingRay.origin = pick + Vector3(0, 1, 0);
- pickingRay.direction = Vector3(0, -1, 0);
-
- const std::vector<Navmesh::Triangle*>* navmeshTriangles = navmesh->getTriangles();
- for (Navmesh::Triangle* triangle: *navmeshTriangles)
- {
- auto result = intersects(pickingRay, triangle);
- if (std::get<0>(result))
- {
- Vector3 barycentricPosition = Vector3(std::get<2>(result), std::get<3>(result), 1.0f - std::get<2>(result) - std::get<3>(result));
- suspendedAnt->setPosition(triangle, barycentricPosition);
-
- break;
- }
- }
-
- // Release suspended ant
- suspendedAnt->setState(Ant::State::WANDER);
- suspendedAnt = nullptr;
- }
-
- // Reset tweens
- descentTween->reset();
- descentTween->stop();
- ascentTween->reset();
- ascentTween->stop();
- }
-
- Lens::Lens(const Model* model)
- {
- // Setup model instance
- modelInstance.setModel(model);
-
- // Setup spotlight
- spotlight.setColor(Vector3(1.0f));
- spotlight.setIntensity(10000.0f);
- spotlight.setAttenuation(Vector3(1, 0, 1));
- spotlight.setCutoff(glm::radians(45.0f));
- spotlight.setExponent(700.0f);
- spotlight.setActive(false);
-
- unfocusedDistance = 18.0f;
- focusedDistance = 12.0f;
- focused = false;
- sunDirection = Vector3(0, -1, 0);
-
- // Setup timing
- float descentDuration = 0.75f;
- float ascentDuration = 0.25f;
-
- // Allocate tweener and and setup tweens
- tweener = new Tweener();
- descentTween = new Tween<float>(EaseFunction::OUT_CUBIC, 0.0f, descentDuration, unfocusedDistance, focusedDistance - unfocusedDistance);
- ascentTween = new Tween<float>(EaseFunction::OUT_CUBIC, 0.0f, ascentDuration, focusedDistance, unfocusedDistance - focusedDistance);
- descentTween->setEndCallback
- (
- [this](float t)
- {
- focused = true;
- }
- );
- tweener->addTween(descentTween);
- tweener->addTween(ascentTween);
- }
-
- Lens::~Lens()
- {
- delete descentTween;
- delete ascentTween;
- delete tweener;
- }
-
- void Lens::update(float dt)
- {
- /*
- // Rotate to face camera
- hoverDistance = 30.0f;
- Vector3 direction = glm::normalize(cameraController->getCamera()->getTranslation() - pick);
- //direction = cameraController->getCamera()->getForward();
- float distance = glm::distance(pick, cameraController->getCamera()->getTranslation());
-
- Quaternion alignment = glm::angleAxis(cameraController->getAzimuth() + glm::radians(90.0f), Vector3(0, 1, 0));
- Quaternion tilt = glm::rotation(Vector3(0, 1, 0), -direction);
- Quaternion rotation = glm::normalize(tilt * alignment);
-
- Vector3 translation = pick + rotation * Vector3(0, -distance + hoverDistance, 0);
-
- modelInstance.setTranslation(translation);
- modelInstance.setRotation(rotation);
- */
- modelInstance.setActive(active);
- spotlight.setActive(active);
-
- // Update tweener
- tweener->update(dt);
-
- float lensDistance = (focused) ? focusedDistance : unfocusedDistance;
- if (!ascentTween->isStopped())
- {
- lensDistance = ascentTween->getTweenValue();
- }
- else if (!descentTween->isStopped())
- {
- lensDistance = descentTween->getTweenValue();
- }
-
- //Quaternion alignment = glm::angleAxis(cameraController->getAzimuth() + glm::radians(90.0f), Vector3(0, 1, 0));
- Quaternion alignment = glm::rotation(Vector3(0, 1, 0), -sunDirection) * glm::angleAxis(glm::radians(90.0f), Vector3(0, 1, 0));
- Quaternion rotation = glm::normalize(alignment);
- Vector3 translation = pick + sunDirection * -lensDistance;
-
- modelInstance.setTranslation(translation);
- modelInstance.setRotation(rotation);
-
- float spotlightDistanceFactor = (1.0 - (lensDistance - focusedDistance) / (unfocusedDistance - focusedDistance)) * 2.0f - 1.0f;
-
- spotlight.setTranslation(pick + sunDirection * (-lensDistance + 5.0f * spotlightDistanceFactor));
- spotlight.setDirection(sunDirection);
- }
-
- void Lens::setActive(bool active)
- {
- this->active = active;
- if (!active)
- {
- modelInstance.setActive(active);
- spotlight.setActive(active);
- }
- }
-
- void Lens::focus()
- {
- ascentTween->stop();
- descentTween->reset();
- descentTween->start();
- }
-
- void Lens::unfocus()
- {
- descentTween->stop();
- focused = false;
- ascentTween->reset();
- ascentTween->start();
- }
-
- void Lens::setSunDirection(const Vector3& direction)
- {
- sunDirection = direction;
- }
-
- Brush::Brush(const Model* model)
- {
- // Allocate pose and initialize to bind pose
- pose = new Pose(model->getSkeleton());
- pose->reset();
- pose->concatenate();
-
- // Setup model instance
- modelInstance.setModel(model);
- modelInstance.setPose(pose);
-
- hoverDistance = 0.5f;
-
- // Setup timing
- float descentDuration = 0.1f;
- float ascentDuration = 0.1f;
-
- // Allocate tweener and and setup tweens
- tweener = new Tweener();
- descentTween = new Tween<float>(EaseFunction::OUT_CUBIC, 0.0f, descentDuration, hoverDistance, -hoverDistance);
- ascentTween = new Tween<float>(EaseFunction::OUT_CUBIC, 0.0f, ascentDuration, 0.0f, hoverDistance);
- descentTween->setEndCallback
- (
- [this](float t)
- {
- descended = true;
- paint(Vector2(pick.x, pick.z), BRUSH_RADIUS);
- }
- );
- tweener->addTween(descentTween);
- tweener->addTween(ascentTween);
- descended = false;
-
- oldPick = pick;
- tiltAngle = 0.0f;
- targetTiltAngle = 0.0f;
- tiltAxis = Vector3(1.0f, 0.0f, 0.0f);
- targetTiltAxis = tiltAxis;
-
- colony = nullptr;
- }
-
- Brush::~Brush()
- {
- delete pose;
- delete descentTween;
- delete ascentTween;
- delete tweener;
- }
-
- void Brush::update(float dt)
- {
- modelInstance.setActive(active);
-
- // Update tweener
- tweener->update(dt);
-
- float brushDistance = (descended) ? 0.0f : hoverDistance;
- if (!ascentTween->isStopped())
- {
- brushDistance = ascentTween->getTweenValue();
- }
- else if (!descentTween->isStopped())
- {
- brushDistance = descentTween->getTweenValue();
- }
-
- targetTiltAngle = 0.0f;
- if (descended)
- {
- Vector3 difference = pick - oldPick;
- float distanceSquared = glm::dot(difference, difference);
-
- // Calculate tilt
- if (distanceSquared > 0.005f)
- {
- float maxDistance = 0.25f;
- float maxTiltAngle = glm::radians(45.0f);
- float distance = std::sqrt(distanceSquared);
- float tiltFactor = std::min<float>(maxDistance, distance) / maxDistance;
-
- targetTiltAngle = maxTiltAngle * tiltFactor;
- targetTiltAxis = glm::normalize(Vector3(difference.z, 0.0f, -difference.x));
- }
-
- // Paint pheromones
- Vector2 difference2D = Vector2(pick.x, pick.z) - Vector2(oldPick.x, oldPick.z);
- float distance2DSquared = glm::dot(difference2D, difference2D);
- if (distance2DSquared != 0.0f)
- {
- float distance2D = sqrt(distance2DSquared);
- Vector2 direction2D = difference2D / distance2D;
-
- if (distance2D <= BRUSH_RADIUS)
- {
- paint(Vector2(pick.x, pick.z), BRUSH_RADIUS);
- }
- else
- {
- float stepDistance = BRUSH_RADIUS * 0.5f;
- int stepCount = static_cast<int>(distance2D / stepDistance + 0.5f);
-
- for (int i = 0; i < stepCount; ++i)
- {
- Vector2 circleCenter = Vector2(oldPick.x, oldPick.z) + direction2D * (stepDistance * i);
-
- paint(circleCenter, BRUSH_RADIUS);
- }
-
- paint(Vector2(pick.x, pick.z), BRUSH_RADIUS);
- }
- }
- }
-
- float angleInterpolationFactor = 0.1f / (1.0 / 60.0f) * dt;
- float axisInterpolationFactor = 0.2f / (1.0 / 60.0f) * dt;
- tiltAngle = glm::mix(tiltAngle, targetTiltAngle, angleInterpolationFactor);
- tiltAxis = glm::mix(tiltAxis, targetTiltAxis, axisInterpolationFactor);
-
- Quaternion tilt = glm::angleAxis(tiltAngle, tiltAxis);
-
- Quaternion alignment = glm::angleAxis(cameraController->getAzimuth(), Vector3(0, 1, 0));
- Quaternion rotation = glm::normalize(tilt);
- Vector3 translation = pick + Vector3(0, brushDistance, 0);
-
- modelInstance.setTranslation(translation);
- modelInstance.setRotation(rotation);
-
-
- if (descended)
- {
- Vector2 paintPosition = Vector2(pick.x, pick.z);
- paint(paintPosition, BRUSH_RADIUS);
-
-
- }
-
- oldPick = pick;
- }
-
- void Brush::press()
- {
- ascentTween->stop();
- descentTween->reset();
- descentTween->start();
- }
-
- void Brush::release()
- {
- descentTween->stop();
- descended = false;
- ascentTween->reset();
- ascentTween->start();
- }
-
- void Brush::setColony(Colony* colony)
- {
- this->colony = colony;
- }
-
- void Brush::paint(const Vector2& position, float radius)
- {
- if (!colony)
- {
- return;
- }
-
- PheromoneMatrix* pheromoneMatrix = colony->getRecruitmentMatrix();
-
- float concentration = 1.0f;
- float radiusSquared = radius * radius;
- Vector2 cell;
- Vector2 difference;
-
- for (cell.y = position.y - radius; cell.y <= position.y + radius; cell.y += pheromoneMatrix->getCellHeight())
- {
- difference.y = cell.y - position.y;
-
- for (cell.x = position.x - radius; cell.x <= position.x + radius; cell.x += pheromoneMatrix->getCellWidth())
- {
- difference.x = cell.x - position.x;
-
- if (glm::dot(difference, difference) <= radiusSquared)
- {
- pheromoneMatrix->deposit(cell, concentration);
- }
- }
- }
- }
|