This commit is contained in:
Stephen Birarda 2015-09-17 09:37:29 -07:00
commit fb38fd05ed
25 changed files with 231 additions and 161 deletions

View file

@ -153,11 +153,11 @@ void Agent::run() {
qDebug() << "Downloaded script:" << scriptContents; qDebug() << "Downloaded script:" << scriptContents;
_scriptEngine = new ScriptEngine(scriptContents, _payload); _scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(scriptContents, _payload));
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
// setup an Avatar for the script to use // setup an Avatar for the script to use
ScriptableAvatar scriptedAvatar(_scriptEngine); ScriptableAvatar scriptedAvatar(_scriptEngine.get());
scriptedAvatar.setForceFaceTrackerConnected(true); scriptedAvatar.setForceFaceTrackerConnected(true);
// call model URL setters with empty URLs so our avatar, if user, will have the default models // call model URL setters with empty URLs so our avatar, if user, will have the default models
@ -191,22 +191,21 @@ void Agent::run() {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
_scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer);
// we need to make sure that init has been called for our EntityScriptingInterface
// so that it actually has a jurisdiction listener when we ask it for it next
entityScriptingInterface->init();
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener()); _entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
_entityViewer.init(); _entityViewer.init();
entityScriptingInterface->setEntityTree(_entityViewer.getTree()); entityScriptingInterface->setEntityTree(_entityViewer.getTree());
// wire up our additional agent related processing to the update signal // wire up our additional agent related processing to the update signal
QObject::connect(_scriptEngine, &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio); QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio);
_scriptEngine->run(); _scriptEngine->run();
setFinished(true); setFinished(true);
// kill the avatar identity timer
delete _avatarIdentityTimer;
// delete the script engine
delete _scriptEngine;
} }
void Agent::setIsAvatar(bool isAvatar) { void Agent::setIsAvatar(bool isAvatar) {
@ -227,10 +226,17 @@ void Agent::setIsAvatar(bool isAvatar) {
} }
if (!_isAvatar) { if (!_isAvatar) {
delete _avatarIdentityTimer; if (_avatarIdentityTimer) {
_avatarIdentityTimer = NULL; _avatarIdentityTimer->stop();
delete _avatarBillboardTimer; delete _avatarIdentityTimer;
_avatarBillboardTimer = NULL; _avatarIdentityTimer = nullptr;
}
if (_avatarBillboardTimer) {
_avatarIdentityTimer->stop();
delete _avatarIdentityTimer;
_avatarBillboardTimer = nullptr;
}
} }
} }

View file

@ -12,6 +12,7 @@
#ifndef hifi_Agent_h #ifndef hifi_Agent_h
#define hifi_Agent_h #define hifi_Agent_h
#include <memory>
#include <vector> #include <vector>
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
@ -61,7 +62,7 @@ private slots:
void processAgentAvatarAndAudio(float deltaTime); void processAgentAvatarAndAudio(float deltaTime);
private: private:
ScriptEngine* _scriptEngine; std::unique_ptr<ScriptEngine> _scriptEngine;
EntityEditPacketSender _entityEditSender; EntityEditPacketSender _entityEditSender;
EntityTreeHeadlessViewer _entityViewer; EntityTreeHeadlessViewer _entityViewer;
QTimer* _pingTimer; QTimer* _pingTimer;

View file

@ -4347,6 +4347,8 @@ void Application::stopScript(const QString &scriptName, bool restart) {
} }
void Application::reloadAllScripts() { void Application::reloadAllScripts() {
DependencyManager::get<ScriptCache>()->clearCache();
getEntities()->reloadEntityScripts();
stopAllScripts(true); stopAllScripts(true);
} }

View file

@ -1302,7 +1302,7 @@ void MyAvatar::initAnimGraph() {
// or run a local web-server // or run a local web-server
// python -m SimpleHTTPServer& // python -m SimpleHTTPServer&
//auto graphUrl = QUrl("http://localhost:8000/avatar.json"); //auto graphUrl = QUrl("http://localhost:8000/avatar.json");
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/04a02c47eb56d8bfaebb/raw/5f2a4e268d35147c83d44881e268f83a6296e89b/ik-avatar-hands.json"); auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/04a02c47eb56d8bfaebb/raw/72517b231f606b724c5169e02642e401f9af5a54/ik-avatar-hands.json");
_rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); _rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry());
} }

View file

