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