From ee1545f649a254dba61ac3e2a1c7156365a45b25 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 12 Nov 2015 11:28:23 -0800 Subject: [PATCH] Cleaning up clip and transform --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 74 +++++++++------- libraries/avatars/src/AvatarData.h | 11 +-- libraries/recording/src/recording/Clip.cpp | 2 +- libraries/recording/src/recording/Clip.h | 7 +- libraries/recording/src/recording/Deck.h | 4 - libraries/recording/src/recording/Forward.h | 2 + libraries/recording/src/recording/Frame.cpp | 2 +- libraries/recording/src/recording/Frame.h | 5 +- .../src/recording/impl/BufferClip.cpp | 12 +-- .../recording/src/recording/impl/BufferClip.h | 8 +- .../recording/src/recording/impl/FileClip.cpp | 6 +- .../recording/src/recording/impl/FileClip.h | 6 +- .../src/recording/impl/OffsetClip.cpp | 51 +++++++++++ .../recording/src/recording/impl/OffsetClip.h | 37 ++++++++ .../src/recording/impl/WrapperClip.cpp | 60 +++++++++++++ .../src/recording/impl/WrapperClip.h | 48 +++++++++++ libraries/shared/src/NumericalConstants.h | 3 + libraries/shared/src/Transform.cpp | 73 ++++++++++++++++ libraries/shared/src/Transform.h | 29 +++++++ libraries/shared/src/shared/JSONHelpers.cpp | 3 + .../shared/src/shared/UniformTransform.cpp | 84 ------------------- .../shared/src/shared/UniformTransform.h | 40 --------- 23 files changed, 381 insertions(+), 188 deletions(-) create mode 100644 libraries/recording/src/recording/impl/OffsetClip.cpp create mode 100644 libraries/recording/src/recording/impl/OffsetClip.h create mode 100644 libraries/recording/src/recording/impl/WrapperClip.cpp create mode 100644 libraries/recording/src/recording/impl/WrapperClip.h delete mode 100644 libraries/shared/src/shared/UniformTransform.cpp delete mode 100644 libraries/shared/src/shared/UniformTransform.h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 852e1d1389..5e14c66ff1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -126,7 +126,7 @@ MyAvatar::MyAvatar(RigPointer rig) : }); // FIXME how to deal with driving multiple avatars locally? - Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this](Frame::Pointer frame) { + Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this](Frame::ConstPointer frame) { qDebug() << "Playback of avatar frame length: " << frame->data.size(); avatarStateFromFrame(frame->data, this); }); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c4e356d402..adf471c50e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#include "AvatarData.h" + #include #include #include @@ -22,6 +25,7 @@ #include #include +#include #include #include #include @@ -29,12 +33,10 @@ #include #include #include -#include #include #include #include "AvatarLogging.h" -#include "AvatarData.h" quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND; @@ -908,7 +910,7 @@ void AvatarData::play() { } } -std::shared_ptr AvatarData::getRecordingBasis() const { +std::shared_ptr AvatarData::getRecordingBasis() const { return _recordingBasis; } @@ -1506,12 +1508,12 @@ void registerAvatarTypes(QScriptEngine* engine) { new AttachmentDataObject(), QScriptEngine::ScriptOwnership)); } -void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { +void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { if (!recordingBasis) { - recordingBasis = std::make_shared(); - recordingBasis->rotation = getOrientation(); - recordingBasis->translation = getPosition(); - recordingBasis->scale = getTargetScale(); + recordingBasis = std::make_shared(); + recordingBasis->setRotation(getOrientation()); + recordingBasis->setTranslation(getPosition()); + recordingBasis->setScale(getTargetScale()); } _recordingBasis = recordingBasis; } @@ -1520,6 +1522,14 @@ void AvatarData::clearRecordingBasis() { _recordingBasis.reset(); } +Transform AvatarData::getTransform() const { + Transform result; + result.setRotation(getOrientation()); + result.setTranslation(getPosition()); + result.setScale(getTargetScale()); + return result; +} + static const QString JSON_AVATAR_BASIS = QStringLiteral("basisTransform"); static const QString JSON_AVATAR_RELATIVE = QStringLiteral("relativeTransform"); static const QString JSON_AVATAR_JOINT_ROTATIONS = QStringLiteral("jointRotations"); @@ -1557,15 +1567,14 @@ QByteArray avatarStateToFrame(const AvatarData* _avatar) { auto recordingBasis = _avatar->getRecordingBasis(); if (recordingBasis) { - // FIXME if the resulting relative basis is identity, we shouldn't record anything - // Record the transformation basis - root[JSON_AVATAR_BASIS] = recordingBasis->toJson(); + // Find the relative transform + auto relativeTransform = recordingBasis->relativeTransform(_avatar->getTransform()); - // Record the relative transform - auto relativeTransform = recordingBasis->relativeTransform( - UniformTransform(_avatar->getPosition(), _avatar->getOrientation(), _avatar->getTargetScale())); - - root[JSON_AVATAR_RELATIVE] = relativeTransform.toJson(); + // if the resulting relative basis is identity, we shouldn't record anything + if (!relativeTransform.isIdentity()) { + root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform); + root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); + } } QJsonArray jointRotations; @@ -1617,22 +1626,25 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { } } - // During playback you can either have the recording basis set to the avatar current state - // meaning that all playback is relative to this avatars starting position, or - // the basis can be loaded from the recording, meaning the playback is relative to the - // original avatar location - // The first is more useful for playing back recordings on your own avatar, while - // the latter is more useful for playing back other avatars within your scene. - auto currentBasis = _avatar->getRecordingBasis(); - if (!currentBasis) { - currentBasis = UniformTransform::parseJson(root[JSON_AVATAR_BASIS]); - } + if (root.contains(JSON_AVATAR_RELATIVE)) { + // During playback you can either have the recording basis set to the avatar current state + // meaning that all playback is relative to this avatars starting position, or + // the basis can be loaded from the recording, meaning the playback is relative to the + // original avatar location + // The first is more useful for playing back recordings on your own avatar, while + // the latter is more useful for playing back other avatars within your scene. - auto relativeTransform = UniformTransform::parseJson(root[JSON_AVATAR_RELATIVE]); - auto worldTransform = currentBasis->worldTransform(*relativeTransform); - _avatar->setPosition(worldTransform.translation); - _avatar->setOrientation(worldTransform.rotation); - _avatar->setTargetScale(worldTransform.scale); + auto currentBasis = _avatar->getRecordingBasis(); + if (!currentBasis) { + currentBasis = std::make_shared(Transform::fromJson(root[JSON_AVATAR_BASIS])); + } + + auto relativeTransform = Transform::fromJson(root[JSON_AVATAR_RELATIVE]); + auto worldTransform = currentBasis->worldTransform(relativeTransform); + _avatar->setPosition(worldTransform.getTranslation()); + _avatar->setOrientation(worldTransform.getRotation()); + _avatar->setTargetScale(worldTransform.getScale().x); + } #if 0 if (root.contains(JSON_AVATAR_ATTACHEMENTS)) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index ebb6e1a78f..7716a16a1c 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -134,7 +134,8 @@ class QDataStream; class AttachmentData; class JointData; -struct UniformTransform; +class Transform; +using TransformPointer = std::shared_ptr; class AvatarData : public QObject { Q_OBJECT @@ -333,10 +334,10 @@ public: bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } + Transform getTransform() const; void clearRecordingBasis(); - std::shared_ptr getRecordingBasis() const; - void setRecordingBasis(std::shared_ptr recordingBasis = std::shared_ptr()); - + TransformPointer getRecordingBasis() const; + void setRecordingBasis(TransformPointer recordingBasis = TransformPointer()); public slots: void sendAvatarDataPacket(); @@ -437,7 +438,7 @@ protected: // During recording, this holds the starting position, orientation & scale of the recorded avatar // During playback, it holds the - std::shared_ptr _recordingBasis; + TransformPointer _recordingBasis; private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp index ba13b359f0..28e4211fe3 100644 --- a/libraries/recording/src/recording/Clip.cpp +++ b/libraries/recording/src/recording/Clip.cpp @@ -38,7 +38,7 @@ Clip::Pointer Clip::duplicate() { Time currentPosition = position(); seek(0); - Frame::Pointer frame = nextFrame(); + auto frame = nextFrame(); while (frame) { result->addFrame(frame); frame = nextFrame(); diff --git a/libraries/recording/src/recording/Clip.h b/libraries/recording/src/recording/Clip.h index 70fc4b3f7f..a00ab72c98 100644 --- a/libraries/recording/src/recording/Clip.h +++ b/libraries/recording/src/recording/Clip.h @@ -34,16 +34,17 @@ public: virtual void seek(Time offset) = 0; virtual Time position() const = 0; - virtual FramePointer peekFrame() const = 0; - virtual FramePointer nextFrame() = 0; + virtual FrameConstPointer peekFrame() const = 0; + virtual FrameConstPointer nextFrame() = 0; virtual void skipFrame() = 0; - virtual void addFrame(FramePointer) = 0; + virtual void addFrame(FrameConstPointer) = 0; static Pointer fromFile(const QString& filePath); static void toFile(const QString& filePath, Pointer clip); static Pointer newClip(); protected: + friend class WrapperClip; using Mutex = std::recursive_mutex; using Locker = std::unique_lock; diff --git a/libraries/recording/src/recording/Deck.h b/libraries/recording/src/recording/Deck.h index a3b4405210..7086e9759d 100644 --- a/libraries/recording/src/recording/Deck.h +++ b/libraries/recording/src/recording/Deck.h @@ -46,9 +46,6 @@ public: Time position() const; void seek(Time position); - void setPlaybackSpeed(float factor) { _playbackSpeed = factor; } - float getPlaybackSpeed() { return _playbackSpeed; } - signals: void playbackStateChanged(); @@ -62,7 +59,6 @@ private: Clips _clips; quint64 _startEpoch { 0 }; Time _position { 0 }; - float _playbackSpeed { 1.0f }; bool _pause { true }; bool _loop { false }; Time _length { 0 }; diff --git a/libraries/recording/src/recording/Forward.h b/libraries/recording/src/recording/Forward.h index 31aa40521c..4ba54e23a3 100644 --- a/libraries/recording/src/recording/Forward.h +++ b/libraries/recording/src/recording/Forward.h @@ -28,6 +28,8 @@ struct Frame; using FramePointer = std::shared_ptr; +using FrameConstPointer = std::shared_ptr; + // A recording of some set of state from the application, usually avatar // data + audio for a single person class Clip; diff --git a/libraries/recording/src/recording/Frame.cpp b/libraries/recording/src/recording/Frame.cpp index ad2a583424..5b0116519f 100644 --- a/libraries/recording/src/recording/Frame.cpp +++ b/libraries/recording/src/recording/Frame.cpp @@ -104,7 +104,7 @@ Frame::Handler Frame::registerFrameHandler(FrameType type, Handler handler) { return result; } -void Frame::handleFrame(const Frame::Pointer& frame) { +void Frame::handleFrame(const Frame::ConstPointer& frame) { Handler handler; { Locker lock(mutex); diff --git a/libraries/recording/src/recording/Frame.h b/libraries/recording/src/recording/Frame.h index 563b042656..85f5246a4e 100644 --- a/libraries/recording/src/recording/Frame.h +++ b/libraries/recording/src/recording/Frame.h @@ -21,7 +21,8 @@ namespace recording { struct Frame { public: using Pointer = std::shared_ptr; - using Handler = std::function; + using ConstPointer = std::shared_ptr; + using Handler = std::function; static const FrameType TYPE_INVALID = 0xFFFF; static const FrameType TYPE_HEADER = 0x0; @@ -37,7 +38,7 @@ public: static QMap getFrameTypes(); static QMap getFrameTypeNames(); static Handler registerFrameHandler(FrameType type, Handler handler); - static void handleFrame(const Pointer& frame); + static void handleFrame(const ConstPointer& frame); }; } diff --git a/libraries/recording/src/recording/impl/BufferClip.cpp b/libraries/recording/src/recording/impl/BufferClip.cpp index 5dc75bbce2..87bbfbfef7 100644 --- a/libraries/recording/src/recording/impl/BufferClip.cpp +++ b/libraries/recording/src/recording/impl/BufferClip.cpp @@ -18,7 +18,7 @@ using namespace recording; void BufferClip::seek(Time offset) { Locker lock(_mutex); auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset, - [](Frame::Pointer a, Time b)->bool { + [](Frame::ConstPointer a, Time b)->bool { return a->timeOffset < b; } ); @@ -34,18 +34,18 @@ Time BufferClip::position() const { return result; } -FramePointer BufferClip::peekFrame() const { +FrameConstPointer BufferClip::peekFrame() const { Locker lock(_mutex); - FramePointer result; + FrameConstPointer result; if (_frameIndex < _frames.size()) { result = _frames[_frameIndex]; } return result; } -FramePointer BufferClip::nextFrame() { +FrameConstPointer BufferClip::nextFrame() { Locker lock(_mutex); - FramePointer result; + FrameConstPointer result; if (_frameIndex < _frames.size()) { result = _frames[_frameIndex]; ++_frameIndex; @@ -53,7 +53,7 @@ FramePointer BufferClip::nextFrame() { return result; } -void BufferClip::addFrame(FramePointer newFrame) { +void BufferClip::addFrame(FrameConstPointer newFrame) { if (newFrame->timeOffset < 0.0f) { throw std::runtime_error("Frames may not have negative time offsets"); } diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h index bfb0234600..ce81dac730 100644 --- a/libraries/recording/src/recording/impl/BufferClip.h +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -28,15 +28,15 @@ public: virtual void seek(Time offset) override; virtual Time position() const override; - virtual FramePointer peekFrame() const override; - virtual FramePointer nextFrame() override; + virtual FrameConstPointer peekFrame() const override; + virtual FrameConstPointer nextFrame() override; virtual void skipFrame() override; - virtual void addFrame(FramePointer) override; + virtual void addFrame(FrameConstPointer) override; private: virtual void reset() override; - std::vector _frames; + std::vector _frames; mutable size_t _frameIndex { 0 }; }; diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp index e64085517a..b8e1eb26fa 100644 --- a/libraries/recording/src/recording/impl/FileClip.cpp +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -240,12 +240,12 @@ FramePointer FileClip::readFrame(uint32_t frameIndex) const { return result; } -FramePointer FileClip::peekFrame() const { +FrameConstPointer FileClip::peekFrame() const { Locker lock(_mutex); return readFrame(_frameIndex); } -FramePointer FileClip::nextFrame() { +FrameConstPointer FileClip::nextFrame() { Locker lock(_mutex); auto result = readFrame(_frameIndex); if (_frameIndex < _frameHeaders.size()) { @@ -262,7 +262,7 @@ void FileClip::reset() { _frameIndex = 0; } -void FileClip::addFrame(FramePointer) { +void FileClip::addFrame(FrameConstPointer) { throw std::runtime_error("File clips are read only"); } diff --git a/libraries/recording/src/recording/impl/FileClip.h b/libraries/recording/src/recording/impl/FileClip.h index 78256dcf23..18c62936c1 100644 --- a/libraries/recording/src/recording/impl/FileClip.h +++ b/libraries/recording/src/recording/impl/FileClip.h @@ -32,10 +32,10 @@ public: virtual void seek(Time offset) override; virtual Time position() const override; - virtual FramePointer peekFrame() const override; - virtual FramePointer nextFrame() override; + virtual FrameConstPointer peekFrame() const override; + virtual FrameConstPointer nextFrame() override; virtual void skipFrame() override; - virtual void addFrame(FramePointer) override; + virtual void addFrame(FrameConstPointer) override; const QJsonDocument& getHeader() { return _fileHeader; diff --git a/libraries/recording/src/recording/impl/OffsetClip.cpp b/libraries/recording/src/recording/impl/OffsetClip.cpp new file mode 100644 index 0000000000..bccd48d6c8 --- /dev/null +++ b/libraries/recording/src/recording/impl/OffsetClip.cpp @@ -0,0 +1,51 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// Copyright 2015 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 "OffsetClip.h" + +#include + +#include +#include +#include + +#include + +#include "../Frame.h" +#include "../Logging.h" + + +using namespace recording; + +OffsetClip::OffsetClip(const Clip::Pointer& wrappedClip, Time offset) + : WrapperClip(wrappedClip), _offset(offset) { } + +void OffsetClip::seek(Time offset) { + _wrappedClip->seek(offset - _offset); +} + +Time OffsetClip::position() const { + return _wrappedClip->position() + _offset; +} + +FrameConstPointer OffsetClip::peekFrame() const { + auto result = std::make_shared(*_wrappedClip->peekFrame()); + result->timeOffset += _offset; + return result; +} + +FrameConstPointer OffsetClip::nextFrame() { + auto result = std::make_shared(*_wrappedClip->nextFrame()); + result->timeOffset += _offset; + return result; +} + +Time OffsetClip::duration() const { + return _wrappedClip->duration() + _offset; +} + diff --git a/libraries/recording/src/recording/impl/OffsetClip.h b/libraries/recording/src/recording/impl/OffsetClip.h new file mode 100644 index 0000000000..1c6b005b65 --- /dev/null +++ b/libraries/recording/src/recording/impl/OffsetClip.h @@ -0,0 +1,37 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// Copyright 2015 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 +// + +#pragma once +#ifndef hifi_Recording_Impl_OffsetClip_h +#define hifi_Recording_Impl_OffsetClip_h + +#include "WrapperClip.h" + +namespace recording { + +class OffsetClip : public WrapperClip { +public: + using Pointer = std::shared_ptr; + + OffsetClip(const Clip::Pointer& wrappedClip, Time offset); + virtual ~OffsetClip(); + + virtual Time duration() const override; + virtual void seek(Time offset) override; + virtual Time position() const override; + + virtual FrameConstPointer peekFrame() const override; + virtual FrameConstPointer nextFrame() override; + +protected: + const Time _offset; +}; + +} + +#endif diff --git a/libraries/recording/src/recording/impl/WrapperClip.cpp b/libraries/recording/src/recording/impl/WrapperClip.cpp new file mode 100644 index 0000000000..f2bbacabf1 --- /dev/null +++ b/libraries/recording/src/recording/impl/WrapperClip.cpp @@ -0,0 +1,60 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// Copyright 2015 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 "WrapperClip.h" + + +#include + +#include + +#include "../Frame.h" +#include "../Logging.h" + + +using namespace recording; + +WrapperClip::WrapperClip(const Clip::Pointer& wrappedClip) + : _wrappedClip(wrappedClip) { } + +void WrapperClip::seek(Time offset) { + _wrappedClip->seek(offset); +} + +Time WrapperClip::position() const { + return _wrappedClip->position(); +} + +FrameConstPointer WrapperClip::peekFrame() const { + return _wrappedClip->peekFrame(); +} + +FrameConstPointer WrapperClip::nextFrame() { + return _wrappedClip->nextFrame(); +} + +void WrapperClip::skipFrame() { + _wrappedClip->skipFrame(); +} + +void WrapperClip::reset() { + _wrappedClip->reset(); +} + +void WrapperClip::addFrame(FrameConstPointer) { + throw std::runtime_error("Wrapper clips are read only"); +} + +Time WrapperClip::duration() const { + return _wrappedClip->duration(); +} + +size_t WrapperClip::frameCount() const { + return _wrappedClip->frameCount(); +} + diff --git a/libraries/recording/src/recording/impl/WrapperClip.h b/libraries/recording/src/recording/impl/WrapperClip.h new file mode 100644 index 0000000000..3fe013e0ed --- /dev/null +++ b/libraries/recording/src/recording/impl/WrapperClip.h @@ -0,0 +1,48 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// Copyright 2015 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 +// + +#pragma once +#ifndef hifi_Recording_Impl_WrapperClip_h +#define hifi_Recording_Impl_WrapperClip_h + +#include "../Clip.h" + +#include +#include + +#include + +namespace recording { + +class WrapperClip : public Clip { +public: + using Pointer = std::shared_ptr; + + WrapperClip(const Clip::Pointer& wrappedClip); + virtual ~WrapperClip(); + + virtual Time duration() const override; + virtual size_t frameCount() const override; + + virtual void seek(Time offset) override; + virtual Time position() const override; + + virtual FrameConstPointer peekFrame() const override; + virtual FrameConstPointer nextFrame() override; + virtual void skipFrame() override; + virtual void addFrame(FrameConstPointer) override; + +protected: + virtual void reset() override; + + const Clip::Pointer _wrappedClip; +}; + +} + +#endif diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index 9a946e35f7..6b37031d23 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -23,6 +23,9 @@ const float TWO_PI = 2.0f * PI; const float PI_OVER_TWO = 0.5f * PI; const float RADIANS_PER_DEGREE = PI / 180.0f; const float DEGREES_PER_RADIAN = 180.0f / PI; +const float ARCMINUTES_PER_DEGREE = 60.0f; +const float ARCSECONDS_PER_ARCMINUTE = 60.0f; +const float ARCSECONDS_PER_DEGREE = ARCMINUTES_PER_DEGREE * ARCSECONDS_PER_ARCMINUTE; const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations const float SQUARE_ROOT_OF_2 = (float)sqrt(2.0f); diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index 4b2a481f41..1358f396d9 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -11,6 +11,12 @@ #include "Transform.h" +#include +#include +#include + +#include "NumericalConstants.h" +#include "shared/JSONHelpers.h" void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix) { const float ACCURACY_THREASHOLD = 0.00001f; @@ -65,7 +71,74 @@ void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotat rotation = (glm::quat_cast(matRot)); } +Transform Transform::relativeTransform(const Transform& worldTransform) const { + if (isIdentity()) { + return worldTransform; + } + if (*this == worldTransform) { + return Transform(); + } + Transform result; + inverseMult(result, *this, worldTransform); + return result; +} + +Transform Transform::worldTransform(const Transform& relativeTransform) const { + if (relativeTransform.isIdentity()) { + return *this; + } + + if (isIdentity()) { + return relativeTransform; + } + + Transform result; + mult(result, *this, relativeTransform); + return result; +} +static const QString JSON_TRANSLATION = QStringLiteral("translation"); +static const QString JSON_ROTATION = QStringLiteral("rotation"); +static const QString JSON_SCALE = QStringLiteral("scale"); + +Transform Transform::fromJson(const QJsonValue& json) { + if (!json.isObject()) { + return Transform(); + } + QJsonObject obj = json.toObject(); + Transform result; + if (obj.contains(JSON_ROTATION)) { + result.setRotation(quatFromJsonValue(obj[JSON_ROTATION])); + } + if (obj.contains(JSON_TRANSLATION)) { + result.setTranslation(vec3FromJsonValue(obj[JSON_TRANSLATION])); + } + if (obj.contains(JSON_SCALE)) { + result.setScale(vec3FromJsonValue(obj[JSON_TRANSLATION])); + } + return result; +} + +QJsonObject Transform::toJson(const Transform& transform) { + if (transform.isIdentity()) { + return QJsonObject(); + } + + QJsonObject result; + auto json = toJsonValue(transform.getTranslation()); + if (!json.isNull()) { + result[JSON_TRANSLATION] = json; + } + json = toJsonValue(transform.getRotation()); + if (!json.isNull()) { + result[JSON_ROTATION] = json; + } + json = toJsonValue(transform.getScale()); + if (!json.isNull()) { + result[JSON_SCALE] = json; + } + return result; +} diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index bf3b336e90..ec81513f3e 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -21,6 +21,9 @@ #include +class QJsonObject; +class QJsonValue; + inline bool isValidScale(glm::vec3 scale) { bool result = scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f; assert(result); @@ -35,6 +38,7 @@ inline bool isValidScale(float scale) { class Transform { public: + using Pointer = std::shared_ptr; typedef glm::mat4 Mat4; typedef glm::mat3 Mat3; typedef glm::vec4 Vec4; @@ -81,6 +85,10 @@ public: return (*this); } + bool operator==(const Transform& other) const { + return _rotation == other._rotation && _scale == other._scale && _translation == other._translation; + } + Transform& setIdentity(); const Vec3& getTranslation() const; @@ -111,6 +119,8 @@ public: Transform& evalFromRawMatrix(const Mat4& matrix); Transform& evalFromRawMatrix(const Mat3& rotationScalematrix); + Mat4 getMatrix() const; + Mat4 getInverseMatrix() const; Mat4& getMatrix(Mat4& result) const; Mat4& getInverseMatrix(Mat4& result) const; Mat4& getInverseTransposeMatrix(Mat4& result) const; @@ -120,6 +130,9 @@ public: Transform& evalInverse(Transform& result) const; + Transform relativeTransform(const Transform& world) const; + Transform worldTransform(const Transform& relative) const; + static void evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix); static Transform& mult(Transform& result, const Transform& left, const Transform& right); @@ -127,6 +140,10 @@ public: // Left will be inversed before the multiplication static Transform& inverseMult(Transform& result, const Transform& left, const Transform& right); + + static Transform fromJson(const QJsonValue& json); + static QJsonObject toJson(const Transform& transform); + Vec4 transform(const Vec4& pos) const; Vec3 transform(const Vec3& pos) const; @@ -368,6 +385,18 @@ inline Transform& Transform::postScale(const Vec3& scale) { return *this; } +inline Transform::Mat4 Transform::getMatrix() const { + Transform::Mat4 result; + getMatrix(result); + return result; +} + +inline Transform::Mat4 Transform::getInverseMatrix() const { + Transform::Mat4 result; + getInverseMatrix(result); + return result; +} + inline Transform::Mat4& Transform::getMatrix(Transform::Mat4& result) const { if (isRotating()) { Mat3 rot = glm::mat3_cast(_rotation); diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp index dc872574fe..52ece73490 100644 --- a/libraries/shared/src/shared/JSONHelpers.cpp +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -52,6 +52,9 @@ quat quatFromJsonValue(const QJsonValue& q) { } vec3 vec3FromJsonValue(const QJsonValue& v) { + if (v.isDouble()) { + return vec3((float)v.toDouble()); + } return glmFromJson(v); } diff --git a/libraries/shared/src/shared/UniformTransform.cpp b/libraries/shared/src/shared/UniformTransform.cpp deleted file mode 100644 index fdcf489154..0000000000 --- a/libraries/shared/src/shared/UniformTransform.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/11/09 -// Copyright 2013-2015 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 "UniformTransform.h" - -#include "JSONHelpers.h" - -#include -#include -#include - -#include - -const float UniformTransform::DEFAULT_SCALE = 1.0f; - -std::shared_ptr UniformTransform::parseJson(const QJsonValue& basis) { - std::shared_ptr result = std::make_shared(); - result->fromJson(basis); - return result; -} - -static const QString JSON_TRANSLATION = QStringLiteral("translation"); -static const QString JSON_ROTATION = QStringLiteral("rotation"); -static const QString JSON_SCALE = QStringLiteral("scale"); - -void UniformTransform::fromJson(const QJsonValue& basisValue) { - if (!basisValue.isObject()) { - return; - } - QJsonObject basis = basisValue.toObject(); - if (basis.contains(JSON_ROTATION)) { - rotation = quatFromJsonValue(basis[JSON_ROTATION]); - } - if (basis.contains(JSON_TRANSLATION)) { - translation = vec3FromJsonValue(basis[JSON_TRANSLATION]); - } - if (basis.contains(JSON_SCALE)) { - scale = (float)basis[JSON_SCALE].toDouble(); - } -} - -glm::mat4 toMat4(const UniformTransform& transform) { - return glm::translate(glm::mat4(), transform.translation) * glm::mat4_cast(transform.rotation); -} - -UniformTransform fromMat4(const glm::mat4& m) { - UniformTransform result; - result.translation = vec3(m[3]); - result.rotation = glm::quat_cast(m); - return result; -} - -UniformTransform UniformTransform::relativeTransform(const UniformTransform& worldTransform) const { - UniformTransform result = fromMat4(glm::inverse(toMat4(*this)) * toMat4(worldTransform)); - result.scale = scale / worldTransform.scale; - return result; -} - -UniformTransform UniformTransform::worldTransform(const UniformTransform& relativeTransform) const { - UniformTransform result = fromMat4(toMat4(*this) * toMat4(relativeTransform)); - result.scale = relativeTransform.scale * scale; - return result; -} - -QJsonObject UniformTransform::toJson() const { - QJsonObject result; - auto json = toJsonValue(translation); - if (!json.isNull()) { - result[JSON_TRANSLATION] = json; - } - json = toJsonValue(rotation); - if (!json.isNull()) { - result[JSON_ROTATION] = json; - } - if (scale != DEFAULT_SCALE) { - result[JSON_SCALE] = scale; - } - return result; -} diff --git a/libraries/shared/src/shared/UniformTransform.h b/libraries/shared/src/shared/UniformTransform.h deleted file mode 100644 index 5b46de531e..0000000000 --- a/libraries/shared/src/shared/UniformTransform.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/11/09 -// Copyright 2013-2015 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 -// - -#pragma once -#ifndef hifi_Shared_UniformTransform_h -#define hifi_Shared_UniformTransform_h - -#include "../GLMHelpers.h" - -class QJsonValue; - -struct UniformTransform { - static const float DEFAULT_SCALE; - glm::vec3 translation; - glm::quat rotation; - float scale { DEFAULT_SCALE }; - - UniformTransform() {} - - UniformTransform(const glm::vec3& translation, const glm::quat& rotation, const float& scale) - : translation(translation), rotation(rotation), scale(scale) {} - - UniformTransform relativeTransform(const UniformTransform& worldTransform) const; - glm::vec3 relativeVector(const UniformTransform& worldTransform) const; - - UniformTransform worldTransform(const UniformTransform& relativeTransform) const; - glm::vec3 worldVector(const UniformTransform& relativeTransform) const; - - QJsonObject toJson() const; - void fromJson(const QJsonValue& json); - - static std::shared_ptr parseJson(const QJsonValue& json); -}; - -#endif