• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}