mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
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:
commit
8e1d4eb0a6
6 changed files with 155 additions and 21 deletions
|
@ -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
|
||||
|
|
88
assignment-client/src/avatars/ScriptableAvatar.cpp
Normal file
88
assignment-client/src/avatars/ScriptableAvatar.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
40
assignment-client/src/avatars/ScriptableAvatar.h
Normal file
40
assignment-client/src/avatars/ScriptableAvatar.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(""));
|
||||
|
|
Loading…
Reference in a new issue