Do I need to store a generic rotation point/radius for rotating around a point other than the origin for object transforms?
- by Casey
I'm having trouble implementing a non-origin point rotation. I have a class Transform that stores each component separately in three 3D vectors for position, scale, and rotation. This is fine for local rotations based on the center of the object.
The issue is how do I determine/concatenate non-origin rotations in addition to origin rotations. Normally this would be achieved as a Transform-Rotate-Transform for the center rotation followed by a Transform-Rotate-Transform for the non-origin point.
The problem is because I am storing the individual components, the final Transform matrix is not calculated until needed by using the individual components to fill an appropriate Matrix. (See GetLocalTransform())
Do I need to store an additional rotation (and radius) for world rotations as well or is there a method of implementation that works while only using the single rotation value?
Transform.h
#ifndef A2DE_CTRANSFORM_H
#define A2DE_CTRANSFORM_H
#include "../a2de_vals.h"
#include "CMatrix4x4.h"
#include "CVector3D.h"
#include <vector>
A2DE_BEGIN
class Transform {
public:
Transform();
Transform(Transform* parent);
Transform(const Transform& other);
Transform& operator=(const Transform& rhs);
virtual ~Transform();
void SetParent(Transform* parent);
void AddChild(Transform* child);
void RemoveChild(Transform* child);
Transform* FirstChild();
Transform* LastChild();
Transform* NextChild();
Transform* PreviousChild();
Transform* GetChild(std::size_t index);
std::size_t GetChildCount() const;
std::size_t GetChildCount();
void SetPosition(const a2de::Vector3D& position);
const a2de::Vector3D& GetPosition() const;
a2de::Vector3D& GetPosition();
void SetRotation(const a2de::Vector3D& rotation);
const a2de::Vector3D& GetRotation() const;
a2de::Vector3D& GetRotation();
void SetScale(const a2de::Vector3D& scale);
const a2de::Vector3D& GetScale() const;
a2de::Vector3D& GetScale();
a2de::Matrix4x4 GetLocalTransform() const;
a2de::Matrix4x4 GetLocalTransform();
protected:
private:
a2de::Vector3D _position;
a2de::Vector3D _scale;
a2de::Vector3D _rotation;
std::size_t _curChildIndex;
Transform* _parent;
std::vector<Transform*> _children;
};
A2DE_END
#endif
Transform.cpp
#include "CTransform.h"
#include "CVector2D.h"
#include "CVector4D.h"
A2DE_BEGIN
Transform::Transform() :
_position(),
_scale(1.0, 1.0),
_rotation(),
_curChildIndex(0),
_parent(nullptr),
_children()
{
/* DO NOTHING */
}
Transform::Transform(Transform* parent) :
_position(),
_scale(1.0, 1.0),
_rotation(),
_curChildIndex(0),
_parent(parent),
_children()
{
/* DO NOTHING */
}
Transform::Transform(const Transform& other) :
_position(other._position),
_scale(other._scale),
_rotation(other._rotation),
_curChildIndex(0),
_parent(other._parent),
_children(other._children)
{
/* DO NOTHING */
}
Transform& Transform::operator=(const Transform& rhs) {
if(this == &rhs) return *this;
this->_position = rhs._position;
this->_scale = rhs._scale;
this->_rotation = rhs._rotation;
this->_curChildIndex = 0;
this->_parent = rhs._parent;
this->_children = rhs._children;
return *this;
}
Transform::~Transform() {
_children.clear();
_parent = nullptr;
}
void Transform::SetParent(Transform* parent) {
_parent = parent;
}
void Transform::AddChild(Transform* child) {
if(child == nullptr) return;
_children.push_back(child);
}
void Transform::RemoveChild(Transform* child) {
if(_children.empty()) return;
_children.erase(std::remove(_children.begin(), _children.end(), child), _children.end());
}
Transform* Transform::FirstChild() {
if(_children.empty()) return nullptr;
return *(_children.begin());
}
Transform* Transform::LastChild() {
if(_children.empty()) return nullptr;
return *(_children.end());
}
Transform* Transform::NextChild() {
if(_children.empty()) return nullptr;
std::size_t s(_children.size());
if(_curChildIndex >= s) {
_curChildIndex = s;
return nullptr;
}
return _children[_curChildIndex++];
}
Transform* Transform::PreviousChild() {
if(_children.empty()) return nullptr;
if(_curChildIndex == 0) {
return nullptr;
}
return _children[_curChildIndex--];
}
Transform* Transform::GetChild(std::size_t index) {
if(_children.empty()) return nullptr;
if(index > _children.size()) return nullptr;
return _children[index];
}
std::size_t Transform::GetChildCount() const {
if(_children.empty()) return 0;
return _children.size();
}
std::size_t Transform::GetChildCount() {
return static_cast<const Transform&>(*this).GetChildCount();
}
void Transform::SetPosition(const a2de::Vector3D& position) {
_position = position;
}
const a2de::Vector3D& Transform::GetPosition() const {
return _position;
}
a2de::Vector3D& Transform::GetPosition() {
return const_cast<a2de::Vector3D&>(static_cast<const Transform&>(*this).GetPosition());
}
void Transform::SetRotation(const a2de::Vector3D& rotation) {
_rotation = rotation;
}
const a2de::Vector3D& Transform::GetRotation() const {
return _rotation;
}
a2de::Vector3D& Transform::GetRotation() {
return const_cast<a2de::Vector3D&>(static_cast<const Transform&>(*this).GetRotation());
}
void Transform::SetScale(const a2de::Vector3D& scale) {
_scale = scale;
}
const a2de::Vector3D& Transform::GetScale() const {
return _scale;
}
a2de::Vector3D& Transform::GetScale() {
return const_cast<a2de::Vector3D&>(static_cast<const Transform&>(*this).GetScale());
}
a2de::Matrix4x4 Transform::GetLocalTransform() const {
Matrix4x4 p((_parent ? _parent->GetLocalTransform() : a2de::Matrix4x4::GetIdentity()));
Matrix4x4 t(a2de::Matrix4x4::GetTranslationMatrix(_position));
Matrix4x4 r(a2de::Matrix4x4::GetRotationMatrix(_rotation));
Matrix4x4 s(a2de::Matrix4x4::GetScaleMatrix(_scale));
return (p * t * r * s);
}
a2de::Matrix4x4 Transform::GetLocalTransform() {
return static_cast<const Transform&>(*this).GetLocalTransform();
}
A2DE_END