Merge branch 'master' of https://github.com/highfidelity/hifi into temp0

This commit is contained in:
Sam Gateau 2014-11-13 11:04:12 -08:00
commit 420118e9db
16 changed files with 538 additions and 102 deletions

View file

@ -22,6 +22,9 @@ EntityPropertyDialogBox = (function () {
var dimensionZ;
var rescalePercentage;
var editModelID = -1;
var previousAnimationIsPlaying;
var previousAnimationFrameIndex;
var previousAnimationSettings;
that.cleanup = function () {
};
@ -47,10 +50,15 @@ EntityPropertyDialogBox = (function () {
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
array.push({ label: "Animation is playing:", value: properties.animationIsPlaying });
previousAnimationIsPlaying = properties.animationIsPlaying;
index++;
array.push({ label: "Animation FPS:", value: properties.animationFPS });
index++;
array.push({ label: "Animation Frame:", value: properties.animationFrameIndex });
previousAnimationFrameIndex = properties.animationFrameIndex;
index++;
array.push({ label: "Animation Settings:", value: properties.animationSettings });
previousAnimationSettings = properties.animationSettings;
index++;
array.push({ label: "Textures:", value: properties.textures });
index++;
@ -237,9 +245,29 @@ EntityPropertyDialogBox = (function () {
if (properties.type == "Model") {
properties.modelURL = array[index++].value;
properties.animationURL = array[index++].value;
properties.animationIsPlaying = array[index++].value;
var newAnimationIsPlaying = array[index++].value;
if (previousAnimationIsPlaying != newAnimationIsPlaying) {
properties.animationIsPlaying = newAnimationIsPlaying;
} else {
delete properties.animationIsPlaying;
}
properties.animationFPS = array[index++].value;
properties.animationFrameIndex = array[index++].value;
var newAnimationFrameIndex = array[index++].value;
if (previousAnimationFrameIndex != newAnimationFrameIndex) {
properties.animationFrameIndex = newAnimationFrameIndex;
} else {
delete properties.animationFrameIndex;
}
var newAnimationSettings = array[index++].value;
if (previousAnimationSettings != newAnimationSettings) {
properties.animationSettings = newAnimationSettings;
} else {
delete properties.animationSettings;
}
properties.textures = array[index++].value;
index++; // skip textureNames label
}

View file

@ -33,7 +33,7 @@ void AnimationHandle::setPriority(float priority) {
if (_priority == priority) {
return;
}
if (_running) {
if (isRunning()) {
_model->_runningAnimations.removeOne(_self);
if (priority < _priority) {
replaceMatchingPriorities(priority);
@ -47,7 +47,8 @@ void AnimationHandle::setPriority(float priority) {
}
void AnimationHandle::setStartAutomatically(bool startAutomatically) {
if ((_startAutomatically = startAutomatically) && !_running) {
_animationLoop.setStartAutomatically(startAutomatically);
if (getStartAutomatically() && !isRunning()) {
start();
}
}
@ -58,48 +59,59 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
}
void AnimationHandle::setRunning(bool running) {
if (_running == running) {
if (isRunning() == running) {
// if we're already running, this is the same as a restart
if (running) {
// move back to the beginning
_frameIndex = _firstFrame;
setFrameIndex(getFirstFrame());
}
return;
}
if ((_running = running)) {
_animationLoop.setRunning(running);
if (isRunning()) {
if (!_model->_runningAnimations.contains(_self)) {
insertSorted(_model->_runningAnimations, _self);
}
_frameIndex = _firstFrame;
} else {
_model->_runningAnimations.removeOne(_self);
replaceMatchingPriorities(0.0f);
}
emit runningChanged(_running);
emit runningChanged(isRunning());
}
AnimationHandle::AnimationHandle(Model* model) :
QObject(model),
_model(model),
_fps(30.0f),
_priority(1.0f),
_loop(false),
_hold(false),
_startAutomatically(false),
_firstFrame(0.0f),
_lastFrame(FLT_MAX),
_running(false) {
_priority(1.0f)
{
}
AnimationDetails AnimationHandle::getAnimationDetails() const {
AnimationDetails details(_role, _url, _fps, _priority, _loop, _hold,
_startAutomatically, _firstFrame, _lastFrame, _running, _frameIndex);
AnimationDetails details(_role, _url, getFPS(), _priority, getLoop(), getHold(),
getStartAutomatically(), getFirstFrame(), getLastFrame(), isRunning(), getFrameIndex());
return details;
}
void AnimationHandle::setAnimationDetails(const AnimationDetails& details) {
setRole(details.role);
setURL(details.url);
setFPS(details.fps);
setPriority(details.priority);
setLoop(details.loop);
setHold(details.hold);
setStartAutomatically(details.startAutomatically);
setFirstFrame(details.firstFrame);
setLastFrame(details.lastFrame);
setRunning(details.running);
setFrameIndex(details.frameIndex);
// NOTE: AnimationDetails doesn't support maskedJoints
//setMaskedJoints(const QStringList& maskedJoints);
}
void AnimationHandle::simulate(float deltaTime) {
_frameIndex += deltaTime * _fps;
_animationLoop.simulate(deltaTime);
// update the joint mappings if necessary/possible
if (_jointMappings.isEmpty()) {
@ -125,26 +137,15 @@ void AnimationHandle::simulate(float deltaTime) {
stop();
return;
}
float endFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - (_loop ? 0.0f : 1.0f));
float startFrameIndex = qMin(_firstFrame, endFrameIndex);
if ((!_loop && (_frameIndex < startFrameIndex || _frameIndex > endFrameIndex)) || startFrameIndex == endFrameIndex) {
// passed the end; apply the last frame
applyFrame(glm::clamp(_frameIndex, startFrameIndex, endFrameIndex));
if (!_hold) {
stop();
}
return;
}
// wrap within the the desired range
if (_frameIndex < startFrameIndex) {
_frameIndex = endFrameIndex - glm::mod(endFrameIndex - _frameIndex, endFrameIndex - startFrameIndex);
} else if (_frameIndex > endFrameIndex) {
_frameIndex = startFrameIndex + glm::mod(_frameIndex - startFrameIndex, endFrameIndex - startFrameIndex);
}
// TODO: When moving the loop/frame calculations to AnimationLoop class, we changed this behavior
// see AnimationLoop class for more details. Do we need to support clamping the endFrameIndex to
// the max number of frames in the geometry???
//
// float endFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - (_loop ? 0.0f : 1.0f));
// blend between the closest two frames
applyFrame(_frameIndex);
applyFrame(getFrameIndex());
}
void AnimationHandle::applyFrame(float frameIndex) {

View file

@ -19,6 +19,7 @@
#include <QVector>
#include <AnimationCache.h>
#include <AnimationLoop.h>
class AnimationHandle;
class Model;
@ -38,38 +39,40 @@ public:
void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; }
void setFPS(float fps) { _fps = fps; }
float getFPS() const { return _fps; }
void setPriority(float priority);
float getPriority() const { return _priority; }
void setLoop(bool loop) { _loop = loop; }
bool getLoop() const { return _loop; }
void setHold(bool hold) { _hold = hold; }
bool getHold() const { return _hold; }
void setStartAutomatically(bool startAutomatically);
bool getStartAutomatically() const { return _startAutomatically; }
void setFirstFrame(float firstFrame) { _firstFrame = firstFrame; }
float getFirstFrame() const { return _firstFrame; }
void setLastFrame(float lastFrame) { _lastFrame = lastFrame; }
float getLastFrame() const { return _lastFrame; }
void setMaskedJoints(const QStringList& maskedJoints);
const QStringList& getMaskedJoints() const { return _maskedJoints; }
void setRunning(bool running);
bool isRunning() const { return _running; }
void setFrameIndex(float frameIndex) { _frameIndex = glm::clamp(_frameIndex, _firstFrame, _lastFrame); }
float getFrameIndex() const { return _frameIndex; }
void setFPS(float fps) { _animationLoop.setFPS(fps); }
float getFPS() const { return _animationLoop.getFPS(); }
void setLoop(bool loop) { _animationLoop.setLoop(loop); }
bool getLoop() const { return _animationLoop.getLoop(); }
void setHold(bool hold) { _animationLoop.setHold(hold); }
bool getHold() const { return _animationLoop.getHold(); }
void setStartAutomatically(bool startAutomatically);
bool getStartAutomatically() const { return _animationLoop.getStartAutomatically(); }
void setFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); }
float getFirstFrame() const { return _animationLoop.getFirstFrame(); }
void setLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
float getLastFrame() const { return _animationLoop.getLastFrame(); }
void setRunning(bool running);
bool isRunning() const { return _animationLoop.isRunning(); }
void setFrameIndex(float frameIndex) { _animationLoop.setFrameIndex(frameIndex); }
float getFrameIndex() const { return _animationLoop.getFrameIndex(); }
AnimationDetails getAnimationDetails() const;
void setAnimationDetails(const AnimationDetails& details);
signals:
@ -95,17 +98,12 @@ private:
AnimationPointer _animation;
QString _role;
QUrl _url;
float _fps;
float _priority;
bool _loop;
bool _hold;
bool _startAutomatically;
float _firstFrame;
float _lastFrame;
QStringList _maskedJoints;
bool _running;
QVector<int> _jointMappings;
float _frameIndex;
AnimationLoop _animationLoop;
};

View file

@ -94,5 +94,4 @@ Q_DECLARE_METATYPE(AnimationDetails);
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event);
void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& event);
#endif // hifi_AnimationCache_h

View file

@ -0,0 +1,95 @@
//
// AnimationLoop.cpp
// libraries/animation
//
// Created by Brad Hefta-Gaub on 11/12/14.
// Copyright (c) 2014 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 "AnimationCache.h"
#include "AnimationLoop.h"
AnimationLoop::AnimationLoop() :
_fps(30.0f),
_loop(false),
_hold(false),
_startAutomatically(false),
_firstFrame(0.0f),
_lastFrame(FLT_MAX),
_running(false),
_frameIndex(0.0f)
{
}
AnimationLoop::AnimationLoop(const AnimationDetails& animationDetails) :
_fps(animationDetails.fps),
_loop(animationDetails.loop),
_hold(animationDetails.hold),
_startAutomatically(animationDetails.startAutomatically),
_firstFrame(animationDetails.firstFrame),
_lastFrame(animationDetails.lastFrame),
_running(animationDetails.running),
_frameIndex(animationDetails.frameIndex)
{
}
AnimationLoop::AnimationLoop(float fps, bool loop, bool hold, bool startAutomatically, float firstFrame,
float lastFrame, bool running, float frameIndex) :
_fps(fps),
_loop(loop),
_hold(hold),
_startAutomatically(startAutomatically),
_firstFrame(firstFrame),
_lastFrame(lastFrame),
_running(running),
_frameIndex(frameIndex)
{
}
void AnimationLoop::simulate(float deltaTime) {
_frameIndex += deltaTime * _fps;
// If we knew the number of frames from the animation, we'd consider using it here
// animationGeometry.animationFrames.size()
float maxFrame = _lastFrame;
float endFrameIndex = qMin(_lastFrame, maxFrame - (_loop ? 0.0f : 1.0f));
float startFrameIndex = qMin(_firstFrame, endFrameIndex);
if ((!_loop && (_frameIndex < startFrameIndex || _frameIndex > endFrameIndex)) || startFrameIndex == endFrameIndex) {
// passed the end; apply the last frame
_frameIndex = glm::clamp(_frameIndex, startFrameIndex, endFrameIndex);
if (!_hold) {
stop();
}
} else {
// wrap within the the desired range
if (_frameIndex < startFrameIndex) {
_frameIndex = endFrameIndex - glm::mod(endFrameIndex - _frameIndex, endFrameIndex - startFrameIndex);
} else if (_frameIndex > endFrameIndex) {
_frameIndex = startFrameIndex + glm::mod(_frameIndex - startFrameIndex, endFrameIndex - startFrameIndex);
}
}
}
void AnimationLoop::setStartAutomatically(bool startAutomatically) {
if ((_startAutomatically = startAutomatically) && !isRunning()) {
start();
}
}
void AnimationLoop::setRunning(bool running) {
if (_running == running) {
if (running) {
// move back to the beginning
_frameIndex = _firstFrame;
}
return;
}
if ((_running = running)) {
_frameIndex = _firstFrame;
}
}

View file

@ -0,0 +1,63 @@
//
// AnimationLoop.h
// libraries/script-engine/src/
//
// Created by Brad Hefta-Gaub on 11/12/14.
// Copyright (c) 2014 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
//
#ifndef hifi_AnimationLoop_h
#define hifi_AnimationLoop_h
class AnimationDetails;
class AnimationLoop {
public:
AnimationLoop();
AnimationLoop(const AnimationDetails& animationDetails);
AnimationLoop(float fps, bool loop, bool hold, bool startAutomatically, float firstFrame,
float lastFrame, bool running, float frameIndex);
void setFPS(float fps) { _fps = fps; }
float getFPS() const { return _fps; }
void setLoop(bool loop) { _loop = loop; }
bool getLoop() const { return _loop; }
void setHold(bool hold) { _hold = hold; }
bool getHold() const { return _hold; }
void setStartAutomatically(bool startAutomatically);
bool getStartAutomatically() const { return _startAutomatically; }
void setFirstFrame(float firstFrame) { _firstFrame = firstFrame; }
float getFirstFrame() const { return _firstFrame; }
void setLastFrame(float lastFrame) { _lastFrame = lastFrame; }
float getLastFrame() const { return _lastFrame; }
void setRunning(bool running);
bool isRunning() const { return _running; }
void setFrameIndex(float frameIndex) { _frameIndex = glm::clamp(frameIndex, _firstFrame, _lastFrame); }
float getFrameIndex() const { return _frameIndex; }
void start() { setRunning(true); }
void stop() { setRunning(false); }
void simulate(float deltaTime);
private:
float _fps;
bool _loop;
bool _hold;
bool _startAutomatically;
float _firstFrame;
float _lastFrame;
bool _running;
float _frameIndex;
};
#endif // hifi_AnimationLoop_h

View file

@ -11,6 +11,7 @@
#include <QDebug>
#include <QObject>
#include <QtCore/QJsonDocument>
#include <ByteCountCoding.h>
#include <GLMHelpers.h>
@ -66,6 +67,7 @@ EntityItemProperties::EntityItemProperties() :
_animationIsPlaying(ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING),
_animationFrameIndex(ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
_animationFPS(ModelEntityItem::DEFAULT_ANIMATION_FPS),
_animationSettings(""),
_glowLevel(0.0f),
_localRenderAlpha(1.0f),
_isSpotlight(false),
@ -76,6 +78,8 @@ EntityItemProperties::EntityItemProperties() :
_animationIsPlayingChanged(false),
_animationFrameIndexChanged(false),
_animationFPSChanged(false),
_animationSettingsChanged(false),
_glowLevelChanged(false),
_localRenderAlphaChanged(false),
_isSpotlightChanged(false),
@ -117,6 +121,58 @@ void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sitting
}
}
void EntityItemProperties::setAnimationSettings(const QString& value) {
// the animations setting is a JSON string that may contain various animation settings.
// if it includes fps, frameIndex, or running, those values will be parsed out and
// will over ride the regular animation settings
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
QJsonObject settingsAsJsonObject = settingsAsJson.object();
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
if (settingsMap.contains("fps")) {
float fps = settingsMap["fps"].toFloat();
setAnimationFPS(fps);
}
if (settingsMap.contains("frameIndex")) {
float frameIndex = settingsMap["frameIndex"].toFloat();
setAnimationFrameIndex(frameIndex);
}
if (settingsMap.contains("running")) {
bool running = settingsMap["running"].toBool();
setAnimationIsPlaying(running);
}
_animationSettings = value;
_animationSettingsChanged = true;
}
QString EntityItemProperties::getAnimationSettings() const {
// the animations setting is a JSON string that may contain various animation settings.
// if it includes fps, frameIndex, or running, those values will be parsed out and
// will over ride the regular animation settings
QString value = _animationSettings;
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
QJsonObject settingsAsJsonObject = settingsAsJson.object();
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
QVariant fpsValue(getAnimationFPS());
settingsMap["fps"] = fpsValue;
QVariant frameIndexValue(getAnimationFrameIndex());
settingsMap["frameIndex"] = frameIndexValue;
QVariant runningValue(getAnimationIsPlaying());
settingsMap["running"] = runningValue;
settingsAsJsonObject = QJsonObject::fromVariantMap(settingsMap);
QJsonDocument newDocument(settingsAsJsonObject);
QByteArray jsonByteArray = newDocument.toJson(QJsonDocument::Compact);
QString jsonByteString(jsonByteArray);
return jsonByteString;
}
void EntityItemProperties::debugDump() const {
qDebug() << "EntityItemProperties...";
@ -149,6 +205,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FPS, animationFPS);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_SETTINGS, animationSettings);
CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible);
CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint);
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity);
@ -201,8 +258,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFrameIndex);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFrameIndex);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(animationSettings,getAnimationSettings());
COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel);
COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha);
COPY_PROPERTY_TO_QSCRIPTVALUE(ignoreForCollisions);
@ -276,6 +334,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFrameIndex, setAnimationFrameIndex);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(animationSettings, setAnimationSettings);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(glowLevel, setGlowLevel);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localRenderAlpha, setLocalRenderAlpha);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(ignoreForCollisions, setIgnoreForCollisions);
@ -452,6 +511,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, properties.getCutoff());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, appendValue, properties.getLocked());
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, properties.getTextures());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, properties.getAnimationSettings());
}
if (propertyCount > 0) {
int endOfEntityItemData = packetData->getUncompressedByteOffset();
@ -661,7 +721,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_TEXTURES, setTextures);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_SETTINGS, setAnimationSettings);
return valid;
}
@ -714,6 +775,7 @@ void EntityItemProperties::markAllChanged() {
_animationIsPlayingChanged = true;
_animationFrameIndexChanged = true;
_animationFPSChanged = true;
_animationSettingsChanged = true;
_glowLevelChanged = true;
_localRenderAlphaChanged = true;
_isSpotlightChanged = true;

View file

@ -80,8 +80,9 @@ enum EntityPropertyList {
// used by Model entities
PROP_TEXTURES,
PROP_ANIMATION_SETTINGS,
PROP_LAST_ITEM = PROP_CUTOFF
PROP_LAST_ITEM = PROP_ANIMATION_SETTINGS
};
typedef PropertyFlags<EntityPropertyList> EntityPropertyFlags;
@ -178,6 +179,8 @@ public:
float getAnimationFrameIndex() const { return _animationFrameIndex; }
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
float getAnimationFPS() const { return _animationFPS; }
QString getAnimationSettings() const;
float getGlowLevel() const { return _glowLevel; }
float getLocalRenderAlpha() const { return _localRenderAlpha; }
const QString& getScript() const { return _script; }
@ -189,6 +192,8 @@ public:
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; }
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; }
void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; }
void setAnimationSettings(const QString& value);
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
void setLocalRenderAlpha(float value) { _localRenderAlpha = value; _localRenderAlphaChanged = true; }
void setScript(const QString& value) { _script = value; _scriptChanged = true; }
@ -342,6 +347,7 @@ private:
bool _animationIsPlaying;
float _animationFrameIndex;
float _animationFPS;
QString _animationSettings;
float _glowLevel;
float _localRenderAlpha;
bool _isSpotlight;
@ -352,6 +358,7 @@ private:
bool _animationIsPlayingChanged;
bool _animationFrameIndexChanged;
bool _animationFPSChanged;
bool _animationSettingsChanged;
bool _glowLevelChanged;
bool _localRenderAlphaChanged;
bool _isSpotlightChanged;

