💿🐜 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.

393 lines
11 KiB

  1. /*
  2. * Copyright (C) 2017-2019 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper Source Code.
  5. *
  6. * Antkeeper Source Code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper Source Code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "forceps.hpp"
  20. #include "camera-rig.hpp"
  21. #include <stdexcept>
  22. Forceps::Forceps(const Model* model, Animator* animator):
  23. wasActive(false)
  24. {
  25. // Allocate pose and initialize to bind pose
  26. pose = new Pose(model->getSkeleton());
  27. pose->reset();
  28. // Setup model instance
  29. modelInstance.setModel(model);
  30. modelInstance.setPose(pose);
  31. // Find pinch animation
  32. pinchClip = model->getSkeleton()->getAnimationClip("pinch");
  33. if (!pinchClip)
  34. {
  35. throw std::runtime_error("Forceps pinch animation clip not found.");
  36. }
  37. // Find release animation
  38. releaseClip = model->getSkeleton()->getAnimationClip("release");
  39. if (!releaseClip)
  40. {
  41. throw std::runtime_error("Forceps release animation clip not found.");
  42. }
  43. // Scale animation speed
  44. float pinchDuration = 0.1f;
  45. float releaseDuration = 0.05f;
  46. float pinchSpeed = std::get<1>(pinchClip->getTimeFrame()) / pinchDuration;
  47. float releaseSpeed = std::get<1>(releaseClip->getTimeFrame()) / releaseDuration;
  48. // Setup pinch animation callbacks
  49. pinchAnimation.setSpeed(pinchSpeed);
  50. pinchAnimation.setTimeFrame(pinchClip->getTimeFrame());
  51. pinchAnimation.setClip(pinchClip);
  52. pinchAnimation.setAnimateCallback
  53. (
  54. [this](std::size_t id, const Transform& transform)
  55. {
  56. this->pose->setRelativeTransform(id, transform);
  57. }
  58. );
  59. pinchAnimation.setEndCallback
  60. (
  61. [this]()
  62. {
  63. this->pinched = true;
  64. }
  65. );
  66. // Setup release animation callbacks
  67. releaseAnimation.setSpeed(releaseSpeed);
  68. releaseAnimation.setTimeFrame(releaseClip->getTimeFrame());
  69. releaseAnimation.setClip(releaseClip);
  70. releaseAnimation.setAnimateCallback
  71. (
  72. [this](std::size_t id, const Transform& transform)
  73. {
  74. this->pose->setRelativeTransform(id, transform);
  75. }
  76. );
  77. hoverDistance = 1.0f;
  78. // Setup timing
  79. float descentDuration = 0.125f;
  80. float ascentDuration = 0.125f;
  81. /*
  82. // Allocate tweener and and setup tweens
  83. tweener = new Tweener();
  84. descentTween = new Tween<float>(EaseFunction::OUT_CUBIC, 0.0f, descentDuration, hoverDistance, -hoverDistance);
  85. ascentTween = new Tween<float>(EaseFunction::IN_CUBIC, 0.0f, ascentDuration, 0.0f, hoverDistance);
  86. descentTween->setEndCallback(std::bind(&TweenBase::start, ascentTween));
  87. tweener->addTween(descentTween);
  88. tweener->addTween(ascentTween);
  89. */
  90. // Setup initial state
  91. state = Forceps::State::RELEASED;
  92. for (std::size_t i = 0; i < pinchClip->getChannelCount(); ++i)
  93. {
  94. const AnimationChannel<Transform>* channel = pinchClip->getChannelByIndex(i);
  95. pose->setRelativeTransform(channel->getChannelID(), *std::get<1>(channel->getKeyframe(0)));
  96. }
  97. pose->concatenate();
  98. animator->addAnimation(&pinchAnimation);
  99. animator->addAnimation(&releaseAnimation);
  100. }
  101. Forceps::~Forceps()
  102. {
  103. delete pose;
  104. }
  105. void Forceps::update(float dt)
  106. {
  107. // Determine distance from pick point
  108. float forcepsDistance = hoverDistance;
  109. Quaternion alignment = glm::angleAxis(orbitCam->getAzimuth(), Vector3(0, 1, 0));
  110. Quaternion tilt = glm::angleAxis(glm::radians(15.0f), Vector3(0, 0, -1));
  111. tilt = tilt * glm::angleAxis(glm::radians(-70.0f), tilt * Vector3(0, 1, 0));
  112. Quaternion rotation = glm::normalize(alignment * tilt);
  113. Vector3 translation = pick + rotation * Vector3(0, forcepsDistance, 0);
  114. // Set tool position
  115. modelInstance.setTranslation(translation);
  116. modelInstance.setRotation(rotation);
  117. pose->concatenate();
  118. if (active && !wasActive)
  119. {
  120. modelInstance.resetTweens();
  121. modelInstance.setActive(true);
  122. }
  123. else if (!active && wasActive)
  124. {
  125. modelInstance.setActive(false);
  126. }
  127. wasActive = active;
  128. /*
  129. if (state == Forceps::State::RELEASED)
  130. {
  131. }
  132. else if (state == Forceps::State::RELEASING)
  133. {
  134. // Perform release animation
  135. releaseAnimation->animate(pose, animationTime);
  136. pose->concatenate();
  137. // If release animation is finished
  138. if (animationTime >= releaseAnimation->getEndTime())
  139. {
  140. // Changed to released state
  141. state = Forceps::State::RELEASED;
  142. }
  143. }
  144. else if (state == Forceps::State::PINCHED)
  145. {
  146. if (!ascentTween->isStopped())
  147. {
  148. // Calculate interpolation factor
  149. float interpolationFactor = (ascentTween->getTweenValue() - ascentTween->getStartValue()) / ascentTween->getDeltaValue();
  150. // Form tilt quaternion
  151. //Quaternion tilt = glm::angleAxis(glm::radians(15.0f), Vector3(0, 0, -1));
  152. tilt = glm::angleAxis(glm::radians(15.0f), Vector3(0, 0, -1));
  153. // Project camera forward onto XZ plane
  154. Vector3 cameraForwardXZ = orbitCam->getCamera()->getForward();
  155. cameraForwardXZ.y = 0.0f;
  156. cameraForwardXZ = glm::normalize(cameraForwardXZ);
  157. // Form alignment quaternion
  158. //Quaternion alignment = glm::rotation(Vector3(0, 0, -1), cameraForwardXZ);
  159. alignment = glm::angleAxis(orbitCam->getAzimuth(), Vector3(0, 1, 0));
  160. // Calculate target rotation at the top of the ascentTween
  161. rotationTop = glm::normalize(alignment * tilt);
  162. // Interpolate between bottom and top rotations
  163. Quaternion interpolatedRotation = glm::normalize(glm::slerp(rotationBottom, rotationTop, interpolationFactor));
  164. // Set target translation at the top of the ascent
  165. translationTop = pick + rotationTop * Vector3(0, hoverDistance, 0);
  166. // Interpolate between bottom and top translations
  167. Vector3 interpolatedTranslation = glm::lerp(translationBottom, translationTop, interpolationFactor);
  168. // Update model instance transform
  169. modelInstance.setTranslation(interpolatedTranslation);
  170. modelInstance.setRotation(interpolatedRotation);
  171. }
  172. if (suspendedAnt != nullptr)
  173. {
  174. // Project forceps forward vector onto XZ plane
  175. Vector3 forward = glm::normalize(modelInstance.getRotation() * Vector3(0, 0, -1));
  176. forward.y = 0.0f;
  177. forward = glm::normalize(forward);
  178. // Calculate suspension quaternion
  179. Quaternion suspensionRotation = glm::normalize(glm::rotation(Vector3(0, 0, -1), ((flipRotation) ? -forward : forward)));
  180. // Suspend ant
  181. suspendedAnt->suspend(modelInstance.getTranslation(), suspensionRotation);
  182. }
  183. }
  184. else if (state == Forceps::State::PINCHING)
  185. {
  186. // Perform pinch animation
  187. pinchAnimation->animate(pose, animationTime);
  188. pose->concatenate();
  189. // Rotate to align forceps with ant
  190. if (targetedAnt != nullptr)
  191. {
  192. // Calculate interpolation factor
  193. float interpolationFactor = (descentTween->getTweenValue() - descentTween->getStartValue()) / descentTween->getDeltaValue();
  194. // Set target translation at the bottom of the descent
  195. translationBottom = targetedAnt->getPosition();
  196. // Interpolate between top and bottom translations
  197. Vector3 interpolatedTranslation = glm::lerp(translationTop, translationBottom, interpolationFactor);
  198. // Project camera forward onto XZ plane
  199. Vector3 cameraForwardXZ = orbitCam->getCamera()->getForward();
  200. cameraForwardXZ.y = 0.0f;
  201. cameraForwardXZ = glm::normalize(cameraForwardXZ);
  202. // Form tilt quaternion
  203. tilt = glm::angleAxis(glm::radians(15.0f), -cameraForwardXZ);
  204. // Project ant forward onto XZ plane
  205. Vector3 antForwardXZ = targetedAnt->getForward();
  206. antForwardXZ.y = 0.0f;
  207. antForwardXZ = glm::normalize(antForwardXZ);
  208. // Form alignment quaternion
  209. alignment = glm::rotation(Vector3(0, 0, -1), (flipRotation) ? antForwardXZ : -antForwardXZ);
  210. // Calculate target rotation at the bottom of the descent
  211. rotationBottom = glm::normalize(tilt * alignment);
  212. // Interpolate between top and bottom rotations
  213. Quaternion interpolatedRotation = glm::normalize(glm::slerp(rotationTop, rotationBottom, interpolationFactor));
  214. // Update model instance transform
  215. modelInstance.setTranslation(interpolatedTranslation);
  216. modelInstance.setRotation(interpolatedRotation);
  217. }
  218. // If pinch animation is finished
  219. if (animationTime >= pinchAnimation->getEndTime() && descentTween->isStopped())
  220. {
  221. // If an ant was targeted
  222. if (targetedAnt != nullptr)
  223. {
  224. // Suspend targeted ant
  225. suspendedAnt = targetedAnt;
  226. suspendedAnt->setState(Ant::State::SUSPENDED);
  227. //suspendedAnt->suspend(modelInstance.getTranslation());
  228. targetedAnt = nullptr;
  229. }
  230. // Change to pinched state
  231. state = Forceps::State::PINCHED;
  232. }
  233. }
  234. */
  235. }
  236. void Forceps::pinch()
  237. {
  238. releaseAnimation.stop();
  239. pinchAnimation.rewind();
  240. pinchAnimation.play();
  241. /*
  242. // Change state to pinching
  243. state = Forceps::State::PINCHING;
  244. animationTime = 0.0f;
  245. if (colony != nullptr)
  246. {
  247. // Target nearest ant in pinching radius
  248. Sphere pinchingBounds = Sphere(pick, 0.35f);
  249. // Build a list of ants which intersect the pinching bounds
  250. std::list<Agent*> ants;
  251. colony->queryAnts(pinchingBounds, &ants);
  252. // Target ant closest to the center of the pinching bounds
  253. float closestDistance = std::numeric_limits<float>::infinity();
  254. for (Agent* agent: ants)
  255. {
  256. Ant* ant = static_cast<Ant*>(agent);
  257. Vector3 difference = ant->getPosition() - pinchingBounds.getCenter();
  258. float distanceSquared = glm::dot(difference, difference);
  259. if (distanceSquared < closestDistance)
  260. {
  261. closestDistance = distanceSquared;
  262. targetedAnt = ant;
  263. }
  264. }
  265. if (targetedAnt != nullptr)
  266. {
  267. // Start descent tweener
  268. descentTween->start();
  269. // Save translation & rotation
  270. translationTop = modelInstance.getTranslation();
  271. rotationTop = modelInstance.getRotation();
  272. // Project ant forward onto XZ plane
  273. Vector3 antForwardXZ = targetedAnt->getForward();
  274. antForwardXZ.y = 0.0f;
  275. antForwardXZ = glm::normalize(antForwardXZ);
  276. // Project camera forward onto XZ plane
  277. Vector3 cameraForwardXZ = orbitCam->getCamera()->getForward();
  278. cameraForwardXZ.y = 0.0f;
  279. cameraForwardXZ = glm::normalize(cameraForwardXZ);
  280. // Find angle between ant and camera on XZ plane
  281. float angle = std::acos(glm::dot(cameraForwardXZ, antForwardXZ));
  282. // Determine direction to rotate
  283. flipRotation = (angle > glm::radians(90.0f));
  284. }
  285. }
  286. */
  287. }
  288. void Forceps::release()
  289. {
  290. pinchAnimation.stop();
  291. releaseAnimation.rewind();
  292. releaseAnimation.play();
  293. /*
  294. // Change state to releasing
  295. state = Forceps::State::RELEASING;
  296. animationTime = 0.0f;
  297. targetedAnt = nullptr;
  298. if (suspendedAnt != nullptr)
  299. {
  300. Ray pickingRay;
  301. pickingRay.origin = pick + Vector3(0, 1, 0);
  302. pickingRay.direction = Vector3(0, -1, 0);
  303. const std::vector<Navmesh::Triangle*>* navmeshTriangles = navmesh->getTriangles();
  304. for (Navmesh::Triangle* triangle: *navmeshTriangles)
  305. {
  306. auto result = intersects(pickingRay, triangle);
  307. if (std::get<0>(result))
  308. {
  309. Vector3 barycentricPosition = Vector3(std::get<2>(result), std::get<3>(result), 1.0f - std::get<2>(result) - std::get<3>(result));
  310. suspendedAnt->setPosition(triangle, barycentricPosition);
  311. break;
  312. }
  313. }
  314. // Release suspended ant
  315. suspendedAnt->setState(Ant::State::WANDER);
  316. suspendedAnt = nullptr;
  317. }
  318. // Reset tweens
  319. descentTween->reset();
  320. descentTween->stop();
  321. ascentTween->reset();
  322. ascentTween->stop();
  323. */
  324. }