💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

396 lines
12 KiB

/*
* Copyright (C) 2017-2019 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "forceps.hpp"
#include "camera-rig.hpp"
#include <stdexcept>
Forceps::Forceps(const Model* model, Animator* animator):
wasActive(false)
{
// Allocate pose and initialize to bind pose
pose = new Pose(model->getSkeleton());
pose->reset();
// Setup model instance
modelInstance.setModel(model);
modelInstance.setPose(pose);
// Find pinch animation
pinchClip = model->getSkeleton()->getAnimationClip("pinch");
if (!pinchClip)
{
throw std::runtime_error("Forceps pinch animation clip not found.");
}
// Find release animation
releaseClip = model->getSkeleton()->getAnimationClip("release");
if (!releaseClip)
{
throw std::runtime_error("Forceps release animation clip not found.");
}
// Scale animation speed
float pinchDuration = 0.1f;
float releaseDuration = 0.05f;
float pinchSpeed = std::get<1>(pinchClip->getTimeFrame()) / pinchDuration;
float releaseSpeed = std::get<1>(releaseClip->getTimeFrame()) / releaseDuration;
std::cout << std::get<1>(pinchClip->getTimeFrame()) << std::endl;
std::cout << std::get<1>(releaseClip->getTimeFrame()) << std::endl;
// Setup pinch animation callbacks
pinchAnimation.setSpeed(pinchSpeed);
pinchAnimation.setTimeFrame(pinchClip->getTimeFrame());
pinchAnimation.setClip(pinchClip);
pinchAnimation.setAnimateCallback
(
[this](std::size_t id, const Transform& transform)
{
this->pose->setRelativeTransform(id, transform);
}
);
pinchAnimation.setEndCallback
(
[this]()
{
this->pinched = true;
}
);
// Setup release animation callbacks
releaseAnimation.setSpeed(releaseSpeed);
releaseAnimation.setTimeFrame(releaseClip->getTimeFrame());
releaseAnimation.setClip(releaseClip);
releaseAnimation.setAnimateCallback
(
[this](std::size_t id, const Transform& transform)
{
this->pose->setRelativeTransform(id, transform);
}
);
hoverDistance = 1.0f;
// Setup timing
float descentDuration = 0.125f;
float ascentDuration = 0.125f;
/*
// 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;
for (std::size_t i = 0; i < pinchClip->getChannelCount(); ++i)
{
const AnimationChannel<Transform>* channel = pinchClip->getChannelByIndex(i);
pose->setRelativeTransform(channel->getChannelID(), *std::get<1>(channel->getKeyframe(0)));
}
pose->concatenate();
animator->addAnimation(&pinchAnimation);
animator->addAnimation(&releaseAnimation);
}
Forceps::~Forceps()
{
delete pose;
}
void Forceps::update(float dt)
{
// Determine distance from pick point
float forcepsDistance = hoverDistance;
Quaternion alignment = glm::angleAxis(orbitCam->getAzimuth(), Vector3(0, 1, 0));
Quaternion tilt = glm::angleAxis(glm::radians(15.0f), Vector3(0, 0, -1));
tilt = tilt * glm::angleAxis(glm::radians(-70.0f), tilt * Vector3(0, 1, 0));
Quaternion rotation = glm::normalize(alignment * tilt);
Vector3 translation = pick + rotation * Vector3(0, forcepsDistance, 0);
// Set tool position
modelInstance.setTranslation(translation);
modelInstance.setRotation(rotation);
pose->concatenate();
if (active && !wasActive)
{
modelInstance.resetTweens();
modelInstance.setActive(true);
}
else if (!active && wasActive)
{
modelInstance.setActive(false);
}
wasActive = active;
/*
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 = orbitCam->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(orbitCam->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 = orbitCam->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;
}
}
*/
}
void Forceps::pinch()
{
releaseAnimation.stop();
pinchAnimation.rewind();
pinchAnimation.play();
/*
// 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 = orbitCam->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()
{
pinchAnimation.stop();
releaseAnimation.rewind();
releaseAnimation.play();
/*
// 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();
*/
}