1 /*
2  * Copyright (c) 2022 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 "frameworks/bridge/declarative_frontend/jsview/js_sceneview.h"
17 
18 #include <regex>
19 
20 #include "custom/custom_render_descriptor.h"
21 #include "custom/shader_input_buffer.h"
22 #include "data_type/constants.h"
23 #include "data_type/geometry/cone.h"
24 #include "data_type/geometry/cube.h"
25 #include "data_type/geometry/sphere.h"
26 #include "data_type/light.h"
27 
28 #include "base/geometry/quaternion.h"
29 #include "base/geometry/vec3.h"
30 #include "core/components_ng/base/view_stack_model.h"
31 #include "core/components_ng/pattern/model/model_view_ng.h"
32 #include "frameworks/bridge/declarative_frontend/engine/functions/js_click_function.h"
33 #include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
34 
35 #if defined(KIT_3D_ENABLE)
36 #include "scene_adapter/scene_bridge.h"
37 #endif
38 
39 namespace OHOS::Ace {
40 
41 std::unique_ptr<ModelView> ModelView::instance_ = nullptr;
42 std::mutex ModelView::mutex_;
43 
GetInstance()44 ModelView* ModelView::GetInstance()
45 {
46     if (!instance_) {
47         std::lock_guard<std::mutex> lock(mutex_);
48         if (!instance_) {
49 #ifdef NG_BUILD
50             instance_.reset(new NG::ModelViewNG());
51 #else
52             if (Container::IsCurrentUseNewPipeline()) {
53                 instance_.reset(new NG::ModelViewNG());
54             } else {
55                 LOGW("ModelView::GetInstance() NOT NG Pipeline not support");
56             }
57 #endif
58         }
59     }
60     return instance_.get();
61 }
62 
63 } // namespace OHOS::Ace
64 
65 namespace OHOS::Ace::Framework {
66 static const std::regex MODEL_RES_ID_REGEX(R"(^resource://\w+/([0-9]+)\.\w+$)", std::regex::icase);
67 static const std::regex MODEL_APP_RES_PATH_REGEX(R"(^resource://RAWFILE/(.*)$)");
68 static const std::regex MODEL_APP_RES_ID_REGEX(R"(^resource://.*/([0-9]+)\.\w+$)", std::regex::icase);
69 static const std::regex MODEL_RES_NAME_REGEX(R"(^resource://.*/(\w+)\.\w+$)", std::regex::icase);
70 static constexpr uint32_t MODEL_RESOURCE_MATCH_SIZE = 2;
71 
GetResourceId(const std::string & uri,uint32_t & resId)72 bool GetResourceId(const std::string& uri, uint32_t& resId)
73 {
74     std::smatch matches;
75     if (std::regex_match(uri, matches, MODEL_RES_ID_REGEX) && matches.size() == MODEL_RESOURCE_MATCH_SIZE) {
76         resId = static_cast<uint32_t>(std::stoul(matches[1].str()));
77         return true;
78     }
79 
80     std::smatch appMatches;
81     if (std::regex_match(uri, appMatches, MODEL_APP_RES_ID_REGEX) && appMatches.size() == MODEL_RESOURCE_MATCH_SIZE) {
82         resId = static_cast<uint32_t>(std::stoul(appMatches[1].str()));
83         return true;
84     }
85     return false;
86 }
87 
GetResourceId(const std::string & uri,std::string & path)88 bool GetResourceId(const std::string& uri, std::string& path)
89 {
90     std::smatch matches;
91     if (std::regex_match(uri, matches, MODEL_APP_RES_PATH_REGEX) && matches.size() == MODEL_RESOURCE_MATCH_SIZE) {
92         path = matches[1].str();
93         return true;
94     }
95     return false;
96 }
97 
GetResourceName(const std::string & uri,std::string & resName)98 bool GetResourceName(const std::string& uri, std::string& resName)
99 {
100     std::smatch matches;
101     if (std::regex_match(uri, matches, MODEL_RES_NAME_REGEX) && matches.size() == MODEL_RESOURCE_MATCH_SIZE) {
102         resName = matches[1].str();
103         return true;
104     }
105     return false;
106 }
107 
SetOhosPath(const std::string & uri,std::string & ohosPath)108 bool SetOhosPath(const std::string& uri, std::string& ohosPath)
109 {
110     if (GetResourceId(uri, ohosPath)) {
111         ohosPath = "OhosRawFile://" + ohosPath;
112         return true;
113     }
114 
115     uint32_t resId = 0;
116     if (GetResourceId(uri, resId)) {
117         ohosPath = "OhosRawFile://" + std::to_string(resId);
118         return true;
119     }
120 
121     if (GetResourceName(uri, ohosPath)) {
122         ohosPath = "OhosRawFile://" + ohosPath;
123         return true;
124     }
125     // set default format as system resource
126     ohosPath = "file://" + uri;
127     return false;
128 }
129 
130 // get Number data
131 template<typename T>
GetModelProperty(const JSRef<JSObject> & jsValue,const std::string & propertyName,std::unordered_map<std::string,T> & propertyData)132 bool GetModelProperty(
133     const JSRef<JSObject>& jsValue, const std::string& propertyName, std::unordered_map<std::string, T>& propertyData)
134 {
135     auto item = jsValue->GetProperty(propertyName.c_str());
136     if (item->IsObject()) {
137         JSRef<JSObject> itemObj = JSRef<JSObject>::Cast(item);
138         for (auto iter = propertyData.begin(); iter != propertyData.end(); ++iter) {
139             JSRef<JSVal> itemData = itemObj->GetProperty((iter->first).c_str());
140             if (itemData->IsNumber()) {
141                 iter->second = itemData->ToNumber<T>();
142                 continue;
143             }
144             if (itemData->IsBoolean()) {
145                 iter->second = itemData->ToBoolean();
146                 continue;
147             }
148             return false;
149         }
150         return true;
151     }
152     return false;
153 }
154 
JsSetHandleCameraMove(const JSCallbackInfo & info)155 void JSSceneView::JsSetHandleCameraMove(const JSCallbackInfo& info)
156 {
157     if (info.Length() < 1) {
158         return;
159     }
160 
161     if (!info[0]->IsBoolean()) {
162         return;
163     }
164 
165     bool value = info[0]->ToBoolean();
166     ModelView::GetInstance()->SetHandleCameraMove(value);
167 }
168 
169 #if defined(KIT_3D_ENABLE)
UnwrapScene(JSRef<JSVal> obj)170 std::shared_ptr<Render3D::ISceneAdapter> UnwrapScene(JSRef<JSVal> obj)
171 {
172 #if defined(USE_ARK_ENGINE) && !defined(PREVIEW)
173     if (!obj->IsObject()) {
174         return nullptr;
175     }
176 
177     auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
178     if (!runtime) {
179         return nullptr;
180     }
181 
182     auto nativeEngine = runtime->GetNativeEngine();
183     if (nativeEngine == nullptr) {
184         return nullptr;
185     }
186 
187     panda::Local<JsiValue> value = obj.Get().GetLocalHandle();
188     JSValueWrapper valueWrapper = value;
189 
190     napi_env env = reinterpret_cast<napi_env>(nativeEngine);
191     napi_handle_scope scope = nullptr;
192     napi_open_handle_scope(env, &scope);
193     napi_value napiValue = nativeEngine->ValueToNapiValue(valueWrapper);
194 
195     auto ret = Render3D::SceneBridge::UnwrapSceneFromJs(env, napiValue);
196     napi_close_handle_scope(env, scope);
197     return ret;
198 #else
199     return nullptr;
200 #endif
201 }
202 
ParseSceneOpt(const JSCallbackInfo & info,std::string & srcPath,std::shared_ptr<Render3D::ISceneAdapter> & scene,int & surfaceData,std::string & bundleName,std::string & moduleName)203 bool ParseSceneOpt(const JSCallbackInfo& info, std::string& srcPath, std::shared_ptr<Render3D::ISceneAdapter>& scene,
204     int& surfaceData, std::string& bundleName, std::string& moduleName)
205 {
206     if (JSViewAbstract::ParseJsMedia(info[0], srcPath)) {
207         return false;
208     }
209 
210     if (!info[0]->IsObject()) {
211         return false;
212     }
213 
214     JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
215     auto type = jsObj->GetProperty("modelType");
216     if (!type->IsNull()) {
217         surfaceData = type->ToNumber<int32_t>();
218     }
219 
220     // SceneOptings
221     auto sceneOpt = jsObj->GetProperty("scene");
222     if (!sceneOpt->IsObject()) {
223         return false;
224     }
225 
226     JSRef<JSObject> jsObjScene = JSRef<JSObject>::Cast(sceneOpt);
227     scene = UnwrapScene(jsObjScene);
228     if (scene == nullptr) {
229         JSViewAbstract::ParseJsMedia(sceneOpt, srcPath);
230         JSViewAbstract::GetJsMediaBundleInfo(sceneOpt, bundleName, moduleName);
231         return false;
232     }
233 
234     // Scene new api
235     auto prop = jsObjScene->GetProperty("uri");
236     if (!prop->IsNull()) {
237         scene = UnwrapScene(jsObjScene);
238     }
239     return true;
240 }
241 #endif
242 
Create(const JSCallbackInfo & info)243 void JSSceneView::Create(const JSCallbackInfo& info)
244 {
245     const auto& length = info.Length();
246     std::string srcPath("");
247     int surfaceData = 0;
248     std::string bundleName;
249     std::string moduleName;
250 
251     Render3D::SurfaceType surfaceType = OHOS::Render3D::SurfaceType::SURFACE_TEXTURE;
252 #if defined(KIT_3D_ENABLE)
253     std::shared_ptr<Render3D::ISceneAdapter> scene = nullptr;
254     bool isSceneApi = false;
255 #endif
256     if (length == 2) { // 2: info size
257         if (info[1]->IsNumber()) {
258             surfaceData = info[1]->ToNumber<int32_t>();
259         }
260         ParseJsMedia(info[0], srcPath);
261         GetJsMediaBundleInfo(info[0], bundleName, moduleName);
262     } else if (length == 1) {
263 #if defined(KIT_3D_ENABLE)
264         isSceneApi = ParseSceneOpt(info, srcPath, scene, surfaceData, bundleName, moduleName);
265 #else
266         if (!ParseJsMedia(info[0], srcPath)) {
267             // SceneOptions
268             JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
269             auto scene = jsObj->GetProperty("scene");
270             if (!scene->IsNull()) {
271                 ParseJsMedia(scene, srcPath);
272                 GetJsMediaBundleInfo(scene, bundleName, moduleName);
273             }
274             auto type = jsObj->GetProperty("modelType");
275             if (!type->IsNull()) {
276                 surfaceData = type->ToNumber<int32_t>();
277             }
278         }
279 #endif
280     }
281 
282     surfaceType = (surfaceData == 0) ? OHOS::Render3D::SurfaceType::SURFACE_TEXTURE :
283         OHOS::Render3D::SurfaceType::SURFACE_WINDOW;
284 
285     std::string ohosPath("");
286     SetOhosPath(srcPath, ohosPath);
287     LOGD("srcPath after ParseJsMedia(): %s bundleName: %s, moduleName %s", ohosPath.c_str(),
288         bundleName.c_str(), moduleName.c_str());
289 #if defined(KIT_3D_ENABLE)
290     ModelView::GetInstance()->Create({ bundleName, moduleName, surfaceType, scene });
291 #else
292     ModelView::GetInstance()->Create({ bundleName, moduleName, surfaceType });
293 #endif
294     ModelView::GetInstance()->SetModelSource(ohosPath);
295 }
296 
JsSetBackground(const JSCallbackInfo & info)297 void JSSceneView::JsSetBackground(const JSCallbackInfo& info)
298 {
299     if (info.Length() < 1) {
300         return;
301     }
302 
303     std::string srcPath;
304     auto parseOk = ParseJsMedia(info[0], srcPath);
305     if (!parseOk) {
306         return;
307     }
308     std::string ohosPath("");
309     SetOhosPath(srcPath, ohosPath);
310     ModelView::GetInstance()->SetBackground(ohosPath);
311 }
312 
JsAddCustomRender(const JSCallbackInfo & info)313 void JSSceneView::JsAddCustomRender(const JSCallbackInfo& info)
314 {
315     if (info.Length() != 2) {
316         return;
317     }
318 
319     if (info[1]->IsNull() || !info[1]->IsBoolean()) {
320         return;
321     }
322 
323     std::string uri;
324     auto parseOk = ParseJsMedia(info[0], uri);
325     if (!parseOk) {
326         return;
327     }
328 
329     std::string ohosPath("");
330     SetOhosPath(uri, ohosPath);
331     auto desc = std::make_shared<Render3D::CustomRenderDescriptor>(ohosPath, info[1]->ToBoolean());
332     ModelView::GetInstance()->AddCustomRender(desc);
333 }
334 
JsRenderWidth(const JSCallbackInfo & info)335 void JSSceneView::JsRenderWidth(const JSCallbackInfo& info)
336 {
337     if (info.Length() < 1) {
338         return;
339     }
340     CalcDimension value;
341     if (!ParseJsDimensionVp(info[0], value)) {
342         value.SetValue(1.0f);
343         return;
344     }
345 
346     if (info[0]->IsNumber() || info[0]->IsObject()) {
347         value.SetValue(1.0f);
348     }
349 
350     if (LessNotEqual(value.Value(), 0.0f)) {
351         value.SetValue(0.0f);
352     }
353     ModelView::GetInstance()->SetRenderWidth(value);
354 }
355 
JsRenderHeight(const JSCallbackInfo & info)356 void JSSceneView::JsRenderHeight(const JSCallbackInfo& info)
357 {
358     if (info.Length() < 1) {
359         return;
360     }
361     CalcDimension value;
362     if (!ParseJsDimensionVp(info[0], value)) {
363         LOGE("invalid args for render height");
364         value.SetValue(1.0f);
365         return;
366     }
367 
368     if (info[0]->IsNumber() || info[0]->IsObject()) {
369         value.SetValue(1.0f);
370     }
371 
372     if (LessNotEqual(value.Value(), 0.0f)) {
373         value.SetValue(0.0f);
374     }
375     ModelView::GetInstance()->SetRenderHeight(value);
376 }
377 
JsRenderFrameRate(const JSCallbackInfo & info)378 void JSSceneView::JsRenderFrameRate(const JSCallbackInfo& info) {}
379 
JsShader(const JSCallbackInfo & info)380 void JSSceneView::JsShader(const JSCallbackInfo& info)
381 {
382     if (info.Length() != 1) {
383         return;
384     }
385 
386     std::string shaderPath;
387     auto parseOk = ParseJsMedia(info[0], shaderPath);
388     if (!parseOk) {
389         return;
390     }
391 
392     std::string ohosPath("");
393     SetOhosPath(shaderPath, ohosPath);
394     ModelView::GetInstance()->SetShader(ohosPath);
395 }
396 
JsShaderImageTexture(const JSCallbackInfo & info)397 void JSSceneView::JsShaderImageTexture(const JSCallbackInfo& info)
398 {
399     if (info.Length() != 1) {
400         return;
401     }
402 
403     std::string texturePath;
404     auto parseOk = ParseJsMedia(info[0], texturePath);
405     if (!parseOk) {
406         return;
407     }
408 
409     std::string ohosPath("");
410     SetOhosPath(texturePath, ohosPath);
411     ModelView::GetInstance()->AddShaderImageTexture(ohosPath);
412 }
413 
JsShaderInputBuffer(const JSCallbackInfo & info)414 void JSSceneView::JsShaderInputBuffer(const JSCallbackInfo& info)
415 {
416     if (info.Length() != 1 || !info[0]->IsArray()) {
417         return;
418     }
419 
420     JSRef<JSArray> array = JSRef<JSArray>::Cast(info[0]);
421     int32_t length = static_cast<int32_t>(array->Length());
422     if (length <= 0) {
423         return;
424     }
425 
426     auto modelView = ModelView::GetInstance();
427     std::shared_ptr<OHOS::Render3D::ShaderInputBuffer> buffer = nullptr;
428 
429     // same shader input buffer would be rejected to update for nearEqual check
430     buffer = std::make_shared<OHOS::Render3D::ShaderInputBuffer>();
431     if (!buffer->Alloc(length)) {
432         return;
433     }
434 
435     for (uint32_t i = 0; i < static_cast<uint32_t>(length); i++) {
436         JSRef<JSVal> jsValue = array->GetValueAt(i);
437         if (jsValue->IsNumber()) {
438             buffer->Update(jsValue->ToNumber<float>(), i);
439         } else {
440             return;
441         }
442     }
443 
444     modelView->AddShaderInputBuffer(buffer);
445 }
446 
JsOnError(const JSCallbackInfo & info)447 void JSSceneView::JsOnError(const JSCallbackInfo& info) {}
448 
JSBind(BindingTarget globalObj)449 void JSSceneView::JSBind(BindingTarget globalObj)
450 {
451     JSClass<JSSceneView>::Declare("Component3D");
452     MethodOptions opt = MethodOptions::NONE;
453     JSClass<JSSceneView>::StaticMethod("create", &JSSceneView::Create, opt);
454     JSClass<JSSceneView>::StaticMethod("gestureAccess", &JSSceneView::JsSetHandleCameraMove);
455     JSClass<JSSceneView>::StaticMethod("environment", &JSSceneView::JsSetBackground);
456     JSClass<JSSceneView>::StaticMethod("customRender", &JSSceneView::JsAddCustomRender);
457     JSClass<JSSceneView>::StaticMethod("shader", &JSSceneView::JsShader);
458     JSClass<JSSceneView>::StaticMethod("renderWidth", &JSSceneView::JsRenderWidth);
459     JSClass<JSSceneView>::StaticMethod("renderHeight", &JSSceneView::JsRenderHeight);
460     JSClass<JSSceneView>::StaticMethod("renderFrameRateHint", &JSSceneView::JsRenderFrameRate);
461     JSClass<JSSceneView>::StaticMethod("shaderImageTexture", &JSSceneView::JsShaderImageTexture);
462     JSClass<JSSceneView>::StaticMethod("shaderInputBuffer", &JSSceneView::JsShaderInputBuffer);
463     JSClass<JSSceneView>::StaticMethod("OnError", &JSSceneView::JsOnError);
464     JSClass<JSSceneView>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
465     JSClass<JSSceneView>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
466     JSClass<JSSceneView>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
467     JSClass<JSSceneView>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
468     JSClass<JSSceneView>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
469     JSClass<JSSceneView>::InheritAndBind<JSViewAbstract>(globalObj);
470 }
471 
472 } // namespace OHOS::Ace::Framework
473