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?
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);
});

View file

@ -9,6 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AvatarData.h"
#include <cstdio>
#include <cstring>
#include <stdint.h>
@ -22,6 +25,7 @@
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <Transform.h>
#include <NetworkAccessManager.h>
#include <NodeList.h>
#include <udt/PacketHeaders.h>
@ -29,12 +33,10 @@
#include <StreamUtils.h>
#include <UUID.h>
#include <shared/JSONHelpers.h>
#include <shared/UniformTransform.h>
#include <recording/Deck.h>
#include <recording/Clip.h>
#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<UniformTransform> AvatarData::getRecordingBasis() const {
std::shared_ptr<Transform> AvatarData::getRecordingBasis() const {
return _recordingBasis;
}
@ -1506,12 +1508,12 @@ void registerAvatarTypes(QScriptEngine* engine) {
new AttachmentDataObject(), QScriptEngine::ScriptOwnership));
}
void AvatarData::setRecordingBasis(std::shared_ptr<UniformTransform> recordingBasis) {
void AvatarData::setRecordingBasis(std::shared_ptr<Transform> recordingBasis) {
if (!recordingBasis) {
recordingBasis = std::make_shared<UniformTransform>();
recordingBasis->rotation = getOrientation();
recordingBasis->translation = getPosition();
recordingBasis->scale = getTargetScale();
recordingBasis = std::make_shared<Transform>();
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>(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)) {

View file

@ -134,7 +134,8 @@ class QDataStream;
class AttachmentData;
class JointData;
struct UniformTransform;
class Transform;
using TransformPointer = std::shared_ptr<Transform>;
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<UniformTransform> getRecordingBasis() const;
void setRecordingBasis(std::shared_ptr<UniformTransform> recordingBasis = std::shared_ptr<UniformTransform>());
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<UniformTransform> _recordingBasis;
TransformPointer _recordingBasis;
private:
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);

View file

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

View file

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

View file

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

View file

@ -28,6 +28,8 @@ struct 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
// data + audio for a single person
class Clip;

View file

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

View file

@ -21,7 +21,8 @@ namespace recording {
struct Frame {
public:
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_HEADER = 0x0;
@ -37,7 +38,7 @@ public:
static QMap<QString, FrameType> getFrameTypes();
static QMap<FrameType, QString> getFrameTypeNames();
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) {
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");
}

View file

@ -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<FramePointer> _frames;
std::vector<FrameConstPointer> _frames;
mutable size_t _frameIndex { 0 };
};

View file

@ -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");
}

View file

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

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

View file

@ -11,6 +11,12 @@
#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) {
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;
}

View file

@ -21,6 +21,9 @@
#include <memory>
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<Transform>;
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);

View file

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