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