@ -9,6 +9,7 @@
#include "AnimInverseKinematics.h" #include "AnimInverseKinematics.h"
#include <GLMHelpers.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <SharedUtil.h> #include <SharedUtil.h>
@ -54,7 +55,20 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con
void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar) { void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar) {
// if there are dups, last one wins. // if there are dups, last one wins.
_targetVarVec.push_back(IKTargetVar(jointName, positionVar.toStdString(), rotationVar.toStdString())); bool found = false;
for (auto& targetVar: _targetVarVec) {
if (targetVar.jointName == jointName) {
// update existing targetVar
targetVar.positionVar = positionVar.toStdString();
targetVar.rotationVar = rotationVar.toStdString();
found = true;
break;
}
}
if (!found) {
// create a new entry
_targetVarVec.push_back(IKTargetVar(jointName, positionVar.toStdString(), rotationVar.toStdString()));
}
} }
static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int index) { static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int index) {
@ -68,6 +82,12 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde
return rootIndex; return rootIndex;
} }
struct IKTarget {
AnimPose pose;
int index;
int rootIndex;
};
//virtual //virtual
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) { const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) {
@ -76,43 +96,53 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
return _relativePoses; return _relativePoses;
} }
// evaluate target vars // build a list of targets from _targetVarVec
std::vector<IKTarget> targets;
bool removeUnfoundJoints = false;
for (auto& targetVar : _targetVarVec) { for (auto& targetVar : _targetVarVec) {
if (targetVar.jointIndex == -1) {
// lazy look up of jointIndices and insertion into _absoluteTargets map // this targetVar hasn't been validated yet...
if (!targetVar.hasPerformedJointLookup) { int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
targetVar.jointIndex = _skeleton->nameToJointIndex(targetVar.jointName); if (jointIndex >= 0) {
if (targetVar.jointIndex >= 0) { // this targetVar has a valid joint --> cache the indices
// insert into _absoluteTargets map targetVar.jointIndex = jointIndex;
IKTarget target; targetVar.rootIndex = findRootJointInSkeleton(_skeleton, jointIndex);
target.pose = AnimPose::identity;
target.rootIndex = findRootJointInSkeleton(_skeleton, targetVar.jointIndex);
_absoluteTargets[targetVar.jointIndex] = target;
if (targetVar.jointIndex > _maxTargetIndex) {
_maxTargetIndex = targetVar.jointIndex;
}
} else { } else {
qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton"; qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton";
removeUnfoundJoints = true;
} }
targetVar.hasPerformedJointLookup = true; } else {
} // TODO: get this done without a double-lookup of each var in animVars
if (animVars.hasKey(targetVar.positionVar) || animVars.hasKey(targetVar.rotationVar)) {
if (targetVar.jointIndex >= 0) { IKTarget target;
// update pose in _absoluteTargets map
auto iter = _absoluteTargets.find(targetVar.jointIndex);
if (iter != _absoluteTargets.end()) {
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses); AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses);
iter->second.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
iter->second.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot);
target.rootIndex = targetVar.rootIndex;
target.index = targetVar.jointIndex;
targets.push_back(target);
} }
} }
} }
// RELAX! Don't do it. if (removeUnfoundJoints) {
// relaxTowardDefaults(dt); int numVars = _targetVarVec.size();
int i = 0;
while (i < numVars) {
if (_targetVarVec[i].jointIndex == -1) {
if (numVars > 1) {
// swap i for last element
_targetVarVec[i] = _targetVarVec[numVars - 1];
}
_targetVarVec.pop_back();
--numVars;
} else {
++i;
}
}
}
if (_absoluteTargets.empty()) { if (targets.empty()) {
// no IK targets but still need to enforce constraints // no IK targets but still need to enforce constraints
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin(); std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin();
while (constraintItr != _constraints.end()) { while (constraintItr != _constraints.end()) {
@ -135,11 +165,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
quint64 expiry = usecTimestampNow() + MAX_IK_TIME; quint64 expiry = usecTimestampNow() + MAX_IK_TIME;
do { do {
largestError = 0.0f; largestError = 0.0f;
for (auto& targetPair: _absoluteTargets) { for (auto& target: targets) {
int lowestMovedIndex = _relativePoses.size() - 1; int lowestMovedIndex = _relativePoses.size() - 1;
int tipIndex = targetPair.first; int tipIndex = target.index;
AnimPose targetPose = targetPair.second.pose; AnimPose targetPose = target.pose;
int rootIndex = targetPair.second.rootIndex; int rootIndex = target.rootIndex;
if (rootIndex != -1) { if (rootIndex != -1) {
// transform targetPose into skeleton's absolute frame // transform targetPose into skeleton's absolute frame
AnimPose& rootPose = _relativePoses[rootIndex]; AnimPose& rootPose = _relativePoses[rootIndex];
@ -150,11 +180,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
glm::vec3 tip = absolutePoses[tipIndex].trans; glm::vec3 tip = absolutePoses[tipIndex].trans;
float error = glm::length(targetPose.trans - tip); float error = glm::length(targetPose.trans - tip);
// descend toward root, rotating each joint to get tip closer to target // descend toward root, pivoting each joint to get tip closer to target
int index = _skeleton->getParentIndex(tipIndex); int pivotIndex = _skeleton->getParentIndex(tipIndex);
while (index != -1 && error > ACCEPTABLE_RELATIVE_ERROR) { while (pivotIndex != -1 && error > ACCEPTABLE_RELATIVE_ERROR) {
// compute the two lines that should be aligned // compute the two lines that should be aligned
glm::vec3 jointPosition = absolutePoses[index].trans; glm::vec3 jointPosition = absolutePoses[pivotIndex].trans;
glm::vec3 leverArm = tip - jointPosition; glm::vec3 leverArm = tip - jointPosition;
glm::vec3 targetLine = targetPose.trans - jointPosition; glm::vec3 targetLine = targetPose.trans - jointPosition;
@ -173,29 +203,34 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
angle = 0.5f * angle; angle = 0.5f * angle;
glm::quat deltaRotation = glm::angleAxis(angle, axis); glm::quat deltaRotation = glm::angleAxis(angle, axis);
int parentIndex = _skeleton->getParentIndex(index); int parentIndex = _skeleton->getParentIndex(pivotIndex);
if (parentIndex == -1) { if (parentIndex == -1) {
// TODO? apply constraints to root? // TODO? apply constraints to root?
// TODO? harvest the root's transform as movement of entire skeleton? // TODO? harvest the root's transform as movement of entire skeleton?
} else { } else {
// compute joint's new parent-relative rotation // compute joint's new parent-relative rotation
// Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q
glm::quat newRot = glm::normalize(glm::inverse(absolutePoses[parentIndex].rot) * deltaRotation * absolutePoses[index].rot); glm::quat newRot = glm::normalize(glm::inverse(
RotationConstraint* constraint = getConstraint(index); absolutePoses[parentIndex].rot) *
deltaRotation *
absolutePoses[pivotIndex].rot);
RotationConstraint* constraint = getConstraint(pivotIndex);
if (constraint) { if (constraint) {
bool constrained = constraint->apply(newRot); bool constrained = constraint->apply(newRot);
if (constrained) { if (constrained) {
// the constraint will modify the movement of the tip so we have to compute the modified // the constraint will modify the movement of the tip so we have to compute the modified
// model-frame deltaRotation // model-frame deltaRotation
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
deltaRotation = absolutePoses[parentIndex].rot * newRot * glm::inverse(absolutePoses[index].rot); deltaRotation = absolutePoses[parentIndex].rot *
newRot *
glm::inverse(absolutePoses[pivotIndex].rot);
} }
} }
_relativePoses[index].rot = newRot; _relativePoses[pivotIndex].rot = newRot;
} }
// this joint has been changed so we check to see if it has the lowest index // this joint has been changed so we check to see if it has the lowest index
if (index < lowestMovedIndex) { if (pivotIndex < lowestMovedIndex) {
lowestMovedIndex = index; lowestMovedIndex = pivotIndex;
} }
// keep track of tip's new position as we descend towards root // keep track of tip's new position as we descend towards root
@ -203,7 +238,7 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
error = glm::length(targetPose.trans - tip); error = glm::length(targetPose.trans - tip);
} }
} }
index = _skeleton->getParentIndex(index); pivotIndex = _skeleton->getParentIndex(pivotIndex);
} }
if (largestError < error) { if (largestError < error) {
largestError = error; largestError = error;
@ -248,12 +283,16 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
loadPoses(underPoses); loadPoses(underPoses);
} else { } else {
// relax toward underpose // relax toward underpose
const float RELAXATION_TIMESCALE = 0.125f; // HACK: this relaxation needs to be constant per-frame rather than per-realtime
const float alpha = glm::clamp(dt / RELAXATION_TIMESCALE, 0.0f, 1.0f); // in order to prevent IK "flutter" for bad FPS. The bad news is that the good parts
// of this relaxation will be FPS dependent (low FPS will make the limbs align slower
// in real-time), however most people will not notice this and this problem is less
// annoying than the flutter.
const float blend = (1.0f / 60.0f) / (0.25f); // effectively: dt / RELAXATION_TIMESCALE
int numJoints = (int)_relativePoses.size(); int numJoints = (int)_relativePoses.size();
for (int i = 0; i < numJoints; ++i) { for (int i = 0; i < numJoints; ++i) {
float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot, underPoses[i].rot)); float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot, underPoses[i].rot));
_relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, alpha)); _relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, blend));
} }
} }
return evaluate(animVars, dt, triggersOut); return evaluate(animVars, dt, triggersOut);
@ -277,10 +316,6 @@ void AnimInverseKinematics::clearConstraints() {
_constraints.clear(); _constraints.clear();
} }
const glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
const glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
const glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
void AnimInverseKinematics::initConstraints() { void AnimInverseKinematics::initConstraints() {
if (!_skeleton) { if (!_skeleton) {
return; return;
@ -288,7 +323,7 @@ void AnimInverseKinematics::initConstraints() {
// We create constraints for the joints shown here // We create constraints for the joints shown here
// (and their Left counterparts if applicable). // (and their Left counterparts if applicable).
// //
// //
// O RightHand // O RightHand
// Head / // Head /
// O / // O /
@ -306,7 +341,7 @@ void AnimInverseKinematics::initConstraints() {
// y | // y |
// | | // | |
// | O---O---O RightUpLeg // | O---O---O RightUpLeg
// z | | | // z | | Hips2 |
// \ | | | // \ | | |
// \| | | // \| | |
// x -----+ O O RightLeg // x -----+ O O RightLeg
@ -334,7 +369,7 @@ void AnimInverseKinematics::initConstraints() {
_constraints.clear(); _constraints.clear();
for (int i = 0; i < numJoints; ++i) { for (int i = 0; i < numJoints; ++i) {
// compute the joint's baseName and remember if it was Left or not // compute the joint's baseName and remember whether its prefix was "Left" or not
QString baseName = _skeleton->getJointName(i); QString baseName = _skeleton->getJointName(i);
bool isLeft = baseName.startsWith("Left", Qt::CaseInsensitive); bool isLeft = baseName.startsWith("Left", Qt::CaseInsensitive);
float mirror = isLeft ? -1.0f : 1.0f; float mirror = isLeft ? -1.0f : 1.0f;
@ -442,7 +477,7 @@ void AnimInverseKinematics::initConstraints() {
const float MAX_HAND_SWING = PI / 2.0f; const float MAX_HAND_SWING = PI / 2.0f;
minDots.push_back(cosf(MAX_HAND_SWING)); minDots.push_back(cosf(MAX_HAND_SWING));
stConstraint->setSwingLimits(minDots); stConstraint->setSwingLimits(minDots);
constraint = static_cast<RotationConstraint*>(stConstraint); constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (baseName.startsWith("Shoulder", Qt::CaseInsensitive)) { } else if (baseName.startsWith("Shoulder", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
@ -467,6 +502,18 @@ void AnimInverseKinematics::initConstraints() {
minDots.push_back(cosf(MAX_SPINE_SWING)); minDots.push_back(cosf(MAX_SPINE_SWING));
stConstraint->setSwingLimits(minDots); stConstraint->setSwingLimits(minDots);
constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (baseName.startsWith("Hips2", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
const float MAX_SPINE_TWIST = PI / 8.0f;
stConstraint->setTwistLimits(-MAX_SPINE_TWIST, MAX_SPINE_TWIST);
std::vector<float> minDots;
const float MAX_SPINE_SWING = PI / 14.0f;
minDots.push_back(cosf(MAX_SPINE_SWING));
stConstraint->setSwingLimits(minDots);
constraint = static_cast<RotationConstraint*>(stConstraint); constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) { } else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
@ -488,18 +535,18 @@ void AnimInverseKinematics::initConstraints() {
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame // we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
// then measure the angles to swing the yAxis into alignment // then measure the angles to swing the yAxis into alignment
glm::vec3 hingeAxis = - mirror * zAxis; glm::vec3 hingeAxis = - mirror * Vectors::UNIT_Z;
const float MIN_ELBOW_ANGLE = 0.05f; const float MIN_ELBOW_ANGLE = 0.05f;
const float MAX_ELBOW_ANGLE = 11.0f * PI / 12.0f; const float MAX_ELBOW_ANGLE = 11.0f * PI / 12.0f;
glm::quat invReferenceRotation = glm::inverse(referenceRotation); glm::quat invReferenceRotation = glm::inverse(referenceRotation);
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_ELBOW_ANGLE, hingeAxis) * yAxis; glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_ELBOW_ANGLE, hingeAxis) * Vectors::UNIT_Y;
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_ELBOW_ANGLE, hingeAxis) * yAxis; glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_ELBOW_ANGLE, hingeAxis) * Vectors::UNIT_Y;
// for the rest of the math we rotate hingeAxis into the child frame // for the rest of the math we rotate hingeAxis into the child frame
hingeAxis = referenceRotation * hingeAxis; hingeAxis = referenceRotation * hingeAxis;
eConstraint->setHingeAxis(hingeAxis); eConstraint->setHingeAxis(hingeAxis);
glm::vec3 projectedYAxis = glm::normalize(yAxis - glm::dot(yAxis, hingeAxis) * hingeAxis); glm::vec3 projectedYAxis = glm::normalize(Vectors::UNIT_Y - glm::dot(Vectors::UNIT_Y, hingeAxis) * hingeAxis);
float minAngle = acosf(glm::dot(projectedYAxis, minSwingAxis)); float minAngle = acosf(glm::dot(projectedYAxis, minSwingAxis));
if (glm::dot(hingeAxis, glm::cross(projectedYAxis, minSwingAxis)) < 0.0f) { if (glm::dot(hingeAxis, glm::cross(projectedYAxis, minSwingAxis)) < 0.0f) {
minAngle = - minAngle; minAngle = - minAngle;
@ -516,21 +563,21 @@ void AnimInverseKinematics::initConstraints() {
ElbowConstraint* eConstraint = new ElbowConstraint(); ElbowConstraint* eConstraint = new ElbowConstraint();
glm::quat referenceRotation = _defaultRelativePoses[i].rot; glm::quat referenceRotation = _defaultRelativePoses[i].rot;
eConstraint->setReferenceRotation(referenceRotation); eConstraint->setReferenceRotation(referenceRotation);
glm::vec3 hingeAxis = -1.0f * xAxis; glm::vec3 hingeAxis = -1.0f * Vectors::UNIT_X;
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame // we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
// then measure the angles to swing the yAxis into alignment // then measure the angles to swing the yAxis into alignment
const float MIN_KNEE_ANGLE = 0.0f; const float MIN_KNEE_ANGLE = 0.0f;
const float MAX_KNEE_ANGLE = 3.0f * PI / 4.0f; const float MAX_KNEE_ANGLE = 3.0f * PI / 4.0f;
glm::quat invReferenceRotation = glm::inverse(referenceRotation); glm::quat invReferenceRotation = glm::inverse(referenceRotation);
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * yAxis; glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * yAxis; glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
// for the rest of the math we rotate hingeAxis into the child frame // for the rest of the math we rotate hingeAxis into the child frame
hingeAxis = referenceRotation * hingeAxis; hingeAxis = referenceRotation * hingeAxis;
eConstraint->setHingeAxis(hingeAxis); eConstraint->setHingeAxis(hingeAxis);
glm::vec3 projectedYAxis = glm::normalize(yAxis - glm::dot(yAxis, hingeAxis) * hingeAxis); glm::vec3 projectedYAxis = glm::normalize(Vectors::UNIT_Y - glm::dot(Vectors::UNIT_Y, hingeAxis) * hingeAxis);
float minAngle = acosf(glm::dot(projectedYAxis, minSwingAxis)); float minAngle = acosf(glm::dot(projectedYAxis, minSwingAxis));
if (glm::dot(hingeAxis, glm::cross(projectedYAxis, minSwingAxis)) < 0.0f) { if (glm::dot(hingeAxis, glm::cross(projectedYAxis, minSwingAxis)) < 0.0f) {
minAngle = - minAngle; minAngle = - minAngle;
@ -550,8 +597,8 @@ void AnimInverseKinematics::initConstraints() {
// these directions are approximate swing limits in parent-frame // these directions are approximate swing limits in parent-frame
// NOTE: they don't need to be normalized // NOTE: they don't need to be normalized
std::vector<glm::vec3> swungDirections; std::vector<glm::vec3> swungDirections;
swungDirections.push_back(yAxis); swungDirections.push_back(Vectors::UNIT_Y);
swungDirections.push_back(xAxis); swungDirections.push_back(Vectors::UNIT_X);
swungDirections.push_back(glm::vec3(1.0f, 1.0f, 1.0f)); swungDirections.push_back(glm::vec3(1.0f, 1.0f, 1.0f));
swungDirections.push_back(glm::vec3(1.0f, 1.0f, -1.0f)); swungDirections.push_back(glm::vec3(1.0f, 1.0f, -1.0f));
@ -575,13 +622,10 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
AnimNode::setSkeletonInternal(skeleton); AnimNode::setSkeletonInternal(skeleton);
// invalidate all targetVars // invalidate all targetVars
for (auto& targetVar : _targetVarVec) { for (auto& targetVar: _targetVarVec) {
targetVar.hasPerformedJointLookup = false; targetVar.jointIndex = -1;
} }
// invalidate all targets
_absoluteTargets.clear();
_maxTargetIndex = 0; _maxTargetIndex = 0;
if (skeleton) { if (skeleton) {

View file

@ -50,23 +50,17 @@ protected:
rotationVar(rotationVarIn), rotationVar(rotationVarIn),
jointName(jointNameIn), jointName(jointNameIn),
jointIndex(-1), jointIndex(-1),
hasPerformedJointLookup(false) {} rootIndex(-1) {}
std::string positionVar; std::string positionVar;
std::string rotationVar; std::string rotationVar;
QString jointName; QString jointName;
int jointIndex; // cached joint index int jointIndex; // cached joint index
bool hasPerformedJointLookup = false; int rootIndex; // cached root index
};
struct IKTarget {
AnimPose pose;
int rootIndex;
}; };
std::map<int, RotationConstraint*> _constraints; std::map<int, RotationConstraint*> _constraints;
std::vector<IKTargetVar> _targetVarVec; std::vector<IKTargetVar> _targetVarVec;
std::map<int, IKTarget> _absoluteTargets; // IK targets of end-points
AnimPoseVec _defaultRelativePoses; // poses of the relaxed state AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
AnimPoseVec _relativePoses; // current relative poses AnimPoseVec _relativePoses; // current relative poses

View file

@ -1,5 +1,5 @@
// //
// AnimController.cpp // AnimManipulator.cpp
// //
// Created by Anthony J. Thibault on 9/8/15. // Created by Anthony J. Thibault on 9/8/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
@ -8,24 +8,25 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "AnimController.h" #include "AnimManipulator.h"
#include "AnimUtil.h"
#include "AnimationLogging.h" #include "AnimationLogging.h"
AnimController::AnimController(const std::string& id, float alpha) : AnimManipulator::AnimManipulator(const std::string& id, float alpha) :
AnimNode(AnimNode::Type::Controller, id), AnimNode(AnimNode::Type::Manipulator, id),
_alpha(alpha) { _alpha(alpha) {
} }
AnimController::~AnimController() { AnimManipulator::~AnimManipulator() {
} }
const AnimPoseVec& AnimController::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
return overlay(animVars, dt, triggersOut, _skeleton->getRelativeBindPoses()); return overlay(animVars, dt, triggersOut, _skeleton->getRelativeBindPoses());
} }
const AnimPoseVec& AnimController::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
_alpha = animVars.lookup(_alphaVar, _alpha); _alpha = animVars.lookup(_alphaVar, _alpha);
for (auto& jointVar : _jointVars) { for (auto& jointVar : _jointVars) {
@ -33,52 +34,56 @@ const AnimPoseVec& AnimController::overlay(const AnimVariantMap& animVars, float
QString qJointName = QString::fromStdString(jointVar.jointName); QString qJointName = QString::fromStdString(jointVar.jointName);
jointVar.jointIndex = _skeleton->nameToJointIndex(qJointName); jointVar.jointIndex = _skeleton->nameToJointIndex(qJointName);
if (jointVar.jointIndex < 0) { if (jointVar.jointIndex < 0) {
qCWarning(animation) << "AnimController could not find jointName" << qJointName << "in skeleton"; qCWarning(animation) << "AnimManipulator could not find jointName" << qJointName << "in skeleton";
} }
jointVar.hasPerformedJointLookup = true; jointVar.hasPerformedJointLookup = true;
} }
if (jointVar.jointIndex >= 0) { if (jointVar.jointIndex >= 0) {
AnimPose defaultPose; AnimPose defaultAbsPose;
glm::quat absRot; AnimPose defaultRelPose;
glm::quat parentAbsRot; AnimPose parentAbsPose = AnimPose::identity;
if (jointVar.jointIndex <= (int)underPoses.size()) { if (jointVar.jointIndex <= (int)underPoses.size()) {
// jointVar is an absolute rotation, if it is not set we will use the underPose as our default value // jointVar is an absolute rotation, if it is not set we will use the underPose as our default value
defaultPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); defaultRelPose = underPoses[jointVar.jointIndex];
absRot = animVars.lookup(jointVar.var, defaultPose.rot); defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses);
defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot);
// because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose.
int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex);
if (parentIndex >= 0) { if (parentIndex >= 0) {
parentAbsRot = _skeleton->getAbsolutePose(parentIndex, underPoses).rot; parentAbsPose = _skeleton->getAbsolutePose(parentIndex, underPoses);
} }
} else { } else {
// jointVar is an absolute rotation, if it is not set we will use the bindPose as our default value // jointVar is an absolute rotation, if it is not set we will use the bindPose as our default value
defaultPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex); defaultRelPose = AnimPose::identity;
absRot = animVars.lookup(jointVar.var, defaultPose.rot); defaultAbsPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex);
defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot);
// because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose
// here we use the bind pose // here we use the bind pose
int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex);
if (parentIndex >= 0) { if (parentIndex >= 0) {
parentAbsRot = _skeleton->getAbsoluteBindPose(parentIndex).rot; parentAbsPose = _skeleton->getAbsoluteBindPose(parentIndex);
} }
} }
// convert from absolute to relative // convert from absolute to relative
glm::quat relRot = glm::inverse(parentAbsRot) * absRot; AnimPose relPose = parentAbsPose.inverse() * defaultAbsPose;
_poses[jointVar.jointIndex] = AnimPose(defaultPose.scale, relRot, defaultPose.trans);
// blend with underPose
::blend(1, &defaultRelPose, &relPose, _alpha, &_poses[jointVar.jointIndex]);
} }
} }
return _poses; return _poses;
} }
void AnimController::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { void AnimManipulator::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
AnimNode::setSkeletonInternal(skeleton); AnimNode::setSkeletonInternal(skeleton);
// invalidate all jointVar indices // invalidate all jointVar indices
@ -97,10 +102,10 @@ void AnimController::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
} }
// for AnimDebugDraw rendering // for AnimDebugDraw rendering
const AnimPoseVec& AnimController::getPosesInternal() const { const AnimPoseVec& AnimManipulator::getPosesInternal() const {
return _poses; return _poses;
} }
void AnimController::addJointVar(const JointVar& jointVar) { void AnimManipulator::addJointVar(const JointVar& jointVar) {
_jointVars.push_back(jointVar); _jointVars.push_back(jointVar);
} }

View file

@ -1,5 +1,5 @@
// //
// AnimController.h // AnimManipulator.h
// //
// Created by Anthony J. Thibault on 9/8/15. // Created by Anthony J. Thibault on 9/8/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
@ -8,19 +8,19 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#ifndef hifi_AnimController_h #ifndef hifi_AnimManipulator_h
#define hifi_AnimController_h #define hifi_AnimManipulator_h
#include "AnimNode.h" #include "AnimNode.h"
// Allows procedural control over a set of joints. // Allows procedural control over a set of joints.
class AnimController : public AnimNode { class AnimManipulator : public AnimNode {
public: public:
friend class AnimTests; friend class AnimTests;
AnimController(const std::string& id, float alpha); AnimManipulator(const std::string& id, float alpha);
virtual ~AnimController() override; virtual ~AnimManipulator() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
@ -50,9 +50,9 @@ protected:
std::vector<JointVar> _jointVars; std::vector<JointVar> _jointVars;
// no copies // no copies
AnimController(const AnimController&) = delete; AnimManipulator(const AnimManipulator&) = delete;
AnimController& operator=(const AnimController&) = delete; AnimManipulator& operator=(const AnimManipulator&) = delete;
}; };
#endif // hifi_AnimController_h #endif // hifi_AnimManipulator_h

View file

@ -40,7 +40,7 @@ public:
BlendLinear, BlendLinear,
Overlay, Overlay,
StateMachine, StateMachine,
Controller, Manipulator,
InverseKinematics, InverseKinematics,
NumTypes NumTypes
}; };

View file

@ -20,7 +20,7 @@
#include "AnimOverlay.h" #include "AnimOverlay.h"
#include "AnimNodeLoader.h" #include "AnimNodeLoader.h"
#include "AnimStateMachine.h" #include "AnimStateMachine.h"
#include "AnimController.h" #include "AnimManipulator.h"
#include "AnimInverseKinematics.h" #include "AnimInverseKinematics.h"
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
@ -31,7 +31,7 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString&
static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
// called after children have been loaded // called after children have been loaded
@ -40,7 +40,7 @@ static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static bool processControllerNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static bool processManipulatorNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static bool processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static const char* animNodeTypeToString(AnimNode::Type type) { static const char* animNodeTypeToString(AnimNode::Type type) {
@ -49,7 +49,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
case AnimNode::Type::BlendLinear: return "blendLinear"; case AnimNode::Type::BlendLinear: return "blendLinear";
case AnimNode::Type::Overlay: return "overlay"; case AnimNode::Type::Overlay: return "overlay";
case AnimNode::Type::StateMachine: return "stateMachine"; case AnimNode::Type::StateMachine: return "stateMachine";
case AnimNode::Type::Controller: return "controller"; case AnimNode::Type::Manipulator: return "manipulator";
case AnimNode::Type::InverseKinematics: return "inverseKinematics"; case AnimNode::Type::InverseKinematics: return "inverseKinematics";
case AnimNode::Type::NumTypes: return nullptr; case AnimNode::Type::NumTypes: return nullptr;
}; };
@ -62,7 +62,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
case AnimNode::Type::BlendLinear: return loadBlendLinearNode; case AnimNode::Type::BlendLinear: return loadBlendLinearNode;
case AnimNode::Type::Overlay: return loadOverlayNode; case AnimNode::Type::Overlay: return loadOverlayNode;
case AnimNode::Type::StateMachine: return loadStateMachineNode; case AnimNode::Type::StateMachine: return loadStateMachineNode;
case AnimNode::Type::Controller: return loadControllerNode; case AnimNode::Type::Manipulator: return loadManipulatorNode;
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode; case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
case AnimNode::Type::NumTypes: return nullptr; case AnimNode::Type::NumTypes: return nullptr;
}; };
@ -75,7 +75,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
case AnimNode::Type::BlendLinear: return processBlendLinearNode; case AnimNode::Type::BlendLinear: return processBlendLinearNode;
case AnimNode::Type::Overlay: return processOverlayNode; case AnimNode::Type::Overlay: return processOverlayNode;
case AnimNode::Type::StateMachine: return processStateMachineNode; case AnimNode::Type::StateMachine: return processStateMachineNode;
case AnimNode::Type::Controller: return processControllerNode; case AnimNode::Type::Manipulator: return processManipulatorNode;
case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode; case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode;
case AnimNode::Type::NumTypes: return nullptr; case AnimNode::Type::NumTypes: return nullptr;
}; };
@ -122,7 +122,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
static AnimNode::Type stringToEnum(const QString& str) { static AnimNode::Type stringToEnum(const QString& str) {
// O(n), move to map when number of types becomes large. // O(n), move to map when number of types becomes large.
const int NUM_TYPES = static_cast<int>(AnimNode::Type::NumTypes); const int NUM_TYPES = static_cast<int>(AnimNode::Type::NumTypes);
for (int i = 0; i < NUM_TYPES; i++ ) { for (int i = 0; i < NUM_TYPES; i++) {
AnimNode::Type type = static_cast<AnimNode::Type>(i); AnimNode::Type type = static_cast<AnimNode::Type>(i);
if (str == animNodeTypeToString(type)) { if (str == animNodeTypeToString(type)) {
return type; return type;
@ -288,10 +288,10 @@ static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const
return node; return node;
} }
static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
auto node = std::make_shared<AnimController>(id.toStdString(), alpha); auto node = std::make_shared<AnimManipulator>(id.toStdString(), alpha);
READ_OPTIONAL_STRING(alphaVar, jsonObj); READ_OPTIONAL_STRING(alphaVar, jsonObj);
if (!alphaVar.isEmpty()) { if (!alphaVar.isEmpty()) {
@ -315,7 +315,7 @@ static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QS
READ_STRING(var, jointObj, id, jsonUrl, nullptr); READ_STRING(var, jointObj, id, jsonUrl, nullptr);
READ_STRING(jointName, jointObj, id, jsonUrl, nullptr); READ_STRING(jointName, jointObj, id, jsonUrl, nullptr);
AnimController::JointVar jointVar(var.toStdString(), jointName.toStdString()); AnimManipulator::JointVar jointVar(var.toStdString(), jointName.toStdString());
node->addJointVar(jointVar); node->addJointVar(jointVar);
}; };

View file

@ -173,8 +173,8 @@ void AnimOverlay::buildEmptyBoneSet() {
void AnimOverlay::buildLeftHandBoneSet() { void AnimOverlay::buildLeftHandBoneSet() {
assert(_skeleton); assert(_skeleton);
buildEmptyBoneSet(); buildEmptyBoneSet();
int headJoint = _skeleton->nameToJointIndex("LeftHand"); int handJoint = _skeleton->nameToJointIndex("LeftHand");
for_each_child_joint(_skeleton, headJoint, [&](int i) { for_each_child_joint(_skeleton, handJoint, [&](int i) {
_boneSetVec[i] = 1.0f; _boneSetVec[i] = 1.0f;
}); });
} }
@ -182,8 +182,8 @@ void AnimOverlay::buildLeftHandBoneSet() {
void AnimOverlay::buildRightHandBoneSet() { void AnimOverlay::buildRightHandBoneSet() {
assert(_skeleton); assert(_skeleton);
buildEmptyBoneSet(); buildEmptyBoneSet();
int headJoint = _skeleton->nameToJointIndex("RightHand"); int handJoint = _skeleton->nameToJointIndex("RightHand");
for_each_child_joint(_skeleton, headJoint, [&](int i) { for_each_child_joint(_skeleton, handJoint, [&](int i) {
_boneSetVec[i] = 1.0f; _boneSetVec[i] = 1.0f;
}); });
} }

View file

@ -154,6 +154,8 @@ public:
void setTrigger(const std::string& key) { _triggers.insert(key); } void setTrigger(const std::string& key) { _triggers.insert(key); }
void clearTriggers() { _triggers.clear(); } void clearTriggers() { _triggers.clear(); }
bool hasKey(const std::string& key) const { return _map.find(key) != _map.end(); }
protected: protected:
std::map<std::string, AnimVariant> _map; std::map<std::string, AnimVariant> _map;
std::set<std::string> _triggers; std::set<std::string> _triggers;

View file

@ -742,12 +742,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
if (_enableAnimGraph && _animSkeleton) { if (_enableAnimGraph && _animSkeleton) {
if (endIndex == _leftHandJointIndex) { if (endIndex == _leftHandJointIndex) {
auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; _animVars.set("leftHandPosition", targetPosition);
_animVars.set("leftHandPosition", targetPosition + rootTrans);
_animVars.set("leftHandRotation", targetRotation); _animVars.set("leftHandRotation", targetRotation);
} else if (endIndex == _rightHandJointIndex) { } else if (endIndex == _rightHandJointIndex) {
auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; _animVars.set("rightHandPosition", targetPosition);
_animVars.set("rightHandPosition", targetPosition + rootTrans);
_animVars.set("rightHandRotation", targetRotation); _animVars.set("rightHandRotation", targetRotation);
} }
return; return;
@ -995,10 +993,9 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS)); glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS));
_animVars.set("headRotation", realLocalHeadOrientation); _animVars.set("headRotation", realLocalHeadOrientation);
auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans;
// There's a theory that when not in hmd, we should _animVars.unset("headPosition"). // There's a theory that when not in hmd, we should _animVars.unset("headPosition").
// However, until that works well, let's always request head be positioned where requested by hmd, camera, or default. // However, until that works well, let's always request head be positioned where requested by hmd, camera, or default.
_animVars.set("headPosition", params.localHeadPosition + rootTrans); _animVars.set("headPosition", params.localHeadPosition);
} else if (!_enableAnimGraph) { } else if (!_enableAnimGraph) {
auto& state = _jointStates[index]; auto& state = _jointStates[index];

View file

@ -93,6 +93,15 @@ void EntityTreeRenderer::clear() {
OctreeRenderer::clear(); OctreeRenderer::clear();
} }
void EntityTreeRenderer::reloadEntityScripts() {
_entitiesScriptEngine->unloadAllEntityScripts();
foreach(auto entity, _entitiesInScene) {
if (!entity->getScript().isEmpty()) {
_entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true);
}
}
}
void EntityTreeRenderer::init() { void EntityTreeRenderer::init() {
OctreeRenderer::init(); OctreeRenderer::init();
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree); EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);

View file

@ -62,6 +62,9 @@ public:
/// clears the tree /// clears the tree
virtual void clear(); virtual void clear();
/// reloads the entity scripts, calling unload and preload
void reloadEntityScripts();
/// if a renderable entity item needs a model, we will allocate it for them /// if a renderable entity item needs a model, we will allocate it for them
Q_INVOKABLE Model* allocateModel(const QString& url, const QString& collisionUrl); Q_INVOKABLE Model* allocateModel(const QString& url, const QString& collisionUrl);

View file

@ -1,6 +1,6 @@
// //
// EntityScriptingInterface.h // EntityScriptingInterface.h
// libraries/models/src // libraries/entities/src
// //
// Created by Brad Hefta-Gaub on 12/6/13. // Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc. // Copyright 2013 High Fidelity, Inc.

View file

@ -23,9 +23,6 @@ OctreeHeadlessViewer::OctreeHeadlessViewer() :
_viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); _viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
} }
OctreeHeadlessViewer::~OctreeHeadlessViewer() {
}
void OctreeHeadlessViewer::init() { void OctreeHeadlessViewer::init() {
OctreeRenderer::init(); OctreeRenderer::init();
setViewFrustum(&_viewFrustum); setViewFrustum(&_viewFrustum);
@ -34,8 +31,9 @@ void OctreeHeadlessViewer::init() {
void OctreeHeadlessViewer::queryOctree() { void OctreeHeadlessViewer::queryOctree() {
char serverType = getMyNodeType(); char serverType = getMyNodeType();
PacketType packetType = getMyQueryMessageType(); PacketType packetType = getMyQueryMessageType();
NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions(); NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions();
bool wantExtraDebugging = false; bool wantExtraDebugging = false;
if (wantExtraDebugging) { if (wantExtraDebugging) {

View file

@ -29,7 +29,7 @@ class OctreeHeadlessViewer : public OctreeRenderer {
Q_OBJECT Q_OBJECT
public: public:
OctreeHeadlessViewer(); OctreeHeadlessViewer();
virtual ~OctreeHeadlessViewer(); virtual ~OctreeHeadlessViewer() {};
virtual void renderElement(OctreeElementPointer element, RenderArgs* args) { /* swallow these */ } virtual void renderElement(OctreeElementPointer element, RenderArgs* args) { /* swallow these */ }
virtual void init(); virtual void init();
@ -65,7 +65,7 @@ public slots:
private: private:
ViewFrustum _viewFrustum; ViewFrustum _viewFrustum;
JurisdictionListener* _jurisdictionListener; JurisdictionListener* _jurisdictionListener = nullptr;
OctreeQuery _octreeQuery; OctreeQuery _octreeQuery;
float _voxelSizeScale; float _voxelSizeScale;
int _boundaryLevelAdjust; int _boundaryLevelAdjust;

View file

@ -54,6 +54,7 @@ void OctreeScriptingInterface::init() {
if (_initialized) { if (_initialized) {
return; return;
} }
if (_jurisdictionListener) { if (_jurisdictionListener) {
_managedJurisdictionListener = false; _managedJurisdictionListener = false;
} else { } else {

View file

@ -21,8 +21,7 @@
class OctreeScriptingInterface : public QObject { class OctreeScriptingInterface : public QObject {
Q_OBJECT Q_OBJECT
public: public:
OctreeScriptingInterface(OctreeEditPacketSender* packetSender = NULL, OctreeScriptingInterface(OctreeEditPacketSender* packetSender = NULL, JurisdictionListener* jurisdictionListener = NULL);
JurisdictionListener* jurisdictionListener = NULL);
~OctreeScriptingInterface(); ~OctreeScriptingInterface();
@ -86,8 +85,8 @@ public slots:
protected: protected:
/// attached OctreeEditPacketSender that handles queuing and sending of packets to VS /// attached OctreeEditPacketSender that handles queuing and sending of packets to VS
OctreeEditPacketSender* _packetSender; OctreeEditPacketSender* _packetSender = nullptr;
JurisdictionListener* _jurisdictionListener; JurisdictionListener* _jurisdictionListener = nullptr;
bool _managedPacketSender; bool _managedPacketSender;
bool _managedJurisdictionListener; bool _managedJurisdictionListener;
bool _initialized; bool _initialized;

View file

@ -373,7 +373,10 @@ void DeferredLightingEffect::render(RenderArgs* args) {
projMats[0] = monoProjMat; projMats[0] = monoProjMat;
deferredTransforms[0].projection = monoProjMat; deferredTransforms[0].projection = monoProjMat;
deferredTransforms[0].viewInverse = monoViewMat; deferredTransforms[0].viewInverse = monoViewMat;
viewTransforms[0] = monoViewTransform;
deferredTransforms[0].stereoSide = 0.0f; deferredTransforms[0].stereoSide = 0.0f;
clipQuad[0] = glm::vec4(sMin, tMin, sWidth, tHeight); clipQuad[0] = glm::vec4(sMin, tMin, sWidth, tHeight);

View file

@ -27,6 +27,10 @@ ScriptCache::ScriptCache(QObject* parent) {
// nothing to do here... // nothing to do here...
} }
void ScriptCache::clearCache() {
_scriptCache.clear();
}
QString ScriptCache::getScript(const QUrl& unnormalizedURL, ScriptUser* scriptUser, bool& isPending, bool reload) { QString ScriptCache::getScript(const QUrl& unnormalizedURL, ScriptUser* scriptUser, bool& isPending, bool reload) {
QUrl url = ResourceManager::normalizeURL(unnormalizedURL); QUrl url = ResourceManager::normalizeURL(unnormalizedURL);
QString scriptContents; QString scriptContents;

View file

@ -28,6 +28,7 @@ class ScriptCache : public QObject, public Dependency {
SINGLETON_DEPENDENCY SINGLETON_DEPENDENCY
public: public:
void clearCache();
void getScriptContents(const QString& scriptOrURL, contentAvailableCallback contentAvailable, bool forceDownload = false); void getScriptContents(const QString& scriptOrURL, contentAvailableCallback contentAvailable, bool forceDownload = false);

View file

@ -263,7 +263,7 @@ void ScriptEngine::init() {
} }
_isInitialized = true; _isInitialized = true;
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->init(); entityScriptingInterface->init();
@ -405,7 +405,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func
QScriptValue setterFunction = newFunction(setter, 1); QScriptValue setterFunction = newFunction(setter, 1);
QScriptValue getterFunction = newFunction(getter); QScriptValue getterFunction = newFunction(getter);
if (!parent.isNull()) { if (!parent.isNull() && !parent.isEmpty()) {
QScriptValue object = globalObject().property(parent); QScriptValue object = globalObject().property(parent);
if (object.isValid()) { if (object.isValid()) {
object.setProperty(name, setterFunction, QScriptValue::PropertySetter); object.setProperty(name, setterFunction, QScriptValue::PropertySetter);
@ -559,6 +559,7 @@ void ScriptEngine::run() {
if (!_isInitialized) { if (!_isInitialized) {
init(); init();
} }
_isRunning = true; _isRunning = true;
_isFinished = false; _isFinished = false;
if (_wantSignals) { if (_wantSignals) {

View file

@ -33,7 +33,7 @@
"children": [] "children": []
}, },
{ {
"id": "controllerOverlay", "id": "manipulatorOverlay",
"type": "overlay", "type": "overlay",
"data": { "data": {
"alpha": 1.0, "alpha": 1.0,
@ -42,7 +42,7 @@
"children": [ "children": [
{ {
"id": "spineLean", "id": "spineLean",
"type": "controller", "type": "manipulator",
"data": { "data": {
"alpha": 1.0, "alpha": 1.0,
"joints": [ "joints": [