Skip to content

Commit f197487

Browse files
committed
feat(soba): add clouds
1 parent 380ded6 commit f197487

File tree

6 files changed

+770
-0
lines changed

6 files changed

+770
-0
lines changed

apps/examples/src/app/soba/soba.routes.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,16 @@ const routes: Routes = [
246246
},
247247
},
248248
},
249+
{
250+
path: 'thunder-clouds',
251+
loadComponent: () => import('./thunder-clouds/thunder-clouds'),
252+
data: {
253+
credits: {
254+
title: 'Thunder Clouds',
255+
link: 'https://codesandbox.io/p/sandbox/gwthnh?file=%2Fsrc%2FApp.js%3A7%2C1',
256+
},
257+
},
258+
},
249259
{
250260
path: '',
251261
redirectTo: 'stars',
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
CUSTOM_ELEMENTS_SCHEMA,
5+
ElementRef,
6+
input,
7+
viewChild,
8+
} from '@angular/core';
9+
import { beforeRender, NgtVector3 } from 'angular-three';
10+
import {
11+
NgtrAnyCollider,
12+
NgtrBallCollider,
13+
NgtrContactForcePayload,
14+
NgtrCuboidCollider,
15+
NgtrPhysics,
16+
NgtrRigidBody,
17+
} from 'angular-three-rapier';
18+
import { NgtsPerspectiveCamera } from 'angular-three-soba/cameras';
19+
import { NgtsOrbitControls } from 'angular-three-soba/controls';
20+
import {
21+
NgtsCameraShake,
22+
NgtsCloud,
23+
NgtsClouds,
24+
NgtsContactShadows,
25+
NgtsEnvironment,
26+
} from 'angular-three-soba/staging';
27+
import { CameraShake } from 'angular-three-soba/vanilla-exports';
28+
import { random } from 'maath';
29+
import * as THREE from 'three';
30+
31+
@Component({
32+
selector: 'app-puffy-cloud',
33+
template: `
34+
<ngt-object3D
35+
rigidBody
36+
[options]="{ colliders: false, linearDamping: 4, angularDamping: 1, friction: 0.1 }"
37+
[userData]="{ cloud: true }"
38+
[position]="position()"
39+
(contactForce)="onContactForce($event)"
40+
>
41+
<ngt-object3D [ballCollider]="[4]" />
42+
<ngts-cloud
43+
[options]="{
44+
seed: seed(),
45+
fade: 30,
46+
speed: 0.1,
47+
growth: 4,
48+
segments: 40,
49+
volume: 6,
50+
opacity: 0.6,
51+
bounds: [4, 3, 1],
52+
}"
53+
/>
54+
<ngts-cloud
55+
[options]="{
56+
seed: seed() + 1,
57+
fade: 30,
58+
position: [0, 1, 0],
59+
speed: 0.5,
60+
growth: 4,
61+
volume: 10,
62+
opacity: 1,
63+
bounds: [6, 2, 1],
64+
}"
65+
/>
66+
<ngt-point-light #light [position.z]="0.5" color="blue" />
67+
</ngt-object3D>
68+
`,
69+
changeDetection: ChangeDetectionStrategy.OnPush,
70+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
71+
imports: [NgtrRigidBody, NgtrBallCollider, NgtsCloud],
72+
})
73+
export class PuffyCloud {
74+
seed = input.required<number>();
75+
position = input.required<NgtVector3>();
76+
cameraShake = input.required<CameraShake>();
77+
78+
private lightRef = viewChild.required<ElementRef<THREE.PointLight>>('light');
79+
private rigidBody = viewChild.required(NgtrRigidBody);
80+
81+
private vec = new THREE.Vector3();
82+
private flash = new random.FlashGen({ count: 10, minDuration: 40, maxDuration: 200 });
83+
84+
constructor() {
85+
beforeRender(({ clock, delta }) => {
86+
const impulse = this.flash.update(clock.elapsedTime, delta);
87+
this.lightRef().nativeElement.intensity = impulse * 15000;
88+
if (impulse === 1) {
89+
this.cameraShake().intensity = 1;
90+
}
91+
92+
const rigidBody = this.rigidBody().rigidBody();
93+
if (!rigidBody) return;
94+
95+
rigidBody.applyImpulse(this.vec.copy(rigidBody.translation()).negate().multiplyScalar(10), true);
96+
});
97+
}
98+
99+
protected onContactForce(payload: NgtrContactForcePayload) {
100+
if (payload.other.rigidBodyObject?.userData?.['cloud'] && payload.totalForceMagnitude / 1000 > 100) {
101+
this.flash.burst();
102+
}
103+
}
104+
}
105+
106+
@Component({
107+
selector: 'app-pointer',
108+
template: `
109+
<ngt-object3D rigidBody="kinematicPosition" [options]="{ colliders: false }" [userData]="{ cloud: true }">
110+
<ngt-object3D [ballCollider]="[4]" />
111+
</ngt-object3D>
112+
`,
113+
changeDetection: ChangeDetectionStrategy.OnPush,
114+
imports: [NgtrAnyCollider, NgtrRigidBody, NgtrBallCollider],
115+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
116+
})
117+
export class Pointer {
118+
private rigidBodyRef = viewChild.required(NgtrRigidBody);
119+
120+
private vec = new THREE.Vector3();
121+
private dir = new THREE.Vector3();
122+
123+
constructor() {
124+
beforeRender(({ pointer, camera }) => {
125+
this.vec.set(pointer.x, pointer.y, 0.5).unproject(camera);
126+
this.dir.copy(this.vec).sub(camera.position).normalize();
127+
this.vec.add(this.dir.multiplyScalar(camera.position.length()));
128+
this.rigidBodyRef().rigidBody()?.setNextKinematicTranslation(this.vec);
129+
});
130+
}
131+
}
132+
133+
@Component({
134+
selector: 'app-scene-graph',
135+
template: `
136+
<ngt-ambient-light [intensity]="Math.PI / 2" />
137+
138+
<ngts-perspective-camera
139+
[options]="{ makeDefault: true, position: [0, -4, 18], fov: 90 }"
140+
(updated)="$event.lookAt(0, 0, 0)"
141+
>
142+
<ngt-spot-light
143+
[position]="[0, 40, 2]"
144+
[angle]="0.5"
145+
[decay]="1"
146+
[distance]="45"
147+
[penumbra]="1"
148+
[intensity]="2000"
149+
/>
150+
<ngt-spot-light
151+
color="red"
152+
[position]="[-19, 0, -8]"
153+
[angle]="0.25"
154+
[decay]="0.75"
155+
[distance]="185"
156+
[penumbra]="-1"
157+
[intensity]="400"
158+
/>
159+
</ngts-perspective-camera>
160+
161+
<ngts-camera-shake
162+
#shake="cameraShake"
163+
[options]="{
164+
decay: true,
165+
decayRate: 0.95,
166+
maxYaw: 0.05,
167+
maxPitch: 0.01,
168+
yawFrequency: 4,
169+
pitchFrequency: 2,
170+
rollFrequency: 2,
171+
intensity: 0.5,
172+
}"
173+
/>
174+
175+
<ngts-clouds [options]="{ limit: 400 }">
176+
<ngtr-physics [options]="{ gravity: [0, 0, 0] }">
177+
<ng-template>
178+
@let cameraShake = shake.cameraShaker();
179+
<app-pointer />
180+
<app-puffy-cloud [seed]="10" [position]="[50, 0, 0]" [cameraShake]="cameraShake" />
181+
<app-puffy-cloud [seed]="20" [position]="[0, 50, 0]" [cameraShake]="cameraShake" />
182+
<app-puffy-cloud [seed]="30" [position]="[50, 0, 50]" [cameraShake]="cameraShake" />
183+
<app-puffy-cloud [seed]="40" [position]="[0, 0, -50]" [cameraShake]="cameraShake" />
184+
<ngt-object3D [cuboidCollider]="[400, 10, 400]" [position]="[0, -15, 0]" />
185+
</ng-template>
186+
</ngtr-physics>
187+
</ngts-clouds>
188+
189+
<ngt-mesh [scale]="200">
190+
<ngt-sphere-geometry />
191+
<ngt-mesh-standard-material color="#999" [roughness]="0.7" [side]="BackSide" />
192+
</ngt-mesh>
193+
194+
<ngts-contact-shadows
195+
[options]="{ opacity: 0.25, color: 'black', position: [0, -10, 0], scale: 50, blur: 2.5, far: 40 }"
196+
/>
197+
198+
<ngts-orbit-controls
199+
[options]="{
200+
makeDefault: true,
201+
autoRotate: true,
202+
enableZoom: false,
203+
enablePan: false,
204+
minPolarAngle: Math.PI / 1.7,
205+
maxPolarAngle: Math.PI / 1.7,
206+
}"
207+
/>
208+
209+
<ngts-environment
210+
[options]="{ files: 'https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/1k/blue_lagoon_night_1k.hdr' }"
211+
/>
212+
`,
213+
214+
imports: [
215+
NgtsPerspectiveCamera,
216+
NgtsContactShadows,
217+
NgtsOrbitControls,
218+
NgtsEnvironment,
219+
NgtsCameraShake,
220+
NgtsClouds,
221+
NgtrPhysics,
222+
Pointer,
223+
PuffyCloud,
224+
NgtrCuboidCollider,
225+
],
226+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
227+
changeDetection: ChangeDetectionStrategy.OnPush,
228+
})
229+
export class SceneGraph {
230+
protected readonly Math = Math;
231+
protected readonly BackSide = THREE.BackSide;
232+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Component } from '@angular/core';
2+
import { NgtCanvas } from 'angular-three/dom';
3+
import { SceneGraph } from './scene';
4+
5+
@Component({
6+
template: `
7+
<ngt-canvas>
8+
<app-scene-graph *canvasContent />
9+
</ngt-canvas>
10+
`,
11+
imports: [NgtCanvas, SceneGraph],
12+
host: { class: 'thunder-clouds-soba' },
13+
})
14+
export default class ThunderClouds {}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2+
import { Meta } from '@storybook/angular';
3+
import { NgtsCloud, NgtsClouds } from 'angular-three-soba/staging';
4+
import { storyDecorators, storyObject } from '../setup-canvas';
5+
6+
@Component({
7+
template: `
8+
<ngts-clouds>
9+
<ngts-cloud [options]="{ position: [-4, -2, 0], color: '#ff6b9d' }" />
10+
<ngts-cloud [options]="{ position: [-4, 2, 0], color: '#c44569' }" />
11+
<ngts-cloud [options]="{ color: '#feca57' }" />
12+
<ngts-cloud [options]="{ position: [4, -2, 0], color: '#48dbfb' }" />
13+
<ngts-cloud [options]="{ position: [4, 2, 0], color: '#ff9ff3' }" />
14+
</ngts-clouds>
15+
`,
16+
imports: [NgtsCloud, NgtsClouds],
17+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
18+
changeDetection: ChangeDetectionStrategy.OnPush,
19+
})
20+
class DefaultCloudStory {}
21+
22+
export default {
23+
title: 'Staging/Cloud',
24+
decorators: storyDecorators(),
25+
} as Meta;
26+
27+
export const Default = storyObject(DefaultCloudStory, {
28+
camera: { position: [0, 5, 10] },
29+
});

libs/soba/staging/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './lib/bounds';
55
export * from './lib/camera-shake';
66
export * from './lib/caustics';
77
export * from './lib/center';
8+
export * from './lib/cloud';
89
export * from './lib/contact-shadows';
910
export * from './lib/environment/environment';
1011
export * from './lib/environment/environment-resource';

0 commit comments

Comments
 (0)