// Copyright (c) 2022 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #pragma once #include "scene/Scene.hpp" #include "util/array.hpp" namespace Dawn { class SceneItemComponent; class SceneItem : public StateOwner, public std::enable_shared_from_this { private: std::vector> components; // Local (real) values glm::vec3 localPosition; glm::vec3 localScale; glm::quat localRotation; // Cached (non-real) values glm::mat4 transformLocal; glm::mat4 transformWorld; // glm::vec3 position; // glm::vec3 scale; // glm::quat rotation; // Heirarchy std::weak_ptr parent; std::vector> children; // Hidden methods void updateLocalValuesFromLocalTransform(); void updateLocalTransformFromLocalValues(); void updateWorldTransformFromLocalTransform(); void updateLocalTransformFromWorldTransform(); void updateChildrenTransforms(); public: Scene *scene; sceneitemid_t id; // I have no idea if I'm keeping this StateEvent<> eventTransformUpdated; /** * Constructor for a SceneItem. Scene Items should only be called and * initialized by the scene itself. * * @param scene Weak pointer to the Scene that this SceneItem belongs to. * @param id Scene Item ID that the Scene assigned this SceneItem. */ SceneItem(Scene *scene, sceneitemid_t id); /** * Called by the Scene the frame after we were constructed so we can begin * existing. */ void init(); /** * Called by the Scene when cleanup needs to occur but before anything in * the scene has been disposed. */ void destroy(); /** * Adds a component to this scene item. Components will only have their * init methods invoked on the first frame we enter the scene. If you add * a component to this item after this time you will either need to try * manually invoking its' init method, or ensure the component is aware of * the entire SceneItem lifecycle and listen for added callbacks. * * @tparam T Type of component being added to this scene item. * @return A shared pointer to the newly added component. */ template std::shared_ptr addComponent() { auto component = std::make_shared(this); this->components.push_back(component); return component; } /** * Returns a component attached to this SceneItem. This method will return * either a shared pointer to the component, or nullptr if the item does * not have the queried component. * * @tparam T Type of component to be fetched. * @return A shared pointer to the component, or nullptr if not found. */ template std::shared_ptr getComponent() { auto it = this->components.begin(); while(it != this->components.end()) { std::shared_ptr castedAs = std::dynamic_pointer_cast(*it); if(castedAs != nullptr) return castedAs; ++it; } return nullptr; } /** * Returns all components attached to this SceneItem that match the * queried component type. This method will return an array of shared * pointers to the components, or an empty array if the item does not have * any components of the queried type. * * @tparam T Type of component to be fetched. * @return An array of pointers to the components. */ template std::vector> getComponents() { std::vector> components; auto it = this->components.begin(); while(it != this->components.end()) { T *castedAs = std::dynamic_pointer_cast(it); if(castedAs != nullptr) components.push_back(castedAs); ++it; } return components; } /** * Finds a (direct) child of this component that has a matching component. * * @tparam T Component to find child of. * @return Pointer to the child, or nullptr if not found. */ template std::shared_ptr findChild() { auto it = this->children.begin(); while(it != this->children.end()) { auto childItem = it->lock(); if(childItem == nullptr) { ++it; continue; } auto childComponent = childItem->getComponent(); if(childComponent != nullptr) return childComponent; ++it; } return nullptr; } /** * Finds all (direct) children of this component that match the queried * component. * * @tparam T Component to find children for. * @return Array of pointers to matching children. */ template std::vector> findChildren() { auto it = this->children.begin(); std::vector> children; while(it != this->children.end()) { auto childItem = it->lock(); if(childItem == nullptr) { ++it; continue; } auto childComponent = childItem->getComponent(); if(childComponent != nullptr) children.push_back(childComponent); ++it; } return children; } /** * Finds all children, and children of children, recursively that match * the queried component. * * @tparam T Component Type to find. * @return Array of pointers to matching children components. */ template std::vector> findChildrenDeep() { auto childrenToCheck = this->children; std::vector> itemsFound; while(childrenToCheck.size() > 0) { auto it = *childrenToCheck.begin(); auto otherItem = it.lock(); if(otherItem != nullptr) { vectorAppend(childrenToCheck, otherItem->children); } auto component = otherItem->getComponent(); if(component != nullptr) itemsFound.push_back(component); childrenToCheck.erase(childrenToCheck.begin()); } return itemsFound; } // // // // // // // // // // // // // // // // // // // // // // // // // /** * Orients this transform to look at a given point in world space. * * @param position Position of the origin of this transform. * @param look Position in world space this transform looks at. */ void lookAt(const glm::vec3 position, const glm::vec3 look); void lookAt( const glm::vec3 position, const glm::vec3 look, const glm::vec3 up ); /** * Shorthand combined for lookAt and perspectivePixelPerfectDistance * to allow you to create pixel perfect lookAt camera view matricies. * * @param position Position of the camera. Z is for an offset. * @param look Position in world space this transform looks at. * @param viewportHeight Height of the viewport. * @param fov Field of view (in radians). * @return The Z distance that was calculated. */ float_t lookAtPixelPerfect( const glm::vec3 position, const glm::vec3 look, const float_t viewportHeight, const float_t fov ); /** * Returns the local position (position relative to "my parent"). * @return The 3D local position in parent-relative space. */ glm::vec3 getLocalPosition(); /** * Update / Set the local position of this transform relative to my parent * @param position Position to set for the local transform. */ void setLocalPosition(const glm::vec3 position); /** * Retusn the scale of this item, relative to my parent. * @return 3D Scale vector of this item in parent-relative space. */ glm::vec3 getLocalScale(); /** * Set the local scale of this item. * @param scale Scale of this item, relative to its parent. */ void setLocalScale(const glm::vec3 scale); /** * Returns the local rotation for this transform. * @return The local rotation (parent-relative). */ glm::quat getLocalRotation(); /** * Set the local (parent-relative) rotation for this transform. * @param rotation Rotation in parent relative space. */ void setLocalRotation(const glm::quat rotation); /** * Returns the transform matrix for this transform, in parent-relative * space. * @return The transform origin in parent-relative space. */ glm::mat4 getLocalTransform(); /** * Sets the local transform matrix for this transform. * @param transform Local (parent-relative) transform to set. */ void setLocalTransform(const glm::mat4 transform); /** * Returns the position of the origin of this transform in world-space. * * @return Transform origin in world-space. */ glm::vec3 getWorldPosition(); /** * Returns the transformation matrix for this transform, in world-space. * @return The transform origin in world-space. */ glm::mat4 getWorldTransform(); /** * Updates the transform's world-space. * @param transform Sets the transform position in world-space. */ void setWorldTransform(const glm::mat4 transform); /** * Updates the transform that this transform is a child of. Will also * handle disconnecting any existing parent. * * @param p Parent that this transform is now a child of. */ void setParent(const std::shared_ptr p); /** * Returns the parent transform of this transform, or nullptr if there is * no parent for this transform. * @return Pointer to the parent transform, or nullptr. */ std::weak_ptr getParent(); /** * Returns true if this transform is a child of the given transform, this * climbs up the heirarchy until it finds a match. * * @param p Transform to check if this transform is a child of. * @return True if this transform is a child of the given transform. */ bool_t isChildOf(std::shared_ptr p); /** * Destroy this SceneItem. */ ~SceneItem(); }; }