// Copyright (c) 2022 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #include "Transform.hpp" #include "scene/SceneItem.hpp" #include "util/mathutils.hpp" using namespace Dawn; Transform::Transform(SceneItem *item) : transformLocal(1.0f), transformWorld(1.0f) { assertNotNull(item); this->item = item; this->updateLocalValuesFromLocalTransform(); } void Transform::updateLocalValuesFromLocalTransform() { glm::vec3 skew; glm::vec4 perspective; glm::decompose( this->transformLocal, this->localScale, this->localRotation, this->localPosition, skew, perspective ); } void Transform::updateLocalTransformFromLocalValues() { glm::mat4 translate = glm::translate(glm::mat4(1.0), this->localPosition); glm::mat4 rotate = glm::mat4_cast(this->localRotation); glm::mat4 scale = glm::scale(glm::mat4(1.0), this->localScale); this->transformLocal = translate * rotate * scale; this->updateWorldTransformFromLocalTransform(); } void Transform::updateWorldTransformFromLocalTransform() { auto parent = this->getParent(); if(parent != nullptr) { auto newWorld = parent->getWorldTransform(); this->transformWorld = newWorld * transformLocal; } else { this->transformWorld = transformLocal; } } void Transform::updateLocalTransformFromWorldTransform() { glm::mat4 parentMat(1.0f); auto parent = this->getParent(); if(parent != nullptr) parentMat = parent->getWorldTransform(); this->transformLocal = parentMat / this->transformWorld; this->updateLocalValuesFromLocalTransform(); } void Transform::updateChildrenTransforms() { auto it = this->children.begin(); while(it != this->children.end()) { (*it)->updateWorldTransformFromLocalTransform(); ++it; } } void Transform::lookAt(glm::vec3 pos, glm::vec3 look) { this->lookAt(pos, look, glm::vec3(0, 1, 0)); } void Transform::lookAt(glm::vec3 pos, glm::vec3 look, glm::vec3 up) { this->setWorldTransform(glm::lookAt(pos, look, up)); } float_t Transform::lookAtPixelPerfect( glm::vec3 position, glm::vec3 look, float_t viewportHeight, float_t fov ) { float_t z = ( tanf((mathDeg2Rad(180.0f) - fov) / 2.0f) * (viewportHeight/2.0f) ); this->lookAt(glm::vec3(position.x, position.y, position.z + z), look); return z; } glm::vec3 Transform::getLocalPosition() { return this->localPosition; } void Transform::setLocalPosition(glm::vec3 position) { this->localPosition = position; this->updateLocalTransformFromLocalValues(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } glm::vec3 Transform::getLocalScale() { return this->localScale; } void Transform::setLocalScale(glm::vec3 scale) { this->localScale = scale; this->updateLocalTransformFromLocalValues(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } glm::quat Transform::getLocalRotation() { return this->localRotation; } void Transform::setLocalRotation(glm::quat rotation) { this->localRotation = rotation; this->updateLocalTransformFromLocalValues(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } glm::mat4 Transform::getLocalTransform() { return this->transformLocal; } void Transform::setLocalTransform(glm::mat4 transform) { this->transformLocal = transform; this->updateLocalValuesFromLocalTransform(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } glm::vec3 Transform::getWorldPosition() { return this->transformWorld[3]; } glm::mat4 Transform::getWorldTransform() { return this->transformWorld; } void Transform::setWorldTransform(glm::mat4 transform) { this->transformWorld = transform; this->updateLocalTransformFromWorldTransform(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } void Transform::setParent(Transform *parent) { assertTrue(parent != this); auto currentParent = this->getParent(); if(currentParent == parent) return; if(currentParent != nullptr) { auto it = currentParent->children.begin(); while(it != currentParent->children.end()) { if(*it == this) { currentParent->children.erase(it); break; } ++it; } } this->parent = parent; if(parent != nullptr) parent->children.push_back(this); this->updateLocalTransformFromWorldTransform(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } Transform * Transform::getParent() { return this->parent; } bool_t Transform::isChildOf(Transform *parent) { Transform *current = this->getParent(); while(current != nullptr) { if(current == parent) return true; current = current->getParent(); } return false; } Transform::~Transform() { this->setParent(nullptr); auto it = this->children.begin(); while(it != this->children.end()) { (*it)->setParent(nullptr); ++it; } }