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 "ShaderJS.h"
17
18 #include <meta/api/util.h>
19 #include <scene/ext/intf_internal_scene.h>
20 #include <scene/interface/intf_image.h>
21 #include <scene/interface/intf_mesh.h>
22 #include <scene/interface/intf_scene.h>
23
24 #include "SceneJS.h"
25 #include "Vec2Proxy.h"
26 #include "Vec3Proxy.h"
27 #include "Vec4Proxy.h"
28
29 using IntfPtr = META_NS::SharedPtrIInterface;
30 using IntfWeakPtr = META_NS::WeakPtrIInterface;
31
Init(napi_env env,napi_value exports)32 void ShaderJS::Init(napi_env env, napi_value exports)
33 {
34 using namespace NapiApi;
35 BASE_NS::vector<napi_property_descriptor> node_props;
36
37 SceneResourceImpl::GetPropertyDescs(node_props);
38
39 napi_value func;
40 auto status = napi_define_class(env, "Shader", NAPI_AUTO_LENGTH, BaseObject::ctor<ShaderJS>(), nullptr,
41 node_props.size(), node_props.data(), &func);
42
43 NapiApi::MyInstanceState* mis;
44 NapiApi::MyInstanceState::GetInstance(env, reinterpret_cast<void**>(&mis));
45 if (mis) {
46 mis->StoreCtor("Shader", func);
47 }
48 }
49
ShaderJS(napi_env e,napi_callback_info i)50 ShaderJS::ShaderJS(napi_env e, napi_callback_info i)
51 : BaseObject(e, i), SceneResourceImpl(SceneResourceImpl::SHADER)
52 {
53 NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
54 NapiApi::Object meJs(fromJs.This());
55
56 NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene...
57 NapiApi::Object args = fromJs.Arg<1>(); // other args
58 scene_ = { scene };
59 if (!scene_.GetObject().GetNative<SCENE_NS::IScene>()) {
60 LOG_F("INVALID SCENE!");
61 }
62
63 if (const auto sceneJS = scene_.GetObject().GetJsWrapper<SceneJS>()) {
64 sceneJS->DisposeHook(reinterpret_cast<uintptr_t>(&scene_), meJs);
65 }
66
67 NapiApi::Object material = args.Get<NapiApi::Object>("Material"); // see if we SHOULD be bound to a material.
68 if (material) {
69 BindToMaterial(meJs, material);
70 }
71
72 BASE_NS::string name;
73 if (auto prm = args.Get<BASE_NS::string>("name"); prm.IsDefined()) {
74 name = prm;
75 } else {
76 if (const auto named = GetNativeObject()) {
77 name = named->GetName();
78 }
79 }
80 meJs.Set("name", name);
81 }
82
BindToMaterial(NapiApi::Object meJs,NapiApi::Object material)83 void ShaderJS::BindToMaterial(NapiApi::Object meJs, NapiApi::Object material)
84 {
85 // unbind existing inputs.
86 UnbindInputs();
87
88 // inputs are actually owned (and used) by the material.
89 // create the input object
90 NapiApi::Object inputs(meJs.GetEnv());
91
92 napi_env e = inputs.GetEnv();
93 auto* tro = material.GetRoot();
94 if (!tro) {
95 LOG_E("null material root object");
96 return;
97 }
98 auto mat = interface_pointer_cast<SCENE_NS::IMaterial>(tro->GetNativeObject());
99 if (!mat) {
100 LOG_E("null material engine object");
101 return;
102 }
103
104 BASE_NS::vector<napi_property_descriptor> inputProps;
105
106 META_NS::IMetadata::Ptr customProperties;
107 BASE_NS::vector<SCENE_NS::ITexture::Ptr> Textures = mat->Textures()->GetValue();
108 customProperties = mat->GetCustomProperties();
109
110 if (!Textures.empty()) {
111 // list of default names
112 BASE_NS::string_view default_names[] = { "BASE_COLOR", "NORMAL", "MATERIAL", "EMISSIVE", "AO", "CLEARCOAT",
113 "CLEARCOAT_ROUGHNESS", "CLEARCOAT_NORMAL", "SHEEN", "TRANSMISSION", "SPECULAR" };
114 int index = 0;
115 for (auto t : Textures) {
116 BASE_NS::string name;
117 auto nn = interface_cast<META_NS::IObject>(t);
118 if (nn) {
119 name = nn->GetName();
120 } else {
121 name = "TextureInfo_" + BASE_NS::to_string(index);
122 }
123 BASE_NS::shared_ptr<PropertyProxy> proxt;
124 // factor
125 if (proxt = BASE_NS::shared_ptr { new Vec4Proxy(e, t->Factor()) }) {
126 auto n = (name.empty() ? BASE_NS::string_view("") : name + BASE_NS::string_view("_")) +
127 t->Factor()->GetName();
128
129 const auto& res = proxies_.insert_or_assign(n, proxt);
130 inputProps.push_back(CreateProxyDesc(res.first->first.c_str(), BASE_NS::move(proxt)));
131 }
132
133 if (proxt = BASE_NS::shared_ptr { new ImageProxy(scene_.GetObject(), inputs, t->Image()) }) {
134 auto n = (name.empty() ? BASE_NS::string_view("") : name + BASE_NS::string_view("_")) +
135 t->Image()->GetName();
136 const auto& res = proxies_.insert_or_assign(n, proxt);
137 inputProps.push_back(CreateProxyDesc(res.first->first.c_str(), BASE_NS::move(proxt)));
138 }
139
140 // create default named property, if it was renamed
141 if (name != default_names[index]) {
142 BASE_NS::shared_ptr<PropertyProxy> proxt;
143 if (proxt = BASE_NS::shared_ptr { new Vec4Proxy(e, t->Factor()) }) {
144 auto n = default_names[index] + "_" + t->Factor()->GetName();
145 const auto& res = proxies_.insert_or_assign(n, proxt);
146 inputProps.push_back(CreateProxyDesc(res.first->first.c_str(), BASE_NS::move(proxt)));
147 }
148 if (proxt = BASE_NS::shared_ptr { new ImageProxy(scene_.GetObject(), inputs, t->Image()) }) {
149 auto n = default_names[index] + "_" + t->Image()->GetName();
150 const auto& res = proxies_.insert_or_assign(n, proxt);
151 inputProps.push_back(CreateProxyDesc(res.first->first.c_str(), BASE_NS::move(proxt)));
152 }
153 }
154 index++;
155 }
156 }
157 // default stuff
158 {
159 auto proxt = BASE_NS::shared_ptr { new TypeProxy<float>(inputs, mat->AlphaCutoff()) };
160 if (proxt) {
161 auto res = proxies_.insert_or_assign(mat->AlphaCutoff()->GetName(), proxt);
162 inputProps.push_back(CreateProxyDesc(res.first->first.c_str(), BASE_NS::move(proxt)));
163 }
164 }
165 if (customProperties) {
166 BASE_NS::shared_ptr<CORE_NS::IInterface> intsc;
167 if (auto scene = scene_.GetObject().GetNative<SCENE_NS::IScene>()) {
168 if (auto ints = scene->GetInternalScene()) {
169 intsc = interface_pointer_cast<CORE_NS::IInterface>(ints);
170 }
171 }
172 if (intsc) {
173 for (auto t : customProperties->GetProperties()) {
174 BASE_NS::shared_ptr<PropertyProxy> proxt = PropertyToProxy(scene_.GetObject(), inputs, t);
175 if (proxt) {
176 proxt->SetExtra(intsc);
177 auto res = proxies_.insert_or_assign(t->GetName(), proxt);
178 inputProps.push_back(CreateProxyDesc(res.first->first.c_str(), BASE_NS::move(proxt)));
179 }
180 }
181 }
182 }
183 if (!inputProps.empty()) {
184 napi_define_properties(e, inputs.ToNapiValue(), inputProps.size(), inputProps.data());
185 }
186
187 inputs_ = NapiApi::StrongRef(inputs);
188 meJs.Set("inputs", inputs_.GetValue());
189 }
190
~ShaderJS()191 ShaderJS::~ShaderJS()
192 {
193 DisposeNative(nullptr);
194 }
195
GetInstanceImpl(uint32_t id)196 void* ShaderJS::GetInstanceImpl(uint32_t id)
197 {
198 if (id == ShaderJS::ID)
199 return this;
200 return SceneResourceImpl::GetInstanceImpl(id);
201 }
UnbindInputs()202 void ShaderJS::UnbindInputs()
203 {
204 /// destroy the input object.
205 if (!inputs_.IsEmpty()) {
206 NapiApi::Object inp = inputs_.GetObject();
207 while (!proxies_.empty()) {
208 auto it = proxies_.begin();
209 // removes hooks for meta property & jsproperty.
210 inp.DeleteProperty(it->first);
211 // destroy the proxy.
212 proxies_.erase(it);
213 }
214 }
215 inputs_.Reset();
216 }
DisposeNative(void * in)217 void ShaderJS::DisposeNative(void* in)
218 {
219 if (!disposed_) {
220 disposed_ = true;
221 UnbindInputs();
222 UnsetNativeObject();
223
224 if (const auto sceneJS = scene_.GetObject().GetJsWrapper<SceneJS>()) {
225 sceneJS->ReleaseDispose(reinterpret_cast<uintptr_t>(&scene_));
226 }
227 scene_.Reset();
228 }
229 }
Finalize(napi_env env)230 void ShaderJS::Finalize(napi_env env)
231 {
232 DisposeNative(scene_.GetObject().GetJsWrapper<SceneJS>());
233 BaseObject::Finalize(env);
234 }
235