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 colorFlag: number = 0; 21let intensityFlag: number = 0; 22let shadowFlag: boolean = true; 23 24const colors: scene3d.Color[] = [ 25 { r: 0.8, g: 0.1, b: 0.2, a: 1.0 }, // color 1 26 { r: 0.1, g: 0.8, b: 0.2, a: 1.0 }, // color 2 27 { r: 0.2, g: 0.1, b: 0.8, a: 1.0 }, // color 3 28]; 29 30const intensities: number[] = [ 31 100.0, 500.0, 1000.0, 1500.0, 2000.0, // light intensity 32] 33 34function sub(l: scene3d.Vec3, r: scene3d.Vec3): scene3d.Vec3 { 35 return { x: l.x - r.x, y: l.y - r.y, z: l.z - r.z }; 36} 37 38function dot(l: scene3d.Vec3, r: scene3d.Vec3): number { 39 return l.x * r.x + l.y * r.y + r.z * l.z; 40} 41 42function normalize(l: scene3d.Vec3): scene3d.Vec3 { 43 let d = Math.sqrt(dot(l, l)); 44 return { x: l.x / d, y: l.y / d, z: l.z / d }; 45} 46 47function cross(l: scene3d.Vec3, r: scene3d.Vec3): scene3d.Vec3 { 48 return { x: (l.y * r.z - l.z * r.y), y: (l.z * r.x - l.x * r.z), z: (l.x * r.y - l.y * r.x) }; 49} 50 51function mul(l: scene3d.Quaternion, d: number): scene3d.Quaternion { 52 return { x: l.x * d, y: l.y * d, z: l.z * d, w: l.w * d }; 53} 54 55function lookAt(cam: scene3d.Node, eye: scene3d.Vec3, center: scene3d.Vec3, up: scene3d.Vec3) { 56 57 let t: number; 58 59 let q: scene3d.Quaternion = { x: 0.0, y: 0.0, z: 0.0, w: 0.0 }; 60 let f = normalize(sub(center, eye)); 61 let m0 = normalize(cross(f, up)); 62 let m1 = cross(m0, f); 63 let m2: scene3d.Vec3 = { x: -f.x, y: -f.y, z: -f.z }; 64 if (m2.z < 0) { 65 if (m0.x > m1.y) { 66 t = 1.0 + m0.x - m1.y - m2.z; 67 q = { x: t, y: m0.y + m1.x, z: m2.x + m0.z, w: m1.z - m2.y }; 68 } 69 else { 70 t = 1.0 - m0.x + m1.y - m2.z; 71 q = { x: m0.y + m1.x, y: t, z: m1.z + m2.y, w: m2.x - m0.z }; 72 } 73 } 74 else { 75 if (m0.x < -m1.y) { 76 t = 1.0 - m0.x - m1.y + m2.z; 77 q = { x: m2.x + m0.z, y: m1.z + m2.y, z: t, w: m0.y - m1.x }; 78 } 79 else { 80 t = 1.0 + m0.x + m1.y + m2.z; 81 q = { x: m1.z - m2.y, y: m2.x - m0.z, z: m0.y - m1.x, w: t }; 82 } 83 } 84 cam.position = eye; 85 cam.rotation = mul(q, 0.5 / Math.sqrt(t)); 86} 87 88@Entry 89@Component 90struct nodeLight { 91 scene: scene3d.Scene | null = null; 92 @State sceneOpt: SceneOptions | null = null; 93 cam: scene3d.Camera | null = null; 94 directionalLight: scene3d.DirectionalLight | null | undefined = null; 95 spotLight: scene3d.SpotLight | null = null; 96 @State lgt: scene3d.Light | null = null; 97 radianceImg1: scene3d.Image | null = null; 98 99 onPageShow(): void { 100 this.init(); 101 } 102 onPageHide(): void { 103 if (this.scene) { 104 this.scene.destroy(); 105 } 106 this.cam = null; 107 this.scene = null; 108 } 109 110 init(): void { 111 if (this.scene === null) { 112 scene3d.Scene.load($rawfile("gltf/CubeWithFloor/glTF/AnimatedCube.gltf")) 113 .then(async (result: scene3d.Scene) => { 114 this.scene = result; 115 this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions; 116 let rf: scene3d.SceneResourceFactory = this.scene.getResourceFactory(); 117 this.cam = await rf.createCamera({ "name": "Camera1" }); 118 this.cam.position.z = 5; 119 this.cam.enabled = true; 120 // camera look at direction 121 lookAt(this.cam, { x: 10, y: 5, z: 15 }, { x: 0, y: 0.0, z: 0.0 }, { x: 0, y: 1, z: 0 }); 122 123 this.radianceImg1 = await rf.createImage({ 124 name: "radianceImg1", 125 uri: $rawfile("gltf/Environment/glTF/images/quarry_02_2k_radiance.ktx") 126 }); 127 128 this.scene.environment.radianceImage = this.radianceImg1; 129 130 this.directionalLight = 131 await this.scene?.getResourceFactory().createLight({ 132 "name": "DirectionalLight1" 133 }, scene3d.LightType.DIRECTIONAL) as scene3d.DirectionalLight; 134 135 // light look at direction 136 lookAt(this.directionalLight, { x: 10.0, y: 10.0, z: 10.0 }, { x: 0.0, y: 0.0, z: 0.0 }, { 137 x: 0.0, 138 y: 1.0, 139 z: 0.0 140 }); 141 142 this.directionalLight.enabled = false; 143 144 this.spotLight = 145 await this.scene?.getResourceFactory().createLight({ 146 "name": "SpotLight1" 147 }, scene3d.LightType.SPOT) as scene3d.SpotLight; 148 // spot light look at direction 149 lookAt(this.spotLight, { x: 6, y: 6, z: -6 }, { x: 0, y: 0.0, z: 0.0 }, { x: 0, y: 1, z: 0 }); 150 151 this.spotLight.enabled = true; 152 153 this.lgt = this.spotLight; 154 this.UpdateLights(); 155 }) 156 .catch((reason: string) => { 157 Logger.error("init error", reason); 158 }); 159 } 160 } 161 162 UpdateLights(): void { 163 if (this.lgt) { 164 165 this.lgt.color = colors[colorFlag]; 166 this.lgt.intensity = intensities[intensityFlag]; 167 if (this.lgt.lightType === scene3d.LightType.DIRECTIONAL) { 168 this.lgt.intensity = intensities[intensityFlag] / 50.0; // just reduce some intensity when directional light 169 } 170 this.lgt.shadowEnabled = shadowFlag; 171 } 172 } 173 174 build() { 175 Row() { 176 Column() { 177 Column() { 178 if (this.sceneOpt) { 179 Component3D(this.sceneOpt) 180 .renderWidth('60%') 181 .renderHeight('60%') 182 } 183 else { 184 Text("loading 1..."); 185 } 186 } 187 .height('30%') 188 189 if (this.lgt) { 190 Button('Switch light type (' + (this.lgt.lightType === scene3d.LightType.DIRECTIONAL ? 'DIRECTIONAL' : 'SPOT') + ')') 191 .onClick(() => { 192 if (this.lgt) { 193 this.lgt.enabled = false; 194 195 if (this.lgt.lightType === scene3d.LightType.DIRECTIONAL) { 196 this.lgt = this.spotLight; 197 } else if (this.directionalLight) { 198 this.lgt = this.directionalLight; 199 } 200 } 201 202 if (this.lgt) { 203 this.lgt.enabled = true; 204 this.UpdateLights(); 205 } 206 }).id('switch_light') 207 } 208 209 if (this.lgt) { 210 Button(this.lgt.enabled ? 'Disable' : 'Enable').onClick(() => { 211 if (!this.scene || !this.lgt) { 212 return; 213 } 214 215 this.lgt.enabled = !this.lgt.enabled; 216 }).id('enable_light'); 217 218 if (this.lgt.enabled) { 219 Button('change color').onClick(() => { 220 if (!this.scene || !this.lgt) { 221 return; 222 } 223 224 colorFlag = ++colorFlag % colors.length; 225 this.UpdateLights(); 226 227 }).id('change_color'); 228 229 Button('Change intensity (' + this.lgt.intensity + ')').onClick(() => { 230 if (!this.scene || !this.lgt) { 231 return; 232 } 233 234 intensityFlag = (intensityFlag + 1) % intensities.length; 235 this.UpdateLights(); 236 }).id('change_intensity'); 237 238 Button('Shadows (' + (this.lgt.shadowEnabled ? 'enabled' : 'disabled') + ')').onClick(() => { 239 if (!this.scene || !this.lgt) { 240 return; 241 } 242 shadowFlag = !shadowFlag; 243 this.UpdateLights(); 244 }).id('enable_shadow'); 245 246 Button('change light rotation').onClick(() => { 247 if (!this.scene || !this.lgt || this.lgt.lightType != scene3d.LightType.DIRECTIONAL) { 248 return; 249 } 250 this.lgt.position = { x: 4.0, y: 5.0, z: 6.0 }; 251 this.lgt.rotation = { x: 0.3, y: 0.4, z: 0.5, w: 0.2 }; 252 }); 253 } 254 } 255 256 Button('back').onClick(() => { 257 router.back() 258 }).id('light_back') 259 } 260 .width('100%') 261 } 262 .height('100%') 263 } 264}