//// Flow.cpp//// Created by Luis Cuenca on 1/21/2019.// Copyright 2019 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "Flow.h"#include "Rig.h"#include "AnimSkeleton.h" const std::map<QString, FlowPhysicsSettings> PRESET_FLOW_DATA = { { "hair", FlowPhysicsSettings() },{ "skirt", FlowPhysicsSettings(true, 1.0f, DEFAULT_GRAVITY, 0.65f, 0.8f, 0.45f, 0.01f) },{ "breast", FlowPhysicsSettings(true, 1.0f, DEFAULT_GRAVITY, 0.65f, 0.8f, 0.45f, 0.01f) } }; const std::map<QString, FlowCollisionSettings> PRESET_COLLISION_DATA = { { "Spine2", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.2f, 0.0f), 0.14f) }, { "LeftArm", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.02f, 0.0f), 0.05f) }, { "RightArm", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.02f, 0.0f), 0.05f) }, { "HeadTop_End", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, -0.15f, 0.0f), 0.09f) }}; FlowCollisionSphere::FlowCollisionSphere(const int& jointIndex, const FlowCollisionSettings& settings, bool isTouch) { _jointIndex = jointIndex; _radius = _initialRadius = settings._radius; _offset = _initialOffset = settings._offset; _entityID = settings._entityID; _isTouch = isTouch;} FlowCollisionResult FlowCollisionSphere::computeSphereCollision(const glm::vec3& point, float radius) const { FlowCollisionResult result; auto centerToJoint = point - _position; result._distance = glm::length(centerToJoint) - radius; result._offset = _radius - result._distance; result._normal = glm::normalize(centerToJoint); result._radius = _radius; result._position = _position; return result;} FlowCollisionResult FlowCollisionSphere::checkSegmentCollision(const glm::vec3& point1, const glm::vec3& point2, const FlowCollisionResult& collisionResult1, const FlowCollisionResult& collisionResult2) { FlowCollisionResult result; auto segment = point2 - point1; auto segmentLength = glm::length(segment); auto maxDistance = glm::sqrt(powf(collisionResult1._radius, 2.0f) + powf(segmentLength, 2.0f)); if (collisionResult1._distance < maxDistance && collisionResult2._distance < maxDistance) { float segmentPercentage = collisionResult1._distance / (collisionResult1._distance + collisionResult2._distance); glm::vec3 collisionPoint = point1 + segment * segmentPercentage; glm::vec3 centerToSegment = collisionPoint - _position; float distance = glm::length(centerToSegment); if (distance < _radius) { result._offset = _radius - distance; result._position = _position; result._radius = _radius; result._normal = glm::normalize(centerToSegment); result._distance = distance; } } return result;} void FlowCollisionSystem::addCollisionSphere(int jointIndex, const FlowCollisionSettings& settings, const glm::vec3& position, bool isSelfCollision, bool isTouch) { auto collision = FlowCollisionSphere(jointIndex, settings, isTouch); collision.setPosition(position); if (isSelfCollision) { if (!isTouch) { _selfCollisions.push_back(collision); } else { _selfTouchCollisions.push_back(collision); } } else { _othersCollisions.push_back(collision); }}; void FlowCollisionSystem::resetCollisions() { _allCollisions.clear(); _othersCollisions.clear(); _selfTouchCollisions.clear(); _selfCollisions.clear();} FlowCollisionResult FlowCollisionSystem::computeCollision(const std::vector<FlowCollisionResult> collisions) { FlowCollisionResult result; if (collisions.size() > 1) { for (size_t i = 0; i < collisions.size(); i++) { result._offset += collisions[i]._offset; result._normal = result._normal + collisions[i]._normal * collisions[i]._distance; result._position = result._position + collisions[i]._position; result._radius += collisions[i]._radius; result._distance += collisions[i]._distance; } result._offset = result._offset / collisions.size(); result._radius = 0.5f * glm::length(result._normal); result._normal = glm::normalize(result._normal); result._position = result._position / (float)collisions.size(); result._distance = result._distance / collisions.size(); } else if (collisions.size() == 1) { result = collisions[0]; } result._count = (int)collisions.size(); return result;}; void FlowCollisionSystem::setScale(float scale) { _scale = scale; for (size_t j = 0; j < _selfCollisions.size(); j++) { _selfCollisions[j]._radius = _selfCollisions[j]._initialRadius * scale; _selfCollisions[j]._offset = _selfCollisions[j]._initialOffset * scale; } for (size_t j = 0; j < _selfTouchCollisions.size(); j++) { _selfTouchCollisions[j]._radius = _selfTouchCollisions[j]._initialRadius * scale; _selfTouchCollisions[j]._offset = _selfTouchCollisions[j]._initialOffset * scale; }}; std::vector<FlowCollisionResult> FlowCollisionSystem::checkFlowThreadCollisions(FlowThread* flowThread) { std::vector<std::vector<FlowCollisionResult>> FlowThreadResults; FlowThreadResults.resize(flowThread->_joints.size()); for (size_t j = 0; j < _allCollisions.size(); j++) { FlowCollisionSphere &sphere = _allCollisions[j]; FlowCollisionResult rootCollision = sphere.computeSphereCollision(flowThread->_positions[0], flowThread->_radius); std::vector<FlowCollisionResult> collisionData = { rootCollision }; bool tooFar = rootCollision._distance >(flowThread->_length + rootCollision._radius); FlowCollisionResult nextCollision; if (!tooFar) { if (sphere._isTouch) { for (size_t i = 1; i < flowThread->_joints.size(); i++) { auto prevCollision = collisionData[i - 1]; nextCollision = _allCollisions[j].computeSphereCollision(flowThread->_positions[i], flowThread->_radius); collisionData.push_back(nextCollision); if (prevCollision._offset > 0.0f) { if (i == 1) { FlowThreadResults[i - 1].push_back(prevCollision); } } else if (nextCollision._offset > 0.0f) { FlowThreadResults[i].push_back(nextCollision); } else { FlowCollisionResult segmentCollision = _allCollisions[j].checkSegmentCollision(flowThread->_positions[i - 1], flowThread->_positions[i], prevCollision, nextCollision); if (segmentCollision._offset > 0) { FlowThreadResults[i - 1].push_back(segmentCollision); FlowThreadResults[i].push_back(segmentCollision); } } } } else { if (rootCollision._offset > 0.0f) { FlowThreadResults[0].push_back(rootCollision); } for (size_t i = 1; i < flowThread->_joints.size(); i++) { nextCollision = _allCollisions[j].computeSphereCollision(flowThread->_positions[i], flowThread->_radius); if (nextCollision._offset > 0.0f) { FlowThreadResults[i].push_back(nextCollision); } } } } } std::vector<FlowCollisionResult> results; for (size_t i = 0; i < flowThread->_joints.size(); i++) { results.push_back(computeCollision(FlowThreadResults[i])); } return results;}; FlowCollisionSettings FlowCollisionSystem::getCollisionSettingsByJoint(int jointIndex) { for (auto &collision : _selfCollisions) { if (collision._jointIndex == jointIndex) { return FlowCollisionSettings(collision._entityID, FlowCollisionType::CollisionSphere, collision._initialOffset, collision._initialRadius); } } return FlowCollisionSettings();}void FlowCollisionSystem::setCollisionSettingsByJoint(int jointIndex, const FlowCollisionSettings& settings) { for (auto &collision : _selfCollisions) { if (collision._jointIndex == jointIndex) { collision._initialRadius = settings._radius; collision._initialOffset = settings._offset; collision._radius = _scale * settings._radius; collision._offset = _scale * settings._offset; } }}void FlowCollisionSystem::prepareCollisions() { _allCollisions.clear(); _allCollisions.insert(_allCollisions.end(), _selfCollisions.begin(), _selfCollisions.end()); _allCollisions.insert(_allCollisions.end(), _othersCollisions.begin(), _othersCollisions.end()); _allCollisions.insert(_allCollisions.end(), _selfTouchCollisions.begin(), _selfTouchCollisions.end()); _othersCollisions.clear();} FlowNode::FlowNode(const glm::vec3& initialPosition, FlowPhysicsSettings settings) { _initialPosition = _previousPosition = _currentPosition = initialPosition; _initialRadius = settings._radius;} void FlowNode::update(float deltaTime, const glm::vec3& accelerationOffset) { // limit to 20fps so the solver doesn't blow up when the game is minimised deltaTime = std::min(0.05f, deltaTime); _acceleration = glm::vec3(0.0f, _settings._gravity, 0.0f); _previousVelocity = _currentVelocity; _currentVelocity = _currentPosition - _previousPosition; _previousPosition = _currentPosition; if (!_anchored) { // Add inertia const float FPS = 60.0f; float timeRatio = _scale * (FPS * deltaTime); float invertedTimeRatio = timeRatio > 0.0f ? 1.0f / timeRatio : 1.0f; auto deltaVelocity = _previousVelocity - _currentVelocity; auto centrifugeVector = glm::length(deltaVelocity) != 0.0f ? glm::normalize(deltaVelocity) : glm::vec3(); _acceleration = _acceleration + centrifugeVector * _settings._inertia * glm::length(_currentVelocity) * invertedTimeRatio; // Add offset _acceleration += accelerationOffset; float accelerationFactor = powf(_settings._delta, 2.0f) * timeRatio; glm::vec3 deltaAcceleration = _acceleration * accelerationFactor; // Calculate new position _currentPosition = _currentPosition + (_currentVelocity * _settings._damping) + deltaAcceleration; // This only seems to happen on a single timestep - usually after teleporting, // where the velocity blows up since it's taken from a position delta. // Clearing the bad current state lets the solver continue working with a finite delta. if ( !std::isfinite(_currentPosition.x) || !std::isfinite(_currentPosition.y) || !std::isfinite(_currentPosition.z) ) { _acceleration = {}; _currentVelocity = {}; _currentPosition = _initialPosition; } } else { _acceleration = glm::vec3(0.0f); _currentVelocity = glm::vec3(0.0f); }} void FlowNode::solve(const glm::vec3& constrainPoint, float maxDistance, const FlowCollisionResult& collision) { solveConstraints(constrainPoint, maxDistance); solveCollisions(collision);}; void FlowNode::solveConstraints(const glm::vec3& constrainPoint, float maxDistance) { glm::vec3 constrainVector = _currentPosition - constrainPoint; float difference = maxDistance / glm::length(constrainVector); _currentPosition = difference < 1.0f ? constrainPoint + constrainVector * difference : _currentPosition;}; void FlowNode::solveCollisions(const FlowCollisionResult& collision) { _colliding = collision._offset > 0.0f; _collision = collision; if (_colliding) { _currentPosition = _currentPosition + collision._normal * collision._offset; _previousCollision = collision; } else { _previousCollision = FlowCollisionResult(); }}; FlowJoint::FlowJoint(int jointIndex, int parentIndex, int childIndex, const QString& name, const QString& group, const FlowPhysicsSettings& settings) { _index = jointIndex; _name = name; _group = group; _childIndex = childIndex; _parentIndex = parentIndex; FlowNode(glm::vec3(), settings);}; void FlowJoint::setInitialData(const glm::vec3& initialPosition, const glm::vec3& initialTranslation, const glm::quat& initialRotation, const glm::vec3& parentPosition) { _initialPosition = initialPosition; _previousPosition = initialPosition; _currentPosition = initialPosition; _initialTranslation = initialTranslation; _currentRotation = initialRotation; _initialRotation = initialRotation; _translationDirection = glm::normalize(_initialTranslation); _parentPosition = parentPosition; _initialLength = _length = glm::length(_initialPosition - parentPosition);} void FlowJoint::setUpdatedData(const glm::vec3& updatedPosition, const glm::vec3& updatedTranslation, const glm::quat& updatedRotation, const glm::vec3& parentPosition, const glm::quat& parentWorldRotation) { _updatedPosition = updatedPosition; _updatedRotation = updatedRotation; _updatedTranslation = updatedTranslation; _parentPosition = parentPosition; _parentWorldRotation = parentWorldRotation;} void FlowJoint::setRecoveryPosition(const glm::vec3& recoveryPosition) { _recoveryPosition = recoveryPosition; _applyRecovery = true;} void FlowJoint::update(float deltaTime) { if (_settings._active) { glm::vec3 accelerationOffset = glm::vec3(0.0f); if (_settings._stiffness > 0.0f) { glm::vec3 recoveryVector = _recoveryPosition - _currentPosition; float recoveryFactor = powf(_settings._stiffness, 3.0f); accelerationOffset = recoveryVector * recoveryFactor; } FlowNode::update(deltaTime, accelerationOffset); if (_anchored) { if (!_isHelper) { _currentPosition = _updatedPosition; } else { _currentPosition = _parentPosition; } } }}; void FlowJoint::setScale(float scale, bool initScale) { if (initScale) { _initialLength = _length / scale; } _settings._radius = _initialRadius * scale; _length = _initialLength * scale; _scale = scale;} void FlowJoint::solve(const FlowCollisionResult& collision) { FlowNode::solve(_parentPosition, _length, collision);}; void FlowJoint::toHelperJoint(const glm::vec3& initialPosition, float length) { _initialPosition = initialPosition; _isHelper = true; _length = length;} FlowThread::FlowThread(int rootIndex, std::map<int, FlowJoint>* joints, float rigScale) { _rigScale = rigScale; _jointsPointer = joints; computeFlowThread(rootIndex);} void FlowThread::resetLength() { _length = 0.0f; for (size_t i = 1; i < _joints.size(); i++) { int index = _joints[i]; _length += _jointsPointer->at(index)._length; }} void FlowThread::computeFlowThread(int rootIndex) { int parentIndex = rootIndex; if (_jointsPointer->size() == 0) { return; } int childIndex = _jointsPointer->at(parentIndex)._childIndex; std::vector<int> indexes = { parentIndex }; for (size_t i = 0; i < _jointsPointer->size(); i++) { if (childIndex > -1) { indexes.push_back(childIndex); childIndex = _jointsPointer->at(childIndex)._childIndex; } else { break; } } _length = 0.0f; for (size_t i = 0; i < indexes.size(); i++) { int index = indexes[i]; _joints.push_back(index); if (i > 0) { _length += _jointsPointer->at(index)._length; } }}; void FlowThread::computeRecovery() { int parentIndex = _joints[0]; auto parentJoint = _jointsPointer->at(parentIndex); _jointsPointer->at(parentIndex)._recoveryPosition = parentJoint._recoveryPosition = parentJoint._currentPosition; for (size_t i = 1; i < _joints.size(); i++) { glm::quat parentRotation = parentJoint._parentWorldRotation * parentJoint._initialRotation; auto joint = _jointsPointer->at(_joints[i]); _jointsPointer->at(_joints[i])._recoveryPosition = joint._recoveryPosition = parentJoint._recoveryPosition + (parentRotation * (joint._initialTranslation * _rigScale)); parentJoint = joint; }}; void FlowThread::update(float deltaTime) { _positions.clear(); auto &firstJoint = _jointsPointer->at(_joints[0]); _radius = firstJoint._settings._radius; computeRecovery(); for (size_t i = 0; i < _joints.size(); i++) { auto &joint = _jointsPointer->at(_joints[i]); joint.update(deltaTime); _positions.push_back(joint._currentPosition); }}; void FlowThread::solve(FlowCollisionSystem& collisionSystem) { if (collisionSystem.getActive()) { auto bodyCollisions = collisionSystem.checkFlowThreadCollisions(this); for (size_t i = 0; i < _joints.size(); i++) { int index = _joints[i]; _jointsPointer->at(index).solve(bodyCollisions[i]); } } else { for (size_t i = 0; i < _joints.size(); i++) { int index = _joints[i]; _jointsPointer->at(index).solve(FlowCollisionResult()); } }}; void FlowThread::computeJointRotations() { auto pos0 = _rootFramePositions[0]; auto pos1 = _rootFramePositions[1]; auto joint0 = _jointsPointer->at(_joints[0]); auto joint1 = _jointsPointer->at(_joints[1]); auto initial_pos1 = pos0 + (joint0._initialRotation * (joint1._initialTranslation * _rigScale)); auto vec0 = initial_pos1 - pos0; auto vec1 = pos1 - pos0; auto delta = rotationBetween(vec0, vec1); joint0._currentRotation = _jointsPointer->at(_joints[0])._currentRotation = delta * joint0._initialRotation; for (size_t i = 1; i < _joints.size() - 1; i++) { auto nextJoint = _jointsPointer->at(_joints[i + 1]); for (size_t j = i; j < _joints.size(); j++) { _rootFramePositions[j] = glm::inverse(joint0._currentRotation) * _rootFramePositions[j] - (joint0._initialTranslation * _rigScale); } pos0 = _rootFramePositions[i]; pos1 = _rootFramePositions[i + 1]; initial_pos1 = pos0 + joint1._initialRotation * (nextJoint._initialTranslation * _rigScale); vec0 = initial_pos1 - pos0; vec1 = pos1 - pos0; delta = rotationBetween(vec0, vec1); joint1._currentRotation = _jointsPointer->at(joint1._index)._currentRotation = delta * joint1._initialRotation; joint0 = joint1; joint1 = nextJoint; } } void FlowThread::setScale(float scale, bool initScale) { for (size_t i = 0; i < _joints.size(); i++) { auto &joint = _jointsPointer->at(_joints[i]); joint.setScale(scale, initScale); } resetLength();} FlowThread& FlowThread::operator=(const FlowThread& otherFlowThread) { _rigScale = otherFlowThread._rigScale; for (int jointIndex: otherFlowThread._joints) { auto& joint = otherFlowThread._jointsPointer->at(jointIndex); auto& myJoint = _jointsPointer->at(jointIndex); myJoint._acceleration = joint._acceleration; myJoint._currentPosition = joint._currentPosition; myJoint._currentRotation = joint._currentRotation; myJoint._currentVelocity = joint._currentVelocity; myJoint._length = joint._length; myJoint._parentPosition = joint._parentPosition; myJoint._parentWorldRotation = joint._parentWorldRotation; myJoint._previousPosition = joint._previousPosition; myJoint._previousVelocity = joint._previousVelocity; myJoint._scale = joint._scale; myJoint._translationDirection = joint._translationDirection; myJoint._updatedPosition = joint._updatedPosition; myJoint._updatedRotation = joint._updatedRotation; myJoint._updatedTranslation = joint._updatedTranslation; myJoint._isHelper = joint._isHelper; } return *this;} void Flow::calculateConstraints(const std::shared_ptr<AnimSkeleton>& skeleton, AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses) { cleanUp(); if (!skeleton) { return; } auto flowPrefix = FLOW_JOINT_PREFIX.toUpper(); auto simPrefix = SIM_JOINT_PREFIX.toUpper(); std::vector<int> handsIndices; _groupSettings.clear(); for (int i = 0; i < skeleton->getNumJoints(); i++) { auto name = skeleton->getJointName(i); if (std::find(HAND_COLLISION_JOINTS.begin(), HAND_COLLISION_JOINTS.end(), name) != HAND_COLLISION_JOINTS.end()) {Lines 1–500 of 834. Load more
// // Flow.cpp // // Created by Luis Cuenca on 1/21/2019. // Copyright 2019 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "Flow.h" #include "Rig.h" #include "AnimSkeleton.h" const std::map<QString, FlowPhysicsSettings> PRESET_FLOW_DATA = { { "hair", FlowPhysicsSettings() }, { "skirt", FlowPhysicsSettings(true, 1.0f, DEFAULT_GRAVITY, 0.65f, 0.8f, 0.45f, 0.01f) }, { "breast", FlowPhysicsSettings(true, 1.0f, DEFAULT_GRAVITY, 0.65f, 0.8f, 0.45f, 0.01f) } }; const std::map<QString, FlowCollisionSettings> PRESET_COLLISION_DATA = { { "Spine2", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.2f, 0.0f), 0.14f) }, { "LeftArm", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.02f, 0.0f), 0.05f) }, { "RightArm", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.02f, 0.0f), 0.05f) }, { "HeadTop_End", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, -0.15f, 0.0f), 0.09f) } }; FlowCollisionSphere::FlowCollisionSphere(const int& jointIndex, const FlowCollisionSettings& settings, bool isTouch) { _jointIndex = jointIndex; _radius = _initialRadius = settings._radius; _offset = _initialOffset = settings._offset; _entityID = settings._entityID; _isTouch = isTouch; } FlowCollisionResult FlowCollisionSphere::computeSphereCollision(const glm::vec3& point, float radius) const { FlowCollisionResult result; auto centerToJoint = point - _position; result._distance = glm::length(centerToJoint) - radius; result._offset = _radius - result._distance; result._normal = glm::normalize(centerToJoint); result._radius = _radius; result._position = _position; return result; } FlowCollisionResult FlowCollisionSphere::checkSegmentCollision(const glm::vec3& point1, const glm::vec3& point2, const FlowCollisionResult& collisionResult1, const FlowCollisionResult& collisionResult2) { FlowCollisionResult result; auto segment = point2 - point1; auto segmentLength = glm::length(segment); auto maxDistance = glm::sqrt(powf(collisionResult1._radius, 2.0f) + powf(segmentLength, 2.0f)); if (collisionResult1._distance < maxDistance && collisionResult2._distance < maxDistance) { float segmentPercentage = collisionResult1._distance / (collisionResult1._distance + collisionResult2._distance); glm::vec3 collisionPoint = point1 + segment * segmentPercentage; glm::vec3 centerToSegment = collisionPoint - _position; float distance = glm::length(centerToSegment); if (distance < _radius) { result._offset = _radius - distance; result._position = _position; result._radius = _radius; result._normal = glm::normalize(centerToSegment); result._distance = distance; } } return result; } void FlowCollisionSystem::addCollisionSphere(int jointIndex, const FlowCollisionSettings& settings, const glm::vec3& position, bool isSelfCollision, bool isTouch) { auto collision = FlowCollisionSphere(jointIndex, settings, isTouch); collision.setPosition(position); if (isSelfCollision) { if (!isTouch) { _selfCollisions.push_back(collision); } else { _selfTouchCollisions.push_back(collision); } } else { _othersCollisions.push_back(collision); } }; void FlowCollisionSystem::resetCollisions() { _allCollisions.clear(); _othersCollisions.clear(); _selfTouchCollisions.clear(); _selfCollisions.clear(); } FlowCollisionResult FlowCollisionSystem::computeCollision(const std::vector<FlowCollisionResult> collisions) { FlowCollisionResult result; if (collisions.size() > 1) { for (size_t i = 0; i < collisions.size(); i++) { result._offset += collisions[i]._offset; result._normal = result._normal + collisions[i]._normal * collisions[i]._distance; result._position = result._position + collisions[i]._position; result._radius += collisions[i]._radius; result._distance += collisions[i]._distance; } result._offset = result._offset / collisions.size(); result._radius = 0.5f * glm::length(result._normal); result._normal = glm::normalize(result._normal); result._position = result._position / (float)collisions.size(); result._distance = result._distance / collisions.size(); } else if (collisions.size() == 1) { result = collisions[0]; } result._count = (int)collisions.size(); return result; }; void FlowCollisionSystem::setScale(float scale) { _scale = scale; for (size_t j = 0; j < _selfCollisions.size(); j++) { _selfCollisions[j]._radius = _selfCollisions[j]._initialRadius * scale; _selfCollisions[j]._offset = _selfCollisions[j]._initialOffset * scale; } for (size_t j = 0; j < _selfTouchCollisions.size(); j++) { _selfTouchCollisions[j]._radius = _selfTouchCollisions[j]._initialRadius * scale; _selfTouchCollisions[j]._offset = _selfTouchCollisions[j]._initialOffset * scale; } }; std::vector<FlowCollisionResult> FlowCollisionSystem::checkFlowThreadCollisions(FlowThread* flowThread) { std::vector<std::vector<FlowCollisionResult>> FlowThreadResults; FlowThreadResults.resize(flowThread->_joints.size()); for (size_t j = 0; j < _allCollisions.size(); j++) { FlowCollisionSphere &sphere = _allCollisions[j]; FlowCollisionResult rootCollision = sphere.computeSphereCollision(flowThread->_positions[0], flowThread->_radius); std::vector<FlowCollisionResult> collisionData = { rootCollision }; bool tooFar = rootCollision._distance >(flowThread->_length + rootCollision._radius); FlowCollisionResult nextCollision; if (!tooFar) { if (sphere._isTouch) { for (size_t i = 1; i < flowThread->_joints.size(); i++) { auto prevCollision = collisionData[i - 1]; nextCollision = _allCollisions[j].computeSphereCollision(flowThread->_positions[i], flowThread->_radius); collisionData.push_back(nextCollision); if (prevCollision._offset > 0.0f) { if (i == 1) { FlowThreadResults[i - 1].push_back(prevCollision); } } else if (nextCollision._offset > 0.0f) { FlowThreadResults[i].push_back(nextCollision); } else { FlowCollisionResult segmentCollision = _allCollisions[j].checkSegmentCollision(flowThread->_positions[i - 1], flowThread->_positions[i], prevCollision, nextCollision); if (segmentCollision._offset > 0) { FlowThreadResults[i - 1].push_back(segmentCollision); FlowThreadResults[i].push_back(segmentCollision); } } } } else { if (rootCollision._offset > 0.0f) { FlowThreadResults[0].push_back(rootCollision); } for (size_t i = 1; i < flowThread->_joints.size(); i++) { nextCollision = _allCollisions[j].computeSphereCollision(flowThread->_positions[i], flowThread->_radius); if (nextCollision._offset > 0.0f) { FlowThreadResults[i].push_back(nextCollision); } } } } } std::vector<FlowCollisionResult> results; for (size_t i = 0; i < flowThread->_joints.size(); i++) { results.push_back(computeCollision(FlowThreadResults[i])); } return results; }; FlowCollisionSettings FlowCollisionSystem::getCollisionSettingsByJoint(int jointIndex) { for (auto &collision : _selfCollisions) { if (collision._jointIndex == jointIndex) { return FlowCollisionSettings(collision._entityID, FlowCollisionType::CollisionSphere, collision._initialOffset, collision._initialRadius); } } return FlowCollisionSettings(); } void FlowCollisionSystem::setCollisionSettingsByJoint(int jointIndex, const FlowCollisionSettings& settings) { for (auto &collision : _selfCollisions) { if (collision._jointIndex == jointIndex) { collision._initialRadius = settings._radius; collision._initialOffset = settings._offset; collision._radius = _scale * settings._radius; collision._offset = _scale * settings._offset; } } } void FlowCollisionSystem::prepareCollisions() { _allCollisions.clear(); _allCollisions.insert(_allCollisions.end(), _selfCollisions.begin(), _selfCollisions.end()); _allCollisions.insert(_allCollisions.end(), _othersCollisions.begin(), _othersCollisions.end()); _allCollisions.insert(_allCollisions.end(), _selfTouchCollisions.begin(), _selfTouchCollisions.end()); _othersCollisions.clear(); } FlowNode::FlowNode(const glm::vec3& initialPosition, FlowPhysicsSettings settings) { _initialPosition = _previousPosition = _currentPosition = initialPosition; _initialRadius = settings._radius; } void FlowNode::update(float deltaTime, const glm::vec3& accelerationOffset) { // limit to 20fps so the solver doesn't blow up when the game is minimised deltaTime = std::min(0.05f, deltaTime); _acceleration = glm::vec3(0.0f, _settings._gravity, 0.0f); _previousVelocity = _currentVelocity; _currentVelocity = _currentPosition - _previousPosition; _previousPosition = _currentPosition; if (!_anchored) { // Add inertia const float FPS = 60.0f; float timeRatio = _scale * (FPS * deltaTime); float invertedTimeRatio = timeRatio > 0.0f ? 1.0f / timeRatio : 1.0f; auto deltaVelocity = _previousVelocity - _currentVelocity; auto centrifugeVector = glm::length(deltaVelocity) != 0.0f ? glm::normalize(deltaVelocity) : glm::vec3(); _acceleration = _acceleration + centrifugeVector * _settings._inertia * glm::length(_currentVelocity) * invertedTimeRatio; // Add offset _acceleration += accelerationOffset; float accelerationFactor = powf(_settings._delta, 2.0f) * timeRatio; glm::vec3 deltaAcceleration = _acceleration * accelerationFactor; // Calculate new position _currentPosition = _currentPosition + (_currentVelocity * _settings._damping) + deltaAcceleration; // This only seems to happen on a single timestep - usually after teleporting, // where the velocity blows up since it's taken from a position delta. // Clearing the bad current state lets the solver continue working with a finite delta. if ( !std::isfinite(_currentPosition.x) || !std::isfinite(_currentPosition.y) || !std::isfinite(_currentPosition.z) ) { _acceleration = {}; _currentVelocity = {}; _currentPosition = _initialPosition; } } else { _acceleration = glm::vec3(0.0f); _currentVelocity = glm::vec3(0.0f); } } void FlowNode::solve(const glm::vec3& constrainPoint, float maxDistance, const FlowCollisionResult& collision) { solveConstraints(constrainPoint, maxDistance); solveCollisions(collision); }; void FlowNode::solveConstraints(const glm::vec3& constrainPoint, float maxDistance) { glm::vec3 constrainVector = _currentPosition - constrainPoint; float difference = maxDistance / glm::length(constrainVector); _currentPosition = difference < 1.0f ? constrainPoint + constrainVector * difference : _currentPosition; }; void FlowNode::solveCollisions(const FlowCollisionResult& collision) { _colliding = collision._offset > 0.0f; _collision = collision; if (_colliding) { _currentPosition = _currentPosition + collision._normal * collision._offset; _previousCollision = collision; } else { _previousCollision = FlowCollisionResult(); } }; FlowJoint::FlowJoint(int jointIndex, int parentIndex, int childIndex, const QString& name, const QString& group, const FlowPhysicsSettings& settings) { _index = jointIndex; _name = name; _group = group; _childIndex = childIndex; _parentIndex = parentIndex; FlowNode(glm::vec3(), settings); }; void FlowJoint::setInitialData(const glm::vec3& initialPosition, const glm::vec3& initialTranslation, const glm::quat& initialRotation, const glm::vec3& parentPosition) { _initialPosition = initialPosition; _previousPosition = initialPosition; _currentPosition = initialPosition; _initialTranslation = initialTranslation; _currentRotation = initialRotation; _initialRotation = initialRotation; _translationDirection = glm::normalize(_initialTranslation); _parentPosition = parentPosition; _initialLength = _length = glm::length(_initialPosition - parentPosition); } void FlowJoint::setUpdatedData(const glm::vec3& updatedPosition, const glm::vec3& updatedTranslation, const glm::quat& updatedRotation, const glm::vec3& parentPosition, const glm::quat& parentWorldRotation) { _updatedPosition = updatedPosition; _updatedRotation = updatedRotation; _updatedTranslation = updatedTranslation; _parentPosition = parentPosition; _parentWorldRotation = parentWorldRotation; } void FlowJoint::setRecoveryPosition(const glm::vec3& recoveryPosition) { _recoveryPosition = recoveryPosition; _applyRecovery = true; } void FlowJoint::update(float deltaTime) { if (_settings._active) { glm::vec3 accelerationOffset = glm::vec3(0.0f); if (_settings._stiffness > 0.0f) { glm::vec3 recoveryVector = _recoveryPosition - _currentPosition; float recoveryFactor = powf(_settings._stiffness, 3.0f); accelerationOffset = recoveryVector * recoveryFactor; } FlowNode::update(deltaTime, accelerationOffset); if (_anchored) { if (!_isHelper) { _currentPosition = _updatedPosition; } else { _currentPosition = _parentPosition; } } } }; void FlowJoint::setScale(float scale, bool initScale) { if (initScale) { _initialLength = _length / scale; } _settings._radius = _initialRadius * scale; _length = _initialLength * scale; _scale = scale; } void FlowJoint::solve(const FlowCollisionResult& collision) { FlowNode::solve(_parentPosition, _length, collision); }; void FlowJoint::toHelperJoint(const glm::vec3& initialPosition, float length) { _initialPosition = initialPosition; _isHelper = true; _length = length; } FlowThread::FlowThread(int rootIndex, std::map<int, FlowJoint>* joints, float rigScale) { _rigScale = rigScale; _jointsPointer = joints; computeFlowThread(rootIndex); } void FlowThread::resetLength() { _length = 0.0f; for (size_t i = 1; i < _joints.size(); i++) { int index = _joints[i]; _length += _jointsPointer->at(index)._length; } } void FlowThread::computeFlowThread(int rootIndex) { int parentIndex = rootIndex; if (_jointsPointer->size() == 0) { return; } int childIndex = _jointsPointer->at(parentIndex)._childIndex; std::vector<int> indexes = { parentIndex }; for (size_t i = 0; i < _jointsPointer->size(); i++) { if (childIndex > -1) { indexes.push_back(childIndex); childIndex = _jointsPointer->at(childIndex)._childIndex; } else { break; } } _length = 0.0f; for (size_t i = 0; i < indexes.size(); i++) { int index = indexes[i]; _joints.push_back(index); if (i > 0) { _length += _jointsPointer->at(index)._length; } } }; void FlowThread::computeRecovery() { int parentIndex = _joints[0]; auto parentJoint = _jointsPointer->at(parentIndex); _jointsPointer->at(parentIndex)._recoveryPosition = parentJoint._recoveryPosition = parentJoint._currentPosition; for (size_t i = 1; i < _joints.size(); i++) { glm::quat parentRotation = parentJoint._parentWorldRotation * parentJoint._initialRotation; auto joint = _jointsPointer->at(_joints[i]); _jointsPointer->at(_joints[i])._recoveryPosition = joint._recoveryPosition = parentJoint._recoveryPosition + (parentRotation * (joint._initialTranslation * _rigScale)); parentJoint = joint; } }; void FlowThread::update(float deltaTime) { _positions.clear(); auto &firstJoint = _jointsPointer->at(_joints[0]); _radius = firstJoint._settings._radius; computeRecovery(); for (size_t i = 0; i < _joints.size(); i++) { auto &joint = _jointsPointer->at(_joints[i]); joint.update(deltaTime); _positions.push_back(joint._currentPosition); } }; void FlowThread::solve(FlowCollisionSystem& collisionSystem) { if (collisionSystem.getActive()) { auto bodyCollisions = collisionSystem.checkFlowThreadCollisions(this); for (size_t i = 0; i < _joints.size(); i++) { int index = _joints[i]; _jointsPointer->at(index).solve(bodyCollisions[i]); } } else { for (size_t i = 0; i < _joints.size(); i++) { int index = _joints[i]; _jointsPointer->at(index).solve(FlowCollisionResult()); } } }; void FlowThread::computeJointRotations() { auto pos0 = _rootFramePositions[0]; auto pos1 = _rootFramePositions[1]; auto joint0 = _jointsPointer->at(_joints[0]); auto joint1 = _jointsPointer->at(_joints[1]); auto initial_pos1 = pos0 + (joint0._initialRotation * (joint1._initialTranslation * _rigScale)); auto vec0 = initial_pos1 - pos0; auto vec1 = pos1 - pos0; auto delta = rotationBetween(vec0, vec1); joint0._currentRotation = _jointsPointer->at(_joints[0])._currentRotation = delta * joint0._initialRotation; for (size_t i = 1; i < _joints.size() - 1; i++) { auto nextJoint = _jointsPointer->at(_joints[i + 1]); for (size_t j = i; j < _joints.size(); j++) { _rootFramePositions[j] = glm::inverse(joint0._currentRotation) * _rootFramePositions[j] - (joint0._initialTranslation * _rigScale); } pos0 = _rootFramePositions[i]; pos1 = _rootFramePositions[i + 1]; initial_pos1 = pos0 + joint1._initialRotation * (nextJoint._initialTranslation * _rigScale); vec0 = initial_pos1 - pos0; vec1 = pos1 - pos0; delta = rotationBetween(vec0, vec1); joint1._currentRotation = _jointsPointer->at(joint1._index)._currentRotation = delta * joint1._initialRotation; joint0 = joint1; joint1 = nextJoint; } } void FlowThread::setScale(float scale, bool initScale) { for (size_t i = 0; i < _joints.size(); i++) { auto &joint = _jointsPointer->at(_joints[i]); joint.setScale(scale, initScale); } resetLength(); } FlowThread& FlowThread::operator=(const FlowThread& otherFlowThread) { _rigScale = otherFlowThread._rigScale; for (int jointIndex: otherFlowThread._joints) { auto& joint = otherFlowThread._jointsPointer->at(jointIndex); auto& myJoint = _jointsPointer->at(jointIndex); myJoint._acceleration = joint._acceleration; myJoint._currentPosition = joint._currentPosition; myJoint._currentRotation = joint._currentRotation; myJoint._currentVelocity = joint._currentVelocity; myJoint._length = joint._length; myJoint._parentPosition = joint._parentPosition; myJoint._parentWorldRotation = joint._parentWorldRotation; myJoint._previousPosition = joint._previousPosition; myJoint._previousVelocity = joint._previousVelocity; myJoint._scale = joint._scale; myJoint._translationDirection = joint._translationDirection; myJoint._updatedPosition = joint._updatedPosition; myJoint._updatedRotation = joint._updatedRotation; myJoint._updatedTranslation = joint._updatedTranslation; myJoint._isHelper = joint._isHelper; } return *this; } void Flow::calculateConstraints(const std::shared_ptr<AnimSkeleton>& skeleton, AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses) { cleanUp(); if (!skeleton) { return; } auto flowPrefix = FLOW_JOINT_PREFIX.toUpper(); auto simPrefix = SIM_JOINT_PREFIX.toUpper(); std::vector<int> handsIndices; _groupSettings.clear(); for (int i = 0; i < skeleton->getNumJoints(); i++) { auto name = skeleton->getJointName(i); if (std::find(HAND_COLLISION_JOINTS.begin(), HAND_COLLISION_JOINTS.end(), name) != HAND_COLLISION_JOINTS.end()) {