• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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