Allow floating point start/end frames and negative play speeds.

This commit is contained in:
Andrzej Kapolka 2014-06-04 11:47:53 -07:00
parent e48cfb84c1
commit 74afcf5626
6 changed files with 49 additions and 47 deletions

View file

@ -433,11 +433,11 @@ void MyAvatar::removeAnimationHandle(const AnimationHandlePointer& handle) {
} }
void MyAvatar::startAnimation(const QString& url, float fps, float priority, void MyAvatar::startAnimation(const QString& url, float fps, float priority,
bool loop, bool hold, int firstFrame, int lastFrame, const QStringList& maskedJoints) { bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), 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(int, firstFrame), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame),
Q_ARG(int, lastFrame), Q_ARG(const QStringList&, maskedJoints)); Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
return; return;
} }
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
@ -453,11 +453,11 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority,
} }
void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
bool loop, bool hold, int firstFrame, int lastFrame, const QStringList& maskedJoints) { bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startAnimationByRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url), QMetaObject::invokeMethod(this, "startAnimationByRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url),
Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(int, firstFrame), Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame),
Q_ARG(int, lastFrame), Q_ARG(const QStringList&, maskedJoints)); Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
return; return;
} }
// check for a configured animation for the role // check for a configured animation for the role
@ -627,8 +627,8 @@ void MyAvatar::loadData(QSettings* settings) {
handle->setLoop(settings->value("loop", true).toBool()); handle->setLoop(settings->value("loop", true).toBool());
handle->setHold(settings->value("hold", false).toBool()); handle->setHold(settings->value("hold", false).toBool());
handle->setStartAutomatically(settings->value("startAutomatically", true).toBool()); handle->setStartAutomatically(settings->value("startAutomatically", true).toBool());
handle->setFirstFrame(settings->value("firstFrame", 0).toInt()); handle->setFirstFrame(settings->value("firstFrame", 0.0f).toFloat());
handle->setLastFrame(settings->value("lastFrame", INT_MAX).toInt()); handle->setLastFrame(settings->value("lastFrame", INT_MAX).toFloat());
handle->setMaskedJoints(settings->value("maskedJoints").toStringList()); handle->setMaskedJoints(settings->value("maskedJoints").toStringList());
} }
settings->endArray(); settings->endArray();

View file

@ -68,7 +68,7 @@ public:
/// Allows scripts to run animations. /// Allows scripts to run animations.
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
bool hold = false, int firstFrame = 0, int lastFrame = INT_MAX, const QStringList& maskedJoints = QStringList()); bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
/// Stops an animation as identified by a URL. /// Stops an animation as identified by a URL.
Q_INVOKABLE void stopAnimation(const QString& url); Q_INVOKABLE void stopAnimation(const QString& url);
@ -76,8 +76,8 @@ public:
/// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom /// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom
/// animation for the role. /// animation for the role.
Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
float priority = 1.0f, bool loop = false, bool hold = false, int firstFrame = 0, float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
int lastFrame = INT_MAX, const QStringList& maskedJoints = QStringList()); float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
/// Stops an animation identified by its role. /// Stops an animation identified by its role.
Q_INVOKABLE void stopAnimationByRole(const QString& role); Q_INVOKABLE void stopAnimationByRole(const QString& role);

View file

