Merge pull request #3210 from Atlante45/add_start_stop_animation_for_scripted_avatar

Add start stop animation for scripted avatar
This commit is contained in:
Brad Hefta-Gaub 2014-07-28 19:43:58 -07:00
commit 8e1d4eb0a6
6 changed files with 155 additions and 21 deletions

View file

@ -29,6 +29,8 @@
#include <ParticlesScriptingInterface.h> // TODO: consider moving to scriptengine.h
#include <ModelsScriptingInterface.h> // TODO: consider moving to scriptengine.h
#include "avatars/ScriptableAvatar.h"
#include "Agent.h"
Agent::Agent(const QByteArray& packet) :
@ -228,7 +230,7 @@ void Agent::run() {
qDebug() << "Downloaded script:" << scriptContents;
// setup an Avatar for the script to use
AvatarData scriptedAvatar;
ScriptableAvatar scriptedAvatar(&_scriptEngine);
scriptedAvatar.setForceFaceshiftConnected(true);
// call model URL setters with empty URLs so our avatar, if user, will have the default models

View file

@ -0,0 +1,88 @@
//
// 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 "ScriptableAvatar.h"
ScriptableAvatar::ScriptableAvatar(ScriptEngine* scriptEngine) : _scriptEngine(scriptEngine), _animation(NULL) {
connect(_scriptEngine, SIGNAL(update(float)), this, SLOT(update(float)));
}
// 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 = _scriptEngine->getAnimationCache()->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) {
// Run animation
if (_animation != NULL && _animation->isValid() && _animation->getFrames().size() > 0) {
QStringList modelJoints = getJointNames();
QStringList animationJoints = _animation->getJointNames();
if (_jointData.size() != modelJoints.size()) {
_jointData.resize(modelJoints.size());
}
float frameIndex = _animationDetails.frameIndex + deltatime * _animationDetails.fps;
if (_animationDetails.loop || frameIndex < _animationDetails.lastFrame) {
while (frameIndex >= _animationDetails.lastFrame) {
frameIndex -= (_animationDetails.lastFrame - _animationDetails.firstFrame);
}
_animationDetails.frameIndex = frameIndex;
const int frameCount = _animation->getFrames().size();
const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(frameIndex) % frameCount);
const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(frameIndex) % frameCount);
const float frameFraction = glm::fract(frameIndex);
for (int i = 0; i < modelJoints.size(); i++) {
int mapping = animationJoints.indexOf(modelJoints[i]);
if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) {
JointData& data = _jointData[i];
data.valid = true;
data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
} else {
_jointData[i].valid = false;
}
}
} else {
_animation.clear();
}
}
}

View file

@ -0,0 +1,40 @@
//
// ScriptableAvatar.h
//
//
// 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
//
#ifndef hifi_ScriptableAvatar_h
#define hifi_ScriptableAvatar_h
#include <AnimationCache.h>
#include <AvatarData.h>
#include <ScriptEngine.h>
class ScriptableAvatar : public AvatarData {
Q_OBJECT
public:
ScriptableAvatar(ScriptEngine* scriptEngine);
/// Allows scripts to run animations.
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
Q_INVOKABLE void stopAnimation();
Q_INVOKABLE AnimationDetails getAnimationDetails();
private slots:
void update(float deltatime);
private:
ScriptEngine* _scriptEngine;
AnimationPointer _animation;
AnimationDetails _animationDetails;
QStringList _maskedJoints;
};
#endif // hifi_ScriptableAvatar_h

View file

