mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-15 21:18:06 +02:00
Merge pull request #11481 from ZappoMan/animatedOverlays
support animation of model overlays
This commit is contained in:
commit
e7705175a3
2 changed files with 237 additions and 0 deletions
|
@ -9,6 +9,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "ModelOverlay.h"
|
||||
#include <Rig.h>
|
||||
|
||||
|
@ -60,6 +63,15 @@ void ModelOverlay::update(float deltatime) {
|
|||
_model->simulate(deltatime);
|
||||
}
|
||||
_isLoaded = _model->isActive();
|
||||
|
||||
|
||||
if (isAnimatingSomething()) {
|
||||
if (!jointsMapped()) {
|
||||
mapAnimationJoints(_model->getJointNames());
|
||||
}
|
||||
animate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ModelOverlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
|
@ -172,6 +184,51 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
_updateModel = true;
|
||||
}
|
||||
|
||||
auto animationSettings = properties["animationSettings"];
|
||||
if (animationSettings.canConvert(QVariant::Map)) {
|
||||
QVariantMap animationSettingsMap = animationSettings.toMap();
|
||||
|
||||
auto animationURL = animationSettingsMap["url"];
|
||||
auto animationFPS = animationSettingsMap["fps"];
|
||||
auto animationCurrentFrame = animationSettingsMap["currentFrame"];
|
||||
auto animationFirstFrame = animationSettingsMap["firstFrame"];
|
||||
auto animationLastFrame = animationSettingsMap["lastFrame"];
|
||||
auto animationRunning = animationSettingsMap["running"];
|
||||
auto animationLoop = animationSettingsMap["loop"];
|
||||
auto animationHold = animationSettingsMap["hold"];
|
||||
auto animationAllowTranslation = animationSettingsMap["allowTranslation"];
|
||||
|
||||
if (animationURL.canConvert(QVariant::Url)) {
|
||||
_animationURL = animationURL.toUrl();
|
||||
}
|
||||
if (animationFPS.isValid()) {
|
||||
_animationFPS = animationFPS.toFloat();
|
||||
}
|
||||
if (animationCurrentFrame.isValid()) {
|
||||
_animationCurrentFrame = animationCurrentFrame.toFloat();
|
||||
}
|
||||
if (animationFirstFrame.isValid()) {
|
||||
_animationFirstFrame = animationFirstFrame.toFloat();
|
||||
}
|
||||
if (animationLastFrame.isValid()) {
|
||||
_animationLastFrame = animationLastFrame.toFloat();
|
||||
}
|
||||
|
||||
if (animationRunning.canConvert(QVariant::Bool)) {
|
||||
_animationRunning = animationRunning.toBool();
|
||||
}
|
||||
if (animationLoop.canConvert(QVariant::Bool)) {
|
||||
_animationLoop = animationLoop.toBool();
|
||||
}
|
||||
if (animationHold.canConvert(QVariant::Bool)) {
|
||||
_animationHold = animationHold.toBool();
|
||||
}
|
||||
if (animationAllowTranslation.canConvert(QVariant::Bool)) {
|
||||
_animationAllowTranslation = animationAllowTranslation.toBool();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <typename vectorType, typename itemType>
|
||||
|
@ -259,6 +316,24 @@ QVariant ModelOverlay::getProperty(const QString& property) {
|
|||
});
|
||||
}
|
||||
|
||||
// animation properties
|
||||
if (property == "animationSettings") {
|
||||
QVariantMap animationSettingsMap;
|
||||
|
||||
animationSettingsMap["url"] = _animationURL;
|
||||
animationSettingsMap["fps"] = _animationFPS;
|
||||
animationSettingsMap["currentFrame"] = _animationCurrentFrame;
|
||||
animationSettingsMap["firstFrame"] = _animationFirstFrame;
|
||||
animationSettingsMap["lastFrame"] = _animationLastFrame;
|
||||
animationSettingsMap["running"] = _animationRunning;
|
||||
animationSettingsMap["loop"] = _animationLoop;
|
||||
animationSettingsMap["hold"]= _animationHold;
|
||||
animationSettingsMap["allowTranslation"] = _animationAllowTranslation;
|
||||
|
||||
return animationSettingsMap;
|
||||
}
|
||||
|
||||
|
||||
return Volume3DOverlay::getProperty(property);
|
||||
}
|
||||
|
||||
|
@ -301,3 +376,134 @@ QString ModelOverlay::getName() const {
|
|||
}
|
||||
return QString("Overlay:") + getType() + ":" + _url.toString();
|
||||
}
|
||||
|
||||
|
||||
void ModelOverlay::animate() {
|
||||
|
||||
if (!_animation || !_animation->isLoaded() || !_model || !_model->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QVector<JointData> jointsData;
|
||||
|
||||
const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
|
||||
int frameCount = frames.size();
|
||||
if (frameCount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_lastAnimated) {
|
||||
_lastAnimated = usecTimestampNow();
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
auto interval = now - _lastAnimated;
|
||||
_lastAnimated = now;
|
||||
float deltaTime = (float)interval / (float)USECS_PER_SECOND;
|
||||
_animationCurrentFrame += (deltaTime * _animationFPS);
|
||||
|
||||
int animationCurrentFrame = (int)(glm::floor(_animationCurrentFrame)) % frameCount;
|
||||
if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) {
|
||||
animationCurrentFrame = 0;
|
||||
}
|
||||
|
||||
if (animationCurrentFrame == _lastKnownCurrentFrame) {
|
||||
return;
|
||||
}
|
||||
_lastKnownCurrentFrame = animationCurrentFrame;
|
||||
|
||||
if (_jointMapping.size() != _model->getJointStateCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList animationJointNames = _animation->getGeometry().getJointNames();
|
||||
auto& fbxJoints = _animation->getGeometry().joints;
|
||||
|
||||
auto& originalFbxJoints = _model->getFBXGeometry().joints;
|
||||
auto& originalFbxIndices = _model->getFBXGeometry().jointIndices;
|
||||
|
||||
const QVector<glm::quat>& rotations = frames[_lastKnownCurrentFrame].rotations;
|
||||
const QVector<glm::vec3>& translations = frames[_lastKnownCurrentFrame].translations;
|
||||
|
||||
jointsData.resize(_jointMapping.size());
|
||||
for (int j = 0; j < _jointMapping.size(); j++) {
|
||||
int index = _jointMapping[j];
|
||||
|
||||
if (index >= 0) {
|
||||
glm::mat4 translationMat;
|
||||
|
||||
if (_animationAllowTranslation) {
|
||||
if (index < translations.size()) {
|
||||
translationMat = glm::translate(translations[index]);
|
||||
}
|
||||
} else if (index < animationJointNames.size()) {
|
||||
QString jointName = fbxJoints[index].name;
|
||||
|
||||
if (originalFbxIndices.contains(jointName)) {
|
||||
// Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get its translation.
|
||||
int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual.
|
||||
translationMat = glm::translate(originalFbxJoints[remappedIndex].translation);
|
||||
}
|
||||
}
|
||||
glm::mat4 rotationMat;
|
||||
if (index < rotations.size()) {
|
||||
rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation);
|
||||
} else {
|
||||
rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation);
|
||||
}
|
||||
|
||||
glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform *
|
||||
rotationMat * fbxJoints[index].postTransform);
|
||||
auto& jointData = jointsData[j];
|
||||
jointData.translation = extractTranslation(finalMat);
|
||||
jointData.translationSet = true;
|
||||
jointData.rotation = glmExtractRotation(finalMat);
|
||||
jointData.rotationSet = true;
|
||||
}
|
||||
}
|
||||
// Set the data in the model
|
||||
copyAnimationJointDataToModel(jointsData);
|
||||
}
|
||||
|
||||
|
||||
void ModelOverlay::mapAnimationJoints(const QStringList& modelJointNames) {
|
||||
|
||||
// if we don't have animation, or we're already joint mapped then bail early
|
||||
if (!hasAnimation() || jointsMapped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_animation || _animation->getURL() != _animationURL) {
|
||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(_animationURL);
|
||||
}
|
||||
|
||||
if (_animation && _animation->isLoaded()) {
|
||||
QStringList animationJointNames = _animation->getJointNames();
|
||||
|
||||
if (modelJointNames.size() > 0 && animationJointNames.size() > 0) {
|
||||
_jointMapping.resize(modelJointNames.size());
|
||||
for (int i = 0; i < modelJointNames.size(); i++) {
|
||||
_jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]);
|
||||
}
|
||||
_jointMappingCompleted = true;
|
||||
_jointMappingURL = _animationURL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelOverlay::copyAnimationJointDataToModel(QVector<JointData> jointsData) {
|
||||
if (!_model || !_model->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// relay any inbound joint changes from scripts/animation/network to the model/rig
|
||||
for (int index = 0; index < jointsData.size(); ++index) {
|
||||
auto& jointData = jointsData[index];
|
||||
_model->setJointRotation(index, true, jointData.rotation, 1.0f);
|
||||
_model->setJointTranslation(index, true, jointData.translation, 1.0f);
|
||||
}
|
||||
_updateModel = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_ModelOverlay_h
|
||||
|
||||
#include <Model.h>
|
||||
#include <AnimationCache.h>
|
||||
|
||||
#include "Volume3DOverlay.h"
|
||||
|
||||
|
@ -45,6 +46,9 @@ public:
|
|||
|
||||
float getLoadPriority() const { return _loadPriority; }
|
||||
|
||||
bool hasAnimation() const { return !_animationURL.isEmpty(); }
|
||||
bool jointsMapped() const { return _jointMappingURL == _animationURL && _jointMappingCompleted; }
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
|
@ -53,6 +57,14 @@ protected:
|
|||
template <typename vectorType, typename itemType>
|
||||
vectorType mapJoints(mapFunction<itemType> function) const;
|
||||
|
||||
void animate();
|
||||
void mapAnimationJoints(const QStringList& modelJointNames);
|
||||
bool isAnimatingSomething() const {
|
||||
return !_animationURL.isEmpty() && _animationRunning && _animationFPS != 0.0f;
|
||||
}
|
||||
void copyAnimationJointDataToModel(QVector<JointData> jointsData);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
ModelPointer _model;
|
||||
|
@ -62,6 +74,25 @@ private:
|
|||
bool _updateModel = { false };
|
||||
bool _scaleToFit = { false };
|
||||
float _loadPriority { 0.0f };
|
||||
|
||||
AnimationPointer _animation;
|
||||
|
||||
QUrl _animationURL;
|
||||
float _animationFPS { 0.0f };
|
||||
float _animationCurrentFrame { 0.0f };
|
||||
bool _animationRunning { false };
|
||||
bool _animationLoop { false };
|
||||
float _animationFirstFrame { 0.0f };
|
||||
float _animationLastFrame = { 0.0f };
|
||||
bool _animationHold { false };
|
||||
bool _animationAllowTranslation { false };
|
||||
uint64_t _lastAnimated { 0 };
|
||||
int _lastKnownCurrentFrame { -1 };
|
||||
|
||||
QUrl _jointMappingURL;
|
||||
bool _jointMappingCompleted { false };
|
||||
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_ModelOverlay_h
|
||||
|
|
Loading…
Reference in a new issue