// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #pragma once #include #include #include #include #include #include namespace anki { // Base class of the scene class SceneNode : public SceneHierarchy, public IntrusiveListEnabled { friend class SceneComponent; friend class SceneGraph; public: using Base = SceneHierarchy; // The one and only constructor. // name: The unique name of the node. If it's empty the the node is not searchable. SceneNode(CString name); virtual ~SceneNode(); // Return the name. It may be empty for nodes that we don't want to track. CString getName() const { return (!m_name.isEmpty()) ? m_name.toCString() : "Unnamed"; } void setName(CString name); U32 getUuid() const { return m_uuid; } Bool isMarkedForDeletion() const { return m_markedForDeletion; } void markForDeletion(); Timestamp getComponentMaxTimestamp() const { return m_maxComponentTimestamp; } void setComponentMaxTimestamp(Timestamp maxComponentTimestamp) { ANKI_ASSERT(maxComponentTimestamp > 0); m_maxComponentTimestamp = maxComponentTimestamp; } void addChild(SceneNode* obj) { Base::addChild(obj); } void setParent(SceneNode* obj) { Base::setParent(obj); } // This is called by the scenegraph every frame after all component updates. By default it does nothing. // prevUpdateTime: Timestamp of the previous update // crntTime: Timestamp of this update virtual void frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime) { } // Extra serialization for the derived classes virtual Error serialize([[maybe_unused]] SceneSerializer& serializer) { return Error::kNone; } // Iterate all components. template void iterateComponents(TFunct func) const { for(U32 i = 0; i < m_components.getSize(); ++i) { func(*m_components[i]); } } // Iterate all components. template void iterateComponents(TFunct func) { for(U32 i = 0; i < m_components.getSize(); ++i) { func(*m_components[i]); } } // Iterate all components of a specific type template void iterateComponentsOfType(TFunct func) const { if(hasComponent()) { for(U32 i = 0; i < m_components.getSize(); ++i) { if(m_components[i]->getType() == TComponent::kClassType) { func(static_cast(*m_components[i])); } } } } // Iterate all components of a specific type template void iterateComponentsOfType(TFunct func) { if(hasComponent()) { for(U32 i = 0; i < m_components.getSize(); ++i) { if(m_components[i]->getType() == TComponent::kClassType) { func(static_cast(*m_components[i])); } } } } // Try geting a pointer to the first component of the requested type template const TComponent* tryGetFirstComponentOfType() const { if(hasComponent()) { for(U32 i = 0; i < m_components.getSize(); ++i) { if(m_components[i]->getType() == TComponent::kClassType) { return static_cast(m_components[i]); } } } return nullptr; } // Try geting a pointer to the first component of the requested type template TComponent* tryGetFirstComponentOfType() { const TComponent* c = static_cast(this)->tryGetFirstComponentOfType(); return const_cast(c); } // Get a pointer to the first component of the requested type template const TComponent& getFirstComponentOfType() const { const TComponent* out = tryGetFirstComponentOfType(); ANKI_ASSERT(out != nullptr); return *out; } // Get a pointer to the first component of the requested type template TComponent& getFirstComponentOfType() { const TComponent& c = static_cast(this)->getFirstComponentOfType(); return const_cast(c); } // Try geting a pointer to the nth component of the requested type. template const TComponent* tryGetNthComponentOfType(U32 nth) const { if(hasComponent()) { I32 inth = I32(nth); for(U32 i = 0; i < m_components.getSize(); ++i) { if(m_components[i]->getType() == TComponent::kClassType && inth-- == 0) { return static_cast(m_components[i]); } } } return nullptr; } // Try geting a pointer to the nth component of the requested type. template TComponent* tryGetNthComponentOfType(U32 nth) { const TComponent* c = static_cast(this)->tryGetNthComponentOfType(nth); return const_cast(c); } template const TComponent& getNthComponentOfType(U32 nth) const { const TComponent* out = tryGetNthComponentOfType(nth); ANKI_ASSERT(out); return *out; } template TComponent& getNthComponentOfType(U32 nth) { TComponent* out = tryGetNthComponentOfType(nth); ANKI_ASSERT(out); return *out; } // Get the nth component. template TComponent& getComponentAt(U32 idx) { ANKI_ASSERT(m_components[idx]->getType() == TComponent::kClassType); SceneComponent* c = m_components[idx]; return *static_cast(c); } // Get the nth component. template const TComponent& getComponentAt(U32 idx) const { ANKI_ASSERT(m_components[idx]->getType() == TComponent::kClassType); const SceneComponent* c = m_components[idx]; return *static_cast(c); } U32 getComponentCount() const { return m_components.getSize(); } template U32 countComponentsOfType() const { U32 count = 0; iterateComponentsOfType([&]([[maybe_unused]] const TComponent& c) { ++count; }); return count; } // Create and append a component to the components container. The SceneNode has the ownership. template TComponent* newComponent(); template Bool hasComponent() const { return !!((1u << SceneComponentTypeMask(TComponent::kClassType)) & m_componentTypeMask); } SceneComponentTypeMask getSceneComponentMask() const { return m_componentTypeMask; } // Movement methods // // Ignore parent nodes's transform. void setIgnoreParentTransform(Bool ignore) { m_ignoreParentNodeTransform = ignore; } const Transform& getLocalTransform() const { return m_ltrf; } void setLocalTransform(const Transform& x) { m_ltrf = x; m_localTransformDirty = true; } void setLocalOrigin(const Vec3& x) { m_ltrf.setOrigin(x); m_localTransformDirty = true; } Vec3 getLocalOrigin() const { return m_ltrf.getOrigin().xyz; } void setLocalRotation(const Mat3& x) { m_ltrf.setRotation(x); m_localTransformDirty = true; } Mat3 getLocalRotation() const { return m_ltrf.getRotation().getRotationPart(); } void setLocalScale(const Vec3& x) { m_ltrf.setScale(x); m_localTransformDirty = true; } Vec3 getLocalScale() const { return m_ltrf.getScale().xyz; } const Transform& getWorldTransform() const { return m_wtrf; } const Transform& getPreviousWorldTransform() const { return m_prevWTrf; } void rotateLocalX(F32 angleRad) { Mat3x4 r = m_ltrf.getRotation(); r.rotateXAxis(angleRad); m_ltrf.setRotation(r); m_localTransformDirty = true; } void rotateLocalY(F32 angleRad) { Mat3x4 r = m_ltrf.getRotation(); r.rotateYAxis(angleRad); m_ltrf.setRotation(r); m_localTransformDirty = true; } void rotateLocalZ(F32 angleRad) { Mat3x4 r = m_ltrf.getRotation(); r.rotateZAxis(angleRad); m_ltrf.setRotation(r); m_localTransformDirty = true; } void moveLocalX(F32 distance) { Vec3 x_axis = m_ltrf.getRotation().getColumn(0); m_ltrf.setOrigin(m_ltrf.getOrigin() + Vec4(x_axis, 0.0f) * distance); m_localTransformDirty = true; } void moveLocalY(F32 distance) { Vec3 y_axis = m_ltrf.getRotation().getColumn(1); m_ltrf.setOrigin(m_ltrf.getOrigin() + Vec4(y_axis, 0.0) * distance); m_localTransformDirty = true; } void moveLocalZ(F32 distance) { Vec3 z_axis = m_ltrf.getRotation().getColumn(2); m_ltrf.setOrigin(m_ltrf.getOrigin() + Vec4(z_axis, 0.0) * distance); m_localTransformDirty = true; } void scale(F32 s) { m_ltrf.setScale(m_ltrf.getScale() * s); m_localTransformDirty = true; } void lookAtPoint(const Vec4& point) { m_ltrf = m_ltrf.lookAt(point, Vec4::yAxis()); m_localTransformDirty = true; } Bool movedThisFrame() const { return m_transformUpdatedThisFrame; } // Returns true if the transform got updated ANKI_INTERNAL Bool updateTransform(); ANKI_INTERNAL Bool isLocalTransformDirty() const { return m_localTransformDirty; } // End movement methods // private: SceneString m_name; // A unique name. U32 m_uuid; SceneComponentTypeMask m_componentTypeMask = SceneComponentTypeMask::kNone; GrDynamicArray m_components; Timestamp m_maxComponentTimestamp = 0; Transform m_ltrf = Transform::getIdentity(); // The transformation in local space Transform m_wtrf = Transform::getIdentity(); // The transformation in world space (local combined with parent's transformation) Transform m_prevWTrf = Transform::getIdentity(); // Keep the previous transformation for checking if it moved // Flags Bool m_markedForDeletion : 1 = false; Bool m_localTransformDirty : 1 = true; Bool m_ignoreParentNodeTransform : 1 = false; Bool m_transformUpdatedThisFrame : 1 = true; void newComponentInternal(SceneComponent* newc); Error serializeCommon(SceneSerializer& serializer); }; } // end namespace anki