// Copyright (c) 2022 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #include "SceneItem.hpp" #include "SceneItemComponent.hpp" #include "util/mathutils.hpp" using namespace Dawn; SceneItem::SceneItem(std::weak_ptr scene, sceneitemid_t id) : transformLocal(1.0f), transformWorld(1.0f) { this->id = id; this->scene = scene; this->updateLocalValuesFromLocalTransform(); } void SceneItem::init() { // Keep checking all components until they have all inited int32_t waitingOn; do { waitingOn = 0; // For each component auto it = this->components.begin(); while(it != this->components.end()) { // Has this component already inited? if((*it)->hasInitialized) { ++it; continue; } // For each dependency. auto deps = (*it)->getDependencies(); bool_t waiting = false; auto it2 = deps.begin(); while(it2 != deps.end()) { if(*it2 == nullptr) { ++it2; continue; } // Has the dep not yet inited? if(!(*it2)->hasInitialized) { waiting = true; break; } ++it2; } // Are we waiting for a dep? if(waiting) { waitingOn++; } else { (*it)->init(); } ++it; } } while(waitingOn != 0); } void SceneItem::destroy() { this->components.clear(); } void SceneItem::updateLocalValuesFromLocalTransform() { glm::vec3 skew; glm::vec4 perspective; glm::decompose( this->transformLocal, this->localScale, this->localRotation, this->localPosition, skew, perspective ); } void SceneItem::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 SceneItem::updateWorldTransformFromLocalTransform() { auto parent = this->getParent(); if(parent != nullptr) { auto newWorld = parent->getWorldTransform(); this->transformWorld = newWorld * transformLocal; } else { this->transformWorld = transformLocal; } } void SceneItem::updateLocalTransformFromWorldTransform() { glm::mat4 parentMat(1.0f); auto parent = this->getParent(); if(parent != nullptr) parentMat = parent->getWorldTransform(); this->transformLocal = parentMat / this->transformWorld; this->updateLocalValuesFromLocalTransform(); } void SceneItem::updateChildrenTransforms() { auto it = this->children.begin(); while(it != this->children.end()) { auto childTrans = it->lock(); if(childTrans) childTrans->updateWorldTransformFromLocalTransform(); ++it; } } void SceneItem::lookAt(glm::vec3 pos, glm::vec3 look) { this->lookAt(pos, look, glm::vec3(0, 1, 0)); } void SceneItem::lookAt(glm::vec3 pos, glm::vec3 look, glm::vec3 up) { this->setWorldTransform(glm::lookAt(pos, look, up)); } float_t SceneItem::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 SceneItem::getLocalPosition() { return this->localPosition; } void SceneItem::setLocalPosition(glm::vec3 position) { this->localPosition = position; this->updateLocalTransformFromLocalValues(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } glm::vec3 SceneItem::getLocalScale() { return this->localScale; } void SceneItem::setLocalScale(glm::vec3 scale) { this->localScale = scale; this->updateLocalTransformFromLocalValues(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } glm::quat SceneItem::getLocalRotation() { return this->localRotation; } void SceneItem::setLocalRotation(glm::quat rotation) { this->localRotation = rotation; this->updateLocalTransformFromLocalValues(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } glm::mat4 SceneItem::getLocalTransform() { return this->transformLocal; } void SceneItem::setLocalTransform(glm::mat4 transform) { this->transformLocal = transform; this->updateLocalValuesFromLocalTransform(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } glm::vec3 SceneItem::getWorldPosition() { return this->transformWorld[3]; } glm::mat4 SceneItem::getWorldTransform() { return this->transformWorld; } void SceneItem::setWorldTransform(glm::mat4 transform) { this->transformWorld = transform; this->updateLocalTransformFromWorldTransform(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } void SceneItem::setParent(std::shared_ptr parent) { assertTrue( parent == nullptr || parent != shared_from_this(), "SceneItem::setParent: Cannot set parent to self" ); auto currentParent = this->getParent(); if(currentParent == parent) return; if(currentParent != nullptr) { auto it = currentParent->children.begin(); while(it != currentParent->children.end()) { auto child = (*it).lock(); if(child == shared_from_this()) { currentParent->children.erase(it); break; } ++it; } } this->parent = parent; if(parent != nullptr) parent->children.push_back(shared_from_this()); this->updateLocalTransformFromWorldTransform(); this->updateChildrenTransforms(); this->eventTransformUpdated.invoke(); } std::shared_ptr SceneItem::getParent() { return this->parent.lock(); } bool_t SceneItem::isChildOf(std::shared_ptr parent) { auto current = this->getParent(); std::shared_ptr currentLocked; while(currentLocked = current) { if(currentLocked == parent) return true; current = currentLocked->getParent(); } return false; } SceneItem::~SceneItem() { this->setParent(nullptr); auto it = this->children.begin(); while(it != this->children.end()) { auto child = (*it).lock(); if(child != nullptr) child->setParent(nullptr); it = this->children.begin(); } }