@ -748,18 +748,19 @@ function Tooltip() {
this.updateText = function(properties) {
var angles = Quat.safeEulerAngles(properties.modelRotation);
var text = "Model Properties:\n"
text += "x: " + properties.position.x.toFixed(this.decimals) + "\n"
text += "y: " + properties.position.y.toFixed(this.decimals) + "\n"
text += "z: " + properties.position.z.toFixed(this.decimals) + "\n"
text += "pitch: " + angles.x.toFixed(this.decimals) + "\n"
text += "yaw: " + angles.y.toFixed(this.decimals) + "\n"
text += "roll: " + angles.z.toFixed(this.decimals) + "\n"
text += "X: " + properties.position.x.toFixed(this.decimals) + "\n"
text += "Y: " + properties.position.y.toFixed(this.decimals) + "\n"
text += "Z: " + properties.position.z.toFixed(this.decimals) + "\n"
text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n"
text += "Yaw: " + angles.y.toFixed(this.decimals) + "\n"
text += "Roll: " + angles.z.toFixed(this.decimals) + "\n"
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n"
text += "ID: " + properties.id + "\n"
text += "model url: " + properties.modelURL + "\n"
text += "animation url: " + properties.animationURL + "\n"
text += "Model URL: " + properties.modelURL + "\n"
text += "Animation URL: " + properties.animationURL + "\n"
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
if (properties.sittingPoints.length > 0) {
text += properties.sittingPoints.length + " sitting points: "
text += properties.sittingPoints.length + " Sitting points: "
for (var i = 0; i < properties.sittingPoints.length; ++i) {
text += properties.sittingPoints[i].name + " "
}
@ -1146,6 +1147,7 @@ function handeMenuEvent(menuItem){
var decimals = 3;
array.push({ label: "Model URL:", value: selectedModelProperties.modelURL });
array.push({ label: "Animation URL:", value: selectedModelProperties.animationURL });
array.push({ label: "Animation is playing:", value: selectedModelProperties.animationIsPlaying });
array.push({ label: "X:", value: selectedModelProperties.position.x.toFixed(decimals) });
array.push({ label: "Y:", value: selectedModelProperties.position.y.toFixed(decimals) });
array.push({ label: "Z:", value: selectedModelProperties.position.z.toFixed(decimals) });
@ -1158,16 +1160,18 @@ function handeMenuEvent(menuItem){
var propertyName = Window.form("Edit Properties", array);
modelSelected = false;
selectedModelProperties.modelURL = array[0].value;
selectedModelProperties.animationURL = array[1].value;
selectedModelProperties.position.x = array[2].value;
selectedModelProperties.position.y = array[3].value;
selectedModelProperties.position.z = array[4].value;
angles.x = array[5].value;
angles.y = array[6].value;
angles.z = array[7].value;
var index = 0;
selectedModelProperties.modelURL = array[index++].value;
selectedModelProperties.animationURL = array[index++].value;
selectedModelProperties.animationIsPlaying = array[index++].value;
selectedModelProperties.position.x = array[index++].value;
selectedModelProperties.position.y = array[index++].value;
selectedModelProperties.position.z = array[index++].value;
angles.x = array[index++].value;
angles.y = array[index++].value;
angles.z = array[index++].value;
selectedModelProperties.modelRotation = Quat.fromVec3Degrees(angles);
selectedModelProperties.radius = array[8].value / 2;
selectedModelProperties.radius = array[index++].value / 2;
Models.editModel(selectedModelID, selectedModelProperties);
}
@ -1200,6 +1204,7 @@ Controller.keyPressEvent.connect(function(event) {
somethingChanged = true;
}
});
Controller.keyReleaseEvent.connect(function(event) {
if (event.text == "z" || event.text == "Z") {
zIsPressed = false;

View file

@ -1878,8 +1878,6 @@ void MyAvatar::renderLaserPointers() {
//Gets the tip position for the laser pointer
glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
const float PALM_TIP_ROD_LENGTH_MULT = 40.0f;
glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition());
glm::vec3 position = palm->getPosition();

View file

@ -58,6 +58,7 @@ public:
static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; }
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
AnimationCache* getAnimationCache() { return &_animationCache; }
/// sets the script contents, will return false if failed, will fail if script is already running
bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString(""));