@ -1895,8 +1895,8 @@ AnimationHandle::AnimationHandle(Model* model) :
_loop(false), _loop(false),
_hold(false), _hold(false),
_startAutomatically(false), _startAutomatically(false),
_firstFrame(0), _firstFrame(0.0f),
_lastFrame(INT_MAX), _lastFrame(FLT_MAX),
_running(false) { _running(false) {
} }
@ -1927,35 +1927,34 @@ void AnimationHandle::simulate(float deltaTime) {
stop(); stop();
return; return;
} }
int lastFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - 1); float endFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - (_loop ? 0.0f : 1.0f));
int firstFrameIndex = qMin(_firstFrame, lastFrameIndex); float startFrameIndex = qMin(_firstFrame, endFrameIndex);
if ((!_loop && _frameIndex >= lastFrameIndex) || firstFrameIndex == lastFrameIndex) { if ((!_loop && (_frameIndex < startFrameIndex || _frameIndex > endFrameIndex)) || startFrameIndex == endFrameIndex) {
// passed the end; apply the last frame // passed the end; apply the last frame
const FBXAnimationFrame& frame = animationGeometry.animationFrames.at(lastFrameIndex); applyFrame(glm::clamp(_frameIndex, startFrameIndex, endFrameIndex));
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
JointState& state = _model->_jointStates[mapping];
if (_priority >= state._animationPriority) {
state._rotation = frame.rotations.at(i);
state._animationPriority = _priority;
}
}
}
if (!_hold) { if (!_hold) {
stop(); stop();
} }
return; return;
} }
int frameCount = lastFrameIndex - firstFrameIndex + 1; // wrap within the the desired range
_frameIndex = firstFrameIndex + glm::mod(qMax(_frameIndex - firstFrameIndex, 0.0f), (float)frameCount); if (_frameIndex < startFrameIndex) {
_frameIndex = endFrameIndex - glm::mod(endFrameIndex - _frameIndex, endFrameIndex - startFrameIndex);
} else if (_frameIndex > endFrameIndex) {
_frameIndex = startFrameIndex + glm::mod(_frameIndex - startFrameIndex, endFrameIndex - startFrameIndex);
}
// blend between the closest two frames // blend between the closest two frames
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at( applyFrame(_frameIndex);
firstFrameIndex + ((int)glm::ceil(_frameIndex) - firstFrameIndex) % frameCount); }
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at(
firstFrameIndex + ((int)glm::floor(_frameIndex) - firstFrameIndex) % frameCount); void AnimationHandle::applyFrame(float frameIndex) {
float frameFraction = glm::fract(_frameIndex); const FBXGeometry& animationGeometry = _animation->getGeometry();
int frameCount = animationGeometry.animationFrames.size();
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
float frameFraction = glm::fract(frameIndex);
for (int i = 0; i < _jointMappings.size(); i++) { for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i); int mapping = _jointMappings.at(i);
if (mapping != -1) { if (mapping != -1) {

View file

@ -395,11 +395,11 @@ public:
void setStartAutomatically(bool startAutomatically); void setStartAutomatically(bool startAutomatically);
bool getStartAutomatically() const { return _startAutomatically; } bool getStartAutomatically() const { return _startAutomatically; }
void setFirstFrame(int firstFrame) { _firstFrame = firstFrame; } void setFirstFrame(float firstFrame) { _firstFrame = firstFrame; }
int getFirstFrame() const { return _firstFrame; } float getFirstFrame() const { return _firstFrame; }
void setLastFrame(int lastFrame) { _lastFrame = lastFrame; } void setLastFrame(float lastFrame) { _lastFrame = lastFrame; }
int getLastFrame() const { return _lastFrame; } float getLastFrame() const { return _lastFrame; }
void setMaskedJoints(const QStringList& maskedJoints); void setMaskedJoints(const QStringList& maskedJoints);
const QStringList& getMaskedJoints() const { return _maskedJoints; } const QStringList& getMaskedJoints() const { return _maskedJoints; }
@ -423,6 +423,7 @@ private:
AnimationHandle(Model* model); AnimationHandle(Model* model);
void simulate(float deltaTime); void simulate(float deltaTime);
void applyFrame(float frameIndex);
void replaceMatchingPriorities(float newPriority); void replaceMatchingPriorities(float newPriority);
Model* _model; Model* _model;
@ -435,8 +436,8 @@ private:
bool _loop; bool _loop;
bool _hold; bool _hold;
bool _startAutomatically; bool _startAutomatically;
int _firstFrame; float _firstFrame;
int _lastFrame; float _lastFrame;
QStringList _maskedJoints; QStringList _maskedJoints;
bool _running; bool _running;
QVector<int> _jointMappings; QVector<int> _jointMappings;

View file

@ -98,6 +98,7 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
layout->addRow("FPS:", _fps = new QDoubleSpinBox()); layout->addRow("FPS:", _fps = new QDoubleSpinBox());
_fps->setSingleStep(0.01); _fps->setSingleStep(0.01);
_fps->setMinimum(-FLT_MAX);
_fps->setMaximum(FLT_MAX); _fps->setMaximum(FLT_MAX);
_fps->setValue(handle->getFPS()); _fps->setValue(handle->getFPS());
connect(_fps, SIGNAL(valueChanged(double)), SLOT(updateHandle())); connect(_fps, SIGNAL(valueChanged(double)), SLOT(updateHandle()));
@ -128,15 +129,17 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
_startAutomatically->setChecked(handle->getStartAutomatically()); _startAutomatically->setChecked(handle->getStartAutomatically());
connect(_startAutomatically, SIGNAL(toggled(bool)), SLOT(updateHandle())); connect(_startAutomatically, SIGNAL(toggled(bool)), SLOT(updateHandle()));
layout->addRow("First Frame:", _firstFrame = new QSpinBox()); layout->addRow("First Frame:", _firstFrame = new QDoubleSpinBox());
_firstFrame->setSingleStep(0.01);
_firstFrame->setMaximum(INT_MAX); _firstFrame->setMaximum(INT_MAX);
_firstFrame->setValue(handle->getFirstFrame()); _firstFrame->setValue(handle->getFirstFrame());
connect(_firstFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle())); connect(_firstFrame, SIGNAL(valueChanged(double)), SLOT(updateHandle()));
layout->addRow("Last Frame:", _lastFrame = new QSpinBox()); layout->addRow("Last Frame:", _lastFrame = new QDoubleSpinBox());
_lastFrame->setSingleStep(0.01);
_lastFrame->setMaximum(INT_MAX); _lastFrame->setMaximum(INT_MAX);
_lastFrame->setValue(handle->getLastFrame()); _lastFrame->setValue(handle->getLastFrame());
connect(_lastFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle())); connect(_lastFrame, SIGNAL(valueChanged(double)), SLOT(updateHandle()));
QHBoxLayout* buttons = new QHBoxLayout(); QHBoxLayout* buttons = new QHBoxLayout();
layout->addRow(buttons); layout->addRow(buttons);

View file

@ -22,7 +22,6 @@ class QComboBox;
class QDoubleSpinner; class QDoubleSpinner;
class QLineEdit; class QLineEdit;
class QPushButton; class QPushButton;
class QSpinBox;
class QVBoxLayout; class QVBoxLayout;
/// Allows users to edit the avatar animations. /// Allows users to edit the avatar animations.
@ -71,8 +70,8 @@ private:
QCheckBox* _loop; QCheckBox* _loop;
QCheckBox* _hold; QCheckBox* _hold;
QCheckBox* _startAutomatically; QCheckBox* _startAutomatically;
QSpinBox* _firstFrame; QDoubleSpinBox* _firstFrame;
QSpinBox* _lastFrame; QDoubleSpinBox* _lastFrame;
QLineEdit* _maskedJoints; QLineEdit* _maskedJoints;
QPushButton* _chooseMaskedJoints; QPushButton* _chooseMaskedJoints;
QPushButton* _start; QPushButton* _start;