View file

@ -40,6 +40,18 @@
} \
}
#define READ_ENTITY_PROPERTY_SETTER(P,T,M) \
if (propertyFlags.getHasProperty(P)) { \
T fromBuffer; \
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); \
dataAt += sizeof(fromBuffer); \
bytesRead += sizeof(fromBuffer); \
if (overwriteLocalData) { \
M(fromBuffer); \
} \
}
#define READ_ENTITY_PROPERTY_QUAT(P,M) \
if (propertyFlags.getHasProperty(P)) { \
glm::quat fromBuffer; \

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtCore/QJsonDocument>
#include <ByteCountCoding.h>
#include <GLMHelpers.h>
@ -33,7 +35,6 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID, const EntityI
{
_type = EntityTypes::Model;
setProperties(properties, true);
_animationFrameIndex = 0.0f;
_lastAnimated = usecTimestampNow();
_jointMappingCompleted = false;
_color[0] = _color[1] = _color[2] = 0;
@ -50,6 +51,7 @@ EntityItemProperties ModelEntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFPS, getAnimationFPS);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationSettings, getAnimationSettings);
return properties;
}
@ -64,6 +66,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties, bool
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFPS, setAnimationFPS);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationSettings, setAnimationSettings);
if (somethingChanged) {
bool wantDebug = false;
@ -100,10 +103,29 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
READ_ENTITY_PROPERTY_STRING(PROP_MODEL_URL, setModelURL);
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_URL, setAnimationURL);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, _animationFPS);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, _animationFrameIndex);
READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, _animationIsPlaying);
// Because we're using AnimationLoop which will reset the frame index if you change it's running state
// we want to read these values in the order they appear in the buffer, but call our setters in an
// order that allows AnimationLoop to preserve the correct frame rate.
float animationFPS = getAnimationFPS();
float animationFrameIndex = getAnimationFrameIndex();
bool animationIsPlaying = getAnimationIsPlaying();
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, animationFPS);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, animationFrameIndex);
READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, animationIsPlaying);
if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) {
setAnimationIsPlaying(animationIsPlaying);
}
if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) {
setAnimationFPS(animationFPS);
}
if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) {
setAnimationFrameIndex(animationFrameIndex);
}
READ_ENTITY_PROPERTY_STRING(PROP_TEXTURES, setTextures);
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_SETTINGS, setAnimationSettings);
return bytesRead;
}
@ -199,19 +221,25 @@ int ModelEntityItem::oldVersionReadEntityDataFromBuffer(const unsigned char* dat
bytesRead += animationURLLength;
// animationIsPlaying
memcpy(&_animationIsPlaying, dataAt, sizeof(_animationIsPlaying));
dataAt += sizeof(_animationIsPlaying);
bytesRead += sizeof(_animationIsPlaying);
bool animationIsPlaying;
memcpy(&animationIsPlaying, dataAt, sizeof(animationIsPlaying));
dataAt += sizeof(animationIsPlaying);
bytesRead += sizeof(animationIsPlaying);
setAnimationIsPlaying(animationIsPlaying);
// animationFrameIndex
memcpy(&_animationFrameIndex, dataAt, sizeof(_animationFrameIndex));
dataAt += sizeof(_animationFrameIndex);
bytesRead += sizeof(_animationFrameIndex);
float animationFrameIndex;
memcpy(&animationFrameIndex, dataAt, sizeof(animationFrameIndex));
dataAt += sizeof(animationFrameIndex);
bytesRead += sizeof(animationFrameIndex);
setAnimationFrameIndex(animationFrameIndex);
// animationFPS
memcpy(&_animationFPS, dataAt, sizeof(_animationFPS));
dataAt += sizeof(_animationFPS);
bytesRead += sizeof(_animationFPS);
float animationFPS;
memcpy(&animationFPS, dataAt, sizeof(animationFPS));
dataAt += sizeof(animationFPS);
bytesRead += sizeof(animationFPS);
setAnimationFPS(animationFPS);
}
}
return bytesRead;
@ -227,6 +255,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
requestedProperties += PROP_ANIMATION_FPS;
requestedProperties += PROP_ANIMATION_FRAME_INDEX;
requestedProperties += PROP_ANIMATION_PLAYING;
requestedProperties += PROP_ANIMATION_SETTINGS;
requestedProperties += PROP_TEXTURES;
return requestedProperties;
@ -249,6 +278,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, getAnimationFrameIndex());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, getAnimationIsPlaying());
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, getTextures());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, getAnimationSettings());
}
@ -314,7 +344,7 @@ QVector<glm::quat> ModelEntityItem::getAnimationFrame() {
int frameCount = frames.size();
if (frameCount > 0) {
int animationFrameIndex = (int)(glm::floor(_animationFrameIndex)) % frameCount;
int animationFrameIndex = (int)(glm::floor(getAnimationFrameIndex())) % frameCount;
if (animationFrameIndex < 0 || animationFrameIndex > frameCount) {
animationFrameIndex = 0;
@ -363,7 +393,7 @@ void ModelEntityItem::update(const quint64& updateTime) {
if (getAnimationIsPlaying()) {
float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND;
_lastAnimated = now;
_animationFrameIndex += deltaTime * _animationFPS;
_animationLoop.simulate(deltaTime);
} else {
_lastAnimated = now;
}
@ -377,3 +407,94 @@ void ModelEntityItem::debugDump() const {
qDebug() << " model URL:" << getModelURL();
}
void ModelEntityItem::setAnimationSettings(const QString& value) {
// the animations setting is a JSON string that may contain various animation settings.
// if it includes fps, frameIndex, or running, those values will be parsed out and
// will over ride the regular animation settings
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
QJsonObject settingsAsJsonObject = settingsAsJson.object();
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
if (settingsMap.contains("fps")) {
float fps = settingsMap["fps"].toFloat();
setAnimationFPS(fps);
}
if (settingsMap.contains("frameIndex")) {
float frameIndex = settingsMap["frameIndex"].toFloat();
setAnimationFrameIndex(frameIndex);
}
if (settingsMap.contains("running")) {
bool running = settingsMap["running"].toBool();
setAnimationIsPlaying(running);
}
if (settingsMap.contains("firstFrame")) {
float firstFrame = settingsMap["firstFrame"].toFloat();
setAnimationFirstFrame(firstFrame);
}
if (settingsMap.contains("lastFrame")) {
float lastFrame = settingsMap["lastFrame"].toFloat();
setAnimationLastFrame(lastFrame);
}
if (settingsMap.contains("loop")) {
bool loop = settingsMap["loop"].toBool();
setAnimationLoop(loop);
}
if (settingsMap.contains("hold")) {
bool hold = settingsMap["hold"].toBool();
setAnimationHold(hold);
}
if (settingsMap.contains("startAutomatically")) {
bool startAutomatically = settingsMap["startAutomatically"].toBool();
setAnimationStartAutomatically(startAutomatically);
}
_animationSettings = value;
}
QString ModelEntityItem::getAnimationSettings() const {
// the animations setting is a JSON string that may contain various animation settings.
// if it includes fps, frameIndex, or running, those values will be parsed out and
// will over ride the regular animation settings
QString value = _animationSettings;
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
QJsonObject settingsAsJsonObject = settingsAsJson.object();
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
QVariant fpsValue(getAnimationFPS());
settingsMap["fps"] = fpsValue;
QVariant frameIndexValue(getAnimationFrameIndex());
settingsMap["frameIndex"] = frameIndexValue;
QVariant runningValue(getAnimationIsPlaying());
settingsMap["running"] = runningValue;
QVariant firstFrameValue(getAnimationFirstFrame());
settingsMap["firstFrame"] = firstFrameValue;
QVariant lastFrameValue(getAnimationLastFrame());
settingsMap["lastFrame"] = lastFrameValue;
QVariant loopValue(getAnimationLoop());
settingsMap["loop"] = loopValue;
QVariant holdValue(getAnimationHold());
settingsMap["hold"] = holdValue;
QVariant startAutomaticallyValue(getAnimationStartAutomatically());
settingsMap["startAutomatically"] = startAutomaticallyValue;
settingsAsJsonObject = QJsonObject::fromVariantMap(settingsMap);
QJsonDocument newDocument(settingsAsJsonObject);
QByteArray jsonByteArray = newDocument.toJson(QJsonDocument::Compact);
QString jsonByteString(jsonByteArray);
return jsonByteString;
}

View file

@ -12,6 +12,8 @@
#ifndef hifi_ModelEntityItem_h
#define hifi_ModelEntityItem_h
#include <AnimationLoop.h>
#include "EntityItem.h"
class ModelEntityItem : public EntityItem {
@ -73,21 +75,38 @@ public:
void setModelURL(const QString& url) { _modelURL = url; }
void setAnimationURL(const QString& url) { _animationURL = url; }
static const float DEFAULT_ANIMATION_FRAME_INDEX;
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; }
void setAnimationFrameIndex(float value) { _animationLoop.setFrameIndex(value); }
void setAnimationSettings(const QString& value);
static const bool DEFAULT_ANIMATION_IS_PLAYING;
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; }
void setAnimationIsPlaying(bool value) { _animationLoop.setRunning(value); }
static const float DEFAULT_ANIMATION_FPS;
void setAnimationFPS(float value) { _animationFPS = value; }
void setAnimationFPS(float value) { _animationLoop.setFPS(value); }
void setAnimationLoop(bool loop) { _animationLoop.setLoop(loop); }
bool getAnimationLoop() const { return _animationLoop.getLoop(); }
void setAnimationHold(bool hold) { _animationLoop.setHold(hold); }
bool getAnimationHold() const { return _animationLoop.getHold(); }
void setAnimationStartAutomatically(bool startAutomatically) { _animationLoop.setStartAutomatically(startAutomatically); }
bool getAnimationStartAutomatically() const { return _animationLoop.getStartAutomatically(); }
void setAnimationFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); }
float getAnimationFirstFrame() const { return _animationLoop.getFirstFrame(); }
void setAnimationLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
void mapJoints(const QStringList& modelJointNames);
QVector<glm::quat> getAnimationFrame();
bool jointsMapped() const { return _jointMappingCompleted; }
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
float getAnimationFrameIndex() const { return _animationFrameIndex; }
float getAnimationFPS() const { return _animationFPS; }
bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); }
float getAnimationFrameIndex() const { return _animationLoop.getFrameIndex(); }
float getAnimationFPS() const { return _animationLoop.getFPS(); }
QString getAnimationSettings() const;
static const QString DEFAULT_TEXTURES;
const QString& getTextures() const { return _textures; }
@ -106,9 +125,8 @@ protected:
quint64 _lastAnimated;
QString _animationURL;
float _animationFrameIndex; // we keep this as a float and round to int only when we need the exact index
bool _animationIsPlaying;
float _animationFPS;
AnimationLoop _animationLoop;
QString _animationSettings;
QString _textures;
// used on client side

