1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import scene3d from '@ohos.graphics.scene' 17import router from '@ohos.router'; 18import Logger from '../util/Logger'; 19 20let fovFlag: number = 0; 21let enabledFlag: number = 0; 22let TonemapTypeFlag: number = 0; 23let clearColorFlag: number = 0; 24 25@Entry 26@Component 27struct nodeCamera { 28 scene: scene3d.Scene | null = null; 29 @State sceneOpt: SceneOptions | null = null; 30 cam: scene3d.Camera | null = null; 31 @State nearPlaneValue: number = 0.1; 32 @State farPlaneValue: number = 100; 33 @State tonemapExposure: number = 1; 34 35 onPageShow(): void { 36 this.init(); 37 } 38 39 onPageHide(): void { 40 if (this.scene) { 41 this.scene.destroy(); 42 } 43 44 this.cam = null; 45 this.scene = null; 46 } 47 48 init(): void { 49 if (this.scene === null) { 50 scene3d.Scene.load($rawfile("gltf/DamagedHelmet/glTF/DamagedHelmet.glb")) 51 .then(async (result: scene3d.Scene) => { 52 this.scene = result; 53 this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions; 54 let rf: scene3d.SceneResourceFactory = this.scene.getResourceFactory(); 55 this.cam = await rf.createCamera({ "name": "Camera1" }); 56 this.cam.position.z = 5; // move camera to position 5 on z 57 this.cam.enabled = true; 58 this.cam.postProcess = { 59 toneMapping: { 60 type: scene3d.ToneMappingType.ACES, 61 exposure: 1.0, 62 } as scene3d.ToneMappingSettings 63 } 64 let env = await rf.createEnvironment({ "name": "Env" }); 65 this.scene.environment.backgroundType = scene3d.EnvironmentBackgroundType.BACKGROUND_NONE; 66 }) 67 .catch((reason: string) => { 68 Logger.error("init error", reason); 69 }); 70 } 71 } 72 73 build() { 74 Row() { 75 Column() { 76 Stack() { 77 Text('HELLO WORLD').height('100%').width('100%').backgroundColor(Color.Green) 78 79 if (this.sceneOpt) { 80 Component3D(this.sceneOpt) 81 .renderWidth('60%') 82 .renderHeight('60%') 83 .backgroundColor(Color.Transparent) 84 .width("90%") 85 .height('100%') 86 } 87 else { 88 Text("loading 1..."); 89 } 90 } 91 .height('30%') 92 93 Button('change fov').onClick(() => { 94 if (!this.scene || !this.cam) { 95 return; 96 } 97 const RADIAN: number = Math.PI / 180; 98 const FOV_COUNT: number = 3; 99 const FOV_0: number = 0; 100 const FOV_1: number = 1; 101 const FOV_2: number = 2; 102 fovFlag = ++fovFlag % FOV_COUNT; 103 if (fovFlag === FOV_0) { 104 let degree = 60; // 60 degree 105 this.cam.fov = degree *RADIAN; 106 } 107 else if (fovFlag === FOV_1) { 108 let degree = 90; // 90 degree 109 this.cam.fov = degree * RADIAN; 110 } 111 else if (fovFlag === FOV_2) { 112 let degree = 45; // 45 degree 113 this.cam.fov = degree * RADIAN; 114 } 115 }).id('change_fov'); 116 117 Column() { 118 Text("nearPlane: " + this.nearPlaneValue.toFixed(1)).fontSize(12) 119 Slider({ 120 value: this.nearPlaneValue, 121 min: 0.1, // near plane min 122 max: 10, // near plane far 123 step: 0.1, // step 124 style: SliderStyle.OutSet 125 }) 126 .showTips(false) 127 .onChange((value: number, mode: SliderChangeMode) => { 128 this.nearPlaneValue = value; 129 if (mode === SliderChangeMode.End) { 130 if (!this.scene || !this.cam) { 131 return; 132 } 133 this.cam.nearPlane = value; 134 } 135 }) 136 .width("100%") 137 } 138 .alignItems(HorizontalAlign.Start) 139 .width('100%') 140 141 Column() { 142 Text("farPlane: " + this.farPlaneValue.toFixed(1)).fontSize(12) 143 Slider({ 144 value: this.farPlaneValue, 145 min: 0.1, // far plane min 146 max: 100, // far plane max 147 step: 1, // step 148 style: SliderStyle.OutSet 149 }) 150 .showTips(false) 151 .onChange((value: number, mode: SliderChangeMode) => { 152 this.farPlaneValue = value; 153 if (mode === SliderChangeMode.End) { 154 if (!this.scene || !this.cam) { 155 return; 156 } 157 this.cam.farPlane = value; 158 } 159 }) 160 .width("100%") 161 } 162 .alignItems(HorizontalAlign.Start) 163 .width('100%') 164 165 Button('enabled').onClick(() => { 166 if (!this.scene || !this.cam) { 167 return; 168 } 169 this.cam.enabled = !this.cam.enabled; 170 }).id('camera_enabled'); 171 172 Button('Change Tonemap Type').onClick(() => { 173 if (!this.scene || !this.cam || !this.cam.postProcess || !this.cam.postProcess.toneMapping) { 174 return; 175 } 176 let type: scene3d.ToneMappingType = scene3d.ToneMappingType.ACES; 177 const TONE_MAPPING_COUNT: number = 3; 178 const TONE_MAPPING_0: number = 0; 179 const TONE_MAPPING_1: number = 1; 180 const TONE_MAPPING_2: number = 2; 181 TonemapTypeFlag = ++TonemapTypeFlag % TONE_MAPPING_COUNT; 182 if (TonemapTypeFlag === TONE_MAPPING_0) { 183 type = scene3d.ToneMappingType.ACES; 184 } else if (TonemapTypeFlag === TONE_MAPPING_1) { 185 type = scene3d.ToneMappingType.ACES_2020; 186 } else if (TonemapTypeFlag === TONE_MAPPING_2) { 187 type = scene3d.ToneMappingType.FILMIC; 188 } 189 this.cam.postProcess = { 190 toneMapping: { 191 exposure: this.cam.postProcess.toneMapping.exposure, 192 type: type 193 } 194 }; 195 }).id('change_tonemapping'); 196 197 Column() { 198 Text("Tonemap Exposure: " + this.tonemapExposure.toFixed(1)).fontSize(12) 199 Slider({ 200 value: this.tonemapExposure, 201 min: 0, // tone mapping exposure min 202 max: 10, // tone mapping exposure max 203 step: 0.1, // tone mapping exposure step 204 style: SliderStyle.OutSet 205 }) 206 .showTips(false) 207 .onChange((value: number, mode: SliderChangeMode) => { 208 this.tonemapExposure = value; 209 if (mode === SliderChangeMode.End) { 210 if (!this.scene || !this.cam || !this.cam.postProcess || !this.cam.postProcess.toneMapping) { 211 return; 212 } 213 this.cam.postProcess = { 214 toneMapping: { 215 exposure: value, 216 type: this.cam.postProcess.toneMapping.type 217 } 218 } 219 } 220 }) 221 .width("100%") 222 } 223 .alignItems(HorizontalAlign.Start) 224 .width('100%') 225 226 Button('set clearColor').onClick(() => { 227 if (!this.scene || !this.cam) { 228 return; 229 } 230 const CLEAR_COLOR_COUNT: number = 3; 231 const CLEAR_COLOR_0: number = 0; 232 const CLEAR_COLOR_1: number = 1; 233 const CLEAR_COLOR_2: number = 2; 234 235 clearColorFlag = ++clearColorFlag % CLEAR_COLOR_COUNT; 236 if (clearColorFlag === CLEAR_COLOR_0) { 237 this.cam.clearColor = null; 238 } 239 else if (clearColorFlag === CLEAR_COLOR_1) { 240 this.cam.clearColor = { r: 0, g: 0, b: 0, a: 0.0 }; 241 } 242 else if (clearColorFlag === CLEAR_COLOR_2) { 243 this.cam.clearColor = { r: 1, g: 0, b: 0, a: 1 }; 244 } 245 }).id('clear_color'); 246 247 Button('back').onClick(() => { 248 router.back() 249 }).id('camera_back') 250 } 251 .width('100%') 252 } 253 .height('100%') 254 } 255}