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 #include "SubMeshJS.h"
17
18 #include <meta/api/make_callback.h>
19 #include <meta/interface/intf_task_queue.h>
20 #include <meta/interface/intf_task_queue_registry.h>
21 #include <scene/interface/intf_camera.h>
22 #include <scene/interface/intf_scene.h>
23 #include <scene/interface/intf_mesh.h>
24
25 #include "MeshJS.h"
26 #include "SceneJS.h"
GetInstanceImpl(uint32_t id)27 void* SubMeshJS::GetInstanceImpl(uint32_t id)
28 {
29 if (id == SubMeshJS::ID)
30 return this;
31 // no id will match
32 return nullptr;
33 }
DisposeNative(void * scn)34 void SubMeshJS::DisposeNative(void* scn)
35 {
36 if (disposed_) {
37 return;
38 }
39 disposed_ = true;
40 LOG_V("SubMeshJS::DisposeNative");
41 if (auto* sceneJS = static_cast<SceneJS*>(scn)) {
42 sceneJS->ReleaseStrongDispose(reinterpret_cast<uintptr_t>(&scene_));
43 }
44 aabbMin_.reset();
45 aabbMax_.reset();
46 parentMesh_.Reset();
47 scene_.Reset();
48 }
49
Finalize(napi_env env)50 void SubMeshJS::Finalize(napi_env env)
51 {
52 DisposeNative(GetJsWrapper<SceneJS>(scene_.GetObject()));
53 BaseObject::Finalize(env);
54 }
55
Dispose(NapiApi::FunctionContext<> & ctx)56 napi_value SubMeshJS::Dispose(NapiApi::FunctionContext<>& ctx)
57 {
58 // Dispose of the native object. (makes the js object invalid)
59 if (TrueRootObject* instance = GetThisRootObject(ctx)) {
60 // see if we have "scenejs" as ext (prefer one given as argument)
61 napi_status stat;
62 SceneJS* ptr { nullptr };
63 NapiApi::FunctionContext<NapiApi::Object> args(ctx);
64 if (args) {
65 if (NapiApi::Object obj = args.Arg<0>()) {
66 if (napi_value ext = obj.Get("SceneJS")) {
67 stat = napi_get_value_external(ctx.GetEnv(), ext, (void**)&ptr);
68 }
69 }
70 }
71 if (!ptr) {
72 ptr = GetJsWrapper<SceneJS>(scene_.GetObject());
73 }
74 SetNativeObject(nullptr, true);
75 instance->DisposeNative(ptr);
76 }
77 return ctx.GetUndefined();
78 }
Init(napi_env env,napi_value exports)79 void SubMeshJS::Init(napi_env env, napi_value exports)
80 {
81 BASE_NS::vector<napi_property_descriptor> node_props;
82
83 using namespace NapiApi;
84 node_props.push_back(GetSetProperty<BASE_NS::string, SubMeshJS, &SubMeshJS::GetName, &SubMeshJS::SetName>("name"));
85 node_props.push_back(GetProperty<Object, SubMeshJS, &SubMeshJS::GetAABB>("aabb"));
86 node_props.push_back(
87 GetSetProperty<Object, SubMeshJS, &SubMeshJS::GetMaterial, &SubMeshJS::SetMaterial>("material"));
88
89 node_props.push_back(MakeTROMethod<FunctionContext<>, SubMeshJS, &SubMeshJS::Dispose>("destroy"));
90
91 napi_value func;
92 auto status = napi_define_class(env, "SubMesh", NAPI_AUTO_LENGTH, BaseObject::ctor<SubMeshJS>(), nullptr,
93 node_props.size(), node_props.data(), &func);
94
95 NapiApi::MyInstanceState* mis;
96 GetInstanceData(env, (void**)&mis);
97 mis->StoreCtor("SubMesh", func);
98 }
99
SubMeshJS(napi_env e,napi_callback_info i)100 SubMeshJS::SubMeshJS(napi_env e, napi_callback_info i) : BaseObject<SubMeshJS>(e, i)
101 {
102 LOG_V("SubMeshJS ++ ");
103 NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object, uint32_t> fromJs(e, i);
104 if (!fromJs) {
105 // okay internal create. we will receive the object after.
106 return;
107 }
108 scene_ = fromJs.Arg<0>().valueOrDefault();
109 parentMesh_ = fromJs.Arg<1>().valueOrDefault();
110 indexInParent_ = fromJs.Arg<2>().valueOrDefault(); // 2: idx
111 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
112 LOG_F("INVALID SCENE!");
113 }
114 {
115 // add the dispose hook to scene. (so that the geometry node is disposed when scene is disposed)
116 NapiApi::Object meJs(fromJs.This());
117 NapiApi::Object scene = fromJs.Arg<0>();
118 if (auto sceneJS = GetJsWrapper<SceneJS>(scene)) {
119 sceneJS->StrongDisposeHook(reinterpret_cast<uintptr_t>(&scene_), meJs);
120 }
121 }
122 }
~SubMeshJS()123 SubMeshJS::~SubMeshJS()
124 {
125 LOG_V("SubMeshJS -- %p", this);
126 }
127
GetAABB(NapiApi::FunctionContext<> & ctx)128 napi_value SubMeshJS::GetAABB(NapiApi::FunctionContext<>& ctx)
129 {
130 auto node = interface_pointer_cast<SCENE_NS::ISubMesh>(GetThisNativeObject(ctx));
131 if (!node) {
132 // return undefined.. as no actual node.
133 return ctx.GetUndefined();
134 }
135 BASE_NS::Math::Vec3 aabmin;
136 BASE_NS::Math::Vec3 aabmax;
137 aabmin = node->AABBMin()->GetValue();
138 aabmax = node->AABBMax()->GetValue();
139 NapiApi::Env env(ctx.Env());
140 NapiApi::Object res(env);
141
142 NapiApi::Object min(env);
143 min.Set("x", NapiApi::Value<float> { env, aabmin.x });
144 min.Set("y", NapiApi::Value<float> { env, aabmin.y });
145 min.Set("z", NapiApi::Value<float> { env, aabmin.z });
146 res.Set("aabbMin", min);
147
148 NapiApi::Object max(env);
149 max.Set("x", NapiApi::Value<float> { env, aabmax.x });
150 max.Set("y", NapiApi::Value<float> { env, aabmax.y });
151 max.Set("z", NapiApi::Value<float> { env, aabmax.z });
152 res.Set("aabbMax", max);
153 return res.ToNapiValue();
154 }
155
GetName(NapiApi::FunctionContext<> & ctx)156 napi_value SubMeshJS::GetName(NapiApi::FunctionContext<>& ctx)
157 {
158 BASE_NS::string name;
159 if (auto node = interface_pointer_cast<META_NS::INamed>(GetThisNativeObject(ctx))) {
160 name = node->Name()->GetValue();
161 }
162 return ctx.GetString(name);
163 }
SetName(NapiApi::FunctionContext<BASE_NS::string> & ctx)164 void SubMeshJS::SetName(NapiApi::FunctionContext<BASE_NS::string>& ctx)
165 {
166 if (auto node = interface_pointer_cast<META_NS::INamed>(GetThisNativeObject(ctx))) {
167 BASE_NS::string name = ctx.Arg<0>();
168 node->Name()->SetValue(name);
169 }
170 }
171
GetMaterial(NapiApi::FunctionContext<> & ctx)172 napi_value SubMeshJS::GetMaterial(NapiApi::FunctionContext<>& ctx)
173 {
174 auto sm = interface_pointer_cast<SCENE_NS::ISubMesh>(GetThisNativeObject(ctx));
175 if (!sm) {
176 // return undefined..
177 return ctx.GetUndefined();
178 }
179 META_NS::IObject::Ptr obj;
180 auto material = sm->Material()->GetValue();
181 obj = interface_pointer_cast<META_NS::IObject>(material);
182 if (auto cached = FetchJsObj(obj)) {
183 // always return the same js object.
184 return cached.ToNapiValue();
185 }
186
187 // No jswrapper for this material , so create it.
188 NapiApi::Env env(ctx.Env());
189 NapiApi::Object argJS(env);
190 napi_value args[] = { scene_.GetValue(), argJS.ToNapiValue() };
191 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
192 LOG_F("INVALID SCENE!");
193 }
194 auto argc = BASE_NS::countof(args);
195 auto argv = args;
196 return CreateFromNativeInstance(env, obj, true, argc, argv).ToNapiValue();
197 }
198
SetMaterial(NapiApi::FunctionContext<NapiApi::Object> & ctx)199 void SubMeshJS::SetMaterial(NapiApi::FunctionContext<NapiApi::Object>& ctx)
200 {
201 auto sm = interface_pointer_cast<SCENE_NS::ISubMesh>(GetThisNativeObject(ctx));
202 if (!sm) {
203 return;
204 }
205 NapiApi::Object obj = ctx.Arg<0>();
206 auto new_material = GetNativeMeta<SCENE_NS::IMaterial>(obj);
207 if (new_material) {
208 auto cur = sm->Material()->GetValue();
209 if (cur != new_material) {
210 sm->Material()->SetValue(new_material);
211 }
212 }
213 UpdateParentMesh();
214 }
215
UpdateParentMesh()216 void SubMeshJS::UpdateParentMesh()
217 {
218 auto success = false;
219 if (auto self = interface_pointer_cast<SCENE_NS::ISubMesh>(GetNativeObject())) {
220 if (auto mesh = GetJsWrapper<MeshJS>(parentMesh_.GetObject())) {
221 success = mesh->UpdateSubmesh(indexInParent_, self);
222 }
223 }
224 if (!success) {
225 LOG_E("Unable to update submesh change to scene");
226 }
227 }
228