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 "MaterialJS.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_plugin/api/light_uid.h>
21 #include <scene_plugin/interface/intf_light.h>
22 #include <scene_plugin/interface/intf_scene.h>
23 
24 #include "SceneJS.h"
25 using IntfPtr = META_NS::SharedPtrIInterface;
26 using IntfWeakPtr = META_NS::WeakPtrIInterface;
27 
BaseMaterial(MaterialType lt)28 BaseMaterial::BaseMaterial(MaterialType lt) : SceneResourceImpl(SceneResourceImpl::MATERIAL), materialType_(lt) {}
~BaseMaterial()29 BaseMaterial::~BaseMaterial() {}
Init(const char * class_name,napi_env env,napi_value exports,napi_callback ctor,BASE_NS::vector<napi_property_descriptor> & node_props)30 void BaseMaterial::Init(const char* class_name, napi_env env, napi_value exports, napi_callback ctor,
31     BASE_NS::vector<napi_property_descriptor>& node_props)
32 {
33     SceneResourceImpl::GetPropertyDescs(node_props);
34 
35     using namespace NapiApi;
36     node_props.push_back(TROGetProperty<float, BaseMaterial, &BaseMaterial::GetMaterialType>("materialType"));
37 
38     napi_value func;
39     auto status = napi_define_class(
40         env, class_name, NAPI_AUTO_LENGTH, ctor, nullptr, node_props.size(), node_props.data(), &func);
41 
42     NapiApi::MyInstanceState* mis;
43     napi_get_instance_data(env, (void**)&mis);
44     mis->StoreCtor(class_name, func);
45 
46     NapiApi::Object exp(env, exports);
47 
48     napi_value eType;
49     napi_value v;
50     napi_create_object(env, &eType);
51 #define DECL_ENUM(enu, x)                         \
52     napi_create_uint32(env, MaterialType::x, &v); \
53     napi_set_named_property(env, enu, #x, v)
54 
55     DECL_ENUM(eType, SHADER);
56 #undef DECL_ENUM
57     exp.Set("MaterialType", eType);
58 }
59 
GetInstanceImpl(uint32_t id)60 void* BaseMaterial::GetInstanceImpl(uint32_t id)
61 {
62     if (id == BaseMaterial::ID) {
63         return (BaseMaterial*)this;
64     }
65     return SceneResourceImpl::GetInstanceImpl(id);
66 }
DisposeNative(TrueRootObject * tro)67 void BaseMaterial::DisposeNative(TrueRootObject* tro)
68 {
69     // do nothing for now..
70     LOG_F("BaseMaterial::DisposeNative");
71     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(tro->GetNativeObject())) {
72         // reset the native object refs
73         tro->SetNativeObject(nullptr, false);
74         tro->SetNativeObject(nullptr, true);
75 
76         ExecSyncTask([material = BASE_NS::move(material)]() mutable {
77             auto node = interface_pointer_cast<SCENE_NS::INode>(material);
78             if (node == nullptr) {
79                 return META_NS::IAny::Ptr {};
80             }
81             auto scene = node->GetScene();
82             if (scene == nullptr) {
83                 return META_NS::IAny::Ptr {};
84             }
85             scene->ReleaseNode(node);
86             return META_NS::IAny::Ptr {};
87         });
88     }
89     scene_.Reset();
90 }
GetMaterialType(NapiApi::FunctionContext<> & ctx)91 napi_value BaseMaterial::GetMaterialType(NapiApi::FunctionContext<>& ctx)
92 {
93     uint32_t type = -1; // return -1 if the object does not exist anymore
94     if (auto node = interface_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
95         type = materialType_;
96     }
97     napi_value value;
98     napi_status status = napi_create_uint32(ctx, type, &value);
99     return value;
100 }
101 
Init(napi_env env,napi_value exports)102 void ShaderMaterialJS::Init(napi_env env, napi_value exports)
103 {
104     BASE_NS::vector<napi_property_descriptor> props = {
105         NapiApi::GetSetProperty<NapiApi::Object, ShaderMaterialJS, &ShaderMaterialJS::GetColorShader,
106             &ShaderMaterialJS::SetColorShader>("colorShader"),
107     };
108 
109     BaseMaterial::Init("ShaderMaterial", env, exports, BaseObject::ctor<ShaderMaterialJS>(), props);
110 }
111 
ShaderMaterialJS(napi_env e,napi_callback_info i)112 ShaderMaterialJS::ShaderMaterialJS(napi_env e, napi_callback_info i)
113     : BaseObject<ShaderMaterialJS>(e, i), BaseMaterial(BaseMaterial::MaterialType::SHADER)
114 {
115     // missing
116     NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
117     NapiApi::Object meJs(e, fromJs.This());
118 
119     NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene... (do i need it here?)
120     NapiApi::Object args = fromJs.Arg<1>();  // other args
121 
122     scene_ = scene;
123     if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
124         CORE_LOG_F("INVALID SCENE!");
125     }
126 
127     auto* tro = scene.Native<TrueRootObject>();
128     auto* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
129     sceneJS->DisposeHook((uintptr_t)&scene_, meJs);
130 
131     auto metaobj = GetNativeObjectParam<META_NS::IObject>(args); // Should be IMaterial
132     SetNativeObject(metaobj, true);
133     StoreJsObj(metaobj, meJs);
134 
135     BASE_NS::string name;
136     if (auto prm = args.Get<BASE_NS::string>("name")) {
137         name = prm;
138     } else {
139         if (auto named = interface_cast<META_NS::INamed>(metaobj)) {
140             name = named->Name()->GetValue();
141         }
142     }
143     meJs.Set("name", name);
144 }
145 
~ShaderMaterialJS()146 ShaderMaterialJS::~ShaderMaterialJS() {}
GetInstanceImpl(uint32_t id)147 void* ShaderMaterialJS::GetInstanceImpl(uint32_t id)
148 {
149     if (id == ShaderMaterialJS::ID) {
150         return this;
151     }
152     return BaseMaterial::GetInstanceImpl(id);
153 }
DisposeNative()154 void ShaderMaterialJS::DisposeNative()
155 {
156     NapiApi::Object obj = scene_.GetObject();
157     if (obj) {
158         auto* tro = obj.Native<TrueRootObject>();
159         SceneJS* sceneJS;
160         if (tro) {
161             sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
162             sceneJS->ReleaseDispose((uintptr_t)&scene_);
163         }
164     }
165     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject())) {
166         SetNativeObject(nullptr, false);
167         SetNativeObject(nullptr, true);
168         if (obj) {
169             ExecSyncTask([mat = BASE_NS::move(material)]() -> META_NS::IAny::Ptr {
170                 mat->MaterialShader()->SetValue(nullptr);
171                 return {};
172             });
173         }
174     } else {
175         SetNativeObject(nullptr, false);
176     }
177     shader_.Reset();
178 
179     BaseMaterial::DisposeNative(this);
180 }
Finalize(napi_env env)181 void ShaderMaterialJS::Finalize(napi_env env)
182 {
183     BaseObject::Finalize(env);
184 }
185 
SetColorShader(NapiApi::FunctionContext<NapiApi::Object> & ctx)186 void ShaderMaterialJS::SetColorShader(NapiApi::FunctionContext<NapiApi::Object>& ctx)
187 {
188     NapiApi::Object shaderJS = ctx.Arg<0>();
189     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject());
190     if (!material) {
191         shader_.Reset();
192         return;
193     }
194     // handle the case where a "bound shader" is attached too.
195     auto shader = GetNativeMeta<SCENE_NS::IShader>(shaderJS);
196     if (shader == nullptr) {
197         // attaching to a bound shader. (if shader was bound to another material, we need to make a new copy)
198         auto boundShader = GetNativeMeta<META_NS::IObject>(shaderJS);
199         return;
200     }
201     // bind it to material (in native)
202     ExecSyncTask([material, &shader]() -> META_NS::IAny::Ptr {
203         material->MaterialShader()->SetValue(shader);
204         material->MaterialShaderState()->SetValue(nullptr);
205         return {};
206     });
207 
208     // construct a "bound" shader object from the "non bound" one.
209     NapiApi::Object parms(ctx);
210     napi_value args[] = {
211         scene_.GetValue(), // <- get the scene
212         parms              // other constructor parameters
213     };
214 
215     parms.Set("name", shaderJS.Get("name"));
216     parms.Set("Material", ctx.This()); // js material object that we are bound to.
217 
218     shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
219     interface_cast<META_NS::IMetadata>(shaderBind_)
220         ->AddProperty(META_NS::ConstructProperty<IntfPtr>(
221             "shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
222     interface_cast<META_NS::IMetadata>(shaderBind_)
223         ->GetPropertyByName<IntfPtr>("shader")
224         ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(shader));
225 
226     auto argc = BASE_NS::countof(args);
227     auto argv = args;
228     MakeNativeObjectParam(ctx, shaderBind_, argc, argv);
229     auto result = CreateJsObj(ctx, "Shader", shaderBind_, false, argc, argv);
230     shader_ = StoreJsObj(shaderBind_, NapiApi::Object(ctx, result));
231 }
GetColorShader(NapiApi::FunctionContext<> & ctx)232 napi_value ShaderMaterialJS::GetColorShader(NapiApi::FunctionContext<>& ctx)
233 {
234     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject());
235     if (!material) {
236         shader_.Reset();
237         return ctx.GetNull();
238     }
239     if (shader_.IsEmpty()) {
240         // no shader set yet..
241         // see if we have one on the native side.
242         // and create the "bound shader" object from it.
243 
244         // check native side..
245         SCENE_NS::IShader::Ptr shader;
246         ExecSyncTask([material, &shader]() -> META_NS::IAny::Ptr {
247             shader = material->MaterialShader()->GetValue();
248             return {};
249         });
250         if (!shader) {
251             // no shader in native also.
252             return ctx.GetNull();
253         }
254 
255         // construct a "bound" shader object from the "non bound" one.
256         NapiApi::Object parms(ctx);
257         napi_value args[] = {
258             scene_.GetValue(), // <- get the scene
259             parms              // other constructor parameters
260         };
261 
262         if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
263             CORE_LOG_F("INVALID SCENE!");
264         }
265         parms.Set("Material", ctx.This()); // js material object that we are bound to.
266 
267         shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
268         interface_cast<META_NS::IMetadata>(shaderBind_)
269             ->AddProperty(META_NS::ConstructProperty<IntfPtr>(
270                 "shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
271         interface_cast<META_NS::IMetadata>(shaderBind_)
272             ->GetPropertyByName<IntfPtr>("shader")
273             ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(shader));
274 
275         auto argc = BASE_NS::countof(args);
276         auto argv = args;
277         MakeNativeObjectParam(ctx, shaderBind_, argc, argv);
278         auto result = CreateJsObj(ctx, "Shader", shaderBind_, false, argc, argv);
279         shader_ = StoreJsObj(shaderBind_, NapiApi::Object(ctx, result));
280     }
281     return shader_.GetValue();
282 }
283