mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-15 14:27:12 +02:00
Because the joint mappings in the .fst can be wrong when the .fst isn't updated for quick changes in the .fbx. Because models now really need the preRotation, which is only in the avatar .fbx.
98 lines
4.5 KiB
C++
98 lines
4.5 KiB
C++
//
|
|
// ScriptableAvatar.cpp
|
|
//
|
|
//
|
|
// Created by Clement on 7/22/14.
|
|
// Copyright 2014 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 <QThread>
|
|
|
|
#include <GLMHelpers.h>
|
|
#include "ScriptableAvatar.h"
|
|
|
|
// hold and priority unused but kept so that client side JS can run.
|
|
void ScriptableAvatar::startAnimation(const QString& url, float fps, float priority,
|
|
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
|
|
if (QThread::currentThread() != thread()) {
|
|
QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps),
|
|
Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame),
|
|
Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
|
|
return;
|
|
}
|
|
_animation = DependencyManager::get<AnimationCache>()->getAnimation(url);
|
|
_animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame);
|
|
_maskedJoints = maskedJoints;
|
|
}
|
|
|
|
void ScriptableAvatar::stopAnimation() {
|
|
if (QThread::currentThread() != thread()) {
|
|
QMetaObject::invokeMethod(this, "stopAnimation");
|
|
return;
|
|
}
|
|
_animation.clear();
|
|
}
|
|
|
|
AnimationDetails ScriptableAvatar::getAnimationDetails() {
|
|
if (QThread::currentThread() != thread()) {
|
|
AnimationDetails result;
|
|
QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection,
|
|
Q_RETURN_ARG(AnimationDetails, result));
|
|
return result;
|
|
}
|
|
return _animationDetails;
|
|
}
|
|
|
|
void ScriptableAvatar::update(float deltatime) {
|
|
if (_bind.isNull() && !_skeletonFBXURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton.
|
|
_bind = DependencyManager::get<AnimationCache>()->getAnimation(_skeletonFBXURL);
|
|
}
|
|
|
|
// Run animation
|
|
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && _bind->isLoaded()) {
|
|
|
|
float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps;
|
|
if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) {
|
|
while (currentFrame >= _animationDetails.lastFrame) {
|
|
currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame);
|
|
}
|
|
_animationDetails.currentFrame = currentFrame;
|
|
|
|
const QVector<FBXJoint>& modelJoints = _bind->getGeometry().joints;
|
|
QStringList animationJointNames = _animation->getJointNames();
|
|
|
|
if (_jointData.size() != modelJoints.size()) {
|
|
_jointData.resize(modelJoints.size());
|
|
}
|
|
|
|
const int frameCount = _animation->getFrames().size();
|
|
const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount);
|
|
const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount);
|
|
const float frameFraction = glm::fract(currentFrame);
|
|
|
|
for (int i = 0; i < animationJointNames.size(); i++) {
|
|
const QString& name = animationJointNames[i];
|
|
// As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than
|
|
// trusting the .fst (which is sometimes not updated to match changes to .fbx).
|
|
int mapping = _bind->getGeometry().getJointIndex(name);
|
|
if (mapping != -1 && !_maskedJoints.contains(name)) {
|
|
JointData& data = _jointData[mapping];
|
|
|
|
auto newRotation = modelJoints[mapping].preRotation *
|
|
safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
|
// We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in),
|
|
// but we don't do so for MyAvatar yet, so let's not be different here.
|
|
if (data.rotation != newRotation) {
|
|
data.rotation = newRotation;
|
|
data.rotationSet = true;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
_animation.clear();
|
|
}
|
|
}
|
|
}
|