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 "PostProcJS.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 <meta/interface/property/property_events.h>
22 #include <scene_plugin/api/camera.h> //for the classid...
23 #include <scene_plugin/api/node_uid.h>
24 #include <scene_plugin/interface/intf_ecs_scene.h>
25 #include <scene_plugin/interface/intf_node.h>
26 #include <scene_plugin/interface/intf_scene.h>
27
28 #include <render/intf_render_context.h>
29
30 #include "CameraJS.h"
31 using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
32 using IntfWeakPtr = BASE_NS::weak_ptr<CORE_NS::IInterface>;
33 using namespace SCENE_NS;
34
Init(napi_env env,napi_value exports)35 void PostProcJS::Init(napi_env env, napi_value exports)
36 {
37 using namespace NapiApi;
38
39 BASE_NS::vector<napi_property_descriptor> node_props;
40 // clang-format off
41
42 node_props.push_back(GetSetProperty<bool, PostProcJS, &PostProcJS::GetBloom, &PostProcJS::SetBloom>("bloom"));
43 node_props.emplace_back(GetSetProperty<Object, PostProcJS, &PostProcJS::GetToneMapping,
44 &PostProcJS::SetToneMapping>("toneMapping"));
45 node_props.push_back(MakeTROMethod<NapiApi::FunctionContext<>, PostProcJS, &PostProcJS::Dispose>("destroy"));
46
47 // clang-format on
48
49 napi_value func;
50 auto status = napi_define_class(env, "PostProcessSettings", NAPI_AUTO_LENGTH, BaseObject::ctor<PostProcJS>(),
51 nullptr, node_props.size(), node_props.data(), &func);
52
53 NapiApi::MyInstanceState* mis;
54 napi_get_instance_data(env, (void**)&mis);
55 mis->StoreCtor("PostProcessSettings", func);
56 }
57
Dispose(NapiApi::FunctionContext<> & ctx)58 napi_value PostProcJS::Dispose(NapiApi::FunctionContext<>& ctx)
59 {
60 LOG_F("PostProcJS::Dispose");
61 DisposeNative();
62 return {};
63 }
DisposeNative()64 void PostProcJS::DisposeNative()
65 {
66 if (!disposed_) {
67 disposed_ = true;
68 LOG_F("PostProcJS::DisposeNative");
69 // make sure we release toneMap settings
70
71 auto tmjs = toneMap_.GetObject();
72 if (tmjs) {
73 NapiApi::Function func = tmjs.Get<NapiApi::Function>("destroy");
74 if (func) {
75 func.Invoke(tmjs);
76 }
77 }
78 toneMap_.Reset();
79
80 if (auto post = interface_pointer_cast<IPostProcess>(GetNativeObject())) {
81 // reset the native object refs
82 SetNativeObject(nullptr, false);
83 SetNativeObject(nullptr, true);
84
85 auto cameraJS = camera_.GetObject();
86 if (cameraJS) {
87 auto* rootobject = cameraJS.Native<TrueRootObject>();
88 CameraJS* cam = (CameraJS*)(rootobject);
89
90 ExecSyncTask([cam, post = BASE_NS::move(post)]() {
91 cam->ReleaseObject(META_NS::interface_pointer_cast<META_NS::IObject>(post));
92 post->Tonemap()->SetValue(nullptr);
93 return META_NS::IAny::Ptr {};
94 });
95 }
96 }
97 }
98 }
GetInstanceImpl(uint32_t id)99 void* PostProcJS::GetInstanceImpl(uint32_t id)
100 {
101 if (id == PostProcJS::ID) {
102 return this;
103 }
104 return nullptr;
105 }
Finalize(napi_env env)106 void PostProcJS::Finalize(napi_env env)
107 {
108 // need to do something BEFORE the object gets deleted..
109 DisposeNative();
110 BaseObject<PostProcJS>::Finalize(env);
111 }
112
PostProcJS(napi_env e,napi_callback_info i)113 PostProcJS::PostProcJS(napi_env e, napi_callback_info i) : BaseObject<PostProcJS>(e, i)
114 {
115 LOG_F("PostProcJS ++");
116 NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
117 if (!fromJs) {
118 // no arguments. so internal create.
119 // expecting caller to finish
120 return;
121 }
122
123 // camera that we bind to..
124 NapiApi::Object cameraJS = fromJs.Arg<0>();
125 camera_ = { cameraJS };
126 auto* rootobject = cameraJS.Native<TrueRootObject>();
127 auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(
128 ((CameraJS*)(rootobject))->CreateObject(SCENE_NS::ClassId::PostProcess));
129
130 // create a postprocess object owned by CameraJS.
131
132 // process constructor args..
133 NapiApi::Object meJs(e, fromJs.This());
134 // weak ref, as we expect to be owned by the camera.
135 SetNativeObject(interface_pointer_cast<META_NS::IObject>(postproc), false);
136 StoreJsObj(interface_pointer_cast<META_NS::IObject>(postproc), meJs);
137 // now, based on parameters, create correct objects.
138 if (NapiApi::Object args = fromJs.Arg<1>()) {
139 if (auto prm = args.Get("toneMapping")) {
140 // enable tonemap.
141 napi_value args[] = {
142 meJs, // postprocess
143 prm // tonemap settings
144 };
145 SCENE_NS::ITonemap::Ptr tone;
146 ExecSyncTask([postproc, &tone]() {
147 tone = postproc->Tonemap()->GetValue();
148 return META_NS::IAny::Ptr {};
149 });
150 MakeNativeObjectParam(e, tone, BASE_NS::countof(args), args);
151 NapiApi::Object tonemapJS(GetJSConstructor(e, "ToneMappingSettings"), BASE_NS::countof(args), args);
152 meJs.Set("toneMapping", tonemapJS);
153 }
154 if (auto prm = args.Get("bloom")) {
155 ExecSyncTask([postproc]() -> META_NS::IAny::Ptr {
156 SCENE_NS::IBloom::Ptr bloom = postproc->Bloom()->GetValue();
157 bloom->Enabled()->SetValue(true);
158 return {};
159 });
160 }
161 }
162 }
163
~PostProcJS()164 PostProcJS::~PostProcJS()
165 {
166 LOG_F("PostProcJS --");
167 DisposeNative();
168 if (!GetNativeObject()) {
169 return;
170 }
171 }
172
SetToneMapping(NapiApi::FunctionContext<NapiApi::Object> & ctx)173 void PostProcJS::SetToneMapping(NapiApi::FunctionContext<NapiApi::Object>& ctx)
174 {
175 auto postproc = interface_cast<SCENE_NS::IPostProcess>(GetNativeObject());
176 if (!postproc) {
177 // not possible.
178 return;
179 }
180 NapiApi::Object tonemapJS = ctx.Arg<0>();
181
182 if (auto currentlySet = toneMap_.GetObject()) {
183 if ((napi_value)currentlySet == (napi_value)tonemapJS) {
184 // setting the exactly the same tonemap setting. do nothing.
185 return;
186 }
187 // dispose the old bound object..
188 NapiApi::Function func = currentlySet.Get<NapiApi::Function>("destroy");
189 if (func) {
190 func.Invoke(currentlySet);
191 }
192 toneMap_.Reset();
193 }
194
195 TrueRootObject* native { nullptr };
196 SCENE_NS::ITonemap::Ptr tonemap;
197 // does the input parameter already have a bridge..
198 native = tonemapJS.Native<TrueRootObject>();
199 if (!native) {
200 // nope.. so create a new bridge object based on the input.
201 napi_value args[] = {
202 ctx.This(), // postproc..
203 ctx.Arg<0>() // "javascript object for values"
204 };
205 NapiApi::Object res(GetJSConstructor(ctx, "ToneMappingSettings"), BASE_NS::countof(args), args);
206 native = res.Native<TrueRootObject>();
207 tonemapJS = res;
208 } else {
209 tonemap = interface_pointer_cast<SCENE_NS::ITonemap>(native->GetNativeObject());
210 ExecSyncTask([postproc, tonemap]() {
211 postproc->Tonemap()->SetValue(tonemap);
212 return META_NS::IAny::Ptr {};
213 });
214 }
215 toneMap_ = { ctx, tonemapJS }; // take ownership of the object.
216 }
217
GetToneMapping(NapiApi::FunctionContext<> & ctx)218 napi_value PostProcJS::GetToneMapping(NapiApi::FunctionContext<>& ctx)
219 {
220 if (auto postproc = interface_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
221 SCENE_NS::ITonemap::Ptr tone;
222 ExecSyncTask([postproc, &tone]() {
223 tone = postproc->Tonemap()->GetValue();
224 return META_NS::IAny::Ptr {};
225 });
226 auto obj = interface_pointer_cast<META_NS::IObject>(tone);
227
228 if (auto cached = FetchJsObj(obj)) {
229 // always return the same js object.
230 return cached;
231 }
232
233 napi_value args[] = {
234 ctx.This() // postproc..
235 };
236 MakeNativeObjectParam(ctx, tone, BASE_NS::countof(args), args);
237 napi_value tonemapJS = CreateFromNativeInstance(ctx, obj, false, BASE_NS::countof(args), args);
238 toneMap_ = { ctx, tonemapJS }; // take ownership of the object.
239 return tonemapJS;
240 }
241 toneMap_.Reset();
242 return ctx.GetUndefined();
243 }
244
GetBloom(NapiApi::FunctionContext<> & ctx)245 napi_value PostProcJS::GetBloom(NapiApi::FunctionContext<>& ctx)
246 {
247 bool enabled = false;
248 if (auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
249 ExecSyncTask([postproc, &enabled]() {
250 SCENE_NS::IBloom::Ptr bloom = postproc->Bloom()->GetValue();
251 enabled = bloom->Enabled()->GetValue();
252 return META_NS::IAny::Ptr {};
253 });
254 }
255
256 napi_value value;
257 napi_status status = napi_get_boolean(ctx, enabled, &value);
258 return value;
259 }
260
SetBloom(NapiApi::FunctionContext<bool> & ctx)261 void PostProcJS::SetBloom(NapiApi::FunctionContext<bool>& ctx)
262 {
263 bool enable = ctx.Arg<0>();
264 if (auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
265 ExecSyncTask([postproc, enable]() {
266 SCENE_NS::IBloom::Ptr bloom = postproc->Bloom()->GetValue();
267 bloom->Enabled()->SetValue(enable);
268 return META_NS::IAny::Ptr {};
269 });
270 }
271 }
272