overte/libraries/animation/src/AnimPoleVectorConstraint.cpp
Anthony Thibault 39b4eaac34 Revert "AnimPose operator* optimizations"
This reverts commit 569bef50fd.
2019-02-14 16:13:00 -08:00

244 lines
9.2 KiB
C++

//
// AnimPoleVectorConstraint.cpp
//
// Created by Anthony J. Thibault on 5/12/18.
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimPoleVectorConstraint.h"
#include "AnimationLogging.h"
#include "AnimUtil.h"
#include "GLMHelpers.h"
const float FRAMES_PER_SECOND = 30.0f;
const float INTERP_DURATION = 6.0f;
AnimPoleVectorConstraint::AnimPoleVectorConstraint(const QString& id, bool enabled, glm::vec3 referenceVector,
const QString& baseJointName, const QString& midJointName, const QString& tipJointName,
const QString& enabledVar, const QString& poleVectorVar) :
AnimNode(AnimNode::Type::PoleVectorConstraint, id),
_enabled(enabled),
_referenceVector(referenceVector),
_baseJointName(baseJointName),
_midJointName(midJointName),
_tipJointName(tipJointName),
_enabledVar(enabledVar),
_poleVectorVar(poleVectorVar) {
}
AnimPoleVectorConstraint::~AnimPoleVectorConstraint() {
}
const AnimPoseVec& AnimPoleVectorConstraint::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
assert(_children.size() == 1);
if (_children.size() != 1) {
return _poses;
}
// evalute underPoses
AnimPoseVec underPoses = _children[0]->evaluate(animVars, context, dt, triggersOut);
// if we don't have a skeleton, or jointName lookup failed.
if (!_skeleton || _baseJointIndex == -1 || _midJointIndex == -1 || _tipJointIndex == -1 || underPoses.size() == 0) {
// pass underPoses through unmodified.
_poses = underPoses;
return _poses;
}
// guard against size changes
if (underPoses.size() != _poses.size()) {
_poses = underPoses;
}
// Look up poleVector from animVars, make sure to convert into geom space.
glm::vec3 poleVector = animVars.lookupRigToGeometryVector(_poleVectorVar, Vectors::UNIT_Z);
// determine if we should interpolate
bool enabled = animVars.lookup(_enabledVar, _enabled);
const float MIN_LENGTH = 1.0e-4f;
if (glm::length(poleVector) < MIN_LENGTH) {
enabled = false;
}
if (enabled != _enabled) {
AnimChain poseChain;
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
if (enabled) {
beginInterp(InterpType::SnapshotToSolve, poseChain);
} else {
beginInterp(InterpType::SnapshotToUnderPoses, poseChain);
}
}
_enabled = enabled;
// don't build chains or do IK if we are disbled & not interping.
if (_interpType == InterpType::None && !enabled) {
_poses = underPoses;
return _poses;
}
// compute chain
AnimChain underChain;
underChain.buildFromRelativePoses(_skeleton, underPoses, _tipJointIndex);
AnimChain ikChain = underChain;
AnimPose baseParentPose = ikChain.getAbsolutePoseFromJointIndex(_baseParentJointIndex);
AnimPose basePose = ikChain.getAbsolutePoseFromJointIndex(_baseJointIndex);
AnimPose midPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
// Look up refVector from animVars, make sure to convert into geom space.
glm::vec3 refVector = midPose.xformVectorFast(_referenceVector);
float refVectorLength = glm::length(refVector);
glm::vec3 axis = basePose.trans() - tipPose.trans();
float axisLength = glm::length(axis);
glm::vec3 unitAxis = axis / axisLength;
glm::vec3 sideVector = glm::cross(unitAxis, refVector);
float sideVectorLength = glm::length(sideVector);
// project refVector onto axis plane
glm::vec3 refVectorProj = refVector - glm::dot(refVector, unitAxis) * unitAxis;
float refVectorProjLength = glm::length(refVectorProj);
// project poleVector on plane formed by axis.
glm::vec3 poleVectorProj = poleVector - glm::dot(poleVector, unitAxis) * unitAxis;
float poleVectorProjLength = glm::length(poleVectorProj);
// double check for zero length vectors or vectors parallel to rotaiton axis.
if (axisLength > MIN_LENGTH && refVectorLength > MIN_LENGTH && sideVectorLength > MIN_LENGTH &&
refVectorProjLength > MIN_LENGTH && poleVectorProjLength > MIN_LENGTH) {
float dot = glm::clamp(glm::dot(refVectorProj / refVectorProjLength, poleVectorProj / poleVectorProjLength), 0.0f, 1.0f);
float sideDot = glm::dot(poleVector, sideVector);
float theta = copysignf(1.0f, sideDot) * acosf(dot);
glm::quat deltaRot = glm::angleAxis(theta, unitAxis);
// transform result back into parent relative frame.
glm::quat relBaseRot = glm::inverse(baseParentPose.rot()) * deltaRot * basePose.rot();
ikChain.setRelativePoseAtJointIndex(_baseJointIndex, AnimPose(relBaseRot, underPoses[_baseJointIndex].trans()));
glm::quat relTipRot = glm::inverse(midPose.rot()) * glm::inverse(deltaRot) * tipPose.rot();
ikChain.setRelativePoseAtJointIndex(_tipJointIndex, AnimPose(relTipRot, underPoses[_tipJointIndex].trans()));
}
// start off by initializing output poses with the underPoses
_poses = underPoses;
// apply smooth interpolation
if (_interpType != InterpType::None) {
_interpAlpha += _interpAlphaVel * dt;
if (_interpAlpha < 1.0f) {
AnimChain interpChain;
if (_interpType == InterpType::SnapshotToUnderPoses) {
interpChain = underChain;
interpChain.blend(_snapshotChain, _interpAlpha);
} else if (_interpType == InterpType::SnapshotToSolve) {
interpChain = ikChain;
interpChain.blend(_snapshotChain, _interpAlpha);
}
// copy interpChain into _poses
interpChain.outputRelativePoses(_poses);
} else {
// interpolation complete
_interpType = InterpType::None;
}
}
if (_interpType == InterpType::None) {
if (enabled) {
// copy chain into _poses
ikChain.outputRelativePoses(_poses);
} else {
// copy under chain into _poses
underChain.outputRelativePoses(_poses);
}
}
if (context.getEnableDebugDrawIKChains()) {
if (_interpType == InterpType::None && enabled) {
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
ikChain.debugDraw(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix(), BLUE);
}
}
if (context.getEnableDebugDrawIKChains()) {
if (enabled) {
const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
const glm::vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
const glm::vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f);
const glm::vec4 YELLOW(1.0f, 0.0f, 1.0f, 1.0f);
const float VECTOR_LENGTH = 0.5f;
glm::mat4 geomToWorld = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
// draw the pole
glm::vec3 start = transformPoint(geomToWorld, basePose.trans());
glm::vec3 end = transformPoint(geomToWorld, tipPose.trans());
DebugDraw::getInstance().drawRay(start, end, CYAN);
// draw the poleVector
glm::vec3 midPoint = 0.5f * (start + end);
glm::vec3 poleVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, poleVector));
DebugDraw::getInstance().drawRay(midPoint, poleVectorEnd, GREEN);
// draw the refVector
glm::vec3 refVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, refVector));
DebugDraw::getInstance().drawRay(midPoint, refVectorEnd, RED);
// draw the sideVector
glm::vec3 sideVector = glm::cross(poleVector, refVector);
glm::vec3 sideVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, sideVector));
DebugDraw::getInstance().drawRay(midPoint, sideVectorEnd, YELLOW);
}
}
processOutputJoints(triggersOut);
return _poses;
}
// for AnimDebugDraw rendering
const AnimPoseVec& AnimPoleVectorConstraint::getPosesInternal() const {
return _poses;
}
void AnimPoleVectorConstraint::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
AnimNode::setSkeletonInternal(skeleton);
lookUpIndices();
}
void AnimPoleVectorConstraint::lookUpIndices() {
assert(_skeleton);
// look up bone indices by name
std::vector<int> indices = _skeleton->lookUpJointIndices({_baseJointName, _midJointName, _tipJointName});
// cache the results
_baseJointIndex = indices[0];
_midJointIndex = indices[1];
_tipJointIndex = indices[2];
if (_baseJointIndex != -1) {
_baseParentJointIndex = _skeleton->getParentIndex(_baseJointIndex);
}
}
void AnimPoleVectorConstraint::beginInterp(InterpType interpType, const AnimChain& chain) {
// capture the current poses in a snapshot.
_snapshotChain = chain;
_interpType = interpType;
_interpAlphaVel = FRAMES_PER_SECOND / INTERP_DURATION;
_interpAlpha = 0.0f;
}