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 16 #ifndef SCENE_API_SCENE_H 17 #define SCENE_API_SCENE_H 18 19 #include <scene/api/node.h> 20 #include <scene/api/resource.h> 21 #include <scene/ext/intf_internal_scene.h> 22 #include <scene/interface/intf_application_context.h> 23 #include <scene/interface/intf_image.h> 24 #include <scene/interface/intf_light.h> 25 #include <scene/interface/intf_mesh.h> 26 #include <scene/interface/intf_node_import.h> 27 #include <scene/interface/intf_scene.h> 28 #include <scene/interface/intf_scene_manager.h> 29 #include <scene/interface/resource/intf_render_resource_manager.h> 30 31 #include <meta/api/animation.h> 32 #include <meta/api/iteration.h> 33 34 SCENE_BEGIN_NAMESPACE() 35 36 class Scene; 37 38 #define SCENE_RESOURCE_FACTORY_CREATE_NODE(NodeName, NodeType, ClassId) \ 39 template<const AsyncCallType& CallType = Sync> \ 40 auto Create##NodeName(BASE_NS::string_view path) \ 41 { \ 42 return CallCreateNode<NodeType, CallType>(path, ClassId); \ 43 } 44 45 /** 46 * @brief The SceneResourceParameters class defines generic parameters for resource creation. 47 */ 48 struct SceneResourceParameters { 49 SceneResourceParameters() = default; SceneResourceParametersSceneResourceParameters50 SceneResourceParameters(BASE_NS::string_view path) : path(path) {} SceneResourceParametersSceneResourceParameters51 SceneResourceParameters(const char* path) : path(path) {} 52 /// Path to load the resource from. Depending on resource type this parameter may be ignored. 53 BASE_NS::string path; 54 }; 55 56 /** 57 * @brief The MaterialResourceParameters class defines Material creation specific parameters. 58 */ 59 struct MaterialResourceParameters : public SceneResourceParameters { 60 /// Type of the material 61 MaterialType type { MaterialType::METALLIC_ROUGHNESS }; 62 }; 63 64 /** 65 * @brief The TextResourceParameters class defines Text3D node creation specific parameters. 66 */ 67 struct TextResourceParameters : public SceneResourceParameters { 68 /// The text string to show 69 BASE_NS::string text; 70 /// Font to use 71 BASE_NS::string font; 72 /// Height of the rasterized 2d texture. 73 float fontSize {}; 74 /// Text color in SRGB RGBA. 75 BASE_NS::Math::Vec4 color; 76 }; 77 78 /** 79 * @brief RenderContextResourceFactory can be used to instantiate new RenderContext specific resources (shared between 80 * all scenes using the same RenderContext). 81 */ 82 class RenderContextResourceFactory { 83 public: 84 RenderContextResourceFactory() = delete; RenderContextResourceFactory(const IScene::Ptr & scene)85 RenderContextResourceFactory(const IScene::Ptr& scene) : scene_(scene) {}; 86 virtual ~RenderContextResourceFactory() = default; META_DEFAULT_COPY_MOVE(RenderContextResourceFactory)87 META_DEFAULT_COPY_MOVE(RenderContextResourceFactory) 88 operator bool() const noexcept 89 { 90 return scene_.lock() != nullptr; 91 } 92 /// Creates a new environment with given parameters. CreateEnvironment(const SceneResourceParameters & params)93 META_API_ASYNC auto CreateEnvironment(const SceneResourceParameters& params) 94 { 95 return CallCreateResource<Environment, CallType>(params, ClassId::Environment); 96 } 97 /// Creates a new material with given parameters. CreateMaterial(const MaterialResourceParameters & params)98 META_API_ASYNC auto CreateMaterial(const MaterialResourceParameters& params) 99 { 100 auto f = CallScene([&](auto& scene) { 101 return scene.CreateObject(ClassId::Material).Then([params](const META_NS::IObject::Ptr& object) { 102 auto material = interface_pointer_cast<IMaterial>(object); 103 if (material) { 104 SetValue(material->Type(), params.type); 105 } 106 return material; 107 }); 108 }); 109 return Internal::UnwrapFuture<CallType, Material>(BASE_NS::move(f)); 110 } 111 /// Creates a new mesh with given parameters. CreateMesh(const SceneResourceParameters & params)112 META_API_ASYNC auto CreateMesh(const SceneResourceParameters& params) 113 { 114 return CallCreateResource<Mesh, CallType>(params, ClassId::Mesh); 115 } 116 /// Creates a new shader with given parameters. CreateShader(const SceneResourceParameters & params)117 META_API_ASYNC auto CreateShader(const SceneResourceParameters& params) 118 { 119 auto f = CallScene([&](auto& scene) { 120 return scene.template CreateObject<IRenderResourceManager>(ClassId::RenderResourceManager) 121 .Then([params](const IRenderResourceManager::Ptr& manager) { 122 return manager ? manager->LoadShader(params.path).GetResult() : IShader::Ptr {}; 123 }); 124 }); 125 return Internal::UnwrapFuture<CallType, Shader>(BASE_NS::move(f)); 126 } 127 /// Creates a new image with given parameters. CreateImage(const SceneResourceParameters & params)128 META_API_ASYNC auto CreateImage(const SceneResourceParameters& params) 129 { 130 auto f = CallScene([&](auto& scene) { 131 return scene.template CreateObject<IRenderResourceManager>(ClassId::RenderResourceManager) 132 .Then([params](const IRenderResourceManager::Ptr& manager) { 133 return manager ? manager->LoadImage(params.path).GetResult() : IImage::Ptr {}; 134 }); 135 }); 136 return Internal::UnwrapFuture<CallType, Image>(BASE_NS::move(f)); 137 } 138 139 protected: 140 template<class Type, const META_NS::AsyncCallType& CallType> CallCreateResource(const SceneResourceParameters & params,META_NS::ObjectId id)141 auto CallCreateResource(const SceneResourceParameters& params, META_NS::ObjectId id) 142 { 143 auto f = CallScene([&](auto& scene) { return scene.CreateObject(id); }); 144 return Internal::UnwrapFuture<CallType, Type>(BASE_NS::move(f)); 145 } 146 template<typename Fn> CallScene(Fn && fn)147 auto CallScene(Fn&& fn) const 148 { 149 auto scene = scene_.lock(); 150 return scene ? fn(*scene) : decltype(fn(*scene)) {}; 151 } 152 153 private: 154 IScene::WeakPtr scene_; 155 }; 156 157 /** 158 * @brief SceneResourceFactory can be used to instantiate new Scene specific resources (unique to each Scene instance). 159 */ 160 class SceneResourceFactory : public RenderContextResourceFactory { 161 public: 162 SceneResourceFactory() = delete; SceneResourceFactory(const IScene::Ptr & scene)163 SceneResourceFactory(const IScene::Ptr& scene) : RenderContextResourceFactory(scene) {}; 164 virtual ~SceneResourceFactory() = default; META_DEFAULT_COPY_MOVE(SceneResourceFactory)165 META_DEFAULT_COPY_MOVE(SceneResourceFactory) 166 /// Creates a new empty node with given parameters. 167 META_API_ASYNC auto CreateNode(const SceneResourceParameters& params) 168 { 169 return CallCreateNode<Node, CallType>(params, ClassId::Node); 170 } 171 /// Creates a new geometry node with given parameters. CreateGeometryNode(const SceneResourceParameters & params,const IMesh::Ptr & mesh)172 META_API_ASYNC auto CreateGeometryNode(const SceneResourceParameters& params, const IMesh::Ptr& mesh) 173 { 174 auto f = CallScene([&](auto& scene) { 175 return scene.template CreateNode<IMeshAccess>(params.path, ClassId::MeshNode) 176 .Then([mesh](const IMeshAccess::Ptr& node) { 177 bool set = !mesh || node->SetMesh(mesh).GetResult(); 178 return set ? interface_pointer_cast<INode>(node) : nullptr; 179 }); 180 }); 181 return Internal::UnwrapFuture<CallType, Geometry>(BASE_NS::move(f)); 182 } 183 /// Creates a new camera node with given parameters. CreateCameraNode(const SceneResourceParameters & params)184 META_API_ASYNC auto CreateCameraNode(const SceneResourceParameters& params) 185 { 186 return CallCreateNode<Camera, CallType>(params, ClassId::CameraNode); 187 } 188 /// Creates a new light node with given parameters. CreateLightNode(const SceneResourceParameters & params)189 META_API_ASYNC auto CreateLightNode(const SceneResourceParameters& params) 190 { 191 return CallCreateNode<Light, CallType>(params, ClassId::LightNode); 192 } 193 /// Creates a new text node with given parameters. CreateTextNode(const TextResourceParameters & params)194 META_API_ASYNC auto CreateTextNode(const TextResourceParameters& params) 195 { 196 return CallCreateNode<Text3D, CallType>(params, ClassId::TextNode); 197 } 198 199 private: 200 template<class Type, const META_NS::AsyncCallType& CallType> CallCreateNode(const SceneResourceParameters & params,META_NS::ObjectId id)201 auto CallCreateNode(const SceneResourceParameters& params, META_NS::ObjectId id) 202 { 203 auto f = CallScene([&](auto& scene) { return scene.CreateNode(params.path, id); }); 204 return Internal::UnwrapFuture<CallType, Type>(BASE_NS::move(f)); 205 } 206 }; 207 208 /** 209 * @brief The Component class is a wrapper for IComponent::Ptr. 210 * Components can be queried from Nodes by calling Scene.GetComponent() or created 211 * at runtime by calling Scene.CreateComponent(). 212 * @note The Component's metadata contains all of the component specific properties. 213 */ 214 class Component : public META_NS::Object { 215 public: META_INTERFACE_OBJECT(Component,META_NS::Object,IComponent)216 META_INTERFACE_OBJECT(Component, META_NS::Object, IComponent) 217 218 /** 219 * @brief Returns a component property with given name. 220 * @param name Name of the component property to return. 221 */ 222 template<typename Type> 223 auto GetProperty(BASE_NS::string_view name) const 224 { 225 auto meta = META_NS::Metadata(*this); 226 auto p = meta.GetProperty<Type>(name); 227 // Could also be "ComponentName.PropertyName" 228 return p ? p : meta.GetProperty<Type>(GetName() + "." + name); 229 } 230 /** 231 * @brief Returns a component array property with given name. 232 * @note Equivalent to calling component.GetProperty<BASE_NS::vector<Type>>(name) 233 * @param name Name of the component property to return. 234 */ 235 template<typename Type> GetArrayProperty(BASE_NS::string_view name)236 auto GetArrayProperty(BASE_NS::string_view name) const 237 { 238 return GetProperty<BASE_NS::vector<Type>>(name); 239 } 240 241 private: InitializeComponent()242 auto& InitializeComponent() 243 { 244 META_INTERFACE_OBJECT_CALL_PTR(PopulateAllProperties()); 245 return *this; 246 } 247 friend class Scene; 248 }; 249 250 /// Wrapper for scene objects which implement SCENE_NS::IScene. 251 class Scene : public META_NS::Object { 252 public: META_INTERFACE_OBJECT(Scene,META_NS::Object,IScene)253 META_INTERFACE_OBJECT(Scene, META_NS::Object, IScene) 254 /// Initialize a Scene object from a Node. 255 explicit Scene(const Node& node) : META_NS::Object(node.GetScene()) {} 256 /** 257 * @brief Load a Scene from a file. 258 * @param uri The uri to load the scene from. 259 * @return A valid Scene object if loading was successful, an invalid object otherwise. 260 */ Load(BASE_NS::string_view uri)261 META_API_ASYNC static auto Load(BASE_NS::string_view uri) 262 { 263 Future<IScene::Ptr> f; 264 // Use default application context for loading 265 if (const auto ctx = GetDefaultApplicationContext()) { 266 if (const auto sm = ctx->GetSceneManager()) { 267 f = sm->CreateScene(uri); 268 } 269 } 270 return Internal::UnwrapFuture<CallType, Scene>(BASE_NS::move(f)); 271 } 272 /// Returns a resource factory for creating resources associated with this scene. GetResourceFactory()273 auto GetResourceFactory() 274 { 275 return SceneResourceFactory(GetPtr<IScene>()); 276 } 277 /// @see IScene::GetRootNode GetRootNode()278 META_API_ASYNC auto GetRootNode() const 279 { 280 return META_INTERFACE_OBJECT_ASYNC_CALL_PTR(Node, GetRootNode()); 281 } 282 /// @see IScene::FindNode 283 META_API_ASYNC auto GetNode(BASE_NS::string_view path, META_NS::ObjectId id = {}) const 284 { 285 return META_INTERFACE_OBJECT_ASYNC_CALL_PTR(Node, FindNode(path, id)); 286 } 287 /** 288 * @brief Removes the node (and its children) from the scene. 289 * @see IScene::RemoveNode 290 * @param node The node to remove. The Node object is unitinialized as a result of this call. 291 */ RemoveNode(Node & node)292 META_API_ASYNC auto RemoveNode(Node& node) 293 { 294 auto ret = META_INTERFACE_OBJECT_ASYNC_CALL_PTR(bool, RemoveNode(node.GetPtr<INode>())); 295 node.Release(); 296 return ret; 297 } 298 /// @see IScene::GetCaemras GetCameras()299 META_API_ASYNC auto GetCameras() const 300 { 301 return META_INTERFACE_OBJECT_ASYNC_CALL_PTR(META_NS::Internal::ArrayCast<Camera>, GetCameras()); 302 } 303 /// @see IScene::GetAnimations GetAnimations()304 META_API_ASYNC auto GetAnimations() const 305 { 306 return META_INTERFACE_OBJECT_ASYNC_CALL_PTR(META_NS::Internal::ArrayCast<META_NS::Animation>, GetAnimations()); 307 } 308 /// @see IScene::SetRenderMode SetRenderMode(RenderMode mode)309 META_API_ASYNC auto SetRenderMode(RenderMode mode) 310 { 311 return META_INTERFACE_OBJECT_ASYNC_CALL_PTR(bool, SetRenderMode(BASE_NS::move(mode))); 312 } 313 /// @see IScene::GetRenderMode GetRenderMode()314 META_API_ASYNC auto GetRenderMode() const 315 { 316 return META_INTERFACE_OBJECT_ASYNC_CALL_PTR(bool, GetRenderMode()); 317 } 318 /** 319 * @brief Returns an underlying ECS component from a Node with given name. 320 * @param node The Node to query from. 321 * @param name The component name to query. 322 * @return a Component with given name, if one exists in the target Node. 323 */ GetComponent(const Node & node,BASE_NS::string_view name)324 auto GetComponent(const Node& node, BASE_NS::string_view name) const 325 { 326 auto component = Component(META_INTERFACE_OBJECT_CALL_PTR(GetComponent(node, name))); 327 component.InitializeComponent(); 328 return component; 329 } 330 /** 331 * @brief Creates an underlying ECS component with given component manager name to the given node. 332 * @note If there is already a component with given name associated with the node, the existing component instance 333 * is returned. 334 * @param node The node to create the component to. 335 * @param name Name of the ECS component manager to instantiate. E.g. "CameraComponent" would create a camera 336 * component and associate it with the node. 337 * @return The component with given component manager name. 338 */ CreateComponent(const Node & node,BASE_NS::string_view name)339 META_API_ASYNC auto CreateComponent(const Node& node, BASE_NS::string_view name) const 340 { 341 auto f = META_INTERFACE_OBJECT_CALL_PTR(CreateComponent(node, name)).Then([](const IComponent::Ptr& component) { 342 if (component) { 343 component->PopulateAllProperties(); 344 } 345 return component; 346 }); 347 return Internal::UnwrapFuture<CallType, Component>(BASE_NS::move(f)); 348 } 349 /** 350 * @brief Imports a Scene into this scene under given node. 351 * @param scene The scene to import. 352 * @param node The node under which the scene should be imported. If null, the imported node is placed under root 353 * node of this scene. 354 * @param name The name which should be given to the root node of the imported scene, placed as a child of node. 355 */ ImportScene(const IScene::Ptr & scene,const INode::Ptr & node,BASE_NS::string_view name)356 META_API_ASYNC auto ImportScene(const IScene::Ptr& scene, const INode::Ptr& node, BASE_NS::string_view name) 357 { 358 Future<INode::Ptr> f; 359 if (auto import = interface_cast<INodeImport>(node)) { 360 f = import->ImportChildScene(scene, name); 361 } else { 362 f = GetRootNode<META_NS::Async>().Then([scene, name = BASE_NS::string(name)](const INode::Ptr& root) { 363 auto import = interface_cast<INodeImport>(root); 364 return import ? import->ImportChildScene(scene, name).GetResult() : nullptr; 365 }); 366 } 367 return Internal::UnwrapFuture<CallType, Node>(BASE_NS::move(f)); 368 } 369 /** 370 * @brief Imports a Scene from a resource into this scene under given node. 371 * @param uri Uri of the scene resource to import (.gltf or .scene) 372 * @param node The node under which the scene should be imported. If null, the imported node is placed under root 373 * node of this scene. 374 * @param name The name which should be given to the root node of the imported scene, placed as a child of node. 375 */ ImportScene(BASE_NS::string_view uri,const INode::Ptr & node,BASE_NS::string_view name)376 META_API_ASYNC auto ImportScene(BASE_NS::string_view uri, const INode::Ptr& node, BASE_NS::string_view name) 377 { 378 Future<INode::Ptr> f; 379 if (auto import = interface_cast<INodeImport>(node)) { 380 f = import->ImportChildScene(uri, name); 381 } else { 382 f = GetRootNode<META_NS::Async>().Then( 383 [uri = BASE_NS::string(uri), name = BASE_NS::string(name)](const INode::Ptr& root) { 384 auto import = interface_cast<INodeImport>(root); 385 return import ? import->ImportChildScene(uri, name).GetResult() : nullptr; 386 }); 387 } 388 return Internal::UnwrapFuture<CallType, Node>(BASE_NS::move(f)); 389 } 390 /** 391 * @brief Finds the first occurrence of a node with given name in the scene. 392 * @note If the full path is known, it is more efficient to call Scene::GetNode 393 * @param name Name of the node to find. 394 * @param traversalType Can be used to control how the scene is traversed, or to limit finding only immediate 395 * children by specifying TraversalType::NO_HIERARCHY. 396 * @return The first node matching the parameters or an invalid node in case of failure. 397 */ 398 META_API_ASYNC auto FindNode( 399 BASE_NS::string_view name, META_NS::TraversalType traversalType = META_NS::TraversalType::FULL_HIERARCHY) const 400 { 401 auto f = META_INTERFACE_OBJECT_CALL_PTR(GetRootNode()) 402 .Then([name = BASE_NS::string(name), traversalType](const INode::Ptr& node) -> INode::Ptr { 403 return Node(Internal::FindNode(node, name, traversalType)); 404 }); 405 return Internal::UnwrapFuture<CallType, Node>(BASE_NS::move(f)); 406 } 407 /** 408 * @brief Finds an animation from the scene. 409 * @param name Name of the animation to find. 410 * @return The animation with given name or an invalid object in case of failure. 411 */ FindAnimation(BASE_NS::string_view name)412 META_API_ASYNC auto FindAnimation(BASE_NS::string_view name) 413 { 414 auto f = 415 META_INTERFACE_OBJECT_CALL_PTR(GetAnimations()) 416 .Then([name = BASE_NS::string(name)]( 417 const BASE_NS::vector<META_NS::IAnimation::Ptr>& animations) -> META_NS::IAnimation::Ptr { 418 return Internal::FindFromContainer<META_NS::IAnimation>(animations, name); 419 }); 420 return Internal::UnwrapFuture<CallType, META_NS::Animation>(BASE_NS::move(f)); 421 } 422 }; 423 424 SCENE_END_NAMESPACE() 425 426 #endif // SCENE_API_SCENE_H 427