View file

@ -75,7 +75,7 @@ PacketVersion versionForPacketType(PacketType type) {
return 1;
case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData:
return VERSION_ENTITIES_SUPPORT_DIMENSIONS;
return VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS;
case PacketTypeEntityErase:
return 2;
case PacketTypeAudioStreamStats:

View file

@ -124,6 +124,7 @@ const PacketVersion VERSION_ROOT_ELEMENT_HAS_DATA = 2;
const PacketVersion VERSION_ENTITIES_SUPPORT_SPLIT_MTU = 3;
const PacketVersion VERSION_ENTITIES_HAS_FILE_BREAKS = VERSION_ENTITIES_SUPPORT_SPLIT_MTU;
const PacketVersion VERSION_ENTITIES_SUPPORT_DIMENSIONS = 4;
const PacketVersion VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS = 5;
const PacketVersion VERSION_VOXELS_HAS_FILE_BREAKS = 1;
#endif // hifi_PacketHeaders_h

View file

@ -23,6 +23,7 @@
#include <GeometryUtil.h>
#include <OctalCode.h>
#include <LogHandler.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <Shape.h>
@ -71,6 +72,10 @@ void Octree::recurseTreeWithPostOperation(RecurseOctreeOperation operation, void
void Octree::recurseElementWithOperation(OctreeElement* element, RecurseOctreeOperation operation, void* extraData,
int recursionCount) {
if (recursionCount > DANGEROUSLY_DEEP_RECURSION) {
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(
"Octree::recurseElementWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!");
qDebug() << "Octree::recurseElementWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!";
return;
}
@ -89,7 +94,11 @@ void Octree::recurseElementWithOperation(OctreeElement* element, RecurseOctreeOp
void Octree::recurseElementWithPostOperation(OctreeElement* element, RecurseOctreeOperation operation, void* extraData,
int recursionCount) {
if (recursionCount > DANGEROUSLY_DEEP_RECURSION) {
qDebug() << "Octree::recurseElementWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!\n";
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(
"Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!");
qDebug() << "Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!";
return;
}
@ -115,6 +124,10 @@ void Octree::recurseElementWithOperationDistanceSorted(OctreeElement* element, R
const glm::vec3& point, void* extraData, int recursionCount) {
if (recursionCount > DANGEROUSLY_DEEP_RECURSION) {
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(
"Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!");
qDebug() << "Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!";
return;
}
@ -152,7 +165,11 @@ void Octree::recurseTreeWithOperator(RecurseOctreeOperator* operatorObject) {
bool Octree::recurseElementWithOperator(OctreeElement* element, RecurseOctreeOperator* operatorObject, int recursionCount) {
if (recursionCount > DANGEROUSLY_DEEP_RECURSION) {
qDebug() << "Octree::recurseElementWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!";
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(
"Octree::recurseElementWithOperator() reached DANGEROUSLY_DEEP_RECURSION, bailing!");
qDebug() << "Octree::recurseElementWithOperator() reached DANGEROUSLY_DEEP_RECURSION, bailing!";
return false;
}

View file

@ -13,6 +13,7 @@
#include <QString>
#include <QStringList>
#include <LogHandler.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
@ -854,8 +855,12 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
const int MAX_RESONABLE_FLIGHT_TIME = 200 * USECS_PER_SECOND; // 200 seconds is more than enough time for a packet to arrive
const int MIN_RESONABLE_FLIGHT_TIME = 0;
if (flightTime > MAX_RESONABLE_FLIGHT_TIME || flightTime < MIN_RESONABLE_FLIGHT_TIME) {
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(
"ignoring unreasonable packet... flightTime: -?\\d+ nodeClockSkewUsec: -?\\d+ usecs");
qDebug() << "ignoring unreasonable packet... flightTime:" << flightTime
<< " nodeClockSkewUsec:" << nodeClockSkewUsec << " usecs";;
<< "nodeClockSkewUsec:" << nodeClockSkewUsec << "usecs";;
return; // ignore any packets that are unreasonable
}

View file

@ -34,6 +34,15 @@ struct xColor {
unsigned char blue;
};
inline QDebug& operator<<(QDebug& dbg, const xColor& c) {
dbg.nospace() << "{type='xColor'"
", red=" << c.red <<
", green=" << c.green <<
", blue=" << c.blue <<
"}";
return dbg;
}
static const float ZERO = 0.0f;
static const float ONE = 1.0f;
static const float ONE_HALF = 0.5f;