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/builtin_objects.h>
19 #include <meta/interface/intf_task_queue.h>
20 #include <meta/interface/intf_task_queue_registry.h>
21 #include <meta/interface/property/construct_property.h>
22 #include <scene/interface/intf_shader.h>
23 #include <scene/interface/intf_scene.h>
24 #include <scene/interface/intf_material.h>
25
26 #include "SceneJS.h"
27 using IntfPtr = META_NS::SharedPtrIInterface;
28 using IntfWeakPtr = META_NS::WeakPtrIInterface;
29
BaseMaterial(MaterialType lt)30 BaseMaterial::BaseMaterial(MaterialType lt) : SceneResourceImpl(SceneResourceImpl::MATERIAL), materialType_(lt) {}
~BaseMaterial()31 BaseMaterial::~BaseMaterial() {}
Init(const char * class_name,napi_env env,napi_value exports,napi_callback ctor,BASE_NS::vector<napi_property_descriptor> & node_props)32 void BaseMaterial::Init(const char* class_name, napi_env env, napi_value exports, napi_callback ctor,
33 BASE_NS::vector<napi_property_descriptor>& node_props)
34 {
35 SceneResourceImpl::GetPropertyDescs(node_props);
36
37 using namespace NapiApi;
38 node_props.emplace_back(TROGetProperty<uint32_t, BaseMaterial, &BaseMaterial::GetMaterialType>("materialType"));
39 node_props.emplace_back(
40 TROGetSetProperty<bool, BaseMaterial, &BaseMaterial::GetShadowReceiver, &BaseMaterial::SetShadowReceiver>(
41 "shadowReceiver"));
42 node_props.emplace_back(
43 TROGetSetProperty<uint32_t, BaseMaterial, &BaseMaterial::GetCullMode, &BaseMaterial::SetCullMode>(
44 "cullMode"));
45 node_props.emplace_back(
46 TROGetSetProperty<NapiApi::Object, BaseMaterial, &BaseMaterial::GetBlend, &BaseMaterial::SetBlend>(
47 "blend"));
48 node_props.emplace_back(
49 TROGetSetProperty<float, BaseMaterial, &BaseMaterial::GetAlphaCutoff, &BaseMaterial::SetAlphaCutoff>(
50 "alphaCutoff"));
51 node_props.emplace_back(
52 TROGetSetProperty<NapiApi::Object, BaseMaterial, &BaseMaterial::GetRenderSort, &BaseMaterial::SetRenderSort>(
53 "renderSort"));
54
55 napi_value func;
56 auto status = napi_define_class(
57 env, class_name, NAPI_AUTO_LENGTH, ctor, nullptr, node_props.size(), node_props.data(), &func);
58
59 NapiApi::MyInstanceState* mis;
60 GetInstanceData(env, (void**)&mis);
61 mis->StoreCtor(class_name, func);
62
63 NapiApi::Object exp1(env, exports);
64 napi_value eType1;
65 napi_value v1;
66 napi_create_object(env, &eType1);
67 #define DECL_ENUM(enu, x) \
68 napi_create_uint32(env, MaterialType::x, &v1); \
69 napi_set_named_property(env, enu, #x, v1);
70
71 DECL_ENUM(eType1, SHADER);
72 DECL_ENUM(eType1, METALLIC_ROUGHNESS);
73 #undef DECL_ENUM
74 exp1.Set("MaterialType", eType1);
75
76 NapiApi::Object exp2(env, exports);
77 napi_value eType2;
78 napi_value v2;
79 napi_create_object(env, &eType2);
80 #define DECL_ENUM(enu, x) \
81 napi_create_uint32(env, CullMode::x, &v2); \
82 napi_set_named_property(env, enu, #x, v2);
83
84 DECL_ENUM(eType2, NONE);
85 DECL_ENUM(eType2, FRONT);
86 DECL_ENUM(eType2, BACK);
87 #undef DECL_ENUM
88 exp2.Set("CullMode", eType2);
89 }
90
GetInstanceImpl(uint32_t id)91 void* BaseMaterial::GetInstanceImpl(uint32_t id)
92 {
93 if (id == BaseMaterial::ID)
94 return (BaseMaterial*)this;
95 return SceneResourceImpl::GetInstanceImpl(id);
96 }
DisposeNative(TrueRootObject * tro)97 void BaseMaterial::DisposeNative(TrueRootObject* tro)
98 {
99 // do nothing for now..
100 LOG_V("BaseMaterial::DisposeNative");
101 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(tro->GetNativeObject())) {
102 // reset the native object refs
103 tro->SetNativeObject(nullptr, false);
104 tro->SetNativeObject(nullptr, true);
105 }
106 scene_.Reset();
107 }
GetMaterialType(NapiApi::FunctionContext<> & ctx)108 napi_value BaseMaterial::GetMaterialType(NapiApi::FunctionContext<>& ctx)
109 {
110 if (!validateSceneRef()) {
111 return ctx.GetUndefined();
112 }
113 uint32_t type = -1; // return -1 if the object does not exist anymore
114 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
115 type = META_NS::GetValue(material->Type()) == SCENE_NS::MaterialType::METALLIC_ROUGHNESS
116 ? BaseMaterial::METALLIC_ROUGHNESS
117 : BaseMaterial::SHADER;
118 }
119 return ctx.GetNumber(type);
120 }
GetShadowReceiver(NapiApi::FunctionContext<> & ctx)121 napi_value BaseMaterial::GetShadowReceiver(NapiApi::FunctionContext<>& ctx)
122 {
123 if (!validateSceneRef()) {
124 return ctx.GetUndefined();
125 }
126 bool shadowReceiver = false;
127 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
128 uint32_t lightingFlags = uint32_t(META_NS::GetValue(material->LightingFlags()));
129 shadowReceiver = lightingFlags & uint32_t(SCENE_NS::LightingFlags::SHADOW_RECEIVER_BIT);
130 }
131 return ctx.GetBoolean(shadowReceiver);
132 }
SetShadowReceiver(NapiApi::FunctionContext<bool> & ctx)133 void BaseMaterial::SetShadowReceiver(NapiApi::FunctionContext<bool>& ctx)
134 {
135 if (!validateSceneRef()) {
136 return;
137 }
138 bool shadowReceiver = ctx.Arg<0>();
139 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
140 uint32_t lightingFlags = uint32_t(META_NS::GetValue(material->LightingFlags()));
141 if (shadowReceiver) {
142 lightingFlags |= uint32_t(SCENE_NS::LightingFlags::SHADOW_RECEIVER_BIT);
143 } else {
144 lightingFlags &= ~uint32_t(SCENE_NS::LightingFlags::SHADOW_RECEIVER_BIT);
145 }
146 META_NS::SetValue(material->LightingFlags(), static_cast<SCENE_NS::LightingFlags>(lightingFlags));
147 }
148 }
GetCullMode(NapiApi::FunctionContext<> & ctx)149 napi_value BaseMaterial::GetCullMode(NapiApi::FunctionContext<>& ctx)
150 {
151 if (!validateSceneRef()) {
152 return ctx.GetUndefined();
153 }
154 auto cullMode = SCENE_NS::CullModeFlags::NONE;
155 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
156 if (auto shader = META_NS::GetValue(material->MaterialShader())) {
157 if (auto scene = GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
158 cullMode = static_cast<SCENE_NS::CullModeFlags>(shader->GetCullMode().GetResult());
159 }
160 }
161 }
162 return ctx.GetNumber(static_cast<uint32_t>(cullMode));
163 }
SetCullMode(NapiApi::FunctionContext<uint32_t> & ctx)164 void BaseMaterial::SetCullMode(NapiApi::FunctionContext<uint32_t>& ctx)
165 {
166 if (!validateSceneRef()) {
167 return;
168 }
169 auto cullMode = static_cast<SCENE_NS::CullModeFlags>((uint32_t)ctx.Arg<0>());
170
171 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
172 if (auto shader = META_NS::GetValue(material->MaterialShader())) {
173 shader->SetCullMode(cullMode);
174 // need to forcefully refresh the material, otherwise renderer will ignore the change
175 auto val = META_NS::GetValue(material->MaterialShader());
176 META_NS::SetValue(material->MaterialShader(), val);
177 }
178 }
179 }
GetBlend(NapiApi::FunctionContext<> & ctx)180 napi_value BaseMaterial::GetBlend(NapiApi::FunctionContext<>& ctx)
181 {
182 if (!validateSceneRef()) {
183 return ctx.GetUndefined();
184 }
185
186 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
187 if (!material) {
188 return ctx.GetUndefined();
189 }
190
191 auto shader = META_NS::GetValue(material->MaterialShader());
192 if (!shader) {
193 return ctx.GetUndefined();
194 }
195
196 bool enableBlend = shader->IsBlendEnabled().GetResult();
197
198 NapiApi::Object blendObjectJS(ctx.Env());
199 blendObjectJS.Set("enabled", ctx.GetBoolean(enableBlend));
200 return blendObjectJS.ToNapiValue();
201 }
SetBlend(NapiApi::FunctionContext<NapiApi::Object> & ctx)202 void BaseMaterial::SetBlend(NapiApi::FunctionContext<NapiApi::Object>& ctx)
203 {
204 if (!validateSceneRef()) {
205 return;
206 }
207
208 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
209 if (!material) {
210 return;
211 }
212
213 auto shader = META_NS::GetValue(material->MaterialShader());
214 if (!shader) {
215 return;
216 }
217
218 NapiApi::Object blendObjectJS = ctx.Arg<0>();
219 bool enableBlend = blendObjectJS.Get<bool>("enabled");
220 shader->EnableBlend(enableBlend);
221
222 // need to forcefully refresh the material, otherwise renderer will ignore the change
223 auto val = META_NS::GetValue(material->MaterialShader());
224 META_NS::SetValue(material->MaterialShader(), val);
225 }
GetAlphaCutoff(NapiApi::FunctionContext<> & ctx)226 napi_value BaseMaterial::GetAlphaCutoff(NapiApi::FunctionContext<>& ctx)
227 {
228 if (!validateSceneRef()) {
229 return ctx.GetUndefined();
230 }
231 float alphaCutoff = 1.0f;
232 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
233 alphaCutoff = META_NS::GetValue(material->AlphaCutoff());
234 }
235 return ctx.GetNumber(alphaCutoff);
236 }
SetAlphaCutoff(NapiApi::FunctionContext<float> & ctx)237 void BaseMaterial::SetAlphaCutoff(NapiApi::FunctionContext<float>& ctx)
238 {
239 if (!validateSceneRef()) {
240 return;
241 }
242 float alphaCutoff = ctx.Arg<0>();
243
244 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
245 META_NS::SetValue(material->AlphaCutoff(), alphaCutoff);
246 }
247 }
GetRenderSort(NapiApi::FunctionContext<> & ctx)248 napi_value BaseMaterial::GetRenderSort(NapiApi::FunctionContext<>& ctx)
249 {
250 if (!validateSceneRef()) {
251 return ctx.GetUndefined();
252 }
253
254 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
255 if (!material) {
256 return ctx.GetUndefined();
257 }
258
259 auto renderSort = META_NS::GetValue(material->RenderSort());
260
261 NapiApi::Object renderSortJS(ctx.Env());
262 renderSortJS.Set("renderSortLayer", ctx.GetNumber(renderSort.renderSortLayer));
263 renderSortJS.Set("renderSortLayerOrder", ctx.GetNumber(renderSort.renderSortLayerOrder));
264 return renderSortJS.ToNapiValue();
265 }
SetRenderSort(NapiApi::FunctionContext<NapiApi::Object> & ctx)266 void BaseMaterial::SetRenderSort(NapiApi::FunctionContext<NapiApi::Object>& ctx)
267 {
268 if (!validateSceneRef()) {
269 return;
270 }
271
272 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
273 if (!material) {
274 return;
275 }
276
277 SCENE_NS::RenderSort renderSort;
278 NapiApi::Object renderSortJS = ctx.Arg<0>();
279 renderSort.renderSortLayer = renderSortJS.Get<uint32_t>("renderSortLayer");
280 renderSort.renderSortLayerOrder = renderSortJS.Get<uint32_t>("renderSortLayerOrder");
281 META_NS::SetValue(material->RenderSort(), renderSort);
282 }
283
Init(napi_env env,napi_value exports)284 void MaterialJS::Init(napi_env env, napi_value exports)
285 {
286 #define MRADDPROP(index, name) \
287 NapiApi::GetSetProperty<NapiApi::Object, MaterialJS, \
288 &MaterialJS::GetMaterialProperty<index>, \
289 &MaterialJS::SetMaterialProperty<index>>(name)
290
291 BASE_NS::vector<napi_property_descriptor> props = {
292 NapiApi::GetSetProperty<NapiApi::Object, MaterialJS, &MaterialJS::GetColorShader,
293 &MaterialJS::SetColorShader>("colorShader"),
294 MRADDPROP(0, "baseColor"),
295 MRADDPROP(1, "normal"),
296 MRADDPROP(2, "material"),
297 MRADDPROP(3, "emissive"),
298 MRADDPROP(4, "ambientOcclusion"),
299 MRADDPROP(5, "clearCoat"),
300 MRADDPROP(6, "clearCoatRoughness"),
301 MRADDPROP(7, "clearCoatNormal"),
302 MRADDPROP(8, "sheen"),
303 MRADDPROP(10, "specular")
304 };
305 #undef MRADDPROP
306
307 BaseMaterial::Init("Material", env, exports, BaseObject::ctor<MaterialJS>(), props);
308 }
309
MaterialJS(napi_env e,napi_callback_info i)310 MaterialJS::MaterialJS(napi_env e, napi_callback_info i)
311 : BaseObject<MaterialJS>(e, i), BaseMaterial(BaseMaterial::MaterialType::METALLIC_ROUGHNESS)
312 {
313 NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
314 NapiApi::Object meJs(fromJs.This());
315
316 NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene... (do i need it here?)
317 NapiApi::Object args = fromJs.Arg<1>(); // other args
318
319 scene_ = scene;
320 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
321 LOG_F("INVALID SCENE!");
322 }
323
324 if (auto sceneJS = GetJsWrapper<SceneJS>(scene)) {
325 sceneJS->DisposeHook(reinterpret_cast<uintptr_t>(&scene_), meJs);
326 }
327
328 auto metaobj = GetNativeObjectParam<META_NS::IObject>(args); // Should be IMaterial
329 if (auto material = interface_cast<SCENE_NS::IMaterial>(metaobj)) {
330 materialType_ = META_NS::GetValue(material->Type()) == SCENE_NS::MaterialType::CUSTOM ?
331 MaterialType::SHADER : MaterialType::METALLIC_ROUGHNESS;
332 }
333 SetNativeObject(metaobj, true);
334 StoreJsObj(metaobj, meJs);
335
336 BASE_NS::string name;
337 if (auto prm = args.Get<BASE_NS::string>("name"); prm.IsDefined()) {
338 name = prm;
339 } else {
340 if (auto named = interface_cast<META_NS::IObject>(metaobj)) {
341 name = named->GetName();
342 }
343 }
344 meJs.Set("name", name);
345 }
346
~MaterialJS()347 MaterialJS::~MaterialJS() {}
GetInstanceImpl(uint32_t id)348 void* MaterialJS::GetInstanceImpl(uint32_t id)
349 {
350 if (id == MaterialJS::ID)
351 return this;
352 return BaseMaterial::GetInstanceImpl(id);
353 }
DisposeNative(void * scn)354 void MaterialJS::DisposeNative(void* scn)
355 {
356 if (disposed_) {
357 return;
358 }
359 disposed_ = true;
360 if (auto* sceneJS = static_cast<SceneJS*>(scn)) {
361 sceneJS->ReleaseDispose(reinterpret_cast<uintptr_t>(&scene_));
362 }
363 SetNativeObject(nullptr, false);
364 SetNativeObject(nullptr, true);
365 shaderBind_.reset();
366 shader_.Reset();
367
368 BaseMaterial::DisposeNative(this);
369 }
Finalize(napi_env env)370 void MaterialJS::Finalize(napi_env env)
371 {
372 DisposeNative(GetJsWrapper<SceneJS>(scene_.GetObject()));
373 BaseObject::Finalize(env);
374 }
SetColorShader(NapiApi::FunctionContext<NapiApi::Object> & ctx)375 void MaterialJS::SetColorShader(NapiApi::FunctionContext<NapiApi::Object>& ctx)
376 {
377 if (!validateSceneRef()) {
378 // owning scene has been destroyed.
379 // the material etc should also be gone.
380 // but there is a possible issue where the native object is still alive.
381
382 // most likely could happen if scene.dispose is not called
383 // but all references to the scene have been released,
384 // and garbage collection may or may not have been done yet. (or is partially done)
385 // if the scene is garbage collected then all the resources should be disposed.
386 return;
387 }
388
389 NapiApi::Object shaderJS = ctx.Arg<0>();
390 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject());
391 if (!material) {
392 // material destroyed, just make sure we have no shader reference anymore.
393 shader_.Reset();
394 return;
395 }
396 auto shader = GetNativeMeta<SCENE_NS::IShader>(shaderJS);
397 materialType_ = shader ? MaterialType::SHADER : MaterialType::METALLIC_ROUGHNESS;
398 META_NS::SetValue(material->Type(), shader ? SCENE_NS::MaterialType::CUSTOM :
399 SCENE_NS::MaterialType::METALLIC_ROUGHNESS);
400
401 // bind it to material (in native)
402 material->MaterialShader()->SetValue(shader);
403
404 if (!shader) {
405 shader_.Reset();
406 return;
407 }
408
409 // construct a "bound" shader object from the "non bound" one.
410 NapiApi::Env env(ctx.Env());
411 NapiApi::Object parms(env);
412 napi_value args[] = {
413 scene_.GetValue(), // bind the new instance of the shader to this javascript scene object
414 parms.ToNapiValue() // other constructor parameters
415 };
416
417 parms.Set("name", shaderJS.Get("name"));
418 parms.Set("Material", ctx.This()); // js material object that we are bound to.
419
420 shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
421 interface_cast<META_NS::IMetadata>(shaderBind_)
422 ->AddProperty(META_NS::ConstructProperty<IntfPtr>(
423 "shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
424 interface_cast<META_NS::IMetadata>(shaderBind_)
425 ->GetProperty<IntfPtr>("shader")
426 ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(shader));
427
428 auto argc = BASE_NS::countof(args);
429 auto argv = args;
430 MakeNativeObjectParam(env, shaderBind_, argc, argv);
431 auto result = CreateJsObj(env, "Shader", shaderBind_, false, argc, argv);
432 shader_ = NapiApi::StrongRef(StoreJsObj(shaderBind_, result));
433 }
GetColorShader(NapiApi::FunctionContext<> & ctx)434 napi_value MaterialJS::GetColorShader(NapiApi::FunctionContext<>& ctx)
435 {
436 if (!validateSceneRef()) {
437 return ctx.GetUndefined();
438 }
439
440 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject());
441 if (!material) {
442 // material destroyed, just make sure we have no shader reference anymore.
443 shader_.Reset();
444 return ctx.GetNull();
445 }
446
447 if (shader_.IsEmpty()) {
448 // no shader set yet..
449 // see if we have one on the native side.
450 // and create the "bound shader" object from it.
451
452 // check native side..
453 SCENE_NS::IShader::Ptr shader = material->MaterialShader()->GetValue();
454 if (!shader) {
455 // no shader in native also.
456 return ctx.GetNull();
457 }
458
459 // construct a "bound" shader object from the "non bound" one.
460 NapiApi::Env env(ctx.Env());
461 NapiApi::Object parms(env);
462 napi_value args[] = {
463 scene_.GetValue(), // bind the new instance of the shader to this javascript scene object
464 parms.ToNapiValue() // other constructor parameters
465 };
466
467 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
468 LOG_F("INVALID SCENE!");
469 }
470 parms.Set("Material", ctx.This()); // js material object that we are bound to.
471
472 shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
473 interface_cast<META_NS::IMetadata>(shaderBind_)
474 ->AddProperty(META_NS::ConstructProperty<IntfPtr>(
475 "shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
476 interface_cast<META_NS::IMetadata>(shaderBind_)
477 ->GetProperty<IntfPtr>("shader")
478 ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(shader));
479
480 auto argc = BASE_NS::countof(args);
481 auto argv = args;
482 MakeNativeObjectParam(env, shaderBind_, argc, argv);
483 auto result = CreateJsObj(env, "Shader", shaderBind_, false, argc, argv);
484 shader_ = NapiApi::StrongRef(StoreJsObj(shaderBind_, result));
485 }
486 return shader_.GetValue();
487 }
488
489 template<size_t Index>
GetMaterialProperty(NapiApi::FunctionContext<> & ctx)490 napi_value MaterialJS::GetMaterialProperty(NapiApi::FunctionContext<>& ctx)
491 {
492 if (!validateSceneRef()) {
493 return ctx.GetUndefined();
494 }
495 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
496 if (!material || !material->Textures()) {
497 return ctx.GetUndefined();
498 }
499 SCENE_NS::ITexture::Ptr texture = material->Textures()->GetValueAt(Index);
500 if (!texture) {
501 return ctx.GetUndefined();
502 }
503
504 auto obj = interface_pointer_cast<META_NS::IObject>(texture);
505 if (auto cached = FetchJsObj(obj)) {
506 // always return the same js object.
507 return cached.ToNapiValue();
508 }
509
510 napi_value args[] = { ctx.This().ToNapiValue() };
511 MakeNativeObjectParam(ctx.GetEnv(), texture, BASE_NS::countof(args), args);
512
513 auto result = CreateJsObj(ctx.GetEnv(), "MaterialProperty", obj, true, BASE_NS::countof(args), args);
514 auto materialPropertyJs = StoreJsObj(obj, result);
515 return materialPropertyJs.ToNapiValue();
516 }
517 template<size_t Index>
SetMaterialProperty(NapiApi::FunctionContext<NapiApi::Object> & ctx)518 void MaterialJS::SetMaterialProperty(NapiApi::FunctionContext<NapiApi::Object>& ctx)
519 {
520 if (!validateSceneRef()) {
521 return;
522 }
523 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
524 if (!material || !material->Textures()) {
525 return;
526 }
527 SCENE_NS::ITexture::Ptr texture = material->Textures()->GetValueAt(Index);
528 if (!texture) {
529 return;
530 }
531
532 NapiApi::Object arg = ctx.Arg<0>();
533 if (auto etex = GetNativeObjectParam<SCENE_NS::ITexture>(arg)) {
534 // Copy the exposed data from the given texture
535 META_NS::SetValue(texture->Image(), META_NS::GetValue(etex->Image()));
536 META_NS::SetValue(texture->Factor(), META_NS::GetValue(etex->Factor()));
537 }
538 }
539