1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
#pragma once
#include "itransformable.h"
#include "math/Matrix4.h"
#include "math/Quaternion.h"
const Vector3 c_translation_identity(0, 0, 0);
const Quaternion c_rotation_identity(Quaternion::Identity());
const Vector3 c_scale_identity(1, 1, 1);
/**
* Base implementation of the ITransformable interface.
*/
class Transformable :
public ITransformable
{
protected:
// Flags to signal which type of transformation this is about
enum TransformationType
{
NoTransform = 0,
Translation = 1 << 0,
Rotation = 1 << 1,
Scale = 1 << 2,
};
private:
Vector3 _translation;
Quaternion _rotation;
Vector3 _scale;
TransformModifierType _type;
unsigned int _transformationType;
public:
Transformable() :
_translation(c_translation_identity),
_rotation(Quaternion::Identity()),
_scale(c_scale_identity),
_type(TRANSFORM_PRIMITIVE),
_transformationType(NoTransform)
{}
void setType(TransformModifierType type) override
{
_type = type;
}
TransformModifierType getType() const
{
return _type;
}
void setTranslation(const Vector3& value) override
{
_translation = value;
_transformationType |= Translation;
_onTransformationChanged();
}
void setRotation(const Quaternion& value) override
{
_rotation = value;
_transformationType |= Rotation;
_onTransformationChanged();
}
void setRotation(const Quaternion& value, const Vector3& worldPivot, const Matrix4& localToWorld) override
{
// greebo: When rotating around a pivot, the operation can be split into a rotation
// and a translation part. Calculate the translation part and apply it.
// Translate the world pivot into local coordinates (we only care about the translation part)
Vector3 localPivot = worldPivot - localToWorld.translation();
Matrix4 rotation = Matrix4::getRotationQuantised(value);
// This is basically T = P - R*P
Vector3 translation(
localPivot.x() - rotation.xx()*localPivot.x() - rotation.yx()*localPivot.y() - rotation.zx()*localPivot.z(),
localPivot.y() - rotation.xy()*localPivot.x() - rotation.yy()*localPivot.y() - rotation.zy()*localPivot.z(),
localPivot.z() - rotation.xz()*localPivot.x() - rotation.yz()*localPivot.y() - rotation.zz()*localPivot.z()
);
_translation = translation;
_transformationType |= Translation;
// Regardless of the pivot, the object rotates "by itself", so let's apply the rotation in any case
_rotation = value;
_transformationType |= Rotation;
_onTransformationChanged();
}
void setScale(const Vector3& value) override
{
_scale = value;
_transformationType |= Scale;
_onTransformationChanged();
}
void freezeTransform() override
{
if (_translation != c_translation_identity ||
_rotation != c_rotation_identity ||
_scale != c_scale_identity)
{
_applyTransformation();
_translation = c_translation_identity;
_rotation = c_rotation_identity;
_scale = c_scale_identity;
_transformationType = NoTransform;
_onTransformationChanged();
}
}
/* greebo: This reverts the currently active transformation
* by setting the scale/rotation/translation to identity.
* It's enough to call _onTransformationChanged() as this
* usually marks the node's geometry as "needs re-evaluation",
* and during next rendering turn everything will be updated.
*/
void revertTransform() override
{
_translation = c_translation_identity;
_rotation = c_rotation_identity;
_scale = c_scale_identity;
_transformationType = NoTransform;
_onTransformationChanged();
}
const Vector3& getTranslation() const
{
return _translation;
}
const Quaternion& getRotation() const
{
return _rotation;
}
const Vector3& getScale() const
{
return _scale;
}
Matrix4 calculateTransform() const
{
return getMatrixForComponents(getTranslation(), getRotation(), getScale());
}
protected:
/**
* Returns a bitmask indicating which transformation classes we're dealing with
* to allow subclasses to specialise their math on the requested transformation.
* See TransformationType enum for bit values.
*/
unsigned int getTransformationType() const
{
return _transformationType;
}
/**
* greebo: Signal method for subclasses. This gets called
* as soon as anything (translation, scale, rotation) is changed.
*
* To be implemented by subclasses
*/
virtual void _onTransformationChanged() = 0;
/**
* greebo: Signal method to be implemented by subclasses.
* Is invoked whenever the transformation is frozen.
*/
virtual void _applyTransformation() = 0;
// For rotation around pivot points the code needs to know the object center
// before the operation started.
// Subclasses need to provide this information.
virtual const Vector3& getUntransformedOrigin() override
{
static Vector3 center(0, 0, 0);
return center;
}
private:
static Matrix4 getMatrixForComponents(const Vector3& translation, const Quaternion& rotation, const Vector3& scale)
{
Matrix4 result(Matrix4::getRotationQuantised(rotation));
result.setXCol(result.xCol3() * scale.x());
result.setYCol(result.yCol3() * scale.y());
result.setZCol(result.zCol3() * scale.z());
result.setTranslation(translation);
return result;
}
};
|