mirror of
https://github.com/overte-org/overte.git
synced 2025-08-19 19:40:57 +02:00
Initial prototype of exposing anim vars to javascript.
This commit is contained in:
parent
073b019458
commit
f25cc93936
5 changed files with 129 additions and 0 deletions
|
@ -106,6 +106,18 @@ public:
|
|||
Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
|
||||
void clearJointAnimationPriorities();
|
||||
// Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update.
|
||||
// The handler will be called with an animStateDictionaryIn that has all those properties specified by the (possibly empty)
|
||||
// propertiesList argument. However for debugging, if the properties argument is null, all internal animGraph state is provided.
|
||||
// The animStateDictionaryOut can be a different object than animStateDictionaryIn. Any properties set in animStateDictionaryOut
|
||||
// will override those of the internal animation machinery.
|
||||
// The animStateDictionaryIn may be shared among multiple handlers, and thus may contain additional properties specified when
|
||||
// adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut)
|
||||
// a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change.
|
||||
// It is not specified in what order multiple handlers are called.
|
||||
Q_INVOKABLE void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { _rig->addAnimationStateHandler(handler, propertiesList); }
|
||||
// Removes a handler previously added by addAnimationStateHandler.
|
||||
Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); }
|
||||
|
||||
// get/set avatar data
|
||||
void saveData();
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <QScriptValue>
|
||||
#include "AnimationLogging.h"
|
||||
|
||||
class AnimVariant {
|
||||
|
@ -158,6 +159,11 @@ public:
|
|||
|
||||
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
|
||||
|
||||
// Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties.
|
||||
QScriptValue animVariantMapToScriptValue(QScriptEngine* engine);
|
||||
// Side-effect us with the value of object's own properties. (No inherited properties.)
|
||||
void animVariantMapFromScriptValue(const QScriptValue& object);
|
||||
|
||||
#ifdef NDEBUG
|
||||
void dump() const {
|
||||
qCDebug(animation) << "AnimVariantMap =";
|
||||
|
|
91
libraries/animation/src/AnimVariantMap.cpp
Normal file
91
libraries/animation/src/AnimVariantMap.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// AnimVariantMap.cpp
|
||||
// library/animation
|
||||
//
|
||||
// Created by Howard Stearns on 10/15/15.
|
||||
// Copyright (c) 2015 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 <QScriptEngine>
|
||||
#include <QScriptValueIterator>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include "AnimVariant.h"
|
||||
|
||||
QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine) {
|
||||
QScriptValue target = engine->newObject();
|
||||
for (auto& pair : _map) {
|
||||
switch (pair.second.getType()) {
|
||||
case AnimVariant::Type::Bool:
|
||||
target.setProperty(pair.first, pair.second.getBool());
|
||||
break;
|
||||
case AnimVariant::Type::Int:
|
||||
target.setProperty(pair.first, pair.second.getInt());
|
||||
break;
|
||||
case AnimVariant::Type::Float:
|
||||
target.setProperty(pair.first, pair.second.getFloat());
|
||||
break;
|
||||
case AnimVariant::Type::String:
|
||||
target.setProperty(pair.first, pair.second.getString());
|
||||
break;
|
||||
case AnimVariant::Type::Vec3:
|
||||
target.setProperty(pair.first, vec3toScriptValue(engine, pair.second.getVec3()));
|
||||
break;
|
||||
case AnimVariant::Type::Quat:
|
||||
target.setProperty(pair.first, quatToScriptValue(engine, pair.second.getQuat()));
|
||||
break;
|
||||
default:
|
||||
// Note that we don't do mat4 in Javascript currently, and there's not yet a reason to start now.
|
||||
assert("AnimVariant::Type" == "valid");
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) {
|
||||
// POTENTIAL OPTIMIZATION: cache the types we've seen. I.e, keep a dictionary mapping property names to an enumeration of types.
|
||||
// Whenever we identify a new outbound type in animVariantMapToScriptValue above, or a new inbound type in the code that follows here,
|
||||
// we would enter it into the dictionary. Then switch on that type here, with the code that follow being executed only if
|
||||
// the type is not known. One problem with that is that there is no checking that two different script use the same name differently.
|
||||
QScriptValueIterator property(source);
|
||||
// Note: QScriptValueIterator iterates only over source's own properties. It does not follow the prototype chain.
|
||||
while (property.hasNext()) {
|
||||
property.next();
|
||||
QScriptValue value = property.value();
|
||||
if (value.isBool()) {
|
||||
set(property.name(), value.toBool());
|
||||
continue;
|
||||
} else if (value.isString()) {
|
||||
set(property.name(), value.toString());
|
||||
continue;
|
||||
} else if (value.isNumber()) {
|
||||
int asInteger = value.toInt32();
|
||||
float asFloat = value.toNumber();
|
||||
if (asInteger == asFloat) {
|
||||
set(property.name(), asInteger);
|
||||
} else {
|
||||
set(property.name(), asFloat);
|
||||
}
|
||||
continue;
|
||||
} else if (value.isObject()) {
|
||||
QScriptValue x = value.property("x");
|
||||
if (x.isNumber()) {
|
||||
QScriptValue y = value.property("y");
|
||||
if (y.isNumber()) {
|
||||
QScriptValue z = value.property("z");
|
||||
if (z.isNumber()) {
|
||||
QScriptValue w = value.property("w");
|
||||
if (w.isNumber()) {
|
||||
set(property.name(), glm::quat(x.toNumber(), y.toNumber(), z.toNumber(), w.toNumber()));
|
||||
} else {
|
||||
set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name();
|
||||
}
|
||||
}
|
|
@ -566,6 +566,20 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_stateHandlers.isValid()) {
|
||||
// TODO: iterate multiple handlers, but with one shared arg.
|
||||
// TODO: fill the properties based on the union of requested properties. (Keep all properties objs and compute new union when add/remove handler.)
|
||||
// TODO: check QScriptEngine::hasUncaughtException()
|
||||
// TODO: call asynchronously (through a signal on script), so that each script is single threaded, and so we never block here.
|
||||
// This will require inboundMaps to be kept in the list of per-handler data.
|
||||
QScriptEngine* engine = _stateHandlers.engine();
|
||||
QScriptValue outboundMap = _animVars.animVariantMapToScriptValue(engine);
|
||||
QScriptValueList args;
|
||||
args << outboundMap;
|
||||
QScriptValue inboundMap = _stateHandlers.call(QScriptValue(), args);
|
||||
_animVars.animVariantMapFromScriptValue(inboundMap);
|
||||
//qCDebug(animation) << _animVars.lookup("foo", QString("not set"));
|
||||
}
|
||||
// evaluate the animation
|
||||
AnimNode::Triggers triggersOut;
|
||||
AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#define __hifi__Rig__
|
||||
|
||||
#include <QObject>
|
||||
#include <QScriptValue>
|
||||
|
||||
#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS
|
||||
|
||||
|
@ -198,6 +199,8 @@ public:
|
|||
AnimNode::ConstPointer getAnimNode() const { return _animNode; }
|
||||
AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }
|
||||
bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics)
|
||||
void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { _stateHandlers = handler; }
|
||||
void removeAnimationStateHandler(QScriptValue handler) { _stateHandlers = QScriptValue(); }
|
||||
|
||||
bool getModelOffset(glm::vec3& modelOffsetOut) const;
|
||||
|
||||
|
@ -238,6 +241,9 @@ public:
|
|||
RigRole _state = RigRole::Idle;
|
||||
float _leftHandOverlayAlpha = 0.0f;
|
||||
float _rightHandOverlayAlpha = 0.0f;
|
||||
|
||||
private:
|
||||
QScriptValue _stateHandlers {};
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Rig__) */
|
||||
|
|
Loading…
Reference in a new issue