Cleaning up clip and transform

This commit is contained in:
Brad Davis 2015-11-12 11:28:23 -08:00
parent b9a0d0b843
commit ee1545f649
23 changed files with 381 additions and 188 deletions

View file

@ -126,7 +126,7 @@ MyAvatar::MyAvatar(RigPointer rig) :
}); });
// FIXME how to deal with driving multiple avatars locally? // 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(); qDebug() << "Playback of avatar frame length: " << frame->data.size();
avatarStateFromFrame(frame->data, this); avatarStateFromFrame(frame->data, this);
}); });

View file

@ -9,6 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "AvatarData.h"
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <stdint.h> #include <stdint.h>
@ -22,6 +25,7 @@
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <Transform.h>
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <NodeList.h> #include <NodeList.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
@ -29,12 +33,10 @@
#include <StreamUtils.h> #include <StreamUtils.h>
#include <UUID.h> #include <UUID.h>
#include <shared/JSONHelpers.h> #include <shared/JSONHelpers.h>
#include <shared/UniformTransform.h>
#include <recording/Deck.h> #include <recording/Deck.h>
#include <recording/Clip.h> #include <recording/Clip.h>
#include "AvatarLogging.h" #include "AvatarLogging.h"
#include "AvatarData.h"
quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND; quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND;
@ -908,7 +910,7 @@ void AvatarData::play() {
} }
} }
std::shared_ptr<UniformTransform> AvatarData::getRecordingBasis() const { std::shared_ptr<Transform> AvatarData::getRecordingBasis() const {
return _recordingBasis; return _recordingBasis;
} }
@ -1506,12 +1508,12 @@ void registerAvatarTypes(QScriptEngine* engine) {
new AttachmentDataObject(), QScriptEngine::ScriptOwnership)); new AttachmentDataObject(), QScriptEngine::ScriptOwnership));
} }
void AvatarData::setRecordingBasis(std::shared_ptr<UniformTransform> recordingBasis) { void AvatarData::setRecordingBasis(std::shared_ptr<Transform> recordingBasis) {
if (!recordingBasis) { if (!recordingBasis) {
recordingBasis = std::make_shared<UniformTransform>(); recordingBasis = std::make_shared<Transform>();
recordingBasis->rotation = getOrientation(); recordingBasis->setRotation(getOrientation());
recordingBasis->translation = getPosition(); recordingBasis->setTranslation(getPosition());
recordingBasis->scale = getTargetScale(); recordingBasis->setScale(getTargetScale());
} }
_recordingBasis = recordingBasis; _recordingBasis = recordingBasis;
} }
@ -1520,6 +1522,14 @@ void AvatarData::clearRecordingBasis() {
_recordingBasis.reset(); _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_BASIS = QStringLiteral("basisTransform");
static const QString JSON_AVATAR_RELATIVE = QStringLiteral("relativeTransform"); static const QString JSON_AVATAR_RELATIVE = QStringLiteral("relativeTransform");
static const QString JSON_AVATAR_JOINT_ROTATIONS = QStringLiteral("jointRotations"); static const QString JSON_AVATAR_JOINT_ROTATIONS = QStringLiteral("jointRotations");
@ -1557,15 +1567,14 @@ QByteArray avatarStateToFrame(const AvatarData* _avatar) {
auto recordingBasis = _avatar->getRecordingBasis(); auto recordingBasis = _avatar->getRecordingBasis();
if (recordingBasis) { if (recordingBasis) {
// FIXME if the resulting relative basis is identity, we shouldn't record anything // Find the relative transform
// Record the transformation basis auto relativeTransform = recordingBasis->relativeTransform(_avatar->getTransform());
root[JSON_AVATAR_BASIS] = recordingBasis->toJson();
// Record the relative transform // if the resulting relative basis is identity, we shouldn't record anything
auto relativeTransform = recordingBasis->relativeTransform( if (!relativeTransform.isIdentity()) {
UniformTransform(_avatar->getPosition(), _avatar->getOrientation(), _avatar->getTargetScale())); root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform);
root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis);
root[JSON_AVATAR_RELATIVE] = relativeTransform.toJson(); }
} }
QJsonArray jointRotations; 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 if (root.contains(JSON_AVATAR_RELATIVE)) {
// meaning that all playback is relative to this avatars starting position, or // During playback you can either have the recording basis set to the avatar current state
// the basis can be loaded from the recording, meaning the playback is relative to the // meaning that all playback is relative to this avatars starting position, or
// original avatar location // the basis can be loaded from the recording, meaning the playback is relative to the
// The first is more useful for playing back recordings on your own avatar, while // original avatar location
// the latter is more useful for playing back other avatars within your scene. // The first is more useful for playing back recordings on your own avatar, while
auto currentBasis = _avatar->getRecordingBasis(); // the latter is more useful for playing back other avatars within your scene.
if (!currentBasis) {
currentBasis = UniformTransform::parseJson(root[JSON_AVATAR_BASIS]);
}
auto relativeTransform = UniformTransform::parseJson(root[JSON_AVATAR_RELATIVE]); auto currentBasis = _avatar->getRecordingBasis();
auto worldTransform = currentBasis->worldTransform(*relativeTransform); if (!currentBasis) {
_avatar->setPosition(worldTransform.translation); currentBasis = std::make_shared<Transform>(Transform::fromJson(root[JSON_AVATAR_BASIS]));
_avatar->setOrientation(worldTransform.rotation); }
_avatar->setTargetScale(worldTransform.scale);
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 0
if (root.contains(JSON_AVATAR_ATTACHEMENTS)) { if (root.contains(JSON_AVATAR_ATTACHEMENTS)) {

View file

@ -134,7 +134,8 @@ class QDataStream;
class AttachmentData; class AttachmentData;
class JointData; class JointData;
struct UniformTransform; class Transform;
using TransformPointer = std::shared_ptr<Transform>;
class AvatarData : public QObject { class AvatarData : public QObject {
Q_OBJECT Q_OBJECT
@ -333,10 +334,10 @@ public:
bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; }
Transform getTransform() const;
void clearRecordingBasis(); void clearRecordingBasis();
std::shared_ptr<UniformTransform> getRecordingBasis() const; TransformPointer getRecordingBasis() const;
void setRecordingBasis(std::shared_ptr<UniformTransform> recordingBasis = std::shared_ptr<UniformTransform>()); void setRecordingBasis(TransformPointer recordingBasis = TransformPointer());
public slots: public slots:
void sendAvatarDataPacket(); void sendAvatarDataPacket();
@ -437,7 +438,7 @@ protected:
// During recording, this holds the starting position, orientation & scale of the recorded avatar // During recording, this holds the starting position, orientation & scale of the recorded avatar
// During playback, it holds the // During playback, it holds the
std::shared_ptr<UniformTransform> _recordingBasis; TransformPointer _recordingBasis;
private: private:
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);

View file

@ -38,7 +38,7 @@ Clip::Pointer Clip::duplicate() {
Time currentPosition = position(); Time currentPosition = position();
seek(0); seek(0);
Frame::Pointer frame = nextFrame(); auto frame = nextFrame();
while (frame) { while (frame) {
result->addFrame(frame); result->addFrame(frame);
frame = nextFrame(); frame = nextFrame();

View file

@ -34,16 +34,17 @@ public:
virtual void seek(Time offset) = 0; virtual void seek(Time offset) = 0;
virtual Time position() const = 0; virtual Time position() const = 0;
virtual FramePointer peekFrame() const = 0; virtual FrameConstPointer peekFrame() const = 0;
virtual FramePointer nextFrame() = 0; virtual FrameConstPointer nextFrame() = 0;
virtual void skipFrame() = 0; virtual void skipFrame() = 0;
virtual void addFrame(FramePointer) = 0; virtual void addFrame(FrameConstPointer) = 0;
static Pointer fromFile(const QString& filePath); static Pointer fromFile(const QString& filePath);
static void toFile(const QString& filePath, Pointer clip); static void toFile(const QString& filePath, Pointer clip);
static Pointer newClip(); static Pointer newClip();
protected: protected:
friend class WrapperClip;
using Mutex = std::recursive_mutex; using Mutex = std::recursive_mutex;
using Locker = std::unique_lock<Mutex>; using Locker = std::unique_lock<Mutex>;

View file

@ -46,9 +46,6 @@ public:
Time position() const; Time position() const;
void seek(Time position); void seek(Time position);
void setPlaybackSpeed(float factor) { _playbackSpeed = factor; }
float getPlaybackSpeed() { return _playbackSpeed; }
signals: signals:
void playbackStateChanged(); void playbackStateChanged();
@ -62,7 +59,6 @@ private:
Clips _clips; Clips _clips;
quint64 _startEpoch { 0 }; quint64 _startEpoch { 0 };
Time _position { 0 }; Time _position { 0 };
float _playbackSpeed { 1.0f };
bool _pause { true }; bool _pause { true };
bool _loop { false }; bool _loop { false };
Time _length { 0 }; Time _length { 0 };

View file

@ -28,6 +28,8 @@ struct Frame;
using FramePointer = std::shared_ptr<Frame>; using FramePointer = std::shared_ptr<Frame>;
using FrameConstPointer = std::shared_ptr<const Frame>;
// A recording of some set of state from the application, usually avatar // A recording of some set of state from the application, usually avatar
// data + audio for a single person // data + audio for a single person
class Clip; class Clip;

View file

@ -104,7 +104,7 @@ Frame::Handler Frame::registerFrameHandler(FrameType type, Handler handler) {
return result; return result;
} }
void Frame::handleFrame(const Frame::Pointer& frame) { void Frame::handleFrame(const Frame::ConstPointer& frame) {
Handler handler; Handler handler;
{ {
Locker lock(mutex); Locker lock(mutex);

View file

@ -21,7 +21,8 @@ namespace recording {
struct Frame { struct Frame {
public: public:
using Pointer = std::shared_ptr<Frame>; using Pointer = std::shared_ptr<Frame>;
using Handler = std::function<void(Frame::Pointer frame)>; using ConstPointer = std::shared_ptr<const Frame>;
using Handler = std::function<void(Frame::ConstPointer frame)>;
static const FrameType TYPE_INVALID = 0xFFFF; static const FrameType TYPE_INVALID = 0xFFFF;
static const FrameType TYPE_HEADER = 0x0; static const FrameType TYPE_HEADER = 0x0;
@ -37,7 +38,7 @@ public:
static QMap<QString, FrameType> getFrameTypes(); static QMap<QString, FrameType> getFrameTypes();
static QMap<FrameType, QString> getFrameTypeNames(); static QMap<FrameType, QString> getFrameTypeNames();
static Handler registerFrameHandler(FrameType type, Handler handler); static Handler registerFrameHandler(FrameType type, Handler handler);
static void handleFrame(const Pointer& frame); static void handleFrame(const ConstPointer& frame);
}; };
} }

View file

@ -18,7 +18,7 @@ using namespace recording;
void BufferClip::seek(Time offset) { void BufferClip::seek(Time offset) {
Locker lock(_mutex); Locker lock(_mutex);
auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset, 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; return a->timeOffset < b;
} }
); );
@ -34,18 +34,18 @@ Time BufferClip::position() const {
return result; return result;
} }
FramePointer BufferClip::peekFrame() const { FrameConstPointer BufferClip::peekFrame() const {
Locker lock(_mutex); Locker lock(_mutex);
FramePointer result; FrameConstPointer result;
if (_frameIndex < _frames.size()) { if (_frameIndex < _frames.size()) {
result = _frames[_frameIndex]; result = _frames[_frameIndex];
} }
return result; return result;
} }
FramePointer BufferClip::nextFrame() { FrameConstPointer BufferClip::nextFrame() {
Locker lock(_mutex); Locker lock(_mutex);
FramePointer result; FrameConstPointer result;
if (_frameIndex < _frames.size()) { if (_frameIndex < _frames.size()) {
result = _frames[_frameIndex]; result = _frames[_frameIndex];
++_frameIndex; ++_frameIndex;
@ -53,7 +53,7 @@ FramePointer BufferClip::nextFrame() {
return result; return result;
} }
void BufferClip::addFrame(FramePointer newFrame) { void BufferClip::addFrame(FrameConstPointer newFrame) {
if (newFrame->timeOffset < 0.0f) { if (newFrame->timeOffset < 0.0f) {
throw std::runtime_error("Frames may not have negative time offsets"); throw std::runtime_error("Frames may not have negative time offsets");
} }

View file

@ -28,15 +28,15 @@ public:
virtual void seek(Time offset) override; virtual void seek(Time offset) override;
virtual Time position() const override; virtual Time position() const override;
virtual FramePointer peekFrame() const override; virtual FrameConstPointer peekFrame() const override;
virtual FramePointer nextFrame() override; virtual FrameConstPointer nextFrame() override;
virtual void skipFrame() override; virtual void skipFrame() override;
virtual void addFrame(FramePointer) override; virtual void addFrame(FrameConstPointer) override;
private: private:
virtual void reset() override; virtual void reset() override;
std::vector<FramePointer> _frames; std::vector<FrameConstPointer> _frames;
mutable size_t _frameIndex { 0 }; mutable size_t _frameIndex { 0 };
}; };

View file

@ -240,12 +240,12 @@ FramePointer FileClip::readFrame(uint32_t frameIndex) const {
return result; return result;
} }
FramePointer FileClip::peekFrame() const { FrameConstPointer FileClip::peekFrame() const {
Locker lock(_mutex); Locker lock(_mutex);
return readFrame(_frameIndex); return readFrame(_frameIndex);
} }
FramePointer FileClip::nextFrame() { FrameConstPointer FileClip::nextFrame() {
Locker lock(_mutex); Locker lock(_mutex);
auto result = readFrame(_frameIndex); auto result = readFrame(_frameIndex);
if (_frameIndex < _frameHeaders.size()) { if (_frameIndex < _frameHeaders.size()) {
@ -262,7 +262,7 @@ void FileClip::reset() {
_frameIndex = 0; _frameIndex = 0;
} }
void FileClip::addFrame(FramePointer) { void FileClip::addFrame(FrameConstPointer) {
throw std::runtime_error("File clips are read only"); throw std::runtime_error("File clips are read only");
} }

View file

@ -32,10 +32,10 @@ public:
virtual void seek(Time offset) override; virtual void seek(Time offset) override;
virtual Time position() const override; virtual Time position() const override;
virtual FramePointer peekFrame() const override; virtual FrameConstPointer peekFrame() const override;
virtual FramePointer nextFrame() override; virtual FrameConstPointer nextFrame() override;
virtual void skipFrame() override; virtual void skipFrame() override;
virtual void addFrame(FramePointer) override; virtual void addFrame(FrameConstPointer) override;
const QJsonDocument& getHeader() { const QJsonDocument& getHeader() {
return _fileHeader; return _fileHeader;

View file

@ -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 <algorithm>
#include <QtCore/QDebug>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <Finally.h>
#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<Frame>(*_wrappedClip->peekFrame());
result->timeOffset += _offset;
return result;
}
FrameConstPointer OffsetClip::nextFrame() {
auto result = std::make_shared<Frame>(*_wrappedClip->nextFrame());
result->timeOffset += _offset;
return result;
}
Time OffsetClip::duration() const {
return _wrappedClip->duration() + _offset;
}

View file

@ -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>;
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

View file

@ -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 <QtCore/QDebug>
#include <Finally.h>
#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();
}

View file

@ -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 <QtCore/QFile>
#include <QtCore/QJsonDocument>
#include <mutex>
namespace recording {
class WrapperClip : public Clip {
public:
using Pointer = std::shared_ptr<WrapperClip>;
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

View file

@ -23,6 +23,9 @@ const float TWO_PI = 2.0f * PI;
const float PI_OVER_TWO = 0.5f * PI; const float PI_OVER_TWO = 0.5f * PI;
const float RADIANS_PER_DEGREE = PI / 180.0f; const float RADIANS_PER_DEGREE = PI / 180.0f;
const float DEGREES_PER_RADIAN = 180.0f / PI; 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 EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations
const float SQUARE_ROOT_OF_2 = (float)sqrt(2.0f); const float SQUARE_ROOT_OF_2 = (float)sqrt(2.0f);

View file

@ -11,6 +11,12 @@
#include "Transform.h" #include "Transform.h"
#include <QtCore/QJsonValue>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include "NumericalConstants.h"
#include "shared/JSONHelpers.h"
void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix) { void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix) {
const float ACCURACY_THREASHOLD = 0.00001f; 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)); 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;
}

View file

@ -21,6 +21,9 @@
#include <memory> #include <memory>
class QJsonObject;
class QJsonValue;
inline bool isValidScale(glm::vec3 scale) { inline bool isValidScale(glm::vec3 scale) {
bool result = scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f; bool result = scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f;
assert(result); assert(result);
@ -35,6 +38,7 @@ inline bool isValidScale(float scale) {
class Transform { class Transform {
public: public:
using Pointer = std::shared_ptr<Transform>;
typedef glm::mat4 Mat4; typedef glm::mat4 Mat4;
typedef glm::mat3 Mat3; typedef glm::mat3 Mat3;
typedef glm::vec4 Vec4; typedef glm::vec4 Vec4;
@ -81,6 +85,10 @@ public:
return (*this); return (*this);
} }
bool operator==(const Transform& other) const {
return _rotation == other._rotation && _scale == other._scale && _translation == other._translation;
}
Transform& setIdentity(); Transform& setIdentity();
const Vec3& getTranslation() const; const Vec3& getTranslation() const;
@ -111,6 +119,8 @@ public:
Transform& evalFromRawMatrix(const Mat4& matrix); Transform& evalFromRawMatrix(const Mat4& matrix);
Transform& evalFromRawMatrix(const Mat3& rotationScalematrix); Transform& evalFromRawMatrix(const Mat3& rotationScalematrix);
Mat4 getMatrix() const;
Mat4 getInverseMatrix() const;
Mat4& getMatrix(Mat4& result) const; Mat4& getMatrix(Mat4& result) const;
Mat4& getInverseMatrix(Mat4& result) const; Mat4& getInverseMatrix(Mat4& result) const;
Mat4& getInverseTransposeMatrix(Mat4& result) const; Mat4& getInverseTransposeMatrix(Mat4& result) const;
@ -120,6 +130,9 @@ public:
Transform& evalInverse(Transform& result) const; 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 void evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix);
static Transform& mult(Transform& result, const Transform& left, const Transform& right); static Transform& mult(Transform& result, const Transform& left, const Transform& right);
@ -127,6 +140,10 @@ public:
// Left will be inversed before the multiplication // Left will be inversed before the multiplication
static Transform& inverseMult(Transform& result, const Transform& left, const Transform& right); 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; Vec4 transform(const Vec4& pos) const;
Vec3 transform(const Vec3& pos) const; Vec3 transform(const Vec3& pos) const;
@ -368,6 +385,18 @@ inline Transform& Transform::postScale(const Vec3& scale) {
return *this; 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 { inline Transform::Mat4& Transform::getMatrix(Transform::Mat4& result) const {
if (isRotating()) { if (isRotating()) {
Mat3 rot = glm::mat3_cast(_rotation); Mat3 rot = glm::mat3_cast(_rotation);

View file

@ -52,6 +52,9 @@ quat quatFromJsonValue(const QJsonValue& q) {
} }
vec3 vec3FromJsonValue(const QJsonValue& v) { vec3 vec3FromJsonValue(const QJsonValue& v) {
if (v.isDouble()) {
return vec3((float)v.toDouble());
}
return glmFromJson<vec3>(v); return glmFromJson<vec3>(v);
} }

View file

@ -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 <QtCore/QJsonValue>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <glm/gtc/matrix_transform.hpp>
const float UniformTransform::DEFAULT_SCALE = 1.0f;
std::shared_ptr<UniformTransform> UniformTransform::parseJson(const QJsonValue& basis) {
std::shared_ptr<UniformTransform> result = std::make_shared<UniformTransform>();
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;
}

View file

@ -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<UniformTransform> parseJson(const QJsonValue& json);
};
#endif