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 <meta/api/make_callback.h>
17 #include <meta/interface/intf_task_queue.h>
18 #include <meta/interface/intf_task_queue_registry.h>
19 #include <scene/interface/intf_material.h>
20
21 #include "MaterialJS.h"
22 #include "MaterialPropertyJS.h"
23 #include "SamplerJS.h"
24
25 #include <optional>
26
27 using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
28 using IntfWeakPtr = BASE_NS::weak_ptr<CORE_NS::IInterface>;
29 using namespace SCENE_NS;
30
Init(napi_env env,napi_value exports)31 void MaterialPropertyJS::Init(napi_env env, napi_value exports)
32 {
33 using namespace NapiApi;
34
35 BASE_NS::vector<napi_property_descriptor> node_props;
36 node_props.push_back(
37 GetSetProperty<Object, MaterialPropertyJS, &MaterialPropertyJS::GetImage, &MaterialPropertyJS::SetImage>(
38 "image"));
39 node_props.push_back(
40 GetSetProperty<Object, MaterialPropertyJS, &MaterialPropertyJS::GetFactor, &MaterialPropertyJS::SetFactor>(
41 "factor"));
42 node_props.push_back(
43 GetSetProperty<Object, MaterialPropertyJS, &MaterialPropertyJS::GetSampler, &MaterialPropertyJS::SetSampler>(
44 "sampler"));
45 node_props.push_back(
46 MakeTROMethod<FunctionContext<>, MaterialPropertyJS, &MaterialPropertyJS::Dispose>("destroy"));
47
48 napi_value func;
49 auto status = napi_define_class(env, "MaterialProperty", NAPI_AUTO_LENGTH, BaseObject::ctor<MaterialPropertyJS>(),
50 nullptr, node_props.size(), node_props.data(), &func);
51
52 MyInstanceState* mis;
53 NapiApi::MyInstanceState::GetInstance(env, (void**)&mis);
54 if (mis) {
55 mis->StoreCtor("MaterialProperty", func);
56 }
57 }
MaterialPropertyJS(napi_env e,napi_callback_info i)58 MaterialPropertyJS::MaterialPropertyJS(napi_env e, napi_callback_info i) : BaseObject(e, i) {}
~MaterialPropertyJS()59 MaterialPropertyJS::~MaterialPropertyJS()
60 {
61 DisposeNative(nullptr);
62 if (!GetNativeObject()) {
63 return;
64 }
65 }
GetInstanceImpl(uint32_t id)66 void* MaterialPropertyJS::GetInstanceImpl(uint32_t id)
67 {
68 if (id == MaterialPropertyJS::ID)
69 return this;
70 return nullptr;
71 }
Dispose(NapiApi::FunctionContext<> & ctx)72 napi_value MaterialPropertyJS::Dispose(NapiApi::FunctionContext<>& ctx)
73 {
74 DisposeNative(nullptr);
75 return {};
76 }
Finalize(napi_env env)77 void MaterialPropertyJS::Finalize(napi_env env)
78 {
79 DisposeNative(nullptr);
80 BaseObject::Finalize(env);
81 }
DisposeNative(void *)82 void MaterialPropertyJS::DisposeNative(void*)
83 {
84 if (!disposed_) {
85 disposed_ = true;
86 sampler_.Reset();
87 factorProxy_.reset();
88 UnsetNativeObject();
89 }
90 }
GetImage(NapiApi::FunctionContext<> & ctx)91 napi_value MaterialPropertyJS::GetImage(NapiApi::FunctionContext<>& ctx)
92 {
93 auto texture = ctx.This().GetNative<SCENE_NS::ITexture>();
94 if (!texture) {
95 return ctx.GetUndefined();
96 }
97
98 SCENE_NS::IBitmap::Ptr image = META_NS::GetValue(texture->Image());
99 napi_value args[] = { ctx.This().ToNapiValue() };
100 return CreateFromNativeInstance(ctx.GetEnv(), image, PtrType::STRONG, args).ToNapiValue();
101 }
SetImage(NapiApi::FunctionContext<NapiApi::Object> & ctx)102 void MaterialPropertyJS::SetImage(NapiApi::FunctionContext<NapiApi::Object>& ctx)
103 {
104 auto texture = ctx.This().GetNative<SCENE_NS::ITexture>();
105 if (!texture) {
106 return;
107 }
108
109 NapiApi::Object imageJS = ctx.Arg<0>();
110 if (auto nat = imageJS.GetRoot()) {
111 SCENE_NS::IBitmap::Ptr image = interface_pointer_cast<SCENE_NS::IBitmap>(nat->GetNativeObject());
112 META_NS::SetValue(texture->Image(), image);
113 }
114 }
GetFactor(NapiApi::FunctionContext<> & ctx)115 napi_value MaterialPropertyJS::GetFactor(NapiApi::FunctionContext<> &ctx)
116 {
117 if (auto texture = ctx.This().GetNative<SCENE_NS::ITexture>()) {
118 if (!factorProxy_) {
119 factorProxy_ = BASE_NS::make_unique<Vec4Proxy>(ctx.Env(), texture->Factor());
120 }
121 return factorProxy_->Value();
122 }
123 return ctx.GetUndefined();
124 }
SetFactor(NapiApi::FunctionContext<NapiApi::Object> & ctx)125 void MaterialPropertyJS::SetFactor(NapiApi::FunctionContext<NapiApi::Object>& ctx)
126 {
127 auto texture = ctx.This().GetNative<SCENE_NS::ITexture>();
128 if (!texture) {
129 return;
130 }
131
132 if (NapiApi::Object factorJS = ctx.Arg<0>()) {
133 auto x = factorJS.Get<float>("x");
134 auto y = factorJS.Get<float>("y");
135 auto z = factorJS.Get<float>("z");
136 auto w = factorJS.Get<float>("w");
137 META_NS::SetValue(texture->Factor(), { x, y, z, w });
138 }
139 }
GetSampler(NapiApi::FunctionContext<> & ctx)140 napi_value MaterialPropertyJS::GetSampler(NapiApi::FunctionContext<>& ctx)
141 {
142 if (auto existing = sampler_.GetObject()) {
143 return existing.ToNapiValue();
144 }
145
146 if (auto texture = GetNativeObject<ITexture>()) {
147 if (auto sampler = META_NS::GetValue(texture->Sampler())) {
148 napi_value args[] = { ctx.This().ToNapiValue() };
149 auto object = CreateFromNativeInstance(ctx.GetEnv(), sampler, PtrType::STRONG, args);
150 sampler_ = NapiApi::StrongRef(object);
151 return object.ToNapiValue();
152 }
153 }
154 return ctx.GetUndefined();
155 }
156
SetSampler(NapiApi::FunctionContext<NapiApi::Object> & ctx)157 void MaterialPropertyJS::SetSampler(NapiApi::FunctionContext<NapiApi::Object>& ctx)
158 {
159 auto texture = ctx.This().GetNative<SCENE_NS::ITexture>();
160 if (!texture) {
161 return;
162 }
163
164 auto target = META_NS::GetValue(texture->Sampler());
165 NapiApi::Object source = ctx.Arg<0>();
166
167 struct SamplerChangeSet {
168 std::optional<SCENE_NS::SamplerFilter> minFilter;
169 std::optional<SCENE_NS::SamplerFilter> magFilter;
170 std::optional<SCENE_NS::SamplerFilter> mipMapMode;
171 std::optional<SCENE_NS::SamplerAddressMode> addressModeU;
172 std::optional<SCENE_NS::SamplerAddressMode> addressModeV;
173 std::optional<SCENE_NS::SamplerAddressMode> addressModeW;
174
175 bool HasChanges() const
176 {
177 return minFilter.has_value() || magFilter.has_value() || mipMapMode.has_value() ||
178 addressModeU.has_value() || addressModeV.has_value() || addressModeW.has_value();
179 }
180 };
181
182 SamplerChangeSet changes;
183 bool defined = source && source.IsDefined() && !source.IsNull();
184 if (defined) {
185 if (source.Has("magFilter")) {
186 changes.magFilter = SamplerJS::ConvertToFilter(source.Get<uint32_t>("magFilter"));
187 }
188 if (source.Has("minFilter")) {
189 changes.minFilter = SamplerJS::ConvertToFilter(source.Get<uint32_t>("minFilter"));
190 }
191 if (source.Has("mipMapMode")) {
192 changes.mipMapMode = SamplerJS::ConvertToFilter(source.Get<uint32_t>("mipMapMode"));
193 }
194 if (source.Has("addressModeU")) {
195 changes.addressModeU = SamplerJS::ConvertToAddressMode(source.Get<uint32_t>("addressModeU"));
196 }
197 if (source.Has("addressModeV")) {
198 changes.addressModeV = SamplerJS::ConvertToAddressMode(source.Get<uint32_t>("addressModeV"));
199 }
200 if (source.Has("addressModeW")) {
201 changes.addressModeW = SamplerJS::ConvertToAddressMode(source.Get<uint32_t>("addressModeW"));
202 }
203 }
204
205 ExecSyncTask([&]() {
206 // Apply given object as a changeset on top of default sampler
207 if (auto resetable = interface_cast<META_NS::IResetableObject>(target)) {
208 resetable->ResetObject(); // First, reset to default state
209 }
210 // Then apply those properties that have been set in our input object
211 if (changes.HasChanges()) {
212 if (changes.magFilter) {
213 META_NS::SetValue(target->MagFilter(), changes.magFilter.value());
214 }
215 if (changes.minFilter) {
216 META_NS::SetValue(target->MinFilter(), changes.minFilter.value());
217 }
218 if (changes.mipMapMode) {
219 META_NS::SetValue(target->MipMapMode(), changes.mipMapMode.value());
220 }
221 if (changes.addressModeU) {
222 META_NS::SetValue(target->AddressModeU(), changes.addressModeU.value());
223 }
224 if (changes.addressModeV) {
225 META_NS::SetValue(target->AddressModeV(), changes.addressModeV.value());
226 }
227 if (changes.addressModeW) {
228 META_NS::SetValue(target->AddressModeW(), changes.addressModeW.value());
229 }
230 }
231 return META_NS::IAny::Ptr {};
232 });
233 }