mirror of
https://github.com/overte-org/overte.git
synced 2025-04-16 18:53:37 +02:00
Merge pull request #6097 from howard-stearns/expose-anim-vars
Expose animation vars to Javascript
This commit is contained in:
commit
fcfb44207f
10 changed files with 300 additions and 9 deletions
|
@ -248,6 +248,16 @@ QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const {
|
|||
return _localLights;
|
||||
}
|
||||
|
||||
QVector<QUuid> AvatarManager::getAvatarIdentifiers() {
|
||||
QReadLocker locker(&_hashLock);
|
||||
return _avatarHash.keys().toVector();
|
||||
}
|
||||
AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
|
||||
QReadLocker locker(&_hashLock);
|
||||
return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar.
|
||||
}
|
||||
|
||||
|
||||
void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
result.swap(_motionStatesToDelete);
|
||||
|
|
|
@ -52,6 +52,10 @@ public:
|
|||
|
||||
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
||||
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
|
||||
// Currently, your own avatar will be included as the null avatar id.
|
||||
Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
|
||||
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
|
||||
|
||||
|
||||
void getObjectsToDelete(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAdd(VectorOfMotionStates& motionStates);
|
||||
|
|
|
@ -111,6 +111,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 QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _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();
|
||||
|
|
|
@ -111,9 +111,6 @@ static const PalmData* getPalmWithIndex(Hand* hand, int index) {
|
|||
const float PALM_PRIORITY = DEFAULT_PRIORITY;
|
||||
// Called within Model::simulate call, below.
|
||||
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||
if (_owningAvatar->isMyAvatar()) {
|
||||
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
|
||||
}
|
||||
Head* head = _owningAvatar->getHead();
|
||||
if (_owningAvatar->isMyAvatar()) {
|
||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||
|
@ -182,6 +179,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
_rig->updateFromHandParameters(handParams, deltaTime);
|
||||
|
||||
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
|
||||
// evaluate AnimGraph animation and update jointStates.
|
||||
Model::updateRig(deltaTime, parentTransform);
|
||||
|
||||
|
@ -196,7 +194,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
_rig->updateFromEyeParameters(eyeParams);
|
||||
|
||||
// rebuild the jointState transform for the eyes only
|
||||
// rebuild the jointState transform for the eyes only. Must be after updateRig.
|
||||
_rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform);
|
||||
_rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform);
|
||||
|
||||
|
|
121
libraries/animation/src/AnimVariant.cpp
Normal file
121
libraries/animation/src/AnimVariant.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// 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 <QThread>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include "AnimVariant.h" // which has AnimVariant/AnimVariantMap
|
||||
|
||||
QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const {
|
||||
if (QThread::currentThread() != engine->thread()) {
|
||||
qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread();
|
||||
Q_ASSERT(false);
|
||||
return QScriptValue();
|
||||
}
|
||||
QScriptValue target = engine->newObject();
|
||||
auto setOne = [&] (const QString& name, const AnimVariant& value) {
|
||||
switch (value.getType()) {
|
||||
case AnimVariant::Type::Bool:
|
||||
target.setProperty(name, value.getBool());
|
||||
break;
|
||||
case AnimVariant::Type::Int:
|
||||
target.setProperty(name, value.getInt());
|
||||
break;
|
||||
case AnimVariant::Type::Float:
|
||||
target.setProperty(name, value.getFloat());
|
||||
break;
|
||||
case AnimVariant::Type::String:
|
||||
target.setProperty(name, value.getString());
|
||||
break;
|
||||
case AnimVariant::Type::Vec3:
|
||||
target.setProperty(name, vec3toScriptValue(engine, value.getVec3()));
|
||||
break;
|
||||
case AnimVariant::Type::Quat:
|
||||
target.setProperty(name, quatToScriptValue(engine, value.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");
|
||||
}
|
||||
};
|
||||
if (useNames) { // copy only the requested names
|
||||
for (const QString& name : names) {
|
||||
auto search = _map.find(name);
|
||||
if (search != _map.end()) { // scripts are allowed to request names that do not exist
|
||||
setOne(name, search->second);
|
||||
}
|
||||
}
|
||||
|
||||
} else { // copy all of them
|
||||
for (auto& pair : _map) {
|
||||
setOne(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) {
|
||||
for (auto& pair : other._map) {
|
||||
_map[pair.first] = pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) {
|
||||
if (QThread::currentThread() != source.engine()->thread()) {
|
||||
qCWarning(animation) << "Cannot examine Javacript object from non-script thread" << QThread::currentThread();
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
// 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());
|
||||
} else if (value.isString()) {
|
||||
set(property.name(), value.toString());
|
||||
} else if (value.isNumber()) {
|
||||
int asInteger = value.toInt32();
|
||||
float asFloat = value.toNumber();
|
||||
if (asInteger == asFloat) {
|
||||
set(property.name(), asInteger);
|
||||
} else {
|
||||
set(property.name(), asFloat);
|
||||
}
|
||||
} else { // Try to get x,y,z and possibly w
|
||||
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; // we got either a vector or quaternion object, so don't fall through to warning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name();
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,11 +12,14 @@
|
|||
#define hifi_AnimVariant_h
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <QScriptValue>
|
||||
#include "AnimationLogging.h"
|
||||
#include "StreamUtils.h"
|
||||
|
||||
class AnimVariant {
|
||||
public:
|
||||
|
@ -58,8 +61,9 @@ public:
|
|||
void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; }
|
||||
|
||||
bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; }
|
||||
int getInt() const { assert(_type == Type::Int); return _val.intVal; }
|
||||
float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; }
|
||||
int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; }
|
||||
float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; }
|
||||
|
||||
const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); }
|
||||
const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); }
|
||||
const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); }
|
||||
|
@ -156,8 +160,15 @@ public:
|
|||
void setTrigger(const QString& key) { _triggers.insert(key); }
|
||||
void clearTriggers() { _triggers.clear(); }
|
||||
|
||||
void clearMap() { _map.clear(); }
|
||||
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, const QStringList& names, bool useNames) const;
|
||||
// Side-effect us with the value of object's own properties. (No inherited properties.)
|
||||
void animVariantMapFromScriptValue(const QScriptValue& object);
|
||||
void copyVariantsFrom(const AnimVariantMap& other);
|
||||
|
||||
#ifdef NDEBUG
|
||||
void dump() const {
|
||||
qCDebug(animation) << "AnimVariantMap =";
|
||||
|
@ -196,4 +207,8 @@ protected:
|
|||
std::set<QString> _triggers;
|
||||
};
|
||||
|
||||
typedef std::function<void(QScriptValue)> AnimVariantResultHandler;
|
||||
Q_DECLARE_METATYPE(AnimVariantResultHandler);
|
||||
Q_DECLARE_METATYPE(AnimVariantMap)
|
||||
|
||||
#endif // hifi_AnimVariant_h
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <queue>
|
||||
#include <QScriptValueIterator>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
#include <DebugDraw.h>
|
||||
|
@ -576,6 +577,71 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
_lastPosition = worldPosition;
|
||||
}
|
||||
|
||||
// Allow script to add/remove handlers and report results, from within their thread.
|
||||
QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread
|
||||
QMutexLocker locker(&_stateMutex);
|
||||
// Find a safe id, even if there are lots of many scripts add and remove handlers repeatedly.
|
||||
while (!_nextStateHandlerId || _stateHandlers.contains(_nextStateHandlerId)) { // 0 is unused, and don't reuse existing after wrap.
|
||||
_nextStateHandlerId++;
|
||||
}
|
||||
StateHandler& data = _stateHandlers[_nextStateHandlerId];
|
||||
data.function = handler;
|
||||
data.useNames = propertiesList.isArray();
|
||||
if (data.useNames) {
|
||||
data.propertyNames = propertiesList.toVariant().toStringList();
|
||||
}
|
||||
return QScriptValue(_nextStateHandlerId); // suitable for giving to removeAnimationStateHandler
|
||||
}
|
||||
void Rig::removeAnimationStateHandler(QScriptValue identifier) { // called in script thread
|
||||
QMutexLocker locker(&_stateMutex);
|
||||
_stateHandlers.remove(identifier.isNumber() ? identifier.toInt32() : 0); // silently continues if handler not present. 0 is unused
|
||||
}
|
||||
void Rig::animationStateHandlerResult(int identifier, QScriptValue result) { // called synchronously from script
|
||||
QMutexLocker locker(&_stateMutex);
|
||||
auto found = _stateHandlers.find(identifier);
|
||||
if (found == _stateHandlers.end()) {
|
||||
return; // Don't use late-breaking results that got reported after the handler was removed.
|
||||
}
|
||||
found.value().results.animVariantMapFromScriptValue(result); // Into our own copy.
|
||||
}
|
||||
|
||||
void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread)
|
||||
QMutexLocker locker(&_stateMutex);
|
||||
// It might pay to produce just one AnimVariantMap copy here, with a union of all the requested propertyNames,
|
||||
// rather than having each callAnimationStateHandler invocation make its own copy.
|
||||
// However, that copying is done on the script's own time rather than ours, so even if it's less cpu, it would be more
|
||||
// work on the avatar update thread (which is possibly the main thread).
|
||||
for (auto data = _stateHandlers.begin(); data != _stateHandlers.end(); data++) {
|
||||
// call out:
|
||||
int identifier = data.key();
|
||||
StateHandler& value = data.value();
|
||||
QScriptValue& function = value.function;
|
||||
auto handleResult = [this, identifier](QScriptValue result) { // called in script thread to get the result back to us.
|
||||
animationStateHandlerResult(identifier, result);
|
||||
};
|
||||
// invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture
|
||||
// the state of _animVars and allow continued changes to _animVars in this thread without conflict.
|
||||
QMetaObject::invokeMethod(function.engine(), "callAnimationStateHandler", Qt::QueuedConnection,
|
||||
Q_ARG(QScriptValue, function),
|
||||
Q_ARG(AnimVariantMap, _animVars),
|
||||
Q_ARG(QStringList, value.propertyNames),
|
||||
Q_ARG(bool, value.useNames),
|
||||
Q_ARG(AnimVariantResultHandler, handleResult));
|
||||
// It turns out that, for thread-safety reasons, ScriptEngine::callAnimationStateHandler will invoke itself if called from other
|
||||
// than the script thread. Thus the above _could_ be replaced with an ordinary call, which will then trigger the same
|
||||
// invokeMethod as is done explicitly above. However, the script-engine library depends on this animation library, not vice versa.
|
||||
// We could create an AnimVariantCallingMixin class in shared, with an abstract virtual slot
|
||||
// AnimVariantCallingMixin::callAnimationStateHandler (and move AnimVariantMap/AnimVaraintResultHandler to shared), but the
|
||||
// call site here would look like this instead of the above:
|
||||
// dynamic_cast<AnimVariantCallingMixin*>(function.engine())->callAnimationStateHandler(function, ..., handleResult);
|
||||
// This works (I tried it), but the result would be that we would still have same runtime type checks as the invokeMethod above
|
||||
// (occuring within the ScriptEngine::callAnimationStateHandler invokeMethod trampoline), _plus_ another runtime check for the dynamic_cast.
|
||||
|
||||
// gather results in (likely from an earlier update):
|
||||
_animVars.copyVariantsFrom(value.results); // If multiple handlers write the same anim var, the last registgered wins. (_map preserves order).
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
||||
|
||||
if (_enableAnimGraph) {
|
||||
|
@ -583,6 +649,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
|||
return;
|
||||
}
|
||||
|
||||
updateAnimationStateHandlers();
|
||||
// evaluate the animation
|
||||
AnimNode::Triggers triggersOut;
|
||||
AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#define __hifi__Rig__
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QScriptValue>
|
||||
|
||||
#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS
|
||||
|
||||
|
@ -51,6 +53,12 @@ typedef std::shared_ptr<Rig> RigPointer;
|
|||
|
||||
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
||||
public:
|
||||
struct StateHandler {
|
||||
AnimVariantMap results;
|
||||
QStringList propertyNames;
|
||||
QScriptValue function;
|
||||
bool useNames;
|
||||
};
|
||||
|
||||
struct HeadParameters {
|
||||
float leanSideways = 0.0f; // degrees
|
||||
|
@ -199,10 +207,14 @@ 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)
|
||||
QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList);
|
||||
void removeAnimationStateHandler(QScriptValue handler);
|
||||
void animationStateHandlerResult(int identifier, QScriptValue result);
|
||||
|
||||
bool getModelOffset(glm::vec3& modelOffsetOut) const;
|
||||
|
||||
protected:
|
||||
void updateAnimationStateHandlers();
|
||||
|
||||
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
||||
void updateNeckJoint(int index, const HeadParameters& params);
|
||||
|
@ -241,6 +253,11 @@ public:
|
|||
float _desiredStateAge = 0.0f;
|
||||
float _leftHandOverlayAlpha = 0.0f;
|
||||
float _rightHandOverlayAlpha = 0.0f;
|
||||
|
||||
private:
|
||||
QMap<int, StateHandler> _stateHandlers;
|
||||
int _nextStateHandlerId {0};
|
||||
QMutex _stateMutex;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Rig__) */
|
||||
|
|
|
@ -285,6 +285,27 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) {
|
|||
}
|
||||
}
|
||||
|
||||
// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
|
||||
// callAnimationStateHandler requires that the type be registered.
|
||||
// These two are meaningful, if we ever do want to use them...
|
||||
static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) {
|
||||
QStringList unused;
|
||||
return parameters.animVariantMapToScriptValue(engine, unused, false);
|
||||
}
|
||||
static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) {
|
||||
parameters.animVariantMapFromScriptValue(value);
|
||||
}
|
||||
// ... while these two are not. But none of the four are ever used.
|
||||
static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine, const AnimVariantResultHandler& resultHandler) {
|
||||
qCCritical(scriptengine) << "Attempt to marshall result handler to javascript";
|
||||
assert(false);
|
||||
return QScriptValue();
|
||||
}
|
||||
static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantResultHandler& resultHandler) {
|
||||
qCCritical(scriptengine) << "Attempt to marshall result handler from javascript";
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void ScriptEngine::init() {
|
||||
if (_isInitialized) {
|
||||
return; // only initialize once
|
||||
|
@ -343,6 +364,8 @@ void ScriptEngine::init() {
|
|||
registerGlobalObject("Vec3", &_vec3Library);
|
||||
registerGlobalObject("Uuid", &_uuidLibrary);
|
||||
registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
|
||||
qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
|
||||
|
||||
// constants
|
||||
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
|
||||
|
@ -357,7 +380,7 @@ void ScriptEngine::init() {
|
|||
void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
|
||||
qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "registerValue",
|
||||
Q_ARG(const QString&, valueName),
|
||||
|
@ -743,6 +766,27 @@ void ScriptEngine::stop() {
|
|||
}
|
||||
}
|
||||
|
||||
// Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread.
|
||||
void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qDebug() << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "callAnimationStateHandler",
|
||||
Q_ARG(QScriptValue, callback),
|
||||
Q_ARG(AnimVariantMap, parameters),
|
||||
Q_ARG(QStringList, names),
|
||||
Q_ARG(bool, useNames),
|
||||
Q_ARG(AnimVariantResultHandler, resultHandler));
|
||||
return;
|
||||
}
|
||||
QScriptValue javascriptParameters = parameters.animVariantMapToScriptValue(this, names, useNames);
|
||||
QScriptValueList callingArguments;
|
||||
callingArguments << javascriptParameters;
|
||||
QScriptValue result = callback.call(QScriptValue(), callingArguments);
|
||||
resultHandler(result);
|
||||
}
|
||||
|
||||
void ScriptEngine::timerFired() {
|
||||
QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
|
||||
QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
|
||||
|
@ -928,9 +972,8 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin
|
|||
if (QThread::currentThread() != thread()) {
|
||||
qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
|
||||
assert(false);
|
||||
return;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (!_registeredHandlers.contains(entityID)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <AnimationCache.h>
|
||||
#include <AnimVariant.h>
|
||||
#include <AvatarData.h>
|
||||
#include <AvatarHashMap.h>
|
||||
#include <LimitedNodeList.h>
|
||||
|
@ -142,6 +143,9 @@ public:
|
|||
// NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety
|
||||
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
||||
|
||||
public slots:
|
||||
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
|
||||
|
||||
signals:
|
||||
void scriptLoaded(const QString& scriptFilename);
|
||||
void errorLoadingScript(const QString& scriptFilename);
|
||||
|
|
Loading…
Reference in a new issue