已关闭
AR Engine #2
weitingting创建于 8月5日关闭于 22 天前
AR Engine #2
已关闭
从已删除 :master合入到HarmonyOS_Samples/arengine_samplecode_clientdemo_arktsmaster
共 25 个文件变更+1345-526
| @@ -0,0 +1,78 @@ | |||
| 1 | + Copyright (c) 2026 Huawei Device Co., Ltd. All rights reserved. | ||
| 2 | + | ||
| 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 | +Apache License, Version 2.0 | ||
| 16 | +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
| 17 | + | ||
| 18 | +1. Definitions. | ||
| 19 | + | ||
| 20 | +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. | ||
| 21 | + | ||
| 22 | +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. | ||
| 23 | + | ||
| 24 | +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. | ||
| 25 | + | ||
| 26 | +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. | ||
| 27 | + | ||
| 28 | +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. | ||
| 29 | + | ||
| 30 | +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. | ||
| 31 | + | ||
| 32 | +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). | ||
| 33 | + | ||
| 34 | +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. | ||
| 35 | + | ||
| 36 | +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." | ||
| 37 | + | ||
| 38 | +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. | ||
| 39 | + | ||
| 40 | +2. Grant of Copyright License. | ||
| 41 | + | ||
| 42 | +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. | ||
| 43 | + | ||
| 44 | +3. Grant of Patent License. | ||
| 45 | + | ||
| 46 | +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. | ||
| 47 | + | ||
| 48 | +4. Redistribution. | ||
| 49 | + | ||
| 50 | +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: | ||
| 51 | +1.You must give any other recipients of the Work or Derivative Works a copy of this License; and | ||
| 52 | +2.You must cause any modified files to carry prominent notices stating that You changed the files; and | ||
| 53 | +3.You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and | ||
| 54 | +4.If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. | ||
| 55 | + | ||
| 56 | +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. | ||
| 57 | + | ||
| 58 | +5. Submission of Contributions. | ||
| 59 | + | ||
| 60 | +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. | ||
| 61 | + | ||
| 62 | +6. Trademarks. | ||
| 63 | + | ||
| 64 | +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. | ||
| 65 | + | ||
| 66 | +7. Disclaimer of Warranty. | ||
| 67 | + | ||
| 68 | +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. | ||
| 69 | + | ||
| 70 | +8. Limitation of Liability. | ||
| 71 | + | ||
| 72 | +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. | ||
| 73 | + | ||
| 74 | +9. Accepting Warranty or Additional Liability. | ||
| 75 | + | ||
| 76 | +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. | ||
| 77 | + | ||
| 78 | +END OF TERMS AND CONDITIONS | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | + | ||
| 2 | +<configuration> | ||
| 3 | + <oatconfig> | ||
| 4 | + <licensefile></licensefile> | ||
| 5 | + <policylist> | ||
| 6 | + <policy name="projectPolicy" desc=""> | ||
| 7 | + <!--policyitem type="compatibility" name="GPL-2.0+" path="abc/.*" desc="Process that runs independently, invoked by the X process."/--> | ||
| 8 | + <!--policyitem type="license" name="LGPL" path="abc/.*" desc="Dynamically linked by module X"/--> | ||
| 9 | + <!--policyitem type="copyright" name="xxx" path="abc/.*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc="Developed by X Company"/--> | ||
| 10 | + <policyitem type="license" name="CC-BY-4.0" path=".*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter" desc="License for documents"/> | ||
| 11 | + </policy> | ||
| 12 | + </policylist> | ||
| 13 | + <filefilterlist> | ||
| 14 | + <filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies"> | ||
| 15 | + <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/--> | ||
| 16 | + <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/--> | ||
| 17 | + <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/--> | ||
| 18 | + <filteritem type="filename" name="*.jpg|*.png|*.gif|*.pdf|*.xlsm|*.zip|*.ogg|*.mp3|*.jpeg|*.webp|*.glb" desc="already checked"/> | ||
| 19 | + </filefilter> | ||
| 20 | + <filefilter name="copyrightPolicyFilter" desc="Filters for copyright policies"> | ||
| 21 | + <filteritem type="filename" name="*.shader" desc="already checked"/> | ||
| 22 | + </filefilter> | ||
| 23 | + </filefilterlist> | ||
| 24 | + <licensematcherlist> | ||
| 25 | + <!--licensematcher name="uvwxyz License" desc="If the scanning result is InvalidLicense, you can define matching rules here. Note that quotation marks must be escaped."> | ||
| 26 | + <licensetext name=" | ||
| 27 | + uvwxyz license textA xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | ||
| 28 | + " desc=""/> | ||
| 29 | + <licensetext name=" | ||
| 30 | + uvwxyz license textB xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | ||
| 31 | + " desc=""/> | ||
| 32 | + </licensematcher--> | ||
| 33 | + </licensematcherlist> | ||
| 34 | + </oatconfig> | ||
| 35 | +</configuration> | ||
| @@ -65,6 +65,27 @@ provided by AR Engine. | |||
| 65 | 3. If you choose to add a local image, the image adding screen will be displayed. Otherwise, this step is skipped. | 65 | 3. If you choose to add a local image, the image adding screen will be displayed. Otherwise, this step is skipped. |
| 66 | 4. The tracking starts for the target image, which is framed in blue. | 66 | 4. The tracking starts for the target image, which is framed in blue. |
| 67 | 67 | ||
| 68 | +### ARFace preview | ||
| 69 | + | ||
| 70 | +| **App home screen** | **Recognizing and rendering faces** | | ||
| 71 | +|:-----------------------------:|:-----------------------------------:| | ||
| 72 | +|  |  | | ||
| 73 | + | ||
| 74 | +1. On the home screen of your phone, tap ARSampleArkts to start the app. The ARFace button is displayed on the main screen. | ||
| 75 | +2. Select ARFace. | ||
| 76 | +3. After entering the screen, you can scan your face. | ||
| 77 | +4. After the face is recognized, the face mesh can be rendered. | ||
| 78 | + | ||
| 79 | +### ARBody preview | ||
| 80 | + | ||
| 81 | +| **App home screen** | **Identify and render human skeletal points** | | ||
| 82 | +|:-----------------------------:|:-------------------------:| | ||
| 83 | +|  |  | | ||
| 84 | + | ||
| 85 | +1. On the home screen of your phone, tap ARSampleArkts to start the app. On the main screen, you can see the ARBody button. | ||
| 86 | +2. Tap ARBody and scan a human body. | ||
| 87 | +3. After the human body is recognized, the skeleton points of the human body can be rendered. | ||
| 88 | + | ||
| 68 | ## Usage Instructions | 89 | ## Usage Instructions |
| 69 | 90 | ||
| 70 | Developers can open this project using DevEco, sign it, and directly run it on a real device. | 91 | Developers can open this project using DevEco, sign it, and directly run it on a real device. |
| @@ -78,7 +99,9 @@ Developers can open this project using DevEco, sign it, and directly run it on a | |||
| 78 | │ ├──entryability | 99 | │ ├──entryability |
| 79 | │ │ └──EntryAbility.ets // Entry point class | 100 | │ │ └──EntryAbility.ets // Entry point class |
| 80 | │ ├──pages | 101 | │ ├──pages |
| 102 | +│ │ ├──ARBody.ets // ARBody screen | ||
| 81 | │ │ ├──ARDepth.ets // ARDepth screen | 103 | │ │ ├──ARDepth.ets // ARDepth screen |
| 104 | +│ │ ├──ARFace.ets // ARFace screen | ||
| 82 | │ │ ├──ARImage.ets // ARImage mode selection screen | 105 | │ │ ├──ARImage.ets // ARImage mode selection screen |
| 83 | │ │ ├──ARImageByAdd.ets // ARImage local image mode screen | 106 | │ │ ├──ARImageByAdd.ets // ARImage local image mode screen |
| 84 | │ │ ├──ARImageByDatabase.ets // ARImage local database mode screen | 107 | │ │ ├──ARImageByDatabase.ets // ARImage local database mode screen |
| @@ -100,43 +123,16 @@ Developers can open this project using DevEco, sign it, and directly run it on a | |||
| 100 | 1. Developers can use interface calls to determine whether the current device supports AR Engine. | 123 | 1. Developers can use interface calls to determine whether the current device supports AR Engine. |
| 101 | The interface is called in the following way: | 124 | The interface is called in the following way: |
| 102 | 125 | ||
| 103 | -```javascript | 126 | + ```typescript |
| 104 | -import { arViewController, ARView } from '@kit.AREngine'; | 127 | + import { arEngine, arViewController } from '@kit.AREngine'; |
| 105 | -import { BusinessError } from '@kit.BasicServicesKit'; | 128 | + |
| 106 | -@Component | 129 | + let ret: boolean = arViewController.isARTypeSupported(arEngine.ARFeatureType.ARENGINE_FEATURE_TYPE_FACE); |
| 107 | -struct ARTest { | 130 | + ``` |
| 108 | - @State arContext?: arViewController.ARViewContext = undefined; | 131 | + If the error code returned by the corresponding interface is 801, AR Engine does not support the current device. |
| 109 | - build() { | ||
| 110 | - NavDestination() { | ||
| 111 | - RelativeContainer() { | ||
| 112 | - if (this.arContext) { | ||
| 113 | - ARView({ context: this.arContext }) | ||
| 114 | - } | ||
| 115 | - } | ||
| 116 | - } | ||
| 117 | - .onAppear(() => { | ||
| 118 | - this.initARView() | ||
| 119 | - }) | ||
| 120 | - .onWillDisappear(() => { | ||
| 121 | - this.arContext?.destroy(); | ||
| 122 | - }) | ||
| 123 | - } | ||
| 124 | 132 | ||
| 125 | - private initARView(): void { | 133 | +2. The HarmonyOS version must be HarmonyOS 6.1.0 Release or later. |
| 126 | - let context = new arViewController.ARViewContext() | 134 | +3. The DevEco Studio version must be DevEco Studio 6.1.0 Release or later. |
| 127 | - context.init().then(() => { | 135 | +4. The HarmonyOS SDK version must be HarmonyOS 6.1.0 Release SDK or later. |
| 128 | - this.arContext = context; | ||
| 129 | - console.info(`Succeeded in initializing ARView.`); | ||
| 130 | - }).catch((err: BusinessError) => { | ||
| 131 | - console.error(`Failed to init context. Code is ${err.code}, message is ${err.message}`); | ||
| 132 | - }); | ||
| 133 | - } | ||
| 134 | -} | ||
| 135 | -``` | ||
| 136 | -If the error code returned by the corresponding interface is 801, AR Engine does not support the current device. | ||
| 137 | - | ||
| 138 | -2. It is recommended that DevEco Studio 5.1.0 Release or later be used. | ||
| 139 | -3. This sample is based on the stage model. It is recommended that HarmonyOS 5.1.0 Release SDK or later be used. | ||
| 140 | 136 | ||
| 141 | ## AR Engine Depth Estimation Technology Limitations and Disclaimer | 137 | ## AR Engine Depth Estimation Technology Limitations and Disclaimer |
| 142 | 138 | ||
| @@ -163,20 +159,6 @@ If the error code returned by the corresponding interface is 801, AR Engine does | |||
| 163 | device. It is not intended for use as medical equipment. It also hasn't been approved to meet accessibility or | 159 | device. It is not intended for use as medical equipment. It also hasn't been approved to meet accessibility or |
| 164 | life safety standards. | 160 | life safety standards. |
| 165 | 161 | ||
| 166 | -## Change log | ||
| 167 | - | ||
| 168 | -### Features of Version 1.0.0 Update | ||
| 169 | - | ||
| 170 | -1. **ARWorld** capability launched.<br> | ||
| 171 | - Plane recognition, collision detection, and object placement features are now available. | ||
| 172 | -2. **ARDepth** capability launched.<br> | ||
| 173 | - Depth estimation and depth map rendering features are now available. | ||
| 174 | -3. **ARMesh** capability launched.<br> | ||
| 175 | - Mesh scanning and mesh map rendering capabilities are now available. | ||
| 176 | -4. **ARImage** capability launched.<br> | ||
| 177 | - Image tracking capability is now available, enabling developers to detect environmental target feature points via | ||
| 178 | - local images. | ||
| 179 | - | ||
| 180 | ## Related documents | 162 | ## Related documents |
| 181 | 163 | ||
| 182 | [AREngine Development Guide](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ar-engine-kit-guide) | 164 | [AREngine Development Guide](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ar-engine-kit-guide) |
| @@ -52,6 +52,27 @@ AR Engine(以下统称AR引擎)助力应用融合虚拟世界与现实世界 | |||
| 52 | 3. 若选择添加图片,则弹出图片添加窗口,否则直接进入下一步。 | 52 | 3. 若选择添加图片,则弹出图片添加窗口,否则直接进入下一步。 |
| 53 | 4. 开始跟踪目标图片,屏幕上可看见蓝色线框框选的目标图片。 | 53 | 4. 开始跟踪目标图片,屏幕上可看见蓝色线框框选的目标图片。 |
| 54 | 54 | ||
| 55 | +### ARFace效果预览 | ||
| 56 | + | ||
| 57 | +| **应用首页** | **识别人脸并渲染** | | ||
| 58 | +|:-----------------------------:|:-----------------------------------:| | ||
| 59 | +|  |  | | ||
| 60 | + | ||
| 61 | +1. 在手机的主屏幕,点击“ARSampleArkts”,启动应用,在主界面可见“ARFace”按钮。 | ||
| 62 | +2. 选择ARFace。 | ||
| 63 | +3. 进入后可以对着人脸进行扫描。 | ||
| 64 | +4. 识别到人脸后可以渲染出人脸Mesh。 | ||
| 65 | + | ||
| 66 | +### ARBody效果预览 | ||
| 67 | + | ||
| 68 | +| **应用首页** | **识别并渲染人体骨骼点** | | ||
| 69 | +|:-----------------------------:|:-------------------------:| | ||
| 70 | +|  |  | | ||
| 71 | + | ||
| 72 | +1. 在手机的主屏幕,点击“ARSampleArkts”,启动应用,在主界面可见“ARBody”按钮。 | ||
| 73 | +2. 点击“ARBody”并扫描人体。 | ||
| 74 | +3. 识别到人体后可以渲染处人体骨骼点。 | ||
| 75 | + | ||
| 55 | ## 使用说明 | 76 | ## 使用说明 |
| 56 | 77 | ||
| 57 | 开发者使用DevEco Studio打开本工程,签名后直接在真机设备上运行即可。 | 78 | 开发者使用DevEco Studio打开本工程,签名后直接在真机设备上运行即可。 |
| @@ -65,7 +86,9 @@ AR Engine(以下统称AR引擎)助力应用融合虚拟世界与现实世界 | |||
| 65 | │ ├──entryability | 86 | │ ├──entryability |
| 66 | │ │ └──EntryAbility.ets // 程序入口类 | 87 | │ │ └──EntryAbility.ets // 程序入口类 |
| 67 | │ ├──pages | 88 | │ ├──pages |
| 89 | +│ │ ├──ARBody.ets // ARBody界面 | ||
| 68 | │ │ ├──ARDepth.ets // ARDepth选择模式界面 | 90 | │ │ ├──ARDepth.ets // ARDepth选择模式界面 |
| 91 | +│ │ ├──ARFace.ets // ARFace界面 | ||
| 69 | │ │ ├──ARImage.ets // ARImage选择模式界面 | 92 | │ │ ├──ARImage.ets // ARImage选择模式界面 |
| 70 | │ │ ├──ARImageByAdd.ets // ARImage本地图片模式界面 | 93 | │ │ ├──ARImageByAdd.ets // ARImage本地图片模式界面 |
| 71 | │ │ ├──ARImageByDatabase.ets // ARImage本地数据库模式界面 | 94 | │ │ ├──ARImageByDatabase.ets // ARImage本地数据库模式界面 |
| @@ -86,43 +109,15 @@ AR Engine(以下统称AR引擎)助力应用融合虚拟世界与现实世界 | |||
| 86 | 109 | ||
| 87 | 1. 开发者可通过接口调用判断当前设备是否支持AR Engine。接口的调用参考方式如下: | 110 | 1. 开发者可通过接口调用判断当前设备是否支持AR Engine。接口的调用参考方式如下: |
| 88 | 111 | ||
| 89 | -```javascript | 112 | + ```typescript |
| 90 | -import { arViewController, ARView } from '@kit.AREngine'; | 113 | + import { arEngine, arViewController } from '@kit.AREngine'; |
| 91 | -import { BusinessError } from '@kit.BasicServicesKit'; | 114 | + |
| 92 | -@Component | 115 | + let ret: boolean = arViewController.isARTypeSupported(arEngine.ARFeatureType.ARENGINE_FEATURE_TYPE_FACE); |
| 93 | -struct ARTest { | 116 | + ``` |
| 94 | - @State arContext?: arViewController.ARViewContext = undefined; | 117 | + 若对应接口返回错误码为ARENGINE_ERROR_DEVICE_NOT_SUPPORTED,则表示AR Engine不支持当前设备。 |
| 95 | - build() { | 118 | +2. HarmonyOS系统:6.1.0 Release及以上。 |
| 96 | - NavDestination() { | 119 | +3. DevEco Studio版本:DevEco Studio 6.1.0 Release及以上。 |
| 97 | - RelativeContainer() { | 120 | +4. HarmonyOS SDK版本:推荐HarmonyOS 6.1.0 Release SDK及以上。 |
| 98 | - if (this.arContext) { | ||
| 99 | - ARView({ context: this.arContext }) | ||
| 100 | - } | ||
| 101 | - } | ||
| 102 | - } | ||
| 103 | - .onAppear(() => { | ||
| 104 | - this.initARView() | ||
| 105 | - }) | ||
| 106 | - .onWillDisappear(() => { | ||
| 107 | - this.arContext?.destroy(); | ||
| 108 | - }) | ||
| 109 | - } | ||
| 110 | - | ||
| 111 | - private initARView(): void { | ||
| 112 | - let context = new arViewController.ARViewContext() | ||
| 113 | - context.init().then(() => { | ||
| 114 | - this.arContext = context; | ||
| 115 | - console.info(`Succeeded in initializing ARView.`); | ||
| 116 | - }).catch((err: BusinessError) => { | ||
| 117 | - console.error(`Failed to init context. Code is ${err.code}, message is ${err.message}`); | ||
| 118 | - }); | ||
| 119 | - } | ||
| 120 | -} | ||
| 121 | -``` | ||
| 122 | -若对应接口返回错误码为801,则表示AR Engine不支持当前设备。 | ||
| 123 | - | ||
| 124 | -2. 本实例支持的DevEco Studio版本:推荐DevEco Studio 5.1.0 Release及以上。 | ||
| 125 | -3. 本实例为Stage模型,支持的HarmonyOS SDK版本:推荐HarmonyOS 5.1.0 Release SDK及以上。 | ||
| 126 | 121 | ||
| 127 | ## AR Engine深度估计功能技术局限性及免责声明 | 122 | ## AR Engine深度估计功能技术局限性及免责声明 |
| 128 | 123 | ||
| @@ -140,18 +135,52 @@ struct ARTest { | |||
| 140 | 1. 不作为医疗设备或生命安全系统设计。 | 135 | 1. 不作为医疗设备或生命安全系统设计。 |
| 141 | 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。 | 136 | 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。 |
| 142 | 137 | ||
| 143 | -## 更新日志 | 138 | +## AR Engine高精几何重建功能技术局限性及免责声明 |
| 144 | 139 | ||
| 145 | -### 1.0.0版本更新功能介绍 | 140 | +1. 技术局限性声明:本功能提供的能力,涉及几何数值精度可能受以下因素影响: |
| 141 | + 1. 环境光照条件(例如强光/弱光/反光场景)。 | ||
| 142 | + 2. 物体表面材质特性(例如透明/镜面/单色物体)。 | ||
| 143 | + 3. 设备硬件性能差异(例如摄像头/传感器参数)。 | ||
| 144 | + 4. 动态场景下的实时性限制等。 | ||
| 145 | +2. 免责声明: | ||
| 146 | + 本高精几何重建仅提供功能,并不构成对产品的质量保证或任何承诺。开发者自主选择是否使用HarmonyOS提供的功能开发应用软件,开发者对应用软件的使用目的、 | ||
| 147 | + 效果和责任自行负责。若开发用于视障人士避障、残疾人辅助等场景的应用软件,开发者应承担开展多场景压力测试,并建立数据校验机制,特别在安全相关场景需 | ||
| 148 | + 部署冗余保障方案,并确保应用合法合规的开发和运营。HarmonyOS不承担由此产生的任何直接或间接责任。 | ||
| 146 | 149 | ||
| 147 | -1. ARWorld能力上线。<br> | 150 | + 高精几何重建提供的功能: |
| 148 | - 平面识别、碰撞检测、物体摆放功能上线。 | 151 | + 1. 不作为医疗设备或生命安全系统设计。 |
| 149 | -2. ARDepth能力上线。<br> | 152 | + 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。 |
| 150 | - 深度估计、深度图渲染功能上线。 | 153 | + |
| 151 | -3. ARMesh能力上线。<br> | 154 | +## AR Engine人脸跟踪功能技术局限性及免责声明 |
| 152 | - 网格扫描、网格图绘制能力上线。 | 155 | + |
| 153 | -4. ARImage能力上线。<br> | 156 | +1. 技术局限性声明:本功能提供的能力,涉及人脸跟踪精度可能受以下因素影响: |
| 154 | - 图像跟踪能力上线,开发者可通过本地图片检测环境目标特征点。 | 157 | + |
| 158 | + 1. 环境光照条件(例如强光/弱光/反光场景)。 | ||
| 159 | + 2. 物体表面材质特性(例如透明/镜面/单色物体)。 | ||
| 160 | + 3. 设备硬件性能差异(例如摄像头/传感器参数)。 | ||
| 161 | + 4. 动态场景下的实时性限制等。 | ||
| 162 | +2. 免责声明: | ||
| 163 | + 本人脸跟踪仅提供功能,并不构成对产品的质量保证或任何承诺。开发者自主选择是否使用HarmonyOS提供的功能开发应用软件,开发者对应用软件的使用目的、 | ||
| 164 | + 效果和责任自行负责。若开发用于视障人士避障、残疾人辅助等场景的应用软件,开发者应承担开展多场景压力测试,并建立数据校验机制,特别在安全相关场景需 | ||
| 165 | + 部署冗余保障方案,并确保应用合法合规的开发和运营。HarmonyOS不承担由此产生的任何直接或间接责任。 | ||
| 166 | + | ||
| 167 | + 人脸跟踪提供的功能: | ||
| 168 | + 1. 不作为医疗设备或生命安全系统设计。 | ||
| 169 | + 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。 | ||
| 170 | + | ||
| 171 | +## AR Engine人体跟踪与骨骼关键点识别功能技术局限性及免责声明 | ||
| 172 | +1. 技术局限性声明:本功能提供的能力,涉及人体跟踪与骨骼关键点识别精度可能受以下因素影响: | ||
| 173 | + | ||
| 174 | + 1. 环境光照条件(例如强光/弱光/反光场景)。 | ||
| 175 | + 2. 物体表面材质特性(例如透明/镜面/单色物体)。 | ||
| 176 | + 3. 设备硬件性能差异(例如摄像头/传感器参数)。 | ||
| 177 | + 4. 动态场景下的实时性限制等。 | ||
| 178 | +2. 免责声明: | ||
| 179 | + 本人体跟踪与骨骼关键点识别仅提供功能,并不构成对产品的质量保证或任何承诺。开发者自主选择是否使用HarmonyOS提供的功能开发应用软件,开发者对应用软件的使用目的、效果和责任自行负责。若开发用于视障人士避障、残疾人辅助等场景的应用软件,开发者应承担开展多场景压力测试,并建立数据校验机制,特别在安全相关场景需部署冗余保障方案,并确保应用合法合规的开发和运营。HarmonyOS不承担由此产生的任何直接或间接责任。 | ||
| 180 | + | ||
| 181 | + 人体跟踪与骨骼关键点识别提供的功能: | ||
| 182 | + 1. 不作为医疗设备或生命安全系统设计。 | ||
| 183 | + 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。 | ||
| 155 | 184 | ||
| 156 | ## 相关文档 | 185 | ## 相关文档 |
| 157 | 186 | ||
| @@ -6,9 +6,9 @@ | |||
| 6 | { | 6 | { |
| 7 | "name": "default", | 7 | "name": "default", |
| 8 | "signingConfig": "default", | 8 | "signingConfig": "default", |
| 9 | - "compatibleSdkVersion": "6.0.0(20)", | 9 | + "compatibleSdkVersion": "6.1.0(23)", |
| 10 | "runtimeOS": "HarmonyOS", | 10 | "runtimeOS": "HarmonyOS", |
| 11 | - "targetSdkVersion": "6.0.0(20)" | 11 | + "targetSdkVersion": "6.1.0(23)" |
| 12 | } | 12 | } |
| 13 | ], | 13 | ], |
| 14 | "buildModeSet": [ | 14 | "buildModeSet": [ |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -13,10 +13,10 @@ | |||
| 13 | * limitations under the License. | 13 | * limitations under the License. |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | -import { window } from '@kit.ArkUI'; | ||
| 17 | import { ConfigurationConstant, UIAbility } from '@kit.AbilityKit'; | 16 | import { ConfigurationConstant, UIAbility } from '@kit.AbilityKit'; |
| 18 | -import { Logger } from '../utils/Logger'; | 17 | +import { window } from '@kit.ArkUI'; |
| 19 | import { BusinessError } from '@kit.BasicServicesKit'; | 18 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 19 | +import { logger } from '../utils/Logger'; | ||
| 20 | 20 | ||
| 21 | export default class EntryAbility extends UIAbility { | 21 | export default class EntryAbility extends UIAbility { |
| 22 | onCreate(): void { | 22 | onCreate(): void { |
| @@ -24,40 +24,40 @@ export default class EntryAbility extends UIAbility { | |||
| 24 | this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); | 24 | this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); |
| 25 | } catch (error) { | 25 | } catch (error) { |
| 26 | const err: BusinessError = error as BusinessError; | 26 | const err: BusinessError = error as BusinessError; |
| 27 | - Logger.error(`Failed to getApplicationContext. Code is ${err.code}, message is ${err.message}`); | 27 | + logger.error(`Failed to getApplicationContext. Code is ${err.code}, message is ${err.message}`); |
| 28 | } | 28 | } |
| 29 | - Logger.info('Ability onCreate.'); | 29 | + logger.info('Ability onCreate.'); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | onDestroy(): void { | 32 | onDestroy(): void { |
| 33 | - Logger.info('Ability onDestroy.'); | 33 | + logger.info('Ability onDestroy.'); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | onWindowStageCreate(windowStage: window.WindowStage): void { | 36 | onWindowStageCreate(windowStage: window.WindowStage): void { |
| 37 | // Main window is created, set main page for this ability | 37 | // Main window is created, set main page for this ability |
| 38 | - Logger.info('Ability onWindowStageCreate.'); | 38 | + logger.info('Ability onWindowStageCreate.'); |
| 39 | 39 | ||
| 40 | windowStage.loadContent('pages/Selector', (err) => { | 40 | windowStage.loadContent('pages/Selector', (err) => { |
| 41 | if (err.code) { | 41 | if (err.code) { |
| 42 | - Logger.error(`Failed to load the content. Code: ${err.code}, message: ${err.message}.`); | 42 | + logger.error(`Failed to load the content. Code: ${err.code}, message: ${err.message}.`); |
| 43 | return; | 43 | return; |
| 44 | } | 44 | } |
| 45 | - Logger.info('Succeeded in loading the content.'); | 45 | + logger.info('Succeeded in loading the content.'); |
| 46 | }) | 46 | }) |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | onWindowStageDestroy(): void { | 49 | onWindowStageDestroy(): void { |
| 50 | // Main window is destroyed, release UI related resources | 50 | // Main window is destroyed, release UI related resources |
| 51 | - Logger.info('Ability onWindowStageDestroy.'); | 51 | + logger.info('Ability onWindowStageDestroy.'); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | onForeground(): void { | 54 | onForeground(): void { |
| 55 | // Ability has brought to foreground | 55 | // Ability has brought to foreground |
| 56 | - Logger.info('Ability onForeground.'); | 56 | + logger.info('Ability onForeground.'); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | onBackground(): void { | 59 | onBackground(): void { |
| 60 | // Ability has back to background | 60 | // Ability has back to background |
| 61 | - Logger.info('Ability onBackground.'); | 61 | + logger.info('Ability onBackground.'); |
| 62 | } | 62 | } |
| 63 | } | 63 | } |
| @@ -0,0 +1,302 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +import { arEngine, ARView, arViewController } from '@kit.AREngine'; | ||
| 17 | +import { Node, Scene } from '@kit.ArkGraphics3D'; | ||
| 18 | +import { display } from '@kit.ArkUI'; | ||
| 19 | +import { BusinessError } from '@kit.BasicServicesKit'; | ||
| 20 | +import { logger } from '../utils/Logger'; | ||
| 21 | + | ||
| 22 | +interface BodyInfo { | ||
| 23 | + trackId: number, | ||
| 24 | + landmarks: arEngine.ARBodyLandmark2D[] | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +function arLandmarksToMap( | ||
| 28 | + arLandmarks: arEngine.ARBodyLandmark2D[]): Map<arEngine.ARBodyLandmarkType, arEngine.ARBodyLandmark2D> { | ||
| 29 | + let typeToLandmarks: Map<arEngine.ARBodyLandmarkType, arEngine.ARBodyLandmark2D> = new Map(); | ||
| 30 | + for (let arLandmark of arLandmarks) { | ||
| 31 | + typeToLandmarks.set(arLandmark.type, arLandmark); | ||
| 32 | + } | ||
| 33 | + return typeToLandmarks; | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +type OnBodyInfoCallback = (bodyInfo: arEngine.ARBody[]) => void; | ||
| 37 | + | ||
| 38 | +const DEFAULT_WIDTH: number = 1440; | ||
| 39 | +const DEFAULT_HEIGHT: number = 1080; | ||
| 40 | +const DEFAULT_CONVERT_FACTOR: number = 4 / 3; | ||
| 41 | +const FULL_SCREEN_SIZE: string = '100%'; | ||
| 42 | + | ||
| 43 | +class ARViewCallbackImpl extends arViewController.ARViewCallback { | ||
| 44 | + private callback?: OnBodyInfoCallback; | ||
| 45 | + | ||
| 46 | + setCallback(cb: OnBodyInfoCallback) { | ||
| 47 | + this.callback = cb; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void { | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + onAnchorUpdate(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void { | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + async onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): Promise<void> { | ||
| 57 | + if (!ctx.session) { | ||
| 58 | + return; | ||
| 59 | + } | ||
| 60 | + logger.info('onFrameUpdate enter'); | ||
| 61 | + const arSession: arEngine.ARSession = ctx.session; | ||
| 62 | + try { | ||
| 63 | + const frame: arEngine.ARFrame = arSession.getFrame(); | ||
| 64 | + const bodies: arEngine.ARBody[] = frame.acquireBodySkeleton(); | ||
| 65 | + this.callback ? this.callback(bodies) : logger.error(`callback not set`); | ||
| 66 | + } catch (error) { | ||
| 67 | + const err: BusinessError = error as BusinessError; | ||
| 68 | + logger.error(`Failed to update data. Code is ${err.code}, message is ${err.message}.`); | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +@Builder | ||
| 74 | +export function ARBodyBuilder() { | ||
| 75 | + ARBody(); | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +@ComponentV2 | ||
| 79 | +struct ARBody { | ||
| 80 | + @Local arContext?: arViewController.ARViewContext = undefined; | ||
| 81 | + @Local bodyInfos: BodyInfo[] = []; | ||
| 82 | + @Local displayWidth: number = DEFAULT_WIDTH; | ||
| 83 | + @Local displayHeight: number = DEFAULT_HEIGHT; | ||
| 84 | + private uiContext: UIContext = this.getUIContext(); | ||
| 85 | + | ||
| 86 | + build() { | ||
| 87 | + NavDestination() { | ||
| 88 | + Stack() { | ||
| 89 | + Column() { | ||
| 90 | + RelativeContainer() { | ||
| 91 | + if (this.arContext) { | ||
| 92 | + ARView({ context: this.arContext }) | ||
| 93 | + .height(FULL_SCREEN_SIZE) | ||
| 94 | + .width(FULL_SCREEN_SIZE) | ||
| 95 | + .alignRules({ | ||
| 96 | + center: { anchor: '__container__', align: VerticalAlign.Center }, | ||
| 97 | + middle: { anchor: '__container__', align: HorizontalAlign.Center } | ||
| 98 | + }) | ||
| 99 | + } | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + .width(this.uiContext.px2vp(this.displayWidth)) | ||
| 103 | + .height(this.uiContext.px2vp(this.displayHeight)) | ||
| 104 | + .justifyContent(FlexAlign.Center) | ||
| 105 | + .alignItems(HorizontalAlign.Center) | ||
| 106 | + | ||
| 107 | + this.drawBodyPerception() | ||
| 108 | + } | ||
| 109 | + .width(FULL_SCREEN_SIZE) | ||
| 110 | + .height(FULL_SCREEN_SIZE) | ||
| 111 | + } | ||
| 112 | + .onAppear(() => { | ||
| 113 | + this.initARView(); | ||
| 114 | + }) | ||
| 115 | + .onWillDisappear(() => { | ||
| 116 | + this.stopARView(); | ||
| 117 | + }) | ||
| 118 | + .onShown(() => { | ||
| 119 | + this.resumeARView(); | ||
| 120 | + }) | ||
| 121 | + .onHidden(() => { | ||
| 122 | + this.pauseARView(); | ||
| 123 | + }) | ||
| 124 | + .hideTitleBar(true) | ||
| 125 | + .hideBackButton(true) | ||
| 126 | + .hideToolBar(true) | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + @Builder | ||
| 130 | + drawBodyPerception() { | ||
| 131 | + Shape() { | ||
| 132 | + ForEach(this.bodyInfos, (bodyInfo: BodyInfo) => { | ||
| 133 | + this.drawBodyBones(arLandmarksToMap(bodyInfo.landmarks)); | ||
| 134 | + this.drawBodyLandmarks(bodyInfo.landmarks); | ||
| 135 | + }) | ||
| 136 | + } | ||
| 137 | + .width(this.uiContext.px2vp(this.displayWidth)) | ||
| 138 | + .height(this.uiContext.px2vp(this.displayHeight)) | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + @Builder | ||
| 142 | + drawBodyLandmarks(bodyLandmarks: arEngine.ARBodyLandmark2D[]) { | ||
| 143 | + ForEach(bodyLandmarks, (landmark: arEngine.ARBodyLandmark2D) => { | ||
| 144 | + Circle({ width: 4, height: 4 }) | ||
| 145 | + .position({ | ||
| 146 | + x: this.uiContext.px2vp(landmark.x), | ||
| 147 | + y: this.uiContext.px2vp(landmark.y) | ||
| 148 | + }) | ||
| 149 | + .fillOpacity(1) | ||
| 150 | + .fill(Color.White) | ||
| 151 | + }) | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + @Builder | ||
| 155 | + drawBodyBones(bodyLandmarks: Map<arEngine.ARBodyLandmarkType, arEngine.ARBodyLandmark2D>) { | ||
| 156 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.NOSE, arEngine.ARBodyLandmarkType.LEFT_SHOULDER, | ||
| 157 | + Color.Orange); | ||
| 158 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.NOSE, arEngine.ARBodyLandmarkType.RIGHT_SHOULDER, | ||
| 159 | + Color.Orange); | ||
| 160 | + | ||
| 161 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_SHOULDER, | ||
| 162 | + arEngine.ARBodyLandmarkType.RIGHT_SHOULDER, Color.Orange); | ||
| 163 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_SHOULDER, | ||
| 164 | + arEngine.ARBodyLandmarkType.RIGHT_HIP, Color.Orange); | ||
| 165 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_HIP, arEngine.ARBodyLandmarkType.LEFT_HIP, | ||
| 166 | + Color.Orange); | ||
| 167 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_HIP, | ||
| 168 | + arEngine.ARBodyLandmarkType.LEFT_SHOULDER, Color.Orange); | ||
| 169 | + | ||
| 170 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_SHOULDER, | ||
| 171 | + arEngine.ARBodyLandmarkType.LEFT_ELBOW, Color.Green); | ||
| 172 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_ELBOW, arEngine.ARBodyLandmarkType.LEFT_WRIST, | ||
| 173 | + Color.Green); | ||
| 174 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_HIP, arEngine.ARBodyLandmarkType.LEFT_KNEE, | ||
| 175 | + Color.Green); | ||
| 176 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_KNEE, arEngine.ARBodyLandmarkType.LEFT_ANKLE, | ||
| 177 | + Color.Green); | ||
| 178 | + | ||
| 179 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_SHOULDER, | ||
| 180 | + arEngine.ARBodyLandmarkType.RIGHT_ELBOW, Color.Blue); | ||
| 181 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_ELBOW, | ||
| 182 | + arEngine.ARBodyLandmarkType.RIGHT_WRIST, Color.Blue); | ||
| 183 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_HIP, arEngine.ARBodyLandmarkType.RIGHT_KNEE, | ||
| 184 | + Color.Blue); | ||
| 185 | + this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_KNEE, | ||
| 186 | + arEngine.ARBodyLandmarkType.RIGHT_ANKLE, Color.Blue); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + @Builder | ||
| 190 | + drawBodyBoneLine(bodyLandmarks: Map<arEngine.ARBodyLandmarkType, arEngine.ARBodyLandmark2D>, | ||
| 191 | + start: arEngine.ARBodyLandmarkType, end: arEngine.ARBodyLandmarkType, color: Color) { | ||
| 192 | + if (bodyLandmarks.has(start) && bodyLandmarks.has(end)) { | ||
| 193 | + Line() | ||
| 194 | + .startPoint([this.uiContext.px2vp(bodyLandmarks.get(start)?.x), | ||
| 195 | + this.uiContext.px2vp(bodyLandmarks.get(start)?.y)]) | ||
| 196 | + .endPoint([this.uiContext.px2vp(bodyLandmarks.get(end)?.x), this.uiContext.px2vp(bodyLandmarks.get(end)?.y)]) | ||
| 197 | + .stroke(color) | ||
| 198 | + .strokeWidth(3) | ||
| 199 | + } | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + private onBodyInfoCb: OnBodyInfoCallback = (bodyInfos: arEngine.ARBody[]) => { | ||
| 203 | + let preWidth = this.displayWidth; | ||
| 204 | + let preHeight = this.displayHeight; | ||
| 205 | + try { | ||
| 206 | + if (display.getDefaultDisplaySync().availableWidth < display.getDefaultDisplaySync().availableHeight) { | ||
| 207 | + this.displayWidth = display.getDefaultDisplaySync().availableWidth; | ||
| 208 | + this.displayHeight = this.displayWidth * DEFAULT_CONVERT_FACTOR; | ||
| 209 | + } else { | ||
| 210 | + this.displayHeight = display.getDefaultDisplaySync().availableHeight; | ||
| 211 | + this.displayWidth = this.displayHeight * DEFAULT_CONVERT_FACTOR; | ||
| 212 | + } | ||
| 213 | + } catch (e) { | ||
| 214 | + logger.error(`display.getDefaultDisplaySync failed error.message:${e?.message}, error.code:${e?.code}`); | ||
| 215 | + this.displayWidth = preWidth; | ||
| 216 | + this.displayHeight = preHeight; | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + this.bodyInfos = bodyInfos.map((value: arEngine.ARBody) => { | ||
| 220 | + let landmarks: arEngine.ARBodyLandmark2D[] = []; | ||
| 221 | + try { | ||
| 222 | + landmarks = value.getLandmarks2D(); | ||
| 223 | + } catch (error) { | ||
| 224 | + const err: BusinessError = error as BusinessError; | ||
| 225 | + logger.error(`Failed to get Landmarks2D. Code is ${err.code}, message is ${err.message}`); | ||
| 226 | + } | ||
| 227 | + landmarks.forEach((value: arEngine.ARBodyLandmark2D) => { | ||
| 228 | + value.x = value.x * this.displayWidth; | ||
| 229 | + value.y = value.y * this.displayHeight; | ||
| 230 | + }) | ||
| 231 | + let info: BodyInfo = { | ||
| 232 | + trackId: value.trackId, | ||
| 233 | + landmarks: landmarks | ||
| 234 | + } | ||
| 235 | + return info; | ||
| 236 | + }) | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + private async initARView(): Promise<void> { | ||
| 240 | + Scene.load().then(async (scene: Scene) => { | ||
| 241 | + let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext(); | ||
| 242 | + viewContext.scene = scene; | ||
| 243 | + let callback = new ARViewCallbackImpl(); | ||
| 244 | + callback.setCallback(this.onBodyInfoCb); | ||
| 245 | + viewContext.callback = callback; | ||
| 246 | + viewContext.config = { | ||
| 247 | + type: arEngine.ARType.BODY, | ||
| 248 | + planeFindingMode: arEngine.ARPlaneFindingMode.DISABLED, | ||
| 249 | + powerMode: arEngine.ARPowerMode.NORMAL, | ||
| 250 | + semanticMode: arEngine.ARSemanticMode.NONE, | ||
| 251 | + poseMode: arEngine.ARPoseMode.GRAVITY, | ||
| 252 | + depthMode: arEngine.ARDepthMode.DISABLED, | ||
| 253 | + meshMode: arEngine.ARMeshMode.DISABLED, | ||
| 254 | + focusMode: arEngine.ARFocusMode.AUTO, | ||
| 255 | + maxDetectedBodyNum: 1, | ||
| 256 | + cameraLensFacing: 0 | ||
| 257 | + }; | ||
| 258 | + viewContext.init().then(() => { | ||
| 259 | + this.arContext = viewContext; | ||
| 260 | + logger.info('Succeeded in initializing ARView.'); | ||
| 261 | + }).catch((err: BusinessError) => { | ||
| 262 | + logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); | ||
| 263 | + }) | ||
| 264 | + }) | ||
| 265 | + } | ||
| 266 | + | ||
| 267 | + private async stopARView(): Promise<void> { | ||
| 268 | + if (!this.arContext) { | ||
| 269 | + return; | ||
| 270 | + } | ||
| 271 | + try { | ||
| 272 | + await this.arContext.destroy(); | ||
| 273 | + } catch (error) { | ||
| 274 | + const err: BusinessError = error as BusinessError; | ||
| 275 | + logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); | ||
| 276 | + } | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + private pauseARView(): void { | ||
| 280 | + if (!this.arContext) { | ||
| 281 | + return; | ||
| 282 | + } | ||
| 283 | + try { | ||
| 284 | + this.arContext.pause(); | ||
| 285 | + } catch (error) { | ||
| 286 | + const err: BusinessError = error as BusinessError; | ||
| 287 | + logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); | ||
| 288 | + } | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + private resumeARView(): void { | ||
| 292 | + if (!this.arContext) { | ||
| 293 | + return; | ||
| 294 | + } | ||
| 295 | + try { | ||
| 296 | + this.arContext.resume(); | ||
| 297 | + } catch (error) { | ||
| 298 | + const err: BusinessError = error as BusinessError; | ||
| 299 | + logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); | ||
| 300 | + } | ||
| 301 | + } | ||
| 302 | +} | ||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -16,7 +16,7 @@ | |||
| 16 | import { arEngine, ARView, arViewController } from '@kit.AREngine'; | 16 | import { arEngine, ARView, arViewController } from '@kit.AREngine'; |
| 17 | import { Node, Scene } from '@kit.ArkGraphics3D'; | 17 | import { Node, Scene } from '@kit.ArkGraphics3D'; |
| 18 | import { BusinessError } from '@kit.BasicServicesKit'; | 18 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 19 | -import { Logger } from '../utils/Logger'; | 19 | +import { logger } from '../utils/Logger'; |
| 20 | import { arrayBufferInt32ToNumber } from '../utils/Utils'; | 20 | import { arrayBufferInt32ToNumber } from '../utils/Utils'; |
| 21 | 21 | ||
| 22 | @Builder | 22 | @Builder |
| @@ -29,11 +29,11 @@ let centerConfidence: number; | |||
| 29 | 29 | ||
| 30 | @Component | 30 | @Component |
| 31 | struct ARDepth { | 31 | struct ARDepth { |
| 32 | - private delayInterval: number = 33; | ||
| 33 | - private intervalId: number = -1; | ||
| 34 | @State arContext?: arViewController.ARViewContext = undefined; | 32 | @State arContext?: arViewController.ARViewContext = undefined; |
| 35 | @State depthConfidence: number = 0; | 33 | @State depthConfidence: number = 0; |
| 36 | @State depthDistance: string = '0'; | 34 | @State depthDistance: string = '0'; |
| 35 | + private delayInterval: number = 33; | ||
| 36 | + private intervalId: number = -1; | ||
| 37 | 37 | ||
| 38 | build(): void { | 38 | build(): void { |
| 39 | NavDestination() { | 39 | NavDestination() { |
| @@ -109,9 +109,9 @@ struct ARDepth { | |||
| 109 | } | 109 | } |
| 110 | viewContext.init().then(() => { | 110 | viewContext.init().then(() => { |
| 111 | this.arContext = viewContext; | 111 | this.arContext = viewContext; |
| 112 | - Logger.info('Succeeded in initting ARView.'); | 112 | + logger.info('Succeeded in initting ARView.'); |
| 113 | }).catch((err: BusinessError) => { | 113 | }).catch((err: BusinessError) => { |
| 114 | - Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}`); | 114 | + logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}`); |
| 115 | }) | 115 | }) |
| 116 | }) | 116 | }) |
| 117 | } | 117 | } |
| @@ -126,19 +126,22 @@ struct ARDepth { | |||
| 126 | }, this.delayInterval) | 126 | }, this.delayInterval) |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | - private stopARView(): void { | 129 | + private async stopARView(): Promise<void> { |
| 130 | if (!this.arContext) { | 130 | if (!this.arContext) { |
| 131 | return; | 131 | return; |
| 132 | } | 132 | } |
| 133 | + | ||
| 134 | + clearInterval(this.intervalId); | ||
| 135 | + | ||
| 133 | try { | 136 | try { |
| 134 | - clearInterval(this.intervalId); | 137 | + await this.arContext.destroy(); |
| 135 | - this.arContext.destroy(); | ||
| 136 | - centerDistance = 0; | ||
| 137 | - centerConfidence = 0; | ||
| 138 | } catch (error) { | 138 | } catch (error) { |
| 139 | const err: BusinessError = error as BusinessError; | 139 | const err: BusinessError = error as BusinessError; |
| 140 | - Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); | 140 | + logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); |
| 141 | } | 141 | } |
| 142 | + | ||
| 143 | + centerDistance = 0; | ||
| 144 | + centerConfidence = 0; | ||
| 142 | } | 145 | } |
| 143 | 146 | ||
| 144 | private resumeARView(): void { | 147 | private resumeARView(): void { |
| @@ -149,7 +152,7 @@ struct ARDepth { | |||
| 149 | this.arContext.resume(); | 152 | this.arContext.resume(); |
| 150 | } catch (error) { | 153 | } catch (error) { |
| 151 | const err: BusinessError = error as BusinessError; | 154 | const err: BusinessError = error as BusinessError; |
| 152 | - Logger.error(`Failed to resume ARView. Code is ${err.code}, message is ${err.message}`); | 155 | + logger.error(`Failed to resume ARView. Code is ${err.code}, message is ${err.message}`); |
| 153 | } | 156 | } |
| 154 | } | 157 | } |
| 155 | 158 | ||
| @@ -161,7 +164,7 @@ struct ARDepth { | |||
| 161 | this.arContext.pause(); | 164 | this.arContext.pause(); |
| 162 | } catch (error) { | 165 | } catch (error) { |
| 163 | const err: BusinessError = error as BusinessError; | 166 | const err: BusinessError = error as BusinessError; |
| 164 | - Logger.error(`Failed to pause ARView. Code is ${err.code}, message is ${err.message}`); | 167 | + logger.error(`Failed to pause ARView. Code is ${err.code}, message is ${err.message}`); |
| 165 | } | 168 | } |
| 166 | } | 169 | } |
| 167 | } | 170 | } |
| @@ -178,7 +181,7 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 178 | return; | 181 | return; |
| 179 | } | 182 | } |
| 180 | 183 | ||
| 181 | - let session: arEngine.ARSession | undefined = ctx.session; | 184 | + let session: arEngine.ARSession = ctx.session; |
| 182 | 185 | ||
| 183 | try { | 186 | try { |
| 184 | let frame: arEngine.ARFrame = session.getFrame(); | 187 | let frame: arEngine.ARFrame = session.getFrame(); |
| @@ -192,7 +195,7 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 192 | centerConfidence = confidencePlane[index]; | 195 | centerConfidence = confidencePlane[index]; |
| 193 | } catch (error) { | 196 | } catch (error) { |
| 194 | const err: BusinessError = error as BusinessError; | 197 | const err: BusinessError = error as BusinessError; |
| 195 | - Logger.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}`); | 198 | + logger.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}`); |
| 196 | } | 199 | } |
| 197 | } | 200 | } |
| 198 | } | 201 | } |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -14,28 +14,56 @@ | |||
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | import { arEngine, ARView, arViewController } from '@kit.AREngine'; | 16 | import { arEngine, ARView, arViewController } from '@kit.AREngine'; |
| 17 | -import { Node, Scene } from '@kit.ArkGraphics3D'; | 17 | +import { |
| 18 | + CustomGeometry, | ||
| 19 | + Geometry, | ||
| 20 | + Material, | ||
| 21 | + MaterialType, | ||
| 22 | + MeshResource, | ||
| 23 | + Node, | ||
| 24 | + PrimitiveTopology, | ||
| 25 | + Scene, | ||
| 26 | + SceneResourceFactory, | ||
| 27 | + Shader, | ||
| 28 | + ShaderMaterial, | ||
| 29 | + Vec3 | ||
| 30 | +} from '@kit.ArkGraphics3D'; | ||
| 18 | import { BusinessError } from '@kit.BasicServicesKit'; | 31 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 19 | -import { Logger } from '../utils/Logger'; | 32 | +import { logger } from '../utils/Logger'; |
| 20 | - | 33 | +import { |
| 21 | -let cubeVertexData: Array<number>; | 34 | + arrayBufferFloat32ToNumber, |
| 22 | -let cubeConfidence: number; | 35 | + arrayBufferInt32ToNumber, |
| 23 | -let cubeLabel: arEngine.ARSemanticPlaneLabel; | 36 | + generateFaceMeshIndex, |
| 37 | + generateMeshInput, | ||
| 38 | + getFaceFrontVertices | ||
| 39 | +} from '../utils/Utils'; | ||
| 24 | 40 | ||
| 25 | @Builder | 41 | @Builder |
| 26 | -export function ARSemanticDenseBuilder(): void { | 42 | +export function ARFaceBuilder() { |
| 27 | - ARSemanticDense(); | 43 | + ARFace(); |
| 28 | } | 44 | } |
| 29 | 45 | ||
| 30 | -@Component | 46 | +let faceVertices: number[] = []; |
| 31 | -struct ARSemanticDense { | 47 | +let faceGeoArr: Geometry[] = []; |
| 32 | - @State arContext?: arViewController.ARViewContext = undefined; | 48 | +let shader: Shader | undefined = undefined; |
| 33 | - // 平面类型 | 49 | +let material: ShaderMaterial | undefined = undefined; |
| 34 | - private intervalId: number = -1; | 50 | +let rf: SceneResourceFactory | undefined = undefined; |
| 35 | - // 重复调用函数时间间隔为33ms,即设定为30fps | ||
| 36 | - private delayInterval: number = 33; | ||
| 37 | 51 | ||
| 38 | - build(): void { | 52 | +interface Blend { |
| 53 | + enabled: boolean; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +interface Material2 extends Material { | ||
| 57 | + blend: Blend; | ||
| 58 | +}; | ||
| 59 | + | ||
| 60 | +@Component | ||
| 61 | +export struct ARFace { | ||
| 62 | + pageInfos: NavPathStack = new NavPathStack(); | ||
| 63 | + @State context: Context = this.getUIContext().getHostContext() as Context; | ||
| 64 | + @State arContext?: arViewController.ARViewContext = undefined; | ||
| 65 | + | ||
| 66 | + build() { | ||
| 39 | NavDestination() { | 67 | NavDestination() { |
| 40 | RelativeContainer() { | 68 | RelativeContainer() { |
| 41 | if (this.arContext) { | 69 | if (this.arContext) { |
| @@ -49,16 +77,12 @@ struct ARSemanticDense { | |||
| 49 | } | 77 | } |
| 50 | } | 78 | } |
| 51 | } | 79 | } |
| 52 | - .onAppear(() => { | 80 | + .onAppear(async () => { |
| 53 | this.initARView(); | 81 | this.initARView(); |
| 54 | - // 设定在30fps下更新获取物体体积信息 | ||
| 55 | - this.intervalId = setInterval(async () => { | ||
| 56 | - }, this.delayInterval); | ||
| 57 | }) | 82 | }) |
| 58 | - .onWillDisappear(() => { | 83 | + .onWillDisappear(async () => { |
| 59 | - // 退出setInterval函数 | 84 | + await this.stopARView(); |
| 60 | - clearInterval(this.intervalId); | 85 | + this.clearGlobalVariables(); |
| 61 | - this.stopARView(); | ||
| 62 | }) | 86 | }) |
| 63 | .onShown(() => { | 87 | .onShown(() => { |
| 64 | this.resumeARView(); | 88 | this.resumeARView(); |
| @@ -71,51 +95,15 @@ struct ARSemanticDense { | |||
| 71 | .hideToolBar(true) | 95 | .hideToolBar(true) |
| 72 | } | 96 | } |
| 73 | 97 | ||
| 74 | - private initARView(): void { | 98 | + private async stopARView(): Promise<void> { |
| 75 | - Scene.load().then(async (scene: Scene) => { | ||
| 76 | - let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext(); | ||
| 77 | - viewContext.scene = scene; | ||
| 78 | - viewContext.callback = new ARViewCallbackImpl(); | ||
| 79 | - viewContext.config = { | ||
| 80 | - type: arEngine.ARType.WORLD, | ||
| 81 | - planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL, | ||
| 82 | - powerMode: arEngine.ARPowerMode.NORMAL, | ||
| 83 | - semanticDenseMode: arEngine.ARSemanticDenseMode.CUBE_VOLUME, // 开启体积测量 | ||
| 84 | - poseMode: arEngine.ARPoseMode.GRAVITY, | ||
| 85 | - depthMode: arEngine.ARDepthMode.DISABLED, | ||
| 86 | - meshMode: arEngine.ARMeshMode.DISABLED, | ||
| 87 | - focusMode: arEngine.ARFocusMode.AUTO | ||
| 88 | - } | ||
| 89 | - viewContext.init().then(() => { | ||
| 90 | - this.arContext = viewContext; | ||
| 91 | - Logger.info('Succeeded in initting ARView.'); | ||
| 92 | - }).catch((err: BusinessError) => { | ||
| 93 | - Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); | ||
| 94 | - }) | ||
| 95 | - }) | ||
| 96 | - } | ||
| 97 | - | ||
| 98 | - private stopARView(): void { | ||
| 99 | if (!this.arContext) { | 99 | if (!this.arContext) { |
| 100 | return; | 100 | return; |
| 101 | } | 101 | } |
| 102 | try { | 102 | try { |
| 103 | - this.arContext.destroy(); | 103 | + await this.arContext.destroy(); |
| 104 | } catch (error) { | 104 | } catch (error) { |
| 105 | const err: BusinessError = error as BusinessError; | 105 | const err: BusinessError = error as BusinessError; |
| 106 | - Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); | 106 | + logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}`); |
| 107 | - } | ||
| 108 | - } | ||
| 109 | - | ||
| 110 | - private resumeARView(): void { | ||
| 111 | - if (!this.arContext) { | ||
| 112 | - return; | ||
| 113 | - } | ||
| 114 | - try { | ||
| 115 | - this.arContext.resume(); | ||
| 116 | - } catch (error) { | ||
| 117 | - const err: BusinessError = error as BusinessError; | ||
| 118 | - Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); | ||
| 119 | } | 107 | } |
| 120 | } | 108 | } |
| 121 | 109 | ||
| @@ -127,9 +115,59 @@ struct ARSemanticDense { | |||
| 127 | this.arContext.pause(); | 115 | this.arContext.pause(); |
| 128 | } catch (error) { | 116 | } catch (error) { |
| 129 | const err: BusinessError = error as BusinessError; | 117 | const err: BusinessError = error as BusinessError; |
| 130 | - Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); | 118 | + logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}`); |
| 131 | } | 119 | } |
| 132 | } | 120 | } |
| 121 | + | ||
| 122 | + private resumeARView(): void { | ||
| 123 | + if (!this.arContext) { | ||
| 124 | + return; | ||
| 125 | + } | ||
| 126 | + try { | ||
| 127 | + this.arContext.resume(); | ||
| 128 | + } catch (error) { | ||
| 129 | + const err: BusinessError = error as BusinessError; | ||
| 130 | + logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}`); | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + private initARView(): void { | ||
| 135 | + Scene.load().then(async (result: Scene) => { | ||
| 136 | + try { | ||
| 137 | + let ret: boolean = arViewController.isARTypeSupported(arEngine.ARFeatureType.ARENGINE_FEATURE_TYPE_FACE); | ||
| 138 | + logger.info(`ARFace isARTypeSupported is ${ret}`); | ||
| 139 | + } catch (error) { | ||
| 140 | + const err: BusinessError = error as BusinessError; | ||
| 141 | + logger.error( | ||
| 142 | + `Failed to get whether the device is support ARFace. Code is ${err.code}, message is ${err.message}`); | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + let context = new arViewController.ARViewContext(); | ||
| 146 | + context.scene = result; | ||
| 147 | + context.callback = new ARViewCallbackImpl(); | ||
| 148 | + context.config = { | ||
| 149 | + type: arEngine.ARType.FACE, | ||
| 150 | + planeFindingMode: arEngine.ARPlaneFindingMode.DISABLED, | ||
| 151 | + powerMode: arEngine.ARPowerMode.NORMAL, | ||
| 152 | + focusMode: arEngine.ARFocusMode.AUTO, | ||
| 153 | + cameraLensFacing: arEngine.ARCameraLensFacing.FRONT, | ||
| 154 | + multiFaceMode: arEngine.ARMultiFaceMode.MULTIFACE_ENABLE, | ||
| 155 | + }; | ||
| 156 | + context.init().then(() => { | ||
| 157 | + this.arContext = context; | ||
| 158 | + }).catch((err: BusinessError) => { | ||
| 159 | + logger.error(`Failed to init context. Code is ${err.code}, message is ${err.message}`); | ||
| 160 | + }); | ||
| 161 | + }) | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + private clearGlobalVariables() { | ||
| 165 | + // Clearing Global Variables | ||
| 166 | + faceGeoArr = []; | ||
| 167 | + rf = undefined; | ||
| 168 | + shader = undefined; | ||
| 169 | + material = undefined; | ||
| 170 | + } | ||
| 133 | } | 171 | } |
| 134 | 172 | ||
| 135 | class ARViewCallbackImpl extends arViewController.ARViewCallback { | 173 | class ARViewCallbackImpl extends arViewController.ARViewCallback { |
| @@ -141,31 +179,97 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 141 | 179 | ||
| 142 | async onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): Promise<void> { | 180 | async onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): Promise<void> { |
| 143 | if (!ctx.session) { | 181 | if (!ctx.session) { |
| 182 | + logger.error('arSession is undefined'); | ||
| 144 | return; | 183 | return; |
| 145 | } | 184 | } |
| 146 | 185 | ||
| 147 | - let arSession: arEngine.ARSession = ctx.session; | 186 | + let session: arEngine.ARSession = ctx.session; |
| 187 | + if (!rf) { | ||
| 188 | + rf = ctx.scene.getResourceFactory(); | ||
| 189 | + } | ||
| 190 | + if (!shader) { | ||
| 191 | + // The plane color is controlled by the file plane.shader | ||
| 192 | + shader = await rf.createShader({ name: 'CustomShader', uri: $rawfile('shaders/custom_shader/face.shader') }); | ||
| 193 | + } | ||
| 194 | + if (!material) { | ||
| 195 | + material = await rf.createMaterial({ name: 'CustomMaterial' }, MaterialType.SHADER); | ||
| 196 | + material.colorShader = shader; | ||
| 197 | + (material as Material2).blend = { enabled: true }; | ||
| 198 | + } | ||
| 148 | 199 | ||
| 149 | try { | 200 | try { |
| 150 | - let frame: arEngine.ARFrame = arSession.getFrame(); | 201 | + let mesh = new CustomGeometry(); |
| 151 | - if (frame) { | 202 | + let geometry: Geometry | null = null; |
| 152 | - let semanticData: arEngine.ARSemanticDenseData = frame.acquireSemanticDense(); | 203 | + if (session == null) { |
| 153 | - if (semanticData) { | 204 | + logger.error('session is null'); |
| 154 | - if (semanticData.cubeDataSize > 0) { | 205 | + } |
| 155 | - // 获取第一个Cube的体积数据 | 206 | + |
| 156 | - let semanticCubeData: arEngine.ARSemanticDenseCubeData = semanticData.acquireCubeData()[0]; | 207 | + let vertexArray: Vec3[][] = []; |
| 157 | - cubeVertexData = semanticCubeData.vertexData; | 208 | + let indexArray: Map<number, number[]> = new Map; |
| 158 | - cubeConfidence = semanticCubeData.confidence; | 209 | + |
| 159 | - cubeLabel = semanticCubeData.label; | 210 | + // Acquire face data |
| 160 | - } | 211 | + let trackables: arEngine.ARTrackable[] = session.getAllTrackables(arEngine.ARTrackableType.FACE); |
| 161 | - semanticData.release().catch((err: BusinessError) => { | 212 | + logger.debug(`the faceList length is ${trackables.length}`); |
| 162 | - Logger.error(`semanticData failed to release. Code is ${err.code}, message is ${err.message}.`); | 213 | + for (let i = 0; i < trackables.length; ++i) { |
| 163 | - }); | 214 | + let face: arEngine.ARFace = trackables[i] as arEngine.ARFace; |
| 215 | + let centerPose = face.getPose(); | ||
| 216 | + let viewMatrix = centerPose.getMatrix(); | ||
| 217 | + | ||
| 218 | + if (trackables[i].state !== arEngine.ARTrackingState.TRACKING) { | ||
| 219 | + logger.error(`Face not in tracking state`); | ||
| 220 | + continue; | ||
| 221 | + } | ||
| 222 | + // Data Process | ||
| 223 | + let faceGeometry: arEngine.ARGeometry = face.getGeometry(); | ||
| 224 | + let faceBlendShapes: arEngine.ARBlendShapes = face.getBlendShapes(); | ||
| 225 | + let tmpVert = faceGeometry.getVertices(); | ||
| 226 | + let tmpIndices = faceGeometry.getIndices(); | ||
| 227 | + faceVertices = arrayBufferFloat32ToNumber(tmpVert); | ||
| 228 | + let faceIndices: number[] = arrayBufferInt32ToNumber(tmpIndices); | ||
| 229 | + vertexArray.push(getFaceFrontVertices(viewMatrix, faceVertices)); | ||
| 230 | + indexArray.set(i, faceIndices); | ||
| 231 | + | ||
| 232 | + // BlendShapes Print | ||
| 233 | + logger.info('the count of blendShapes is' + faceBlendShapes.count); | ||
| 234 | + logger.info('the data of blendShapes is' + arrayBufferFloat32ToNumber(faceBlendShapes.getData())); | ||
| 235 | + logger.info('the types of blendShapes is' + faceBlendShapes.getTypes()); | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + | ||
| 239 | + // Draw | ||
| 240 | + mesh.topology = PrimitiveTopology.TRIANGLE_LIST; | ||
| 241 | + mesh.vertices = generateMeshInput(vertexArray); | ||
| 242 | + mesh.indices = generateFaceMeshIndex(indexArray, faceVertices.length / 3); | ||
| 243 | + | ||
| 244 | + let meshRes: MeshResource = await rf.createMesh({ name: 'resource' }, mesh); | ||
| 245 | + geometry = await rf.createGeometry({ name: 'face' }, meshRes); | ||
| 246 | + meshRes.destroy(); | ||
| 247 | + | ||
| 248 | + geometry.mesh.materialOverride = material; | ||
| 249 | + | ||
| 250 | + // The node from the previous frame needs to be cleared | ||
| 251 | + if (ctx.scene) { | ||
| 252 | + // face | ||
| 253 | + let len: number = faceGeoArr.length; | ||
| 254 | + for (let index = len - 1; index >= 0; index--) { | ||
| 255 | + let geo = faceGeoArr[index]; | ||
| 256 | + ctx.scene.root?.children.remove(geo); | ||
| 257 | + geo.destroy(); | ||
| 258 | + faceGeoArr.splice(index, 1); | ||
| 259 | + logger.info('geo.destroy()'); | ||
| 164 | } | 260 | } |
| 165 | } | 261 | } |
| 262 | + | ||
| 263 | + // Adding a Face to a Node | ||
| 264 | + if (geometry) { | ||
| 265 | + ctx.scene?.root?.children.append(geometry); | ||
| 266 | + faceGeoArr.push(geometry); | ||
| 267 | + } | ||
| 268 | + vertexArray = []; | ||
| 269 | + indexArray.clear(); | ||
| 166 | } catch (error) { | 270 | } catch (error) { |
| 167 | const err: BusinessError = error as BusinessError; | 271 | const err: BusinessError = error as BusinessError; |
| 168 | - Logger.error(`Failed to update data. Code is ${err.code}, message is ${err.message}.`); | 272 | + logger.error(`Failed to acquire face information. Code is ${err.code}, message is ${err.message}`) |
| 169 | } | 273 | } |
| 170 | } | 274 | } |
| 171 | -} | 275 | +} |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -13,9 +13,9 @@ | |||
| 13 | * limitations under the License. | 13 | * limitations under the License. |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | -import { photoAccessHelper } from '@kit.MediaLibraryKit'; | ||
| 17 | -import { Logger } from '../utils/Logger'; | ||
| 18 | import { BusinessError } from '@kit.BasicServicesKit'; | 16 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 17 | +import { photoAccessHelper } from '@kit.MediaLibraryKit'; | ||
| 18 | +import { logger } from '../utils/Logger'; | ||
| 19 | 19 | ||
| 20 | @Builder | 20 | @Builder |
| 21 | export function ARImageBuilder(): void { | 21 | export function ARImageBuilder(): void { |
| @@ -67,17 +67,21 @@ struct ARImage { | |||
| 67 | 67 | ||
| 68 | photoPicker.select(photoOption).then((photoResult) => { | 68 | photoPicker.select(photoOption).then((photoResult) => { |
| 69 | if (photoResult.photoUris.length > 0 && photoResult.photoUris[0].length > 0) { | 69 | if (photoResult.photoUris.length > 0 && photoResult.photoUris[0].length > 0) { |
| 70 | - this.pageInfos.pushDestinationByName('ARImageByAdd', photoResult.photoUris); | 70 | + this.pageInfos.pushDestinationByName('ARImageByAdd', photoResult.photoUris).catch((err: BusinessError) => { |
| 71 | + logger.error(`Failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`); | ||
| 72 | + }); | ||
| 71 | } | 73 | } |
| 74 | + }).catch((err: BusinessError) => { | ||
| 75 | + logger.warn(`Failed to select photos. Code is ${err.code}, message is ${err.message}.`); | ||
| 72 | }) | 76 | }) |
| 73 | } catch (error) { | 77 | } catch (error) { |
| 74 | - Logger.error(`Failed to select by photoPicker. Code: ${error.code}.`); | 78 | + logger.error(`Failed to select by photoPicker. Code: ${error.code}.`); |
| 75 | } | 79 | } |
| 76 | } | 80 | } |
| 77 | 81 | ||
| 78 | private loadDatabaseToTrack(): void { | 82 | private loadDatabaseToTrack(): void { |
| 79 | this.pageInfos.pushDestinationByName('ARImageByDatabase', null).catch((err: BusinessError) => { | 83 | this.pageInfos.pushDestinationByName('ARImageByDatabase', null).catch((err: BusinessError) => { |
| 80 | - Logger.error(`ARImageByDatabase failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`); | 84 | + logger.error(`ARImageByDatabase failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`); |
| 81 | }) | 85 | }) |
| 82 | } | 86 | } |
| 83 | } | 87 | } |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -34,8 +34,8 @@ import { Matrix4 } from '@kit.ArkUI'; | |||
| 34 | import { BusinessError } from '@kit.BasicServicesKit'; | 34 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 35 | import { fileIo } from '@kit.CoreFileKit'; | 35 | import { fileIo } from '@kit.CoreFileKit'; |
| 36 | import { image } from '@kit.ImageKit'; | 36 | import { image } from '@kit.ImageKit'; |
| 37 | -import { Logger } from '../utils/Logger'; | 37 | +import { logger } from '../utils/Logger'; |
| 38 | -import { calculatePoint, createImageIndex, getImageVertices } from '../utils/Utils'; | 38 | +import { calculatePoint, createImageIndex, getImageVertices, getResourceString } from '../utils/Utils'; |
| 39 | 39 | ||
| 40 | @Builder | 40 | @Builder |
| 41 | export function ARImageByAddBuilder(): void { | 41 | export function ARImageByAddBuilder(): void { |
| @@ -55,28 +55,25 @@ interface CustomerMaterial extends Material { | |||
| 55 | @Component | 55 | @Component |
| 56 | struct ARImageByAdd { | 56 | struct ARImageByAdd { |
| 57 | pageInfos: NavPathStack = new NavPathStack(); | 57 | pageInfos: NavPathStack = new NavPathStack(); |
| 58 | - private imagePathArray: string[] = []; | ||
| 59 | - // When destroy is set to true, the addImage function is used to determine whether to continue adding images. | ||
| 60 | - private isProgramExits: boolean = false; | ||
| 61 | - private isSaveDatabase: boolean = false; | ||
| 62 | @State arContext?: arViewController.ARViewContext = undefined; | 58 | @State arContext?: arViewController.ARViewContext = undefined; |
| 63 | @State context: Context = this.getUIContext().getHostContext() as Context; | 59 | @State context: Context = this.getUIContext().getHostContext() as Context; |
| 64 | - @State totalImageCounts: number = this.imagePathArray.length; | ||
| 65 | @State addFailedImageCounts: number = 0; | 60 | @State addFailedImageCounts: number = 0; |
| 66 | @State succeedImageCounts: number = 0; | 61 | @State succeedImageCounts: number = 0; |
| 67 | @State addFailedMessage: string[] = []; | 62 | @State addFailedMessage: string[] = []; |
| 63 | + private imagePathArray: string[] = []; | ||
| 64 | + @State totalImageCounts: number = this.imagePathArray.length; | ||
| 65 | + // When destroy is set to true, the addImage function is used to determine whether to continue adding images. | ||
| 66 | + private isProgramExits: boolean = false; | ||
| 67 | + private isSaveDatabase: boolean = false; | ||
| 68 | 68 | ||
| 69 | build(): void { | 69 | build(): void { |
| 70 | NavDestination() { | 70 | NavDestination() { |
| 71 | RelativeContainer() { | 71 | RelativeContainer() { |
| 72 | Column() { | 72 | Column() { |
| 73 | - Text(`${this.context.resourceManager.getStringByNameSync( | 73 | + Text(`${getResourceString(this.context, 'add_image_msg_count')} ${this.succeedImageCounts + |
| 74 | - 'add_image_msg_count')} ${this.succeedImageCounts + | ||
| 75 | this.addFailedImageCounts} / ${this.totalImageCounts}`) | 74 | this.addFailedImageCounts} / ${this.totalImageCounts}`) |
| 76 | - Text(`${this.context.resourceManager.getStringByNameSync('add_image_msg_success') + | 75 | + Text(`${getResourceString(this.context, 'add_image_msg_success') + this.succeedImageCounts}`) |
| 77 | - this.succeedImageCounts}`) | 76 | + Text(`${getResourceString(this.context, 'add_image_msg_fail') + this.addFailedImageCounts}`) |
| 78 | - Text(`${this.context.resourceManager.getStringByNameSync('add_image_msg_fail') + | ||
| 79 | - this.addFailedImageCounts}`) | ||
| 80 | 77 | ||
| 81 | if (this.addFailedMessage) { | 78 | if (this.addFailedMessage) { |
| 82 | ForEach(this.addFailedMessage, (item: string) => { | 79 | ForEach(this.addFailedMessage, (item: string) => { |
| @@ -86,7 +83,7 @@ struct ARImageByAdd { | |||
| 86 | } | 83 | } |
| 87 | } | 84 | } |
| 88 | .visibility(this.addFailedImageCounts + this.succeedImageCounts < this.totalImageCounts ? Visibility.Visible : | 85 | .visibility(this.addFailedImageCounts + this.succeedImageCounts < this.totalImageCounts ? Visibility.Visible : |
| 89 | - Visibility.None) | 86 | + Visibility.None) |
| 90 | .foregroundColor(Color.Red) | 87 | .foregroundColor(Color.Red) |
| 91 | .zIndex(1) | 88 | .zIndex(1) |
| 92 | .alignRules({ | 89 | .alignRules({ |
| @@ -112,14 +109,16 @@ struct ARImageByAdd { | |||
| 112 | 109 | ||
| 113 | this.addImage(dataBase).then(() => { | 110 | this.addImage(dataBase).then(() => { |
| 114 | if (this.addFailedImageCounts === this.totalImageCounts) { | 111 | if (this.addFailedImageCounts === this.totalImageCounts) { |
| 115 | - this.ShowDialog(this.context.resourceManager.getStringByNameSync('invalid_image_added')); | 112 | + this.ShowDialog(getResourceString(this.context, 'invalid_image_added')); |
| 116 | } | 113 | } |
| 117 | if (this.totalImageCounts === this.succeedImageCounts + this.addFailedImageCounts) { | 114 | if (this.totalImageCounts === this.succeedImageCounts + this.addFailedImageCounts) { |
| 118 | this.initARView(); | 115 | this.initARView(); |
| 119 | this.isSaveDatabase = true; | 116 | this.isSaveDatabase = true; |
| 120 | } | 117 | } |
| 121 | }) | 118 | }) |
| 122 | - }) | 119 | + }).catch((err: BusinessError) => { |
| 120 | + logger.warn(`Failed to create database. Code is ${err.code}, message is ${err.message}`); | ||
| 121 | + }) | ||
| 123 | }) | 122 | }) |
| 124 | .onWillDisappear(async () => { | 123 | .onWillDisappear(async () => { |
| 125 | this.stopARView(); | 124 | this.stopARView(); |
| @@ -140,6 +139,80 @@ struct ARImageByAdd { | |||
| 140 | .hideToolBar(true) | 139 | .hideToolBar(true) |
| 141 | } | 140 | } |
| 142 | 141 | ||
| 142 | + // Asynchronously execute the task of adding pictures | ||
| 143 | + async addImage(dataBase: arEngine.ARAugmentedImageDatabase): Promise<void> { | ||
| 144 | + for (let index = 0; index < this.totalImageCounts; index++) { | ||
| 145 | + const magePath: string = this.imagePathArray[index]; | ||
| 146 | + let file: fileIo.File; | ||
| 147 | + try { | ||
| 148 | + file = fileIo.openSync(magePath, fileIo.OpenMode.READ_ONLY); | ||
| 149 | + } catch (error) { | ||
| 150 | + const err: BusinessError = error as BusinessError; | ||
| 151 | + logger.error(`Failed to open image. Code is ${err.code}, message is ${err.message}.`); | ||
| 152 | + return; | ||
| 153 | + } | ||
| 154 | + let imageName: string = file.name; | ||
| 155 | + const imageSourceApi: image.ImageSource = image.createImageSource(file.fd); | ||
| 156 | + try { | ||
| 157 | + fileIo.closeSync(file); | ||
| 158 | + } catch (error) { | ||
| 159 | + const err: BusinessError = error as BusinessError; | ||
| 160 | + logger.error(`Failed to closeSync. Code: ${err.code}.`); | ||
| 161 | + this.addFailedImageCounts += 1; | ||
| 162 | + continue; | ||
| 163 | + } | ||
| 164 | + const imageInfo: image.ImageInfo = imageSourceApi.getImageInfoSync(0); | ||
| 165 | + if (!imageInfo) { | ||
| 166 | + logger.error('Failed to obtain the image pixel map information.'); | ||
| 167 | + this.addFailedImageCounts += 1; | ||
| 168 | + continue; | ||
| 169 | + } | ||
| 170 | + const opts: image.DecodingOptions = { | ||
| 171 | + editable: true, | ||
| 172 | + desiredPixelFormat: image.PixelMapFormat.RGBA_8888, | ||
| 173 | + desiredSize: { width: imageInfo.size.width, height: imageInfo.size.height } | ||
| 174 | + } | ||
| 175 | + let pixelMap: image.PixelMap = imageSourceApi.createPixelMapSync(opts); | ||
| 176 | + try { | ||
| 177 | + const info: image.ImageInfo = pixelMap.getImageInfoSync(); | ||
| 178 | + let size: number = pixelMap.getPixelBytesNumber(); | ||
| 179 | + logger.info(`The ets create pixelMap width: ${info.size.width}, height: ${info.size.height}, size: ${size}.`); | ||
| 180 | + } catch (error) { | ||
| 181 | + const err: BusinessError = error as BusinessError; | ||
| 182 | + logger.error(`Failed to get image info. Code is ${err.code}, message is ${err.message}.`); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + | ||
| 186 | + if (this.isProgramExits) { | ||
| 187 | + break; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + await dataBase.addImage(imageName, pixelMap, 10).then((result: arEngine.ARAddAugmentedImageResult) => { | ||
| 191 | + logger.info(`The imageResult: ${result.index} ${result.stateReason}.`); | ||
| 192 | + if (result.stateReason !== arEngine.ARAddAugmentedImageReason.NONE) { | ||
| 193 | + this.addFailedImageCounts += 1; | ||
| 194 | + let stateReason: string = ''; | ||
| 195 | + try { | ||
| 196 | + stateReason = errcode.get(result.stateReason) ?? ''; | ||
| 197 | + } catch (error) { | ||
| 198 | + const err: BusinessError = error as BusinessError; | ||
| 199 | + logger.error(`Failed to get stateReason. Code is ${err.code}, message is ${err.message}.`); | ||
| 200 | + } | ||
| 201 | + this.addFailedMessage.push(`${getResourceString(this.context, 'image_name_by_add') + | ||
| 202 | + imageName + '\n' + getResourceString(this.context, 'image_add_reason_by_add') + | ||
| 203 | + stateReason + '\n'}`); | ||
| 204 | + } else { | ||
| 205 | + this.succeedImageCounts += 1; | ||
| 206 | + } | ||
| 207 | + }).catch(() => { | ||
| 208 | + this.addFailedImageCounts += 1; | ||
| 209 | + }) | ||
| 210 | + | ||
| 211 | + imageSourceApi.release(); | ||
| 212 | + pixelMap.release(); | ||
| 213 | + } | ||
| 214 | + } | ||
| 215 | + | ||
| 143 | private initARView(): void { | 216 | private initARView(): void { |
| 144 | Scene.load().then(async (scene: Scene) => { | 217 | Scene.load().then(async (scene: Scene) => { |
| 145 | let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext(); | 218 | let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext(); |
| @@ -157,9 +230,9 @@ struct ARImageByAdd { | |||
| 157 | } | 230 | } |
| 158 | viewContext.init().then(() => { | 231 | viewContext.init().then(() => { |
| 159 | this.arContext = viewContext; | 232 | this.arContext = viewContext; |
| 160 | - Logger.info('Succeeded in initting ARView.'); | 233 | + logger.info('Succeeded in initting ARView.'); |
| 161 | }).catch((err: BusinessError) => { | 234 | }).catch((err: BusinessError) => { |
| 162 | - Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); | 235 | + logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); |
| 163 | }) | 236 | }) |
| 164 | }) | 237 | }) |
| 165 | } | 238 | } |
| @@ -171,14 +244,14 @@ struct ARImageByAdd { | |||
| 171 | try { | 244 | try { |
| 172 | this.isProgramExits = true; | 245 | this.isProgramExits = true; |
| 173 | if (this.isSaveDatabase) { | 246 | if (this.isSaveDatabase) { |
| 174 | - SaveBufferToLocal(dataBase, this.context); | 247 | + saveBufferToLocal(dataBase, this.context); |
| 175 | } | 248 | } |
| 176 | 249 | ||
| 177 | await dataBase.release(); | 250 | await dataBase.release(); |
| 178 | await this.arContext?.destroy(); | 251 | await this.arContext?.destroy(); |
| 179 | } catch (error) { | 252 | } catch (error) { |
| 180 | const err: BusinessError = error as BusinessError; | 253 | const err: BusinessError = error as BusinessError; |
| 181 | - Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); | 254 | + logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); |
| 182 | } | 255 | } |
| 183 | } | 256 | } |
| 184 | 257 | ||
| @@ -190,7 +263,7 @@ struct ARImageByAdd { | |||
| 190 | this.arContext.resume(); | 263 | this.arContext.resume(); |
| 191 | } catch (error) { | 264 | } catch (error) { |
| 192 | const err: BusinessError = error as BusinessError; | 265 | const err: BusinessError = error as BusinessError; |
| 193 | - Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); | 266 | + logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); |
| 194 | } | 267 | } |
| 195 | } | 268 | } |
| 196 | 269 | ||
| @@ -202,61 +275,7 @@ struct ARImageByAdd { | |||
| 202 | this.arContext.pause(); | 275 | this.arContext.pause(); |
| 203 | } catch (error) { | 276 | } catch (error) { |
| 204 | const err: BusinessError = error as BusinessError; | 277 | const err: BusinessError = error as BusinessError; |
| 205 | - Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); | 278 | + logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); |
| 206 | - } | ||
| 207 | - } | ||
| 208 | - | ||
| 209 | - // Asynchronously execute the task of adding pictures | ||
| 210 | - async addImage(dataBase: arEngine.ARAugmentedImageDatabase): Promise<void> { | ||
| 211 | - for (let index = 0; index < this.totalImageCounts; index++) { | ||
| 212 | - const magePath: string = this.imagePathArray[index]; | ||
| 213 | - let file: fileIo.File = fileIo.openSync(magePath, fileIo.OpenMode.READ_ONLY); | ||
| 214 | - let imageName: string = file.name; | ||
| 215 | - const imageSourceApi: image.ImageSource = image.createImageSource(file.fd); | ||
| 216 | - try { | ||
| 217 | - fileIo.closeSync(file); | ||
| 218 | - } catch (error) { | ||
| 219 | - const err: BusinessError = error as BusinessError; | ||
| 220 | - Logger.error(`Failed to closeSync. Code: ${err.code}.`); | ||
| 221 | - this.addFailedImageCounts += 1; | ||
| 222 | - continue; | ||
| 223 | - } | ||
| 224 | - const imageInfo: image.ImageInfo = imageSourceApi.getImageInfoSync(0); | ||
| 225 | - if (!imageInfo) { | ||
| 226 | - Logger.error('Failed to obtain the image pixel map information.'); | ||
| 227 | - this.addFailedImageCounts += 1; | ||
| 228 | - continue; | ||
| 229 | - } | ||
| 230 | - const opts: image.DecodingOptions = { | ||
| 231 | - editable: true, | ||
| 232 | - desiredPixelFormat: image.PixelMapFormat.RGBA_8888, | ||
| 233 | - desiredSize: { width: imageInfo.size.width, height: imageInfo.size.height } | ||
| 234 | - } | ||
| 235 | - let pixelMap: image.PixelMap = imageSourceApi.createPixelMapSync(opts); | ||
| 236 | - const info: image.ImageInfo = pixelMap.getImageInfoSync(); | ||
| 237 | - let size: number = pixelMap.getPixelBytesNumber(); | ||
| 238 | - Logger.info(`The ets create pixelMap width: ${info.size.width}, height: ${info.size.height}, size: ${size}.`); | ||
| 239 | - | ||
| 240 | - if (this.isProgramExits) { | ||
| 241 | - break; | ||
| 242 | - } | ||
| 243 | - | ||
| 244 | - await dataBase.addImage(imageName, pixelMap, 10).then((result: arEngine.ARAddAugmentedImageResult) => { | ||
| 245 | - Logger.info(`The imageResult: ${result.index} ${result.stateReason}.`); | ||
| 246 | - if (result.stateReason !== arEngine.ARAddAugmentedImageReason.NONE) { | ||
| 247 | - this.addFailedImageCounts += 1; | ||
| 248 | - this.addFailedMessage.push(`${this.context.resourceManager.getStringByNameSync('image_name_by_add') + | ||
| 249 | - imageName + '\n' + this.context.resourceManager.getStringByNameSync('image_add_reason_by_add') + | ||
| 250 | - errcode.get(result.stateReason) + '\n'}`); | ||
| 251 | - } else { | ||
| 252 | - this.succeedImageCounts += 1; | ||
| 253 | - } | ||
| 254 | - }).catch(() => { | ||
| 255 | - this.addFailedImageCounts += 1; | ||
| 256 | - }) | ||
| 257 | - | ||
| 258 | - imageSourceApi.release(); | ||
| 259 | - pixelMap.release(); | ||
| 260 | } | 279 | } |
| 261 | } | 280 | } |
| 262 | 281 | ||
| @@ -281,7 +300,7 @@ struct ARImageByAdd { | |||
| 281 | style: DialogButtonStyle.HIGHLIGHT, | 300 | style: DialogButtonStyle.HIGHLIGHT, |
| 282 | value: $r('app.string.back'), | 301 | value: $r('app.string.back'), |
| 283 | action: () => { | 302 | action: () => { |
| 284 | - Logger.info('Callback when the second button is clicked.'); | 303 | + logger.info('Callback when the second button is clicked.'); |
| 285 | this.pageInfos.pop(); | 304 | this.pageInfos.pop(); |
| 286 | return; | 305 | return; |
| 287 | } | 306 | } |
| @@ -291,23 +310,46 @@ struct ARImageByAdd { | |||
| 291 | } | 310 | } |
| 292 | 311 | ||
| 293 | // Save the file locally | 312 | // Save the file locally |
| 294 | -async function SaveBufferToLocal(dataBase: arEngine.ARAugmentedImageDatabase, context: Context): Promise<void> { | 313 | +async function saveBufferToLocal(dataBase: arEngine.ARAugmentedImageDatabase, context: Context): Promise<void> { |
| 295 | let filesDir: string = context.filesDir; | 314 | let filesDir: string = context.filesDir; |
| 296 | - let file: fileIo.File = fileIo.openSync( | 315 | + let file: fileIo.File; |
| 297 | - filesDir + '/test.bin', fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC); | 316 | + try { |
| 298 | - let buf: ArrayBuffer = await dataBase.serialize(); | 317 | + file = fileIo.openSync(filesDir + '/test.bin', |
| 299 | - let writeLen: number = fileIo.writeSync(file.fd, buf); | 318 | + fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC); |
| 300 | - Logger.info(`The length of buffer is: ${writeLen}.`); | 319 | + } catch (error) { |
| 301 | - fileIo.closeSync(file); | 320 | + const err: BusinessError = error as BusinessError; |
| 321 | + logger.error(`Failed to open database. Code is ${err.code}, message is ${err.message}.`); | ||
| 322 | + return; | ||
| 323 | + } | ||
| 324 | + let buf: ArrayBuffer; | ||
| 325 | + try { | ||
| 326 | + buf = await dataBase.serialize(); | ||
| 327 | + try { | ||
| 328 | + let writeLen: number = fileIo.writeSync(file.fd, buf); | ||
| 329 | + logger.info(`The length of buffer is: ${writeLen}.`); | ||
| 330 | + } catch (error) { | ||
| 331 | + const err: BusinessError = error as BusinessError; | ||
| 332 | + logger.error(`Failed to write database. Code is ${err.code}, message is ${err.message}.`); | ||
| 333 | + } | ||
| 334 | + } catch (error) { | ||
| 335 | + const err: BusinessError = error as BusinessError; | ||
| 336 | + logger.error(`Failed to serialize database. Code is ${err.code}, message is ${err.message}.`); | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | + try { | ||
| 340 | + fileIo.closeSync(file); | ||
| 341 | + } catch (error) { | ||
| 342 | + const err: BusinessError = error as BusinessError; | ||
| 343 | + logger.error(`Failed to close database file. Code is ${err.code}, message is ${err.message}.`); | ||
| 344 | + } | ||
| 302 | } | 345 | } |
| 303 | 346 | ||
| 304 | class ARViewCallbackImpl extends arViewController.ARViewCallback { | 347 | class ARViewCallbackImpl extends arViewController.ARViewCallback { |
| 305 | - geoArr: Geometry[] = []; | 348 | + private geoArr: Geometry[] = []; |
| 306 | - scene: Scene | null = null; | 349 | + private scene: Scene | null = null; |
| 307 | - shader: Shader | null = null; | 350 | + private shader: Shader | null = null; |
| 308 | - material: ShaderMaterial | null = null; | 351 | + private material: ShaderMaterial | null = null; |
| 309 | - image: Image | null = null; | 352 | + private currentGeometry: Geometry | undefined = undefined; |
| 310 | - currentGeometry: Geometry | undefined = undefined; | ||
| 311 | 353 | ||
| 312 | clearAllNode(): void { | 354 | clearAllNode(): void { |
| 313 | let childrenNum: number | undefined = this.scene?.root?.children.count(); | 355 | let childrenNum: number | undefined = this.scene?.root?.children.count(); |
| @@ -338,16 +380,29 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 338 | 380 | ||
| 339 | try { | 381 | try { |
| 340 | let imageNumber: number = dataBase.getImageCount(); | 382 | let imageNumber: number = dataBase.getImageCount(); |
| 341 | - Logger.info(`The number of images in the database is ${imageNumber}.`); | 383 | + logger.info(`The number of images in the database is ${imageNumber}.`); |
| 342 | } catch (error) { | 384 | } catch (error) { |
| 343 | const err: BusinessError = error as BusinessError; | 385 | const err: BusinessError = error as BusinessError; |
| 344 | - Logger.error(`Failed to got image count. Code is ${err.code}, message is ${err.message}`); | 386 | + logger.error(`Failed to get image count. Code is ${err.code}, message is ${err.message}`); |
| 345 | } | 387 | } |
| 346 | 388 | ||
| 347 | - let imageCapacity: number = dataBase.getCapacity(); | 389 | + try { |
| 348 | - Logger.info(`The dataBase image capacity is: ${imageCapacity}.`); | 390 | + let imageCapacity: number = dataBase.getCapacity(); |
| 391 | + logger.info(`The dataBase image capacity is: ${imageCapacity}.`); | ||
| 392 | + } catch (error) { | ||
| 393 | + const err: BusinessError = error as BusinessError; | ||
| 394 | + logger.error(`Failed to get capacity. Code is ${err.code}, message is ${err.message}`); | ||
| 395 | + } | ||
| 349 | 396 | ||
| 350 | - let trackables: arEngine.ARTrackable[] = session.getAllTrackables(arEngine.ARTrackableType.AUGMENTED_IMAGE); | 397 | + |
| 398 | + let trackables: arEngine.ARTrackable[]; | ||
| 399 | + try { | ||
| 400 | + trackables = session.getAllTrackables(arEngine.ARTrackableType.AUGMENTED_IMAGE); | ||
| 401 | + } catch (error) { | ||
| 402 | + const err: BusinessError = error as BusinessError; | ||
| 403 | + logger.error(`Failed to get capacity. Code is ${err.code}, message is ${err.message}`); | ||
| 404 | + return; | ||
| 405 | + } | ||
| 351 | 406 | ||
| 352 | // The target image color is controlled by the file plane.shader | 407 | // The target image color is controlled by the file plane.shader |
| 353 | let rf: SceneResourceFactory = ctx.scene.getResourceFactory(); | 408 | let rf: SceneResourceFactory = ctx.scene.getResourceFactory(); |
| @@ -356,21 +411,35 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 356 | this.material.colorShader = this.shader; | 411 | this.material.colorShader = this.shader; |
| 357 | (this.material as CustomerMaterial).blend = { enabled: true }; | 412 | (this.material as CustomerMaterial).blend = { enabled: true }; |
| 358 | 413 | ||
| 359 | - Logger.info(`The image trackable size: ${trackables.length}.`); | 414 | + logger.info(`The image trackable size: ${trackables.length}.`); |
| 360 | let validImage: number = 0; | 415 | let validImage: number = 0; |
| 361 | for (let i = 0; i < trackables.length; ++i) { | 416 | for (let i = 0; i < trackables.length; ++i) { |
| 362 | if (trackables[i].type === arEngine.ARTrackableType.AUGMENTED_IMAGE) { | 417 | if (trackables[i].type === arEngine.ARTrackableType.AUGMENTED_IMAGE) { |
| 363 | - let arimage: arEngine.ARAugmentedImage = trackables[i] as arEngine.ARAugmentedImage; | 418 | + let arImage: arEngine.ARAugmentedImage = trackables[i] as arEngine.ARAugmentedImage; |
| 364 | - if (arEngine.ARTrackingState.TRACKING !== arimage.state) { | 419 | + if (arEngine.ARTrackingState.TRACKING !== arImage.state) { |
| 365 | continue; | 420 | continue; |
| 366 | } | 421 | } |
| 367 | validImage += 1; | 422 | validImage += 1; |
| 368 | - let centerPose: arEngine.ARPose = arimage.getPose(); | 423 | + let centerPose: arEngine.ARPose; |
| 369 | - let modelMat: Matrix4 = centerPose.getMatrix(); | 424 | + try { |
| 425 | + centerPose = arImage.getPose(); | ||
| 426 | + } catch (error) { | ||
| 427 | + const err: BusinessError = error as BusinessError; | ||
| 428 | + logger.error(`Failed to get pose. Code is ${err.code}, message is ${err.message}`); | ||
| 429 | + return; | ||
| 430 | + } | ||
| 431 | + let modelMat: Matrix4; | ||
| 432 | + try { | ||
| 433 | + modelMat = centerPose.getMatrix(); | ||
| 434 | + } catch (error) { | ||
| 435 | + const err: BusinessError = error as BusinessError; | ||
| 436 | + logger.error(`Failed to get matirx. Code is ${err.code}, message is ${err.message}`); | ||
| 437 | + return; | ||
| 438 | + } | ||
| 370 | let pos: Vec3 = centerPose.translation; | 439 | let pos: Vec3 = centerPose.translation; |
| 371 | - let drawPos: number[] = calculatePoint(arimage.extendX, arimage.extendZ, pos); | 440 | + let drawPos: number[] = calculatePoint(arImage.extendX, arImage.extendZ, pos); |
| 372 | 441 | ||
| 373 | - Logger.info(`The image width: ${arimage.extendX}, height: ${arimage.extendZ}.`); | 442 | + logger.info(`The image width: ${arImage.extendX}, height: ${arImage.extendZ}.`); |
| 374 | 443 | ||
| 375 | if (ctx?.scene) { | 444 | if (ctx?.scene) { |
| 376 | let mesh: CustomGeometry = new CustomGeometry(); | 445 | let mesh: CustomGeometry = new CustomGeometry(); |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -32,7 +32,7 @@ import { | |||
| 32 | import { Matrix4 } from '@kit.ArkUI'; | 32 | import { Matrix4 } from '@kit.ArkUI'; |
| 33 | import { BusinessError } from '@kit.BasicServicesKit'; | 33 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 34 | import { fileIo, ReadOptions } from '@kit.CoreFileKit'; | 34 | import { fileIo, ReadOptions } from '@kit.CoreFileKit'; |
| 35 | -import { Logger } from '../utils/Logger'; | 35 | +import { logger } from '../utils/Logger'; |
| 36 | import { calculatePoint, createImageIndex, getImageVertices } from '../utils/Utils'; | 36 | import { calculatePoint, createImageIndex, getImageVertices } from '../utils/Utils'; |
| 37 | 37 | ||
| 38 | @Builder | 38 | @Builder |
| @@ -76,16 +76,22 @@ struct ARImageByDatabase { | |||
| 76 | dataBase = arDataBase; | 76 | dataBase = arDataBase; |
| 77 | 77 | ||
| 78 | try { | 78 | try { |
| 79 | - let databaseBuffer: ArrayBuffer = ReadBuffer(this.context); | 79 | + let databaseBuffer: ArrayBuffer = readBuffer(this.context); |
| 80 | dataBase.deserialize(databaseBuffer).then(() => { | 80 | dataBase.deserialize(databaseBuffer).then(() => { |
| 81 | this.initARView(); | 81 | this.initARView(); |
| 82 | }) | 82 | }) |
| 83 | + .catch((err: BusinessError) => { | ||
| 84 | + logger.error(`Failed to deserialize database. Code is ${err.code}, message is ${err.message}.`); | ||
| 85 | + }) | ||
| 83 | } catch (error) { | 86 | } catch (error) { |
| 84 | const err: BusinessError = error as BusinessError; | 87 | const err: BusinessError = error as BusinessError; |
| 85 | - Logger.error(`Failed to init context. Code is ${err.code}, message is ${err.message}.`); | 88 | + logger.error(`Failed to init context. Code is ${err.code}, message is ${err.message}.`); |
| 86 | this.ShowDialog(this.context.resourceManager.getStringByNameSync('invalid_image_added')); | 89 | this.ShowDialog(this.context.resourceManager.getStringByNameSync('invalid_image_added')); |
| 87 | } | 90 | } |
| 88 | }) | 91 | }) |
| 92 | + .catch((err: BusinessError) => { | ||
| 93 | + logger.warn(`Failed to create database. Code is ${err.code}, message is ${err.message}`); | ||
| 94 | + }) | ||
| 89 | }) | 95 | }) |
| 90 | .onWillDisappear(async () => { | 96 | .onWillDisappear(async () => { |
| 91 | this.stopARView(); | 97 | this.stopARView(); |
| @@ -104,67 +110,6 @@ struct ARImageByDatabase { | |||
| 104 | .hideToolBar(true) | 110 | .hideToolBar(true) |
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | - private initARView(): void { | ||
| 108 | - Scene.load().then(async (scene: Scene) => { | ||
| 109 | - let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext(); | ||
| 110 | - viewContext.scene = scene; | ||
| 111 | - viewContext.callback = new ARViewCallbackImpl(); | ||
| 112 | - viewContext.config = { | ||
| 113 | - type: arEngine.ARType.IMAGE, | ||
| 114 | - planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL, | ||
| 115 | - powerMode: arEngine.ARPowerMode.NORMAL, | ||
| 116 | - semanticMode: arEngine.ARSemanticMode.NONE, | ||
| 117 | - poseMode: arEngine.ARPoseMode.GRAVITY, | ||
| 118 | - depthMode: arEngine.ARDepthMode.AUTOMATIC, | ||
| 119 | - meshMode: arEngine.ARMeshMode.DISABLED, | ||
| 120 | - focusMode: arEngine.ARFocusMode.AUTO | ||
| 121 | - } | ||
| 122 | - viewContext.init().then(() => { | ||
| 123 | - this.arContext = viewContext; | ||
| 124 | - Logger.info('Succeeded in initting ARView.'); | ||
| 125 | - }).catch((err: BusinessError) => { | ||
| 126 | - Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); | ||
| 127 | - }) | ||
| 128 | - }) | ||
| 129 | - } | ||
| 130 | - | ||
| 131 | - private async stopARView(): Promise<void> { | ||
| 132 | - if (!this.arContext) { | ||
| 133 | - return; | ||
| 134 | - } | ||
| 135 | - try { | ||
| 136 | - await dataBase.release(); | ||
| 137 | - await this.arContext?.destroy(); | ||
| 138 | - } catch (error) { | ||
| 139 | - const err: BusinessError = error as BusinessError; | ||
| 140 | - Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); | ||
| 141 | - } | ||
| 142 | - } | ||
| 143 | - | ||
| 144 | - private resumeARView(): void { | ||
| 145 | - if (!this.arContext) { | ||
| 146 | - return; | ||
| 147 | - } | ||
| 148 | - try { | ||
| 149 | - this.arContext.resume(); | ||
| 150 | - } catch (error) { | ||
| 151 | - const err: BusinessError = error as BusinessError; | ||
| 152 | - Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); | ||
| 153 | - } | ||
| 154 | - } | ||
| 155 | - | ||
| 156 | - private pauseARView(): void { | ||
| 157 | - if (!this.arContext) { | ||
| 158 | - return; | ||
| 159 | - } | ||
| 160 | - try { | ||
| 161 | - this.arContext.pause(); | ||
| 162 | - } catch (error) { | ||
| 163 | - const err: BusinessError = error as BusinessError; | ||
| 164 | - Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); | ||
| 165 | - } | ||
| 166 | - } | ||
| 167 | - | ||
| 168 | ShowDialog(msg: string): void { | 113 | ShowDialog(msg: string): void { |
| 169 | this.getUIContext().showAlertDialog({ | 114 | this.getUIContext().showAlertDialog({ |
| 170 | title: $r('app.string.warning'), | 115 | title: $r('app.string.warning'), |
| @@ -186,41 +131,126 @@ struct ARImageByDatabase { | |||
| 186 | style: DialogButtonStyle.HIGHLIGHT, | 131 | style: DialogButtonStyle.HIGHLIGHT, |
| 187 | value: $r('app.string.back'), | 132 | value: $r('app.string.back'), |
| 188 | action: () => { | 133 | action: () => { |
| 189 | - Logger.info('Callback when the second button is clicked.'); | 134 | + logger.info('Callback when the second button is clicked.'); |
| 190 | this.pageInfos.pop(); | 135 | this.pageInfos.pop(); |
| 191 | return; | 136 | return; |
| 192 | } | 137 | } |
| 193 | }] | 138 | }] |
| 194 | }) | 139 | }) |
| 195 | } | 140 | } |
| 141 | + | ||
| 142 | + private initARView(): void { | ||
| 143 | + Scene.load().then(async (scene: Scene) => { | ||
| 144 | + let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext(); | ||
| 145 | + viewContext.scene = scene; | ||
| 146 | + viewContext.callback = new ARViewCallbackImpl(); | ||
| 147 | + viewContext.config = { | ||
| 148 | + type: arEngine.ARType.IMAGE, | ||
| 149 | + planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL, | ||
| 150 | + powerMode: arEngine.ARPowerMode.NORMAL, | ||
| 151 | + semanticMode: arEngine.ARSemanticMode.NONE, | ||
| 152 | + poseMode: arEngine.ARPoseMode.GRAVITY, | ||
| 153 | + depthMode: arEngine.ARDepthMode.AUTOMATIC, | ||
| 154 | + meshMode: arEngine.ARMeshMode.DISABLED, | ||
| 155 | + focusMode: arEngine.ARFocusMode.AUTO | ||
| 156 | + } | ||
| 157 | + viewContext.init().then(() => { | ||
| 158 | + this.arContext = viewContext; | ||
| 159 | + logger.info('Succeeded in initting ARView.'); | ||
| 160 | + }).catch((err: BusinessError) => { | ||
| 161 | + logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); | ||
| 162 | + }) | ||
| 163 | + }) | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + private async stopARView(): Promise<void> { | ||
| 167 | + if (!this.arContext) { | ||
| 168 | + return; | ||
| 169 | + } | ||
| 170 | + try { | ||
| 171 | + await dataBase.release(); | ||
| 172 | + await this.arContext?.destroy(); | ||
| 173 | + } catch (error) { | ||
| 174 | + const err: BusinessError = error as BusinessError; | ||
| 175 | + logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); | ||
| 176 | + } | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + private resumeARView(): void { | ||
| 180 | + if (!this.arContext) { | ||
| 181 | + return; | ||
| 182 | + } | ||
| 183 | + try { | ||
| 184 | + this.arContext.resume(); | ||
| 185 | + } catch (error) { | ||
| 186 | + const err: BusinessError = error as BusinessError; | ||
| 187 | + logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + private pauseARView(): void { | ||
| 192 | + if (!this.arContext) { | ||
| 193 | + return; | ||
| 194 | + } | ||
| 195 | + try { | ||
| 196 | + this.arContext.pause(); | ||
| 197 | + } catch (error) { | ||
| 198 | + const err: BusinessError = error as BusinessError; | ||
| 199 | + logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); | ||
| 200 | + } | ||
| 201 | + } | ||
| 196 | } | 202 | } |
| 197 | 203 | ||
| 198 | // Read local files into buffer | 204 | // Read local files into buffer |
| 199 | -function ReadBuffer(context: Context): ArrayBuffer { | 205 | +function readBuffer(context: Context): ArrayBuffer { |
| 200 | let filesDir: string = context.filesDir; | 206 | let filesDir: string = context.filesDir; |
| 201 | - let srcFile: fileIo.File = fileIo.openSync( | 207 | + let srcFile: fileIo.File; |
| 202 | - filesDir + '/test.bin', fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); | 208 | + try { |
| 203 | - const fileStat: fileIo.Stat = fileIo.statSync(srcFile.fd); | 209 | + srcFile = fileIo.openSync(filesDir + '/test.bin', fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); |
| 204 | - // Read the contents of the source file and write it to the destination file | 210 | + } catch (error) { |
| 205 | - let readSize: number = 0; | 211 | + const err: BusinessError = error as BusinessError; |
| 206 | - let buf: ArrayBuffer = new ArrayBuffer(fileStat.size); | 212 | + logger.error(`Failed to open file. Code is ${err.code}, message is ${err.message}.`); |
| 207 | - let readOptions: ReadOptions = { | 213 | + return new ArrayBuffer(0); |
| 208 | - offset: readSize, | 214 | + } |
| 209 | - length: fileStat.size | 215 | + let fileStat: fileIo.Stat; |
| 216 | + let buf: ArrayBuffer; | ||
| 217 | + try { | ||
| 218 | + fileStat = fileIo.statSync(srcFile.fd); | ||
| 219 | + // Read the contents of the source file and write it to the destination file | ||
| 220 | + let readSize: number = 0; | ||
| 221 | + buf = new ArrayBuffer(fileStat.size); | ||
| 222 | + let readOptions: ReadOptions = { | ||
| 223 | + offset: readSize, | ||
| 224 | + length: fileStat.size | ||
| 225 | + } | ||
| 226 | + try { | ||
| 227 | + fileIo.readSync(srcFile.fd, buf, readOptions); | ||
| 228 | + } catch (error) { | ||
| 229 | + const err: BusinessError = error as BusinessError; | ||
| 230 | + logger.error(`Failed to read buffer. Code is ${err.code}, message is ${err.message}.`); | ||
| 231 | + } | ||
| 232 | + } catch (error) { | ||
| 233 | + const err: BusinessError = error as BusinessError; | ||
| 234 | + logger.error(`Failed to get file stat. Code is ${err.code}, message is ${err.message}.`); | ||
| 235 | + return new ArrayBuffer(0); | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + | ||
| 239 | + try { | ||
| 240 | + fileIo.closeSync(srcFile); | ||
| 241 | + } catch (error) { | ||
| 242 | + const err: BusinessError = error as BusinessError; | ||
| 243 | + logger.error(`Failed to close database file. Code is ${err.code}, message is ${err.message}.`); | ||
| 210 | } | 244 | } |
| 211 | - let readLen: number = fileIo.readSync(srcFile.fd, buf, readOptions); | ||
| 212 | - Logger.info(`The length of buffer is: ${readLen}.`); | ||
| 213 | - fileIo.closeSync(srcFile); | ||
| 214 | return buf; | 245 | return buf; |
| 215 | } | 246 | } |
| 216 | 247 | ||
| 217 | class ARViewCallbackImpl extends arViewController.ARViewCallback { | 248 | class ARViewCallbackImpl extends arViewController.ARViewCallback { |
| 218 | - geoArr: Geometry[] = []; | 249 | + private geoArr: Geometry[] = []; |
| 219 | - scene: Scene | null = null; | 250 | + private scene: Scene | null = null; |
| 220 | - shader: Shader | null = null; | 251 | + private shader: Shader | null = null; |
| 221 | - material: ShaderMaterial | null = null; | 252 | + private material: ShaderMaterial | null = null; |
| 222 | - image: Image | null = null; | 253 | + private currentGeometry: Geometry | undefined = undefined; |
| 223 | - currentGeometry: Geometry | undefined = undefined; | ||
| 224 | 254 | ||
| 225 | clearAllNode(): void { | 255 | clearAllNode(): void { |
| 226 | let childrenNum: number | undefined = this.scene?.root?.children.count(); | 256 | let childrenNum: number | undefined = this.scene?.root?.children.count(); |
| @@ -251,21 +281,27 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 251 | 281 | ||
| 252 | try { | 282 | try { |
| 253 | let imageNumber: number = dataBase.getImageCount(); | 283 | let imageNumber: number = dataBase.getImageCount(); |
| 254 | - Logger.info(`The number of images in the database is ${imageNumber}.`); | 284 | + logger.info(`The number of images in the database is ${imageNumber}.`); |
| 255 | } catch (error) { | 285 | } catch (error) { |
| 256 | const err: BusinessError = error as BusinessError; | 286 | const err: BusinessError = error as BusinessError; |
| 257 | - Logger.error(`Failed to got image count. Code is ${err.code}, message is ${err.message}.`); | 287 | + logger.error(`Failed to got image count. Code is ${err.code}, message is ${err.message}.`); |
| 258 | } | 288 | } |
| 259 | 289 | ||
| 260 | try { | 290 | try { |
| 261 | let imageCapacity: number = dataBase.getCapacity(); | 291 | let imageCapacity: number = dataBase.getCapacity(); |
| 262 | - Logger.info(`The dataBase image capacity = ${imageCapacity}.`); | 292 | + logger.info(`The dataBase image capacity = ${imageCapacity}.`); |
| 263 | } catch (error) { | 293 | } catch (error) { |
| 264 | const err: BusinessError = error as BusinessError; | 294 | const err: BusinessError = error as BusinessError; |
| 265 | - Logger.error(`Failed to getCapacity. Code is ${err.code}, message is ${err.message}`); | 295 | + logger.error(`Failed to getCapacity. Code is ${err.code}, message is ${err.message}`); |
| 266 | } | 296 | } |
| 267 | 297 | ||
| 268 | - let trackables: arEngine.ARTrackable[] = session.getAllTrackables(arEngine.ARTrackableType.AUGMENTED_IMAGE); | 298 | + let trackables: arEngine.ARTrackable[] = []; |
| 299 | + try { | ||
| 300 | + trackables = session.getAllTrackables(arEngine.ARTrackableType.AUGMENTED_IMAGE); | ||
| 301 | + } catch (error) { | ||
| 302 | + const err: BusinessError = error as BusinessError; | ||
| 303 | + logger.error(`Failed to get all trackables. Code is ${err.code}, message is ${err.message}`); | ||
| 304 | + } | ||
| 269 | 305 | ||
| 270 | // The target image color is controlled by the file plane.shader | 306 | // The target image color is controlled by the file plane.shader |
| 271 | let rf: SceneResourceFactory = ctx.scene.getResourceFactory(); | 307 | let rf: SceneResourceFactory = ctx.scene.getResourceFactory(); |
| @@ -274,21 +310,35 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 274 | this.material.colorShader = this.shader; | 310 | this.material.colorShader = this.shader; |
| 275 | (this.material as CustomerMaterial).blend = { enabled: true }; | 311 | (this.material as CustomerMaterial).blend = { enabled: true }; |
| 276 | 312 | ||
| 277 | - Logger.info(`The image trackable size: ${trackables.length}.`); | 313 | + logger.info(`The image trackable size: ${trackables.length}.`); |
| 278 | let validImage: number = 0; | 314 | let validImage: number = 0; |
| 279 | for (let i = 0; i < trackables.length; ++i) { | 315 | for (let i = 0; i < trackables.length; ++i) { |
| 280 | if (trackables[i].type === arEngine.ARTrackableType.AUGMENTED_IMAGE) { | 316 | if (trackables[i].type === arEngine.ARTrackableType.AUGMENTED_IMAGE) { |
| 281 | - let arimage: arEngine.ARAugmentedImage = trackables[i] as arEngine.ARAugmentedImage; | 317 | + let arImage: arEngine.ARAugmentedImage = trackables[i] as arEngine.ARAugmentedImage; |
| 282 | - if (arEngine.ARTrackingState.TRACKING !== arimage.state) { | 318 | + if (arEngine.ARTrackingState.TRACKING !== arImage.state) { |
| 283 | continue; | 319 | continue; |
| 284 | } | 320 | } |
| 285 | validImage++; | 321 | validImage++; |
| 286 | - let centerPose: arEngine.ARPose = arimage.getPose(); | 322 | + let centerPose: arEngine.ARPose; |
| 287 | - let modelMat: Matrix4 = centerPose.getMatrix(); | 323 | + try { |
| 324 | + centerPose = arImage.getPose(); | ||
| 325 | + } catch (error) { | ||
| 326 | + const err: BusinessError = error as BusinessError; | ||
| 327 | + logger.error(`Failed to get pose. Code is ${err.code}, message is ${err.message}`); | ||
| 328 | + return; | ||
| 329 | + } | ||
| 330 | + let modelMat: Matrix4; | ||
| 331 | + try { | ||
| 332 | + modelMat = centerPose.getMatrix(); | ||
| 333 | + } catch (error) { | ||
| 334 | + const err: BusinessError = error as BusinessError; | ||
| 335 | + logger.error(`Failed to get matirx. Code is ${err.code}, message is ${err.message}`); | ||
| 336 | + return; | ||
| 337 | + } | ||
| 288 | let pos: Vec3 = centerPose.translation; | 338 | let pos: Vec3 = centerPose.translation; |
| 289 | - let drawPos: number[] = calculatePoint(arimage.extendX, arimage.extendZ, pos); | 339 | + let drawPos: number[] = calculatePoint(arImage.extendX, arImage.extendZ, pos); |
| 290 | 340 | ||
| 291 | - Logger.info(`The image width: ${arimage.extendX}, height: ${arimage.extendZ}.`); | 341 | + logger.info(`The image width: ${arImage.extendX}, height: ${arImage.extendZ}.`); |
| 292 | 342 | ||
| 293 | if (ctx?.scene) { | 343 | if (ctx?.scene) { |
| 294 | let mesh: CustomGeometry = new CustomGeometry(); | 344 | let mesh: CustomGeometry = new CustomGeometry(); |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -29,10 +29,9 @@ import { | |||
| 29 | ShaderMaterial, | 29 | ShaderMaterial, |
| 30 | Vec3 | 30 | Vec3 |
| 31 | } from '@kit.ArkGraphics3D'; | 31 | } from '@kit.ArkGraphics3D'; |
| 32 | -import { window } from '@kit.ArkUI'; | ||
| 33 | import { BusinessError } from '@kit.BasicServicesKit'; | 32 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 34 | -import { Logger } from '../utils/Logger'; | 33 | +import { logger } from '../utils/Logger'; |
| 35 | -import { arrayBufferFloat32ToNumber, arrayBufferInt32ToNumber, arrayToVec3 } from '../utils/Utils'; | 34 | +import { arrayBufferFloat32ToNumber, arrayBufferInt32ToNumber, arrayToVec3, getStatusBarHeight } from '../utils/Utils'; |
| 36 | 35 | ||
| 37 | @Builder | 36 | @Builder |
| 38 | export function ARMeshBuilder(): void { | 37 | export function ARMeshBuilder(): void { |
| @@ -40,7 +39,7 @@ export function ARMeshBuilder(): void { | |||
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | let frame: arEngine.ARFrame; | 41 | let frame: arEngine.ARFrame; |
| 43 | -let statusBarHeight: number = 0; | 42 | + |
| 44 | let isDisplayCube: boolean = false; | 43 | let isDisplayCube: boolean = false; |
| 45 | let isStopFrameUpdate: boolean = false; | 44 | let isStopFrameUpdate: boolean = false; |
| 46 | let hitPoseList: Vec3[] = []; | 45 | let hitPoseList: Vec3[] = []; |
| @@ -63,6 +62,11 @@ interface CustomerMaterial extends Material { | |||
| 63 | struct ARMesh { | 62 | struct ARMesh { |
| 64 | @State arContext?: arViewController.ARViewContext = undefined; | 63 | @State arContext?: arViewController.ARViewContext = undefined; |
| 65 | @State context: Context = this.getUIContext().getHostContext() as Context; | 64 | @State context: Context = this.getUIContext().getHostContext() as Context; |
| 65 | + @State statusBarHeight: number = 0; | ||
| 66 | + | ||
| 67 | + async aboutToAppear(): Promise<void> { | ||
| 68 | + this.statusBarHeight = await getStatusBarHeight(this.context); | ||
| 69 | + } | ||
| 66 | 70 | ||
| 67 | build(): void { | 71 | build(): void { |
| 68 | NavDestination() { | 72 | NavDestination() { |
| @@ -83,10 +87,9 @@ struct ARMesh { | |||
| 83 | } | 87 | } |
| 84 | .onAppear(() => { | 88 | .onAppear(() => { |
| 85 | this.initARView(); | 89 | this.initARView(); |
| 86 | - this.getAvoidArea(); | ||
| 87 | }) | 90 | }) |
| 88 | - .onWillDisappear(() => { | 91 | + .onWillDisappear(async () => { |
| 89 | - this.stopARView(); | 92 | + await this.stopARView(); |
| 90 | }) | 93 | }) |
| 91 | .onShown(() => { | 94 | .onShown(() => { |
| 92 | this.resumeARView(); | 95 | this.resumeARView(); |
| @@ -101,12 +104,12 @@ struct ARMesh { | |||
| 101 | 104 | ||
| 102 | private objectCollisionDetection(event: ClickEvent): void { | 105 | private objectCollisionDetection(event: ClickEvent): void { |
| 103 | let x: number = this.getUIContext().vp2px(event.windowX); | 106 | let x: number = this.getUIContext().vp2px(event.windowX); |
| 104 | - let y: number = this.getUIContext().vp2px(event.windowY) - statusBarHeight; | 107 | + let y: number = this.getUIContext().vp2px(event.windowY) - this.statusBarHeight; |
| 105 | - Logger.info(`Get onclick position, x: ${x} y: ${y}.`); | 108 | + logger.info(`Get onclick position, x: ${x} y: ${y}.`); |
| 106 | 109 | ||
| 107 | try { | 110 | try { |
| 108 | let result: arEngine.ARHitResult[] = frame.hitTest(x, y); | 111 | let result: arEngine.ARHitResult[] = frame.hitTest(x, y); |
| 109 | - Logger.info(`The hitresult size is: ${result.length}.`); | 112 | + logger.info(`The hitresult size is: ${result.length}.`); |
| 110 | if (!result) { | 113 | if (!result) { |
| 111 | return; | 114 | return; |
| 112 | } | 115 | } |
| @@ -114,7 +117,7 @@ struct ARMesh { | |||
| 114 | for (let i = 0; i < result.length; i++) { | 117 | for (let i = 0; i < result.length; i++) { |
| 115 | let hitResult: arEngine.ARHitResult = result[i]; | 118 | let hitResult: arEngine.ARHitResult = result[i]; |
| 116 | let distance: number = hitResult.distance; | 119 | let distance: number = hitResult.distance; |
| 117 | - Logger.info(`The hitresult distance is: ${distance}.`); | 120 | + logger.info(`The hitresult distance is: ${distance}.`); |
| 118 | 121 | ||
| 119 | if (distance <= 0) { | 122 | if (distance <= 0) { |
| 120 | continue; | 123 | continue; |
| @@ -131,10 +134,10 @@ struct ARMesh { | |||
| 131 | hitAnchorList.splice(0, 1); | 134 | hitAnchorList.splice(0, 1); |
| 132 | } | 135 | } |
| 133 | } | 136 | } |
| 134 | - Logger.info('Succeeded in getting hit result.'); | 137 | + logger.info('Succeeded in getting hit result.'); |
| 135 | } catch (error) { | 138 | } catch (error) { |
| 136 | const err: BusinessError = error as BusinessError; | 139 | const err: BusinessError = error as BusinessError; |
| 137 | - Logger.error(`Failed to get hitResults. Code is ${err.code}, message is ${err.message}`); | 140 | + logger.error(`Failed to get hitResults. Code is ${err.code}, message is ${err.message}`); |
| 138 | } | 141 | } |
| 139 | } | 142 | } |
| 140 | 143 | ||
| @@ -155,34 +158,22 @@ struct ARMesh { | |||
| 155 | } | 158 | } |
| 156 | viewContext.init().then(() => { | 159 | viewContext.init().then(() => { |
| 157 | this.arContext = viewContext; | 160 | this.arContext = viewContext; |
| 158 | - Logger.info('Succeeded in initting ARView.'); | 161 | + logger.info('Succeeded in initting ARView.'); |
| 159 | }).catch((err: BusinessError) => { | 162 | }).catch((err: BusinessError) => { |
| 160 | - Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); | 163 | + logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); |
| 161 | }) | 164 | }) |
| 162 | }) | 165 | }) |
| 163 | } | 166 | } |
| 164 | 167 | ||
| 165 | - private getAvoidArea(): void { | 168 | + private async stopARView(): Promise<void> { |
| 166 | - let avoidAreaType: window.AvoidAreaType = window.AvoidAreaType.TYPE_SYSTEM; | ||
| 167 | - window.getLastWindow(this.context).then((data) => { | ||
| 168 | - // Get the top status bar height | ||
| 169 | - let avoidArea1: window.AvoidArea = data.getWindowAvoidArea(avoidAreaType); | ||
| 170 | - statusBarHeight = avoidArea1.topRect.height; | ||
| 171 | - Logger.info(`The statusBarHeight is ${statusBarHeight}.`); | ||
| 172 | - }).catch((err: BusinessError) => { | ||
| 173 | - Logger.error(`Failed to obtain the window. Code is ${err.code}, message is ${err.message}.`); | ||
| 174 | - }) | ||
| 175 | - } | ||
| 176 | - | ||
| 177 | - private stopARView(): void { | ||
| 178 | if (!this.arContext) { | 169 | if (!this.arContext) { |
| 179 | return; | 170 | return; |
| 180 | } | 171 | } |
| 181 | try { | 172 | try { |
| 182 | - this.arContext.destroy(); | 173 | + await this.arContext.destroy(); |
| 183 | } catch (error) { | 174 | } catch (error) { |
| 184 | const err: BusinessError = error as BusinessError; | 175 | const err: BusinessError = error as BusinessError; |
| 185 | - Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); | 176 | + logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); |
| 186 | } | 177 | } |
| 187 | } | 178 | } |
| 188 | 179 | ||
| @@ -195,7 +186,7 @@ struct ARMesh { | |||
| 195 | isStopFrameUpdate = false; | 186 | isStopFrameUpdate = false; |
| 196 | } catch (error) { | 187 | } catch (error) { |
| 197 | const err: BusinessError = error as BusinessError; | 188 | const err: BusinessError = error as BusinessError; |
| 198 | - Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); | 189 | + logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); |
| 199 | } | 190 | } |
| 200 | } | 191 | } |
| 201 | 192 | ||
| @@ -208,16 +199,16 @@ struct ARMesh { | |||
| 208 | } catch (error) { | 199 | } catch (error) { |
| 209 | isStopFrameUpdate = true; | 200 | isStopFrameUpdate = true; |
| 210 | const err: BusinessError = error as BusinessError; | 201 | const err: BusinessError = error as BusinessError; |
| 211 | - Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); | 202 | + logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); |
| 212 | } | 203 | } |
| 213 | } | 204 | } |
| 214 | } | 205 | } |
| 215 | 206 | ||
| 216 | class ARViewCallbackImpl extends arViewController.ARViewCallback { | 207 | class ARViewCallbackImpl extends arViewController.ARViewCallback { |
| 208 | + private scene: Scene | null = null; | ||
| 209 | + private shader: Shader | null = null; | ||
| 210 | + private material: ShaderMaterial | null = null; | ||
| 217 | private nodeKey: number = 0; | 211 | private nodeKey: number = 0; |
| 218 | - scene: Scene | null = null; | ||
| 219 | - shader: Shader | null = null; | ||
| 220 | - material: ShaderMaterial | null = null; | ||
| 221 | 212 | ||
| 222 | onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void { | 213 | onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void { |
| 223 | } | 214 | } |
| @@ -335,7 +326,7 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 335 | cubeGeometry.position.z = -pos.x; | 326 | cubeGeometry.position.z = -pos.x; |
| 336 | } catch (error) { | 327 | } catch (error) { |
| 337 | const err: BusinessError = error as BusinessError; | 328 | const err: BusinessError = error as BusinessError; |
| 338 | - Logger.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}.`); | 329 | + logger.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}.`); |
| 339 | } | 330 | } |
| 340 | } | 331 | } |
| 341 | } | 332 | } |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -29,17 +29,22 @@ import { | |||
| 29 | ShaderMaterial, | 29 | ShaderMaterial, |
| 30 | Vec3 | 30 | Vec3 |
| 31 | } from '@kit.ArkGraphics3D'; | 31 | } from '@kit.ArkGraphics3D'; |
| 32 | -import { Matrix4, window } from '@kit.ArkUI'; | 32 | +import { Matrix4 } from '@kit.ArkUI'; |
| 33 | import { BusinessError } from '@kit.BasicServicesKit'; | 33 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 34 | -import { Logger } from '../utils/Logger'; | 34 | +import { logger } from '../utils/Logger'; |
| 35 | -import { arrayBufferFloat32ToNumber, generateMeshIndex, generateMeshInput, getVertices } from '../utils/Utils'; | 35 | +import { |
| 36 | + arrayBufferFloat32ToNumber, | ||
| 37 | + generateMeshIndex, | ||
| 38 | + generateMeshInput, | ||
| 39 | + getStatusBarHeight, | ||
| 40 | + getVertices | ||
| 41 | +} from '../utils/Utils'; | ||
| 36 | 42 | ||
| 37 | @Builder | 43 | @Builder |
| 38 | export function ARWorldBuilder(): void { | 44 | export function ARWorldBuilder(): void { |
| 39 | ARWorld(); | 45 | ARWorld(); |
| 40 | } | 46 | } |
| 41 | 47 | ||
| 42 | -let statusBarHeight: number = 0; | ||
| 43 | let isDisplayCube: boolean = false; | 48 | let isDisplayCube: boolean = false; |
| 44 | let isStopFrameUpdate: boolean = false; | 49 | let isStopFrameUpdate: boolean = false; |
| 45 | let modelMat: Matrix4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; | 50 | let modelMat: Matrix4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; |
| @@ -63,6 +68,11 @@ interface CustomerMaterial extends Material { | |||
| 63 | struct ARWorld { | 68 | struct ARWorld { |
| 64 | @State arContext?: arViewController.ARViewContext = undefined; | 69 | @State arContext?: arViewController.ARViewContext = undefined; |
| 65 | @State context: Context = this.getUIContext().getHostContext() as Context; | 70 | @State context: Context = this.getUIContext().getHostContext() as Context; |
| 71 | + @State statusBarHeight: number = 0; | ||
| 72 | + | ||
| 73 | + async aboutToAppear(): Promise<void> { | ||
| 74 | + this.statusBarHeight = await getStatusBarHeight(this.context); | ||
| 75 | + } | ||
| 66 | 76 | ||
| 67 | build(): void { | 77 | build(): void { |
| 68 | NavDestination() { | 78 | NavDestination() { |
| @@ -81,12 +91,11 @@ struct ARWorld { | |||
| 81 | } | 91 | } |
| 82 | } | 92 | } |
| 83 | } | 93 | } |
| 84 | - .onAppear(() => { | 94 | + .onAppear(async () => { |
| 85 | this.initARView(); | 95 | this.initARView(); |
| 86 | - this.getAvoidArea(); | ||
| 87 | }) | 96 | }) |
| 88 | - .onWillDisappear(() => { | 97 | + .onWillDisappear(async () => { |
| 89 | - this.stopARView(); | 98 | + await this.stopARView(); |
| 90 | }) | 99 | }) |
| 91 | .onShown(() => { | 100 | .onShown(() => { |
| 92 | this.resumeARView(); | 101 | this.resumeARView(); |
| @@ -101,12 +110,12 @@ struct ARWorld { | |||
| 101 | 110 | ||
| 102 | private objectCollisionDetection(event: ClickEvent): void { | 111 | private objectCollisionDetection(event: ClickEvent): void { |
| 103 | let x: number = this.getUIContext().vp2px(event.windowX); | 112 | let x: number = this.getUIContext().vp2px(event.windowX); |
| 104 | - let y: number = this.getUIContext().vp2px(event.windowY) - statusBarHeight; | 113 | + let y: number = this.getUIContext().vp2px(event.windowY) - this.statusBarHeight; |
| 105 | - Logger.info(`Get onclick position, x: ${x} y: ${y}.`); | 114 | + logger.info(`Get onclick position, x: ${x} y: ${y}.`); |
| 106 | 115 | ||
| 107 | try { | 116 | try { |
| 108 | let result: arEngine.ARHitResult[] = frame.hitTest(x, y); | 117 | let result: arEngine.ARHitResult[] = frame.hitTest(x, y); |
| 109 | - Logger.info(`The hitresult size is: ${result.length}.`); | 118 | + logger.info(`The hitresult size is: ${result.length}.`); |
| 110 | if (!result) { | 119 | if (!result) { |
| 111 | return; | 120 | return; |
| 112 | } | 121 | } |
| @@ -123,7 +132,7 @@ struct ARWorld { | |||
| 123 | let hitPose: arEngine.ARPose = hitResult.getHitPose(); | 132 | let hitPose: arEngine.ARPose = hitResult.getHitPose(); |
| 124 | let inPolygon: boolean = hitPlane.isPoseInPolygon(hitPose); | 133 | let inPolygon: boolean = hitPlane.isPoseInPolygon(hitPose); |
| 125 | let distance: number = hitResult.distance; | 134 | let distance: number = hitResult.distance; |
| 126 | - Logger.info(`The hitresult inPolygon is: ${inPolygon}, distance is: ${distance}.`); | 135 | + logger.info(`The hitresult inPolygon is: ${inPolygon}, distance is: ${distance}.`); |
| 127 | 136 | ||
| 128 | if (!inPolygon || distance <= 0) { | 137 | if (!inPolygon || distance <= 0) { |
| 129 | continue; | 138 | continue; |
| @@ -140,10 +149,10 @@ struct ARWorld { | |||
| 140 | hitAnchorList.splice(0, 1); | 149 | hitAnchorList.splice(0, 1); |
| 141 | } | 150 | } |
| 142 | } | 151 | } |
| 143 | - Logger.info('Succeeded in getting hit result.'); | 152 | + logger.info('Succeeded in getting hit result.'); |
| 144 | } catch (error) { | 153 | } catch (error) { |
| 145 | const err: BusinessError = error as BusinessError; | 154 | const err: BusinessError = error as BusinessError; |
| 146 | - Logger.error(`Failed to get hitResults. Code is ${err.code}, message is ${err.message}.`); | 155 | + logger.error(`Failed to get hitResults. Code is ${err.code}, message is ${err.message}.`); |
| 147 | } | 156 | } |
| 148 | } | 157 | } |
| 149 | 158 | ||
| @@ -164,34 +173,22 @@ struct ARWorld { | |||
| 164 | } | 173 | } |
| 165 | viewContext.init().then(() => { | 174 | viewContext.init().then(() => { |
| 166 | this.arContext = viewContext; | 175 | this.arContext = viewContext; |
| 167 | - Logger.info('Succeeded in initting ARView.'); | 176 | + logger.info('Succeeded in initting ARView.'); |
| 168 | }).catch((err: BusinessError) => { | 177 | }).catch((err: BusinessError) => { |
| 169 | - Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); | 178 | + logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`); |
| 170 | }) | 179 | }) |
| 171 | }) | 180 | }) |
| 172 | } | 181 | } |
| 173 | 182 | ||
| 174 | - private getAvoidArea(): void { | 183 | + private async stopARView(): Promise<void> { |
| 175 | - let avoidAreaType: window.AvoidAreaType = window.AvoidAreaType.TYPE_SYSTEM; | ||
| 176 | - window.getLastWindow(this.context).then((data) => { | ||
| 177 | - // Get the top status bar height | ||
| 178 | - let avoidArea: window.AvoidArea = data.getWindowAvoidArea(avoidAreaType); | ||
| 179 | - statusBarHeight = avoidArea.topRect.height; | ||
| 180 | - Logger.info(`The statusBarHeight is ${statusBarHeight}.`); | ||
| 181 | - }).catch((err: BusinessError) => { | ||
| 182 | - Logger.error(`Failed to obtain the window. Code is ${err.code}, message is ${err.message}.`); | ||
| 183 | - }) | ||
| 184 | - } | ||
| 185 | - | ||
| 186 | - private stopARView(): void { | ||
| 187 | if (!this.arContext) { | 184 | if (!this.arContext) { |
| 188 | return; | 185 | return; |
| 189 | } | 186 | } |
| 190 | try { | 187 | try { |
| 191 | - this.arContext.destroy(); | 188 | + await this.arContext.destroy(); |
| 192 | } catch (error) { | 189 | } catch (error) { |
| 193 | const err: BusinessError = error as BusinessError; | 190 | const err: BusinessError = error as BusinessError; |
| 194 | - Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); | 191 | + logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`); |
| 195 | } | 192 | } |
| 196 | } | 193 | } |
| 197 | 194 | ||
| @@ -204,7 +201,7 @@ struct ARWorld { | |||
| 204 | isStopFrameUpdate = false; | 201 | isStopFrameUpdate = false; |
| 205 | } catch (error) { | 202 | } catch (error) { |
| 206 | const err: BusinessError = error as BusinessError; | 203 | const err: BusinessError = error as BusinessError; |
| 207 | - Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); | 204 | + logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`); |
| 208 | } | 205 | } |
| 209 | } | 206 | } |
| 210 | 207 | ||
| @@ -217,16 +214,16 @@ struct ARWorld { | |||
| 217 | isStopFrameUpdate = true; | 214 | isStopFrameUpdate = true; |
| 218 | } catch (error) { | 215 | } catch (error) { |
| 219 | const err: BusinessError = error as BusinessError; | 216 | const err: BusinessError = error as BusinessError; |
| 220 | - Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); | 217 | + logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`); |
| 221 | } | 218 | } |
| 222 | } | 219 | } |
| 223 | } | 220 | } |
| 224 | 221 | ||
| 225 | class ARViewCallbackImpl extends arViewController.ARViewCallback { | 222 | class ARViewCallbackImpl extends arViewController.ARViewCallback { |
| 223 | + private scene: Scene | null = null; | ||
| 224 | + private shader: Shader | null = null; | ||
| 225 | + private material: ShaderMaterial | null = null; | ||
| 226 | private nodeKey: number = 0; | 226 | private nodeKey: number = 0; |
| 227 | - scene: Scene | null = null; | ||
| 228 | - shader: Shader | null = null; | ||
| 229 | - material: ShaderMaterial | null = null; | ||
| 230 | 227 | ||
| 231 | onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void { | 228 | onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void { |
| 232 | } | 229 | } |
| @@ -267,14 +264,14 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 267 | for (let i = 0; i < trackables.length; ++i) { | 264 | for (let i = 0; i < trackables.length; ++i) { |
| 268 | if (trackables[i].state !== arEngine.ARTrackingState.TRACKING) { | 265 | if (trackables[i].state !== arEngine.ARTrackingState.TRACKING) { |
| 269 | // Do not render current plane when not in tracking state | 266 | // Do not render current plane when not in tracking state |
| 270 | - Logger.info('No plane in tracking state!'); | 267 | + logger.info('No plane in tracking state!'); |
| 271 | continue; | 268 | continue; |
| 272 | } | 269 | } |
| 273 | 270 | ||
| 274 | let plane: arEngine.ARPlane = trackables[i] as arEngine.ARPlane; | 271 | let plane: arEngine.ARPlane = trackables[i] as arEngine.ARPlane; |
| 275 | if (plane.getSubsumedBy() !== undefined) { | 272 | if (plane.getSubsumedBy() !== undefined) { |
| 276 | // Do not render current plane when parent plane exists | 273 | // Do not render current plane when parent plane exists |
| 277 | - Logger.info('Parent plane exists!'); | 274 | + logger.info('Parent plane exists!'); |
| 278 | continue; | 275 | continue; |
| 279 | } | 276 | } |
| 280 | 277 | ||
| @@ -363,7 +360,7 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback { | |||
| 363 | cubeGeometry.position.z = -pos.x; | 360 | cubeGeometry.position.z = -pos.x; |
| 364 | } catch (error) { | 361 | } catch (error) { |
| 365 | const err: BusinessError = error as BusinessError; | 362 | const err: BusinessError = error as BusinessError; |
| 366 | - Logger.error(`Failed to update data. Code is ${err.code}, message is ${err.message}.`); | 363 | + logger.error(`Failed to update data. Code is ${err.code}, message is ${err.message}.`); |
| 367 | } | 364 | } |
| 368 | } | 365 | } |
| 369 | } | 366 | } |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -15,15 +15,15 @@ | |||
| 15 | 15 | ||
| 16 | import { abilityAccessCtrl, PermissionRequestResult } from '@kit.AbilityKit'; | 16 | import { abilityAccessCtrl, PermissionRequestResult } from '@kit.AbilityKit'; |
| 17 | import { BusinessError } from '@kit.BasicServicesKit'; | 17 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 18 | -import { Logger } from '../utils/Logger'; | 18 | +import { logger } from '../utils/Logger'; |
| 19 | import { requestPermissionOnSetting } from '../utils/Utils'; | 19 | import { requestPermissionOnSetting } from '../utils/Utils'; |
| 20 | 20 | ||
| 21 | @Entry | 21 | @Entry |
| 22 | @Component | 22 | @Component |
| 23 | struct Selector { | 23 | struct Selector { |
| 24 | pageInfos: NavPathStack = new NavPathStack(); | 24 | pageInfos: NavPathStack = new NavPathStack(); |
| 25 | - private hasPermission: boolean = false; | ||
| 26 | @State context: Context = this.getUIContext().getHostContext() as Context; | 25 | @State context: Context = this.getUIContext().getHostContext() as Context; |
| 26 | + private hasPermission: boolean = false; | ||
| 27 | 27 | ||
| 28 | build(): void { | 28 | build(): void { |
| 29 | Navigation(this.pageInfos) { | 29 | Navigation(this.pageInfos) { |
| @@ -32,7 +32,8 @@ struct Selector { | |||
| 32 | this.sampleButton('ARMesh'); | 32 | this.sampleButton('ARMesh'); |
| 33 | this.sampleButton('ARDepth'); | 33 | this.sampleButton('ARDepth'); |
| 34 | this.sampleButton('ARImage'); | 34 | this.sampleButton('ARImage'); |
| 35 | - this.sampleButton('ARSemanticDense'); | 35 | + this.sampleButton('ARFace'); |
| 36 | + this.sampleButton('ARBody'); | ||
| 36 | } | 37 | } |
| 37 | .justifyContent(FlexAlign.SpaceEvenly) | 38 | .justifyContent(FlexAlign.SpaceEvenly) |
| 38 | .width('100%') | 39 | .width('100%') |
| @@ -62,7 +63,7 @@ struct Selector { | |||
| 62 | } | 63 | } |
| 63 | this.pageInfos.clear(); | 64 | this.pageInfos.clear(); |
| 64 | this.pageInfos.pushDestinationByName(sampleName, null).catch((err: BusinessError) => { | 65 | this.pageInfos.pushDestinationByName(sampleName, null).catch((err: BusinessError) => { |
| 65 | - Logger.error(`Failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`); | 66 | + logger.error(`Failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`); |
| 66 | }); | 67 | }); |
| 67 | }) | 68 | }) |
| 68 | } | 69 | } |
| @@ -74,14 +75,14 @@ struct Selector { | |||
| 74 | let grantStatus: number[] = data.authResults; | 75 | let grantStatus: number[] = data.authResults; |
| 75 | if (grantStatus[0] === 0) { | 76 | if (grantStatus[0] === 0) { |
| 76 | this.hasPermission = true; | 77 | this.hasPermission = true; |
| 77 | - Logger.info('Succeeded in getting requestPermission.'); | 78 | + logger.info('Succeeded in getting requestPermission.'); |
| 78 | } else { | 79 | } else { |
| 79 | this.hasPermission = false; | 80 | this.hasPermission = false; |
| 80 | - Logger.error('Failed to get requestPermission, user rejected.'); | 81 | + logger.error('Failed to get requestPermission, user rejected.'); |
| 81 | } | 82 | } |
| 82 | }) | 83 | }) |
| 83 | .catch((err: BusinessError) => { | 84 | .catch((err: BusinessError) => { |
| 84 | - Logger.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}.`); | 85 | + logger.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}.`); |
| 85 | }) | 86 | }) |
| 86 | } | 87 | } |
| 87 | } | 88 | } |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -42,4 +42,4 @@ class LoggerModel { | |||
| 42 | } | 42 | } |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | -export let Logger = new LoggerModel('[ArDemo][Js]'); | 45 | +export let logger = new LoggerModel('[ArDemo][Js]'); |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -13,11 +13,11 @@ | |||
| 13 | * limitations under the License. | 13 | * limitations under the License. |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | -import { Matrix4 } from '@kit.ArkUI'; | ||
| 17 | -import { Vec3 } from '@kit.ArkGraphics3D'; | ||
| 18 | import { abilityAccessCtrl } from '@kit.AbilityKit'; | 16 | import { abilityAccessCtrl } from '@kit.AbilityKit'; |
| 17 | +import { Vec3 } from '@kit.ArkGraphics3D'; | ||
| 18 | +import { Matrix4, window } from '@kit.ArkUI'; | ||
| 19 | import { BusinessError } from '@kit.BasicServicesKit'; | 19 | import { BusinessError } from '@kit.BasicServicesKit'; |
| 20 | -import { Logger } from './Logger'; | 20 | +import { logger } from './Logger'; |
| 21 | 21 | ||
| 22 | export function arrayBufferFloat32ToNumber(buffer: ArrayBuffer): number[] { | 22 | export function arrayBufferFloat32ToNumber(buffer: ArrayBuffer): number[] { |
| 23 | let view: Float32Array = new Float32Array(buffer); | 23 | let view: Float32Array = new Float32Array(buffer); |
| @@ -38,12 +38,9 @@ export function getVertices(mat: Matrix4, point: number[]): Vec3[] { | |||
| 38 | let result: Vec3[] = []; | 38 | let result: Vec3[] = []; |
| 39 | for (let i = 0; i < point.length; i += 2) { | 39 | for (let i = 0; i < point.length; i += 2) { |
| 40 | let single: Vec3 = { | 40 | let single: Vec3 = { |
| 41 | - x: (mat[2] * point[i] + mat[6] * 0 | 41 | + x: (mat[2] * point[i] + mat[6] * 0 + mat[10] * point[i + 1] + mat[14] * 1.0), |
| 42 | - + mat[10] * point[i + 1] + mat[14] * 1.0), | 42 | + y: mat[1] * point[i] + mat[5] * 0 + mat[9] * point[i + 1] + mat[13] * 1.0, |
| 43 | - y: mat[1] * point[i] + mat[5] * 0 | 43 | + z: -(mat[0] * point[i] + mat[4] * 0 + mat[8] * point[i + 1] + mat[12] * 1.0) |
| 44 | - + mat[9] * point[i + 1] + mat[13] * 1.0, | ||
| 45 | - z: -(mat[0] * point[i] + mat[4] * 0 | ||
| 46 | - + mat[8] * point[i + 1] + mat[12] * 1.0), | ||
| 47 | } | 44 | } |
| 48 | result.push(single); | 45 | result.push(single); |
| 49 | } | 46 | } |
| @@ -54,12 +51,9 @@ export function getImageVertices(mat: Matrix4, point: number[]): Vec3[] { | |||
| 54 | let result: Vec3[] = []; | 51 | let result: Vec3[] = []; |
| 55 | for (let i = 2; i < point.length; i += 2) { | 52 | for (let i = 2; i < point.length; i += 2) { |
| 56 | let single: Vec3 = { | 53 | let single: Vec3 = { |
| 57 | - x: (mat[2] * point[i] + mat[6] * 0 | 54 | + x: (mat[2] * point[i] + mat[6] * 0 + mat[10] * point[i + 1] + mat[14] * 1.0), |
| 58 | - + mat[10] * point[i + 1] + mat[14] * 1.0), | 55 | + y: mat[1] * point[i] + mat[5] * 0 + mat[9] * point[i + 1] + mat[13] * 1.0, |
| 59 | - y: mat[1] * point[i] + mat[5] * 0 | 56 | + z: -(mat[0] * point[i] + mat[4] * 0 + mat[8] * point[i + 1] + mat[12] * 1.0) |
| 60 | - + mat[9] * point[i + 1] + mat[13] * 1.0, | ||
| 61 | - z: -(mat[0] * point[i] + mat[4] * 0 | ||
| 62 | - + mat[8] * point[i + 1] + mat[12] * 1.0), | ||
| 63 | } | 57 | } |
| 64 | result.push(single); | 58 | result.push(single); |
| 65 | } | 59 | } |
| @@ -182,13 +176,89 @@ export async function requestPermissionOnSetting(context: Context): Promise<bool | |||
| 182 | let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); | 176 | let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); |
| 183 | await atManager.requestPermissionOnSetting(context, ['ohos.permission.CAMERA']) | 177 | await atManager.requestPermissionOnSetting(context, ['ohos.permission.CAMERA']) |
| 184 | .then((data: abilityAccessCtrl.GrantStatus[]) => { | 178 | .then((data: abilityAccessCtrl.GrantStatus[]) => { |
| 185 | - Logger.info('data:' + JSON.stringify(data)); | 179 | + logger.info('data:' + JSON.stringify(data)); |
| 186 | if (data[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { | 180 | if (data[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { |
| 187 | requestResult = true; | 181 | requestResult = true; |
| 188 | } | 182 | } |
| 189 | }) | 183 | }) |
| 190 | .catch((err: BusinessError) => { | 184 | .catch((err: BusinessError) => { |
| 191 | - Logger.error('data:' + JSON.stringify(err)); | 185 | + logger.error('data:' + JSON.stringify(err)); |
| 192 | }) | 186 | }) |
| 193 | return requestResult; | 187 | return requestResult; |
| 194 | } | 188 | } |
| 189 | + | ||
| 190 | +export function getFaceFrontVertices(mat: Matrix4, point: number[]): Vec3[] { | ||
| 191 | + let result: Vec3[] = []; | ||
| 192 | + for (let i = 0; i < point.length; i += 3) { | ||
| 193 | + let single: Vec3 = { | ||
| 194 | + x: (mat[2] * point[i] + mat[6] * point[i + 1] + mat[10] * point[i + 2] + mat[14] * 1.0), | ||
| 195 | + y: -(mat[1] * point[i] + mat[5] * point[i + 1] + mat[9] * point[i + 2] + mat[13] * 1.0), | ||
| 196 | + z: -(mat[0] * point[i] + mat[4] * point[i + 1] + mat[8] * point[i + 2] + mat[12] * 1.0), | ||
| 197 | + } | ||
| 198 | + result.push(single); | ||
| 199 | + } | ||
| 200 | + return result; | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | +export function getFaceRearVertices(mat: Matrix4, point: number[]): Vec3[] { | ||
| 204 | + let result: Vec3[] = []; | ||
| 205 | + for (let i = 0; i < point.length; i += 3) { | ||
| 206 | + let single: Vec3 = { | ||
| 207 | + x: (mat[2] * point[i] + mat[6] * point[i + 1] + mat[10] * point[i + 2] + mat[14] * 1.0), | ||
| 208 | + y: (mat[1] * point[i] + mat[5] * point[i + 1] + mat[9] * point[i + 2] + mat[13] * 1.0), | ||
| 209 | + z: -(mat[0] * point[i] + mat[4] * point[i + 1] + mat[8] * point[i + 2] + mat[12] * 1.0), | ||
| 210 | + } | ||
| 211 | + result.push(single); | ||
| 212 | + } | ||
| 213 | + return result; | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +export function generateFaceMeshIndex(input: Map<number, number[]>, offset: number): number[] { | ||
| 217 | + let result: number[] = [] | ||
| 218 | + let start = 0; | ||
| 219 | + | ||
| 220 | + for (let i = 0; i < input.size; i++) { | ||
| 221 | + let tmp = input.get(i); | ||
| 222 | + if (tmp) { | ||
| 223 | + for (let j = 0; j < tmp.length; j++) { | ||
| 224 | + result.push(tmp[j] + start) | ||
| 225 | + } | ||
| 226 | + } | ||
| 227 | + start += offset; | ||
| 228 | + } | ||
| 229 | + return result | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | +export async function getStatusBarHeight(context: Context): Promise<number> { | ||
| 233 | + let lastWindow: window.Window | undefined; | ||
| 234 | + let statusBarHeight: number = 0; | ||
| 235 | + | ||
| 236 | + try { | ||
| 237 | + lastWindow = await window.getLastWindow(context); | ||
| 238 | + } catch (error) { | ||
| 239 | + const err: BusinessError = error as BusinessError; | ||
| 240 | + logger.error(`ailed to obtain the window. Code is ${err.code}, message is ${err.message}`); | ||
| 241 | + return statusBarHeight; | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + try { | ||
| 245 | + let avoidArea: window.AvoidArea = lastWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); | ||
| 246 | + statusBarHeight = avoidArea.topRect.height; | ||
| 247 | + logger.info(`the statusBarHeight is ${statusBarHeight}`); | ||
| 248 | + } catch (error) { | ||
| 249 | + const err: BusinessError = error as BusinessError; | ||
| 250 | + logger.error(`Failed to get avoid area. Code is ${err.code}, message is ${err.message}`); | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + return statusBarHeight; | ||
| 254 | +} | ||
| 255 | + | ||
| 256 | +export function getResourceString(context: Context, resourceName: string): string { | ||
| 257 | + try { | ||
| 258 | + return context.resourceManager.getStringByNameSync(resourceName); | ||
| 259 | + } catch (error) { | ||
| 260 | + const err: BusinessError = error as BusinessError; | ||
| 261 | + logger.warn(`Failed to get string ${resourceName}. Code is ${err.code}, message is ${err.message}`); | ||
| 262 | + return ''; | ||
| 263 | + } | ||
| 264 | +} | ||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. | 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with 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 | 5 | * You may obtain a copy of the License at |
| @@ -21,7 +21,8 @@ | |||
| 21 | "mainElement": "EntryAbility", | 21 | "mainElement": "EntryAbility", |
| 22 | "deviceTypes": [ | 22 | "deviceTypes": [ |
| 23 | "phone", | 23 | "phone", |
| 24 | - "tablet" | 24 | + "tablet", |
| 25 | + "tv" | ||
| 25 | ], | 26 | ], |
| 26 | "deliveryWithInstall": true, | 27 | "deliveryWithInstall": true, |
| 27 | "installationFree": false, | 28 | "installationFree": false, |
| @@ -47,13 +47,20 @@ | |||
| 47 | "data": { | 47 | "data": { |
| 48 | "description": "This is ARWorld." | 48 | "description": "This is ARWorld." |
| 49 | } | 49 | } |
| 50 | + },{ | ||
| 51 | + "name": "ARFace", | ||
| 52 | + "pageSourceFile": "src/main/ets/pages/ARFace.ets", | ||
| 53 | + "buildFunction": "ARFaceBuilder", | ||
| 54 | + "data": { | ||
| 55 | + "description": "This is ARFace" | ||
| 56 | + } | ||
| 50 | }, | 57 | }, |
| 51 | { | 58 | { |
| 52 | - "name": "ARSemanticDense", | 59 | + "name": "ARBody", |
| 53 | - "pageSourceFile": "src/main/ets/pages/ARSemanticDense.ets", | 60 | + "pageSourceFile": "src/main/ets/pages/ARBody.ets", |
| 54 | - "buildFunction": "ARSemanticDenseBuilder", | 61 | + "buildFunction": "ARBodyBuilder", |
| 55 | "data": { | 62 | "data": { |
| 56 | - "description": "This is ARSemanticDense." | 63 | + "description": "This is ARBody." |
| 57 | } | 64 | } |
| 58 | } | 65 | } |
| 59 | ] | 66 | ] |
| @@ -0,0 +1,68 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 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 | + "compatibility_info" : { "version" : "22.00", "type" : "shader" }, | ||
| 17 | + "vert": "3dshaders://shader/core3d_dm_fw.vert.spv", | ||
| 18 | + "frag": "appshaders://custom_shader/custom_material_sample.frag.spv", | ||
| 19 | + "vertexInputDeclaration": "3dvertexinputdeclarations://core3d_dm_fw.shadervid", | ||
| 20 | + "state": { | ||
| 21 | + "rasterizationState": { | ||
| 22 | + "enableDepthClamp": false, | ||
| 23 | + "enableDepthBias": false, | ||
| 24 | + "enableRasterizerDiscard": false, | ||
| 25 | + "polygonMode": "fill", | ||
| 26 | + "cullModeFlags": "back", | ||
| 27 | + "frontFace": "counter_clockwise" | ||
| 28 | + }, | ||
| 29 | + "depthStencilState": { | ||
| 30 | + "enableDepthTest": true, | ||
| 31 | + "enableDepthWrite": true, | ||
| 32 | + "enableDepthBoundsTest": false, | ||
| 33 | + "enableStencilTest": false, | ||
| 34 | + "depthCompareOp": "less_or_equal" | ||
| 35 | + }, | ||
| 36 | + "colorBlendState": { | ||
| 37 | + "colorAttachments": [ | ||
| 38 | + { | ||
| 39 | + "enableBlend": true, | ||
| 40 | + "colorWriteMask": "r_bit|g_bit|b_bit|a_bit", | ||
| 41 | + "srcColorBlendFactor": "one", | ||
| 42 | + "dstColorBlendFactor": "one_minus_src_alpha", | ||
| 43 | + "colorBlendOp": "add", | ||
| 44 | + "srcAlphaBlendFactor": "one", | ||
| 45 | + "dstAlphaBlendFactor": "one_minus_src_alpha", | ||
| 46 | + "alphaBlendOp": "add" | ||
| 47 | + } | ||
| 48 | + ] | ||
| 49 | + } | ||
| 50 | + }, | ||
| 51 | + "materialMetadata": [ | ||
| 52 | + { | ||
| 53 | + "name": "MaterialComponent", | ||
| 54 | + "customProperties": [ | ||
| 55 | + { | ||
| 56 | + "data": [ | ||
| 57 | + { | ||
| 58 | + "name": "vec_1", | ||
| 59 | + "displayName": "Color", | ||
| 60 | + "type": "vec4", | ||
| 61 | + "value" : [0.0,0.6,0.6,0.4] | ||
| 62 | + } | ||
| 63 | + ] | ||
| 64 | + } | ||
| 65 | + ] | ||
| 66 | + } | ||
| 67 | + ] | ||
| 68 | +} | ||
| @@ -1,3 +1,17 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 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 | + */ | ||
| 1 | { | 15 | { |
| 2 | "compatibility_info" : { "version" : "22.00", "type" : "shader" }, | 16 | "compatibility_info" : { "version" : "22.00", "type" : "shader" }, |
| 3 | "vert": "appshaders://custom_shader/custom_material_sample.vert.spv", | 17 | "vert": "appshaders://custom_shader/custom_material_sample.vert.spv", |
| @@ -1,3 +1,17 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 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 | + */ | ||
| 1 | { | 15 | { |
| 2 | "compatibility_info" : { "version" : "22.00", "type" : "shader" }, | 16 | "compatibility_info" : { "version" : "22.00", "type" : "shader" }, |
| 3 | "vert": "3dshaders://shader/core3d_dm_fw.vert.spv", | 17 | "vert": "3dshaders://shader/core3d_dm_fw.vert.spv", |