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 #include "GeometryJS.h"
16
17 #include <meta/api/make_callback.h>
18 #include <meta/interface/intf_task_queue.h>
19 #include <meta/interface/intf_task_queue_registry.h>
20 #include <scene/ext/intf_internal_scene.h>
21 #include <scene/interface/intf_camera.h>
22 #include <scene/interface/intf_mesh.h>
23 #include <scene/interface/intf_mesh_resource.h>
24 #include <scene/interface/intf_scene.h>
25
26 #include "MeshResourceJS.h"
27 #include "SceneJS.h"
28 #include "geometry_definition/GeometryDefinition.h"
29
GetInstanceImpl(uint32_t id)30 void* GeometryJS::GetInstanceImpl(uint32_t id)
31 {
32 if (id == GeometryJS::ID) {
33 return this;
34 }
35 return NodeImpl::GetInstanceImpl(id);
36 }
DisposeNative(void * scn)37 void GeometryJS::DisposeNative(void* scn)
38 {
39 if (disposed_) {
40 return;
41 }
42 LOG_V("GeometryJS::DisposeNative");
43 disposed_ = true;
44
45 SceneJS* sceneJS = static_cast<SceneJS*>(scn);
46 if (sceneJS) {
47 sceneJS->ReleaseDispose(reinterpret_cast<uintptr_t>(&scene_));
48 }
49
50 if (auto node = interface_pointer_cast<SCENE_NS::INode>(GetNativeObject())) {
51 if (!IsAttached()) {
52 if (auto access = interface_pointer_cast<SCENE_NS::IMeshAccess>(node)) {
53 access->SetMesh(nullptr).Wait();
54 }
55 if (auto scene = node->GetScene()) {
56 scene->RemoveNode(BASE_NS::move(node)).Wait();
57 }
58 }
59 // reset the native object refs
60 SetNativeObject(nullptr, false);
61 SetNativeObject(nullptr, true);
62 }
63 scene_.Reset();
64 }
Init(napi_env env,napi_value exports)65 void GeometryJS::Init(napi_env env, napi_value exports)
66 {
67 BASE_NS::vector<napi_property_descriptor> node_props;
68 NodeImpl::GetPropertyDescs(node_props);
69
70 using namespace NapiApi;
71 node_props.push_back(GetProperty<Object, GeometryJS, &GeometryJS::GetMesh>("mesh"));
72
73 napi_value func;
74 auto status = napi_define_class(env, "Geometry", NAPI_AUTO_LENGTH, BaseObject::ctor<GeometryJS>(), nullptr,
75 node_props.size(), node_props.data(), &func);
76
77 NapiApi::MyInstanceState* mis;
78 GetInstanceData(env, (void**)&mis);
79 mis->StoreCtor("Geometry", func);
80 }
81
GeometryJS(napi_env e,napi_callback_info i)82 GeometryJS::GeometryJS(napi_env e, napi_callback_info i) : BaseObject<GeometryJS>(e, i), NodeImpl(NodeImpl::GEOMETRY)
83 {
84 LOG_V("GeometryJS ++ ");
85
86 // Resolve overload with 2 or 3 given args.
87 if (auto ctx = NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> { e, i }) {
88 Construct(e, ctx.This(), ctx.Arg<0>(), ctx.Arg<1>());
89 } else if (auto ctx = NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object, NapiApi::Object> { e, i }) {
90 // Manual creation with an extra arg.
91 if (Construct(e, ctx.This(), ctx.Arg<0>(), ctx.Arg<1>()) == ConstructionState::LACKS_NATIVE) {
92 CreateNativeObject(e, ctx.This(), ctx.Arg<1>(), ctx.Arg<2>()); // 2: index
93 }
94 } else {
95 LOG_E("Bad args given for GeometryJS constructor");
96 return;
97 }
98 }
~GeometryJS()99 GeometryJS::~GeometryJS()
100 {
101 LOG_V("GeometryJS -- ");
102 }
103
Finalize(napi_env env)104 void GeometryJS::Finalize(napi_env env)
105 {
106 DisposeNative(GetJsWrapper<SceneJS>(scene_.GetObject()));
107 BaseObject::Finalize(env);
108 }
Construct(napi_env env,NapiApi::Object meJs,NapiApi::Object scene,NapiApi::Object sceneNodeParameters)109 GeometryJS::ConstructionState GeometryJS::Construct(
110 napi_env env, NapiApi::Object meJs, NapiApi::Object scene, NapiApi::Object sceneNodeParameters)
111 {
112 if (!GetNativeMeta<SCENE_NS::IScene>(scene)) {
113 LOG_F("Invalid scene for GeometryJS!");
114 return ConstructionState::FAILED;
115 }
116 scene_ = NapiApi::WeakRef { env, scene.ToNapiValue() };
117
118 // Add the dispose hook to scene so that the Geometry node is disposed when scene is disposed.
119 if (auto sceneJS = GetJsWrapper<SceneJS>(scene)) {
120 sceneJS->DisposeHook(reinterpret_cast<uintptr_t>(&scene_), meJs);
121 }
122
123 if (const auto name = sceneNodeParameters.Get<BASE_NS::string>("name"); name.IsValid()) {
124 meJs.Set("name", name);
125 }
126
127 auto nativeObject = GetNativeObjectParam<META_NS::IObject>(sceneNodeParameters);
128 if (nativeObject) {
129 StoreJsObj(nativeObject, meJs);
130 return ConstructionState::FINISHED;
131 }
132 return ConstructionState::LACKS_NATIVE;
133 }
134
CreateNativeObject(napi_env env,NapiApi::Object meJs,NapiApi::Object sceneNodeParameters,NapiApi::Object meshResourceParam)135 void GeometryJS::CreateNativeObject(
136 napi_env env, NapiApi::Object meJs, NapiApi::Object sceneNodeParameters, NapiApi::Object meshResourceParam)
137 {
138 auto name = BASE_NS::string {};
139 auto path = BASE_NS::string {};
140 if (auto param = sceneNodeParameters.Get("name")) {
141 name = NapiApi::Value<BASE_NS::string>(env, param).valueOrDefault("");
142 }
143 if (auto param = sceneNodeParameters.Get("path")) {
144 path = NapiApi::Value<BASE_NS::string>(env, param).valueOrDefault("");
145 }
146
147 auto nodePath = BASE_NS::string {};
148 if (!path.empty()) {
149 nodePath = path;
150 } else if (!name.empty()) {
151 // Use the name as path. Create the node directly under root.
152 nodePath = name;
153 }
154
155 auto scene = GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject());
156 // Node creation can fail e.g. due to a bad path. Then we're going to have a null Geometry object.
157 auto meshNode = scene->CreateNode(nodePath, SCENE_NS::ClassId::MeshNode).GetResult();
158 if (auto access = interface_pointer_cast<SCENE_NS::IMeshAccess>(meshNode)) {
159 const auto resource = static_cast<MeshResourceJS*>(meshResourceParam.Native<TrueRootObject>());
160 const auto mesh = resource->CreateMesh();
161 access->SetMesh(mesh).GetResult();
162 }
163 // Always set regardless of success.
164 SetNativeObject(interface_pointer_cast<META_NS::IObject>(meshNode), false);
165 StoreJsObj(GetNativeObject(), meJs);
166 }
GetMesh(NapiApi::FunctionContext<> & ctx)167 napi_value GeometryJS::GetMesh(NapiApi::FunctionContext<>& ctx)
168 {
169 if (!validateSceneRef()) {
170 return ctx.GetUndefined();
171 }
172
173 META_NS::IObject::Ptr mesh = GetNativeObject();
174 if (!interface_cast<SCENE_NS::IMesh>(mesh)) {
175 mesh = nullptr;
176 }
177
178 if (!mesh) {
179 // no mesh.
180 return ctx.GetNull();
181 }
182
183 if (auto cached = FetchJsObj(mesh, "_JSWMesh")) {
184 // always return the same js object.
185 return cached.ToNapiValue();
186 }
187 // create new js object for the native node.
188 NapiApi::Env env(ctx.Env());
189 NapiApi::Object argJS(env);
190 napi_value args[] = { scene_.GetValue(), argJS.ToNapiValue() };
191
192 MakeNativeObjectParam(env, mesh, BASE_NS::countof(args), args);
193
194 auto nodeJS = CreateJsObj(env, "Mesh", mesh, false, BASE_NS::countof(args), args);
195 if (!nodeJS) {
196 LOG_E("Could not create JSObject for Class %s", "Mesh");
197 return {};
198 }
199 return StoreJsObj(mesh, nodeJS, "_JSWMesh").ToNapiValue();
200 }