마지막으로 엔진을 만드는 초석이 되는 프레임워크 중 GameObjcet 마다 개별로 가지고 있는(공용으로 사용되는 쉐이더나 Vertex 정보들이 아닌) 것들을 따로 분리해보자.
유니티에서 부모와 자식 계층을 잘 생각해보자.
자식의 좌표는 부모의 좌표계에 종속된다. 만약 B 가 10,0,0 좌표에 있다고 하고 A는 5,0,0에 있다고 해보자.
B가 A의 자식이 되었을 때, B에 표시되는 좌표는 5,0,0으로 바뀐다. 그 이윤 부모의 좌표계를 기준으로 5,0,0에 있기 때문이다.
그럼 B의 월드 좌표를 얻기 위해서는 어떻게 해야할까? 먼저 부모를 기준으로 하는 변환 행렬을 구한 뒤(SRT) 다시 그 위치를 기준으로 한번 더 SRT를 하면 구할 수 있을 것이다.
이런 것처럼 Transform에는 많은 정보가 있고 해야할 것이 많다.
#pragma once
#include "Component.h"
class Transform : public Component
{
public:
Transform();
~Transform();
virtual void Init() override;
virtual void Update() override;
void UpdateTransform();
// Local
Vec3 GetLocalScale() { return _localScale; }
void SetLocalScale(const Vec3& localScale) { _localScale = localScale; UpdateTransform(); }
Vec3 GetLocalRotation() { return _localRotation; }
void SetLocalRotation(const Vec3& localRotation) { _localRotation = localRotation; UpdateTransform(); }
Vec3 GetLocalPosition() { return _localPosition; }
void SetLocalPosition(const Vec3& localPosition) { _localPosition = localPosition; UpdateTransform(); }
// World
Vec3 GetScale() { return _scale; }
void SetScale(const Vec3& scale);
Vec3 GetRotation() { return _rotation; }
void SetRotation(const Vec3& rotation);
Vec3 GetPosition() { return _position; }
void SetPosition(const Vec3& position);
Matrix GetWorldMatrix() { return _matWorld; }
// 계층 관계
bool HasParent() { return _parent != nullptr; }
shared_ptr<Transform> GetParent() { return _parent; }
void SetParent(shared_ptr<Transform> parent) { _parent = parent; }
const vector<shared_ptr<Transform>>& GetChildren() { return _children; }
void AddChild(shared_ptr<Transform> child) { _children.push_back(child); }
private:
Vec3 _localScale = { 1.f, 1.f, 1.f };
Vec3 _localRotation = { 0.f, 0.f, 0.f };
Vec3 _localPosition = { 0.f, 0.f, 0.f };
// Cache
Matrix _matLocal = Matrix::Identity;
Matrix _matWorld = Matrix::Identity;
Vec3 _scale;
Vec3 _rotation;
Vec3 _position;
Vec3 _right;
Vec3 _up;
Vec3 _look;
private:
shared_ptr<Transform> _parent;
vector<shared_ptr<Transform>> _children;
};
#include "pch.h"
#include "Transform.h"
Transform::Transform()
{
}
Transform::~Transform()
{
}
void Transform::Init()
{
}
void Transform::Update()
{
}
Vec3 ToEulerAngles(Quaternion q)
{
Vec3 angles;
// roll (x-axis rotation)
double sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
double cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
angles.x = std::atan2(sinr_cosp, cosr_cosp);
// pitch (y-axis rotation)
double sinp = std::sqrt(1 + 2 * (q.w * q.y - q.x * q.z));
double cosp = std::sqrt(1 - 2 * (q.w * q.y - q.x * q.z));
angles.y = 2 * std::atan2(sinp, cosp) - 3.14159f / 2;
// yaw (z-axis rotation)
double siny_cosp = 2 * (q.w * q.z + q.x * q.y);
double cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
angles.z = std::atan2(siny_cosp, cosy_cosp);
return angles;
}
void Transform::UpdateTransform()
{
Matrix matScale = Matrix::CreateScale(_localScale);
Matrix matRotation = Matrix::CreateRotationX(_localRotation.x);
matRotation *= Matrix::CreateRotationY(_localRotation.y);
matRotation *= Matrix::CreateRotationZ(_localRotation.z);
Matrix matTranslation = Matrix::CreateTranslation(_localPosition);
_matLocal = matScale * matRotation * matTranslation;
if (HasParent())
{
_matWorld = _matLocal * _parent->GetWorldMatrix();
}
else
{
_matWorld = _matLocal;
}
Quaternion quat;
_matWorld.Decompose(_scale, quat, _position);
_rotation = ToEulerAngles(quat);
_right = Vec3::TransformNormal(Vec3::Right, _matWorld);
_up = Vec3::TransformNormal(Vec3::Up, _matWorld);
_look = Vec3::TransformNormal(Vec3::Backward, _matWorld);
// Children
for (const shared_ptr<Transform>& child : _children)
child->UpdateTransform();
}
void Transform::SetScale(const Vec3& worldScale)
{
if (HasParent())
{
Vec3 parentScale = _parent->GetScale();
Vec3 scale = worldScale;
scale.x /= parentScale.x;
scale.y /= parentScale.y;
scale.z /= parentScale.z;
SetLocalScale(scale);
}
else
{
SetLocalScale(worldScale);
}
}
void Transform::SetRotation(const Vec3& worldRotation)
{
if (HasParent())
{
Matrix inverseMatrix = _parent->GetWorldMatrix().Invert();
Vec3 rotation;
rotation.TransformNormal(worldRotation, inverseMatrix);
SetLocalRotation(rotation);
}
else
SetLocalRotation(worldRotation);
}
void Transform::SetPosition(const Vec3& worldPosition)
{
if (HasParent())
{
Matrix worldToParentLocalMatrix = _parent->GetWorldMatrix().Invert();
Vec3 position;
position.Transform(worldPosition, worldToParentLocalMatrix);
SetLocalPosition(position);
}
else
{
SetLocalPosition(worldPosition);
}
}
우리가 유니티에서 부모를 움직이면 그에 따라 자식들도 영향을 받고 또 자식의 월드 스페이스를 고칠때는 부모의 여부에 따라 다르게 동작해야만 하기 때문에 Set 관련과 UpdateTrasnform은 신중하게 코드를 작성해야한다.
이렇게 작성하게 되면
void GameObject::Update()
{
Vec3 pos = _parent->GetPosition();
pos.x += 0.001f;
_parent->SetPosition(pos);
Vec3 rot = _parent->GetRotation();
rot.z += 0.01f;
_parent->SetRotation(rot);
//Vec3 pos = _transform->GetPosition();
//pos.x += 0.001f;
//_transform->SetPosition(pos);
_transformData.matWorld = _transform->GetWorldMatrix();
_constantBuffer->CopyData(_transformData);
}
이런식으로 좌표가 변한 후 월드 행렬을 바로 상수버퍼에 건네 줄 수 있다.
'C++ > DirectX 11' 카테고리의 다른 글
[DirectX] 엔진 구조로 제작하기 - Managers (0) | 2024.09.06 |
---|---|
[DirectX] 엔진 구조로 제작하기 - Component, MeshRenderer (2) | 2024.09.05 |
[DirectX] 프레임워크 제작기 - Shader, Pipeline, GameObject (1) | 2024.09.03 |
[DirectX] 프레임워크 제작기 - Graphics, IA, Geomotry (1) | 2024.09.02 |
[DirectX] 행렬 - SRT 변환 행렬과 좌표계 변환 행렬 및 예제 (0) | 2024.08.31 |