Skip to content

Commit 5285174

Browse files
Chau TranChau Tran
Chau Tran
authored and
Chau Tran
committed
feat(soba): migrate camera shake
1 parent 1c69a34 commit 5285174

File tree

3 files changed

+250
-0
lines changed

3 files changed

+250
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { Component, CUSTOM_ELEMENTS_SCHEMA, Input } from '@angular/core';
2+
import { Meta, moduleMetadata } from '@storybook/angular';
3+
import { NgtArgs, NgtBeforeRenderEvent } from 'angular-three';
4+
import { NgtsOrbitControls } from 'angular-three-soba/controls';
5+
import { NgtsCameraShake } from 'angular-three-soba/staging';
6+
import * as THREE from 'three';
7+
import { makeStoryObject, number, StorybookSetup } from '../setup-canvas';
8+
9+
const numberArgs = number(0.05, { range: true, max: 1, min: 0, step: 0.05 });
10+
const frequencyArgs = number(0.8, { range: true, max: 10, min: 0, step: 0.1 });
11+
const argsOptions = {
12+
maxPitch: numberArgs,
13+
maxRoll: numberArgs,
14+
maxYaw: numberArgs,
15+
pitchFrequency: frequencyArgs,
16+
rollFrequency: frequencyArgs,
17+
yawFrequency: frequencyArgs,
18+
};
19+
20+
@Component({
21+
selector: 'CameraShakeScene',
22+
standalone: true,
23+
template: `
24+
<ngt-mesh (beforeRender)="onBeforeRender($event)">
25+
<ngt-box-geometry *args="[2, 2, 2]" />
26+
<ngt-mesh-standard-material [wireframe]="true" />
27+
</ngt-mesh>
28+
<ngt-mesh [position]="[0, -6, 0]" [rotation]="[Math.PI / -2, 0, 0]">
29+
<ngt-plane-geometry *args="[200, 200, 75, 75]" />
30+
<ngt-mesh-basic-material [wireframe]="true" color="red" [side]="DoubleSide" />
31+
</ngt-mesh>
32+
`,
33+
imports: [NgtArgs],
34+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
35+
})
36+
class Scene {
37+
readonly Math = Math;
38+
readonly DoubleSide = THREE.DoubleSide;
39+
40+
onBeforeRender({ object: mesh }: NgtBeforeRenderEvent<THREE.Mesh>) {
41+
mesh.rotation.x = mesh.rotation.y += 0.01;
42+
}
43+
}
44+
45+
@Component({
46+
standalone: true,
47+
template: `
48+
<ngts-orbit-controls [makeDefault]="true" />
49+
<ngts-camera-shake
50+
[maxPitch]="maxPitch"
51+
[maxRoll]="maxRoll"
52+
[maxYaw]="maxYaw"
53+
[pitchFrequency]="pitchFrequency"
54+
[rollFrequency]="rollFrequency"
55+
[yawFrequency]="yawFrequency"
56+
/>
57+
<CameraShakeScene />
58+
`,
59+
imports: [NgtsCameraShake, Scene, NgtsOrbitControls],
60+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
61+
})
62+
class WithOrbitControlsStory {
63+
@Input() maxPitch = argsOptions.maxPitch.defaultValue;
64+
@Input() maxRoll = argsOptions.maxRoll.defaultValue;
65+
@Input() maxYaw = argsOptions.maxYaw.defaultValue;
66+
@Input() pitchFrequency = argsOptions.pitchFrequency.defaultValue;
67+
@Input() rollFrequency = argsOptions.rollFrequency.defaultValue;
68+
@Input() yawFrequency = argsOptions.yawFrequency.defaultValue;
69+
}
70+
71+
@Component({
72+
standalone: true,
73+
template: `
74+
<ngts-camera-shake
75+
[maxPitch]="maxPitch"
76+
[maxRoll]="maxRoll"
77+
[maxYaw]="maxYaw"
78+
[pitchFrequency]="pitchFrequency"
79+
[rollFrequency]="rollFrequency"
80+
[yawFrequency]="yawFrequency"
81+
/>
82+
<CameraShakeScene />
83+
`,
84+
imports: [NgtsCameraShake, Scene],
85+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
86+
})
87+
class DefaultCameraShakeStory {
88+
@Input() maxPitch = argsOptions.maxPitch.defaultValue;
89+
@Input() maxRoll = argsOptions.maxRoll.defaultValue;
90+
@Input() maxYaw = argsOptions.maxYaw.defaultValue;
91+
@Input() pitchFrequency = argsOptions.pitchFrequency.defaultValue;
92+
@Input() rollFrequency = argsOptions.rollFrequency.defaultValue;
93+
@Input() yawFrequency = argsOptions.yawFrequency.defaultValue;
94+
}
95+
96+
export default {
97+
title: 'Staging/Camera Shake',
98+
decorators: [moduleMetadata({ imports: [StorybookSetup] })],
99+
} as Meta;
100+
101+
export const Default = makeStoryObject(DefaultCameraShakeStory, {
102+
canvasOptions: { camera: { position: [0, 0, 10] }, controls: false },
103+
argsOptions,
104+
});
105+
106+
export const WithOrbitControls = makeStoryObject(WithOrbitControlsStory, {
107+
canvasOptions: { camera: { position: [0, 0, 10] }, controls: false },
108+
argsOptions,
109+
});
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { computed, Directive, effect, inject, Input, OnInit } from '@angular/core';
2+
import { injectBeforeRender, NgtSignalStore, NgtStore } from 'angular-three';
3+
import { SimplexNoise } from 'three-stdlib';
4+
5+
export interface NgtsCameraShakeState {
6+
intensity: number;
7+
decay: boolean;
8+
decayRate: number;
9+
maxYaw: number;
10+
maxPitch: number;
11+
maxRoll: number;
12+
yawFrequency: number;
13+
pitchFrequency: number;
14+
rollFrequency: number;
15+
}
16+
17+
@Directive({ selector: 'ngts-camera-shake', standalone: true })
18+
export class NgtsCameraShake extends NgtSignalStore<NgtsCameraShakeState> implements OnInit {
19+
readonly #store = inject(NgtStore);
20+
21+
#initialRotation = this.#store.get('camera').rotation.clone();
22+
23+
readonly #yawNoise = new SimplexNoise();
24+
readonly #rollNoise = new SimplexNoise();
25+
readonly #pitchNoise = new SimplexNoise();
26+
27+
@Input() set intensity(intensity: number) {
28+
this.set({ intensity });
29+
}
30+
31+
@Input() set decay(decay: boolean) {
32+
this.set({ decay });
33+
}
34+
35+
@Input() set decayRate(decayRate: number) {
36+
this.set({ decayRate });
37+
}
38+
39+
@Input() set maxYaw(maxYaw: number) {
40+
this.set({ maxYaw });
41+
}
42+
43+
@Input() set maxPitch(maxPitch: number) {
44+
this.set({ maxPitch });
45+
}
46+
47+
@Input() set maxRoll(maxRoll: number) {
48+
this.set({ maxRoll });
49+
}
50+
51+
@Input() set yawFrequency(yawFrequency: number) {
52+
this.set({ yawFrequency });
53+
}
54+
55+
@Input() set pitchFrequency(pitchFrequency: number) {
56+
this.set({ pitchFrequency });
57+
}
58+
59+
@Input() set rollFrequency(rollFrequency: number) {
60+
this.set({ rollFrequency });
61+
}
62+
63+
constructor() {
64+
super({
65+
intensity: 1,
66+
decayRate: 0.65,
67+
maxYaw: 0.1,
68+
maxPitch: 0.1,
69+
maxRoll: 0.1,
70+
yawFrequency: 0.1,
71+
pitchFrequency: 0.1,
72+
rollFrequency: 0.1,
73+
});
74+
75+
injectBeforeRender(({ clock, delta, camera }) => {
76+
const {
77+
intensity,
78+
maxYaw,
79+
maxPitch,
80+
maxRoll,
81+
yawFrequency,
82+
pitchFrequency,
83+
rollFrequency,
84+
decay,
85+
decayRate,
86+
} = this.get();
87+
88+
const shake = Math.pow(intensity, 2);
89+
const yaw = maxYaw * shake * this.#yawNoise.noise(clock.elapsedTime * yawFrequency, 1);
90+
const pitch = maxPitch * shake * this.#pitchNoise.noise(clock.elapsedTime * pitchFrequency, 1);
91+
const roll = maxRoll * shake * this.#rollNoise.noise(clock.elapsedTime * rollFrequency, 1);
92+
93+
camera.rotation.set(
94+
this.#initialRotation.x + pitch,
95+
this.#initialRotation.y + yaw,
96+
this.#initialRotation.z + roll
97+
);
98+
99+
if (decay && intensity > 0) {
100+
this.set({ intensity: intensity - decayRate * delta });
101+
this.#constraintIntensity();
102+
}
103+
});
104+
105+
this.#setChangeEvent();
106+
}
107+
108+
ngOnInit(): void {}
109+
110+
#setChangeEvent() {
111+
const camera = this.#store.select('camera');
112+
const controls = this.#store.select('controls');
113+
const trigger = computed(() => ({ camera: camera(), controls: controls() }));
114+
effect((onCleanup) => {
115+
const { camera, controls } = trigger();
116+
if (controls) {
117+
const callback = () => void (this.#initialRotation = camera.rotation.clone());
118+
controls.addEventListener('change', callback);
119+
callback();
120+
onCleanup(() => void controls.removeEventListener('change', callback));
121+
}
122+
});
123+
}
124+
125+
getIntensity() {
126+
return this.get('intensity');
127+
}
128+
129+
setIntensity(intensity: number) {
130+
this.set({ intensity });
131+
this.#constraintIntensity();
132+
}
133+
134+
#constraintIntensity() {
135+
const intensity = this.get('intensity');
136+
if (intensity < 0 || intensity > 1) {
137+
this.set({ intensity: intensity < 0 ? 0 : 1 });
138+
}
139+
}
140+
}

libs/soba/staging/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './bounds/bounds';
2+
export * from './camera-shake/camera-shake';
23
export * from './center/center';
34
export * from './cloud/cloud';
45
export * from './contact-shadows/contact-shadows';

0 commit comments

Comments
 (0)