1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef SCENEPLUGIN_INTF_NODE_H
16 #define SCENEPLUGIN_INTF_NODE_H
17 
18 #include <scene_plugin/namespace.h>
19 
20 #include <base/containers/vector.h>
21 
22 #include <meta/api/animation/animation.h>
23 #include <meta/api/make_callback.h>
24 #include <meta/base/types.h>
25 #include <meta/interface/intf_container.h>
26 #include <meta/interface/property/property.h>
27 
28 SCENE_BEGIN_NAMESPACE()
29 
30 REGISTER_INTERFACE(IPendingRequest, "59856b13-8674-48f0-9b34-9b1093472958")
31 template<typename T>
32 class IPendingRequest : public CORE_NS::IInterface {
33     META_INTERFACE(CORE_NS::IInterface, IPendingRequest, InterfaceId::IPendingRequest)
34 public:
35     META_EVENT(META_NS::IOnChanged, OnReady)
36     virtual const BASE_NS::vector<T>& GetResults() const = 0;
37 };
38 
39 using IPickingResult = IPendingRequest<BASE_NS::Math::Vec3>;
40 
41 class IMesh;
42 class IScene;
43 class IMultiMeshProxy;
44 
45 enum class NodeState { DETACHED = 0, ATTACHED = 1 };
46 
47 // Implemented by SCENE_NS::ClassId::Camera, SCENE_NS::ClassId::Light, SCENE_NS::ClassId::Model
48 REGISTER_INTERFACE(INode, "e9770092-67fe-42ad-8703-a9be44250841")
49 class INode : public META_NS::INamed {
50     META_INTERFACE(META_NS::INamed, INode, InterfaceId::INode)
51 public:
52     /**
53      * @brief Path of this object on 3D scene (mirrored to META_NS::IContainer, also),
54      * @return property pointer
55      */
56     META_READONLY_PROPERTY(BASE_NS::string, Path)
57 
58     /**
59      * @brief If the node was created from a prefab, the uri is stored here, empty otherwise
60      * @return property pointer
61      */
62 
63     /**
64      * @brief Transform of the node Position
65      * @return property pointer
66      */
67     META_PROPERTY(BASE_NS::Math::Vec3, Position)
68 
69     /**
70      * @brief Transform of the node Scale
71      * @return property pointer
72      */
73     META_PROPERTY(BASE_NS::Math::Vec3, Scale)
74 
75     /**
76      * @brief Transform of the node Rotation
77      * @return property pointer
78      */
79     META_PROPERTY(BASE_NS::Math::Quat, Rotation)
80 
81     /**
82      * @brief Enable node in 3D scene.
83      * @return property pointer
84      */
85     META_PROPERTY(bool, Visible)
86 
87     /**
88      * @brief Bitmask that controls if the node are rendered. ICamera::LayerMask defines the
89      *        bits that need to be active, while nodes may have different combinations of bits
90      * @return property pointer
91      */
92     META_PROPERTY(uint64_t, LayerMask)
93 
94     enum NodeStatus {
95         /** Instance exists, but the properties are not set even locally*/
96         NODE_STATUS_UNINITIALIZED = 0,
97         /** The local properties are set*/
98         NODE_STATUS_CONNECTING = 1,
99         /** The node is bound to 3D scene*/
100         NODE_STATUS_CONNECTED = 2,
101         /** The respective 3D element was not found
102          *  Node properties are preserved but not proxied to 3D scene */
103         NODE_STATUS_DISCONNECTED = 3,
104         /** The node and all its children are bound to 3D scene*/
105         NODE_STATUS_FULLY_CONNECTED = 4
106     };
107     /**
108      * @brief Node status.
109      * @return property pointer
110      */
111     META_READONLY_PROPERTY(uint8_t, Status)
112 
113     /**
114      * @brief Nodes local matrix on 3D scene.
115      * @return Pointer to the property.
116      */
117     META_PROPERTY(BASE_NS::Math::Mat4X4, LocalMatrix)
118 
119     /**
120      * @brief The mesh attached to node, if any.
121      * @return Pointer to the property.
122      */
123     META_PROPERTY(BASE_NS::shared_ptr<IMesh>, Mesh)
124 
125     /**
126      * @brief Event that is invoked when the backing resources for the node have been loaded
127      * @return Reference to the event.
128      */
129     META_EVENT(META_NS::IOnChanged, OnLoaded)
130 
131     /**
132      * @brief Event that is invoked when all the children of this node have been bound to 3d scene
133      * @return Reference to the event.
134      */
135     META_EVENT(META_NS::IOnChanged, OnBound)
136 
137     /**
138      * @brief Deprecated, use Mesh() property. Set the given mesh to this node.
139      * @param the mesh to set.
140      */
141     virtual void SetMesh(BASE_NS::shared_ptr<IMesh> mesh) = 0;
142 
143     /**
144      * @brief Deprecated, use Mesh() property. Returns a mesh from the scene.
145      * @returns always an instance, even the engine node does not no have mesh attached.
146      */
147     virtual BASE_NS::shared_ptr<IMesh> GetMesh() const = 0;
148 
149     virtual BASE_NS::shared_ptr<IMultiMeshProxy> CreateMeshProxy(size_t count, BASE_NS::shared_ptr<IMesh> mesh) = 0;
150     virtual void SetMultiMeshProxy(BASE_NS::shared_ptr<IMultiMeshProxy> multimesh) = 0;
151     virtual BASE_NS::shared_ptr<IMultiMeshProxy> GetMultiMeshProxy() const = 0;
152 
153     /**
154      * @brief Set node parent on the container, reflected to 3D scene.
155      * @param node Node to parent.
156      * @param parent New parent of the node.
157      * @return true on success.
158      */
SetParent(const INode::Ptr & node,const META_NS::IObject::Ptr & parent)159     static bool SetParent(const INode::Ptr& node, const META_NS::IObject::Ptr& parent)
160     {
161         if (auto container = interface_pointer_cast<META_NS::IContainer>(parent); node && container) {
162             return container->Add(node);
163         }
164         return false;
165     }
166 
167     // ToDo: Should perhaps be a property
168     enum BuildBehavior {
169         // Do not build children automatically
170         NODE_BUILD_CHILDREN_NO_BUILD = 0,
171         // Build children gradually asynchronously
172         NODE_BUILD_CHILDREN_GRADUAL = 1,
173         // Build all node descendants on one go
174         NODE_BUILD_CHILDREN_ALL = 2,
175         // Build only direct descendants.
176         // (does not recurse in to children of children, to access them you need to call buildchildren on the children first.)
177         NODE_BUILD_ONLY_DIRECT_CHILDREN = 3
178     };
179     /**
180      * @brief Instantiate children nodes, i.e. mirror all immediate children from 3D system.
181      * @param options controls if the node builds also descendants of immediate children.
182      * @return True if scene is ready to process possible children.
183      */
184     virtual bool BuildChildren(BuildBehavior options) = 0;
185 
186     /** @brief Get the scene attached to this node.
187      */
188     virtual BASE_NS::shared_ptr<IScene> GetScene() const = 0;
189 
190     /**
191      * @brief Add handler for OnLoaded event
192      * @param callback
193      * @return reference to this instance.
194      */
195     template<class Callback>
OnLoaded(Callback && callback)196     auto& OnLoaded(Callback&& callback)
197     {
198         OnLoaded()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(callback));
199         return *this;
200     }
201 
202     /**
203      * @brief Run a callback once object is loaded. if object has been already loaded, runs the callback synchronously.
204      *        Keeps the instance alive using strong ref if callback attaches one and removes the reference on callback.
205      * @param callback
206      * @return true if object is already loaded.
207      */
208     template<class Callback>
OnLoadedSingleShot(Callback && callback)209     bool OnLoadedSingleShot(Callback&& callback)
210     {
211         if (auto status = META_NS::GetValue(Status());
212             (status == NODE_STATUS_FULLY_CONNECTED || status == NODE_STATUS_CONNECTED)) {
213             callback();
214             return true;
215         }
216         auto token = OnLoaded()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(callback));
217         OnLoaded()->AddHandler(
218             META_NS::MakeCallback<META_NS::IOnChanged>([token, this]() { OnLoaded()->RemoveHandler(token); }));
219         return false;
220     }
221 
222     /**
223      * @brief Like above, the function makes a strong assumption that either the callback
224      *        contains a strong pointer to the object itself, or lifetime is otherwise guaranteed.
225      *        ToDo: should make that more explicit
226      * @param self pointer to this instance
227      * @param callback that runs either immediately if the object is ready or later if the construction of node or its
228      * children is not finished
229      * @return true if object is already fully loaded.
230      */
231     template<class Callback>
OnReady(Callback && callback)232     bool OnReady(Callback&& callback)
233     {
234         if (META_NS::GetValue(Status()) == NODE_STATUS_FULLY_CONNECTED) {
235             callback();
236             return true;
237         }
238 
239         auto token = OnBound()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(callback));
240         OnBound()->AddHandler(
241             META_NS::MakeCallback<META_NS::IOnChanged>([token, this]() { OnBound()->RemoveHandler(token); }));
242         return false;
243     }
244 
245     /**
246      * @brief Calculates the global matrix based on node hierarchy and local matrices.
247      *        The local matrix reflects values from transform component, so there can be
248      *        some delay reading back values set using local transformations.
249      * @return Global matrix of Node transformations.
250      */
251     virtual BASE_NS::Math::Mat4X4 GetGlobalTransform() const = 0;
252 
253     /**
254      * @brief Calculates the parents global matrix based on node hierarchy and local matrices
255      *        and subtracts that from the given matrix to store it as local matrix.
256      * @param matrix Global matrix of Node transformations.
257      */
258     virtual void SetGlobalTransform(const BASE_NS::Math::Mat4X4& matrix) = 0;
259 
260     virtual void AttachToHierarchy() = 0;
261 
262     virtual void DetachFromHierarchy() = 0;
263 
264     virtual NodeState GetAttachedState() const = 0;
265 
266     /**
267      * Calculates a world space AABB for this node and optionally all of it's children recursively. Uses the world
268      * matrix component for the calculations i.e. the values are only valid after the ECS has updated all the systems
269      * that manipulate the world matrix.
270      */
271     virtual IPickingResult::Ptr GetWorldMatrixComponentAABB(bool isRecursive) const = 0;
272 
273     /**
274      * Calculates a world space AABB for this node and optionally all of it's children recursively. Uses only the
275      * transform component for the calculations. This way the call will also give valid results even when the ECS has
276      * not been updated. Does not take skinning or animations into account.
277      */
278     virtual IPickingResult::Ptr GetTransformComponentAABB(bool isRecursive) const = 0;
279 };
280 
281 struct NodeDistance {
282     INode::Ptr node;
283     float distance;
284 };
285 
286 using IRayCastResult = IPendingRequest<NodeDistance>;
287 
288 SCENE_END_NAMESPACE()
289 
290 META_TYPE(SCENE_NS::INode::WeakPtr);
291 META_TYPE(SCENE_NS::INode::Ptr);
292 
293 #endif // SCENEPLUGIN_INTF_NODE_H
294