mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 17:39:26 +02:00
244 lines
9.2 KiB
C++
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;
|
|
}
|