Skip to content

Commit 88e5121

Browse files
committed
docs: add skydiving example
1 parent d37e033 commit 88e5121

22 files changed

+338
-35
lines changed

apps/sandbox/src/app/app.component.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { injectBox, injectPlane } from 'angular-three-cannon/services';
88
import { NgtpBloom, NgtpEffectComposer } from 'angular-three-postprocessing';
99
import { NgtsGrid } from 'angular-three-soba/abstractions';
1010
import { NgtsOrbitControls } from 'angular-three-soba/controls';
11-
import { injectNgtsGLTFLoader } from 'angular-three-soba/loaders';
11+
import { NgtsLoader, injectNgtsGLTFLoader } from 'angular-three-soba/loaders';
1212
import { injectNgtsAnimations } from 'angular-three-soba/misc';
1313
import * as THREE from 'three';
14+
import { SkyDivingScene } from './skydiving/scene.component';
1415

1516
extend(THREE);
1617

@@ -126,7 +127,7 @@ export class Scene {
126127

127128
@Component({
128129
standalone: true,
129-
imports: [NgtCanvas, NgIf, NgtKey],
130+
imports: [NgtCanvas, NgtKey, NgtsLoader],
130131
selector: 'sandbox-root',
131132
template: `
132133
<ngt-canvas
@@ -136,12 +137,13 @@ export class Scene {
136137
[shadows]="true"
137138
[gl]="scenes[scene].glOptions"
138139
/>
139-
<button (click)="onToggle()">Toggle scene: {{ scene }}</button>
140+
<ngts-loader />
141+
<!-- <button (click)="onToggle()">Toggle scene: {{ scene }}</button> -->
140142
`,
141143
styleUrls: ['./app.component.css'],
142144
})
143145
export class AppComponent {
144-
scene: 'cannon' | 'bot' = 'cannon';
146+
scene: 'cannon' | 'bot' | 'skydiving' = 'skydiving';
145147
scenes = {
146148
bot: {
147149
scene: Scene,
@@ -153,6 +155,11 @@ export class AppComponent {
153155
cameraOptions: { position: [0, 0, 15] },
154156
glOptions: { useLegacyLights: true },
155157
},
158+
skydiving: {
159+
scene: SkyDivingScene,
160+
cameraOptions: { fov: 70, position: [0, 0, 3] },
161+
glOptions: { useLegacyLights: true },
162+
},
156163
} as const;
157164

158165
onToggle() {
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { NgIf } from '@angular/common';
2+
import { CUSTOM_ELEMENTS_SCHEMA, Component, Signal, effect } from '@angular/core';
3+
import { NgtArgs, injectBeforeRender } from 'angular-three';
4+
import { NgtsGLTF, injectNgtsGLTFLoader, injectNgtsTextureLoader } from 'angular-three-soba/loaders';
5+
import { injectNgtsAnimations } from 'angular-three-soba/misc';
6+
import * as THREE from 'three';
7+
8+
type SkydiverGLTF = NgtsGLTF<{
9+
nodes: {
10+
skydiver_2: THREE.SkinnedMesh<THREE.BufferGeometry, THREE.ShaderMaterial>;
11+
mixamorigHips: THREE.Mesh;
12+
};
13+
}>;
14+
15+
@Component({
16+
selector: 'app-model',
17+
standalone: true,
18+
template: `
19+
<ngt-group [dispose]="null">
20+
<ngt-group *ngIf="skydiverGltf() as skydiverGltf" [ref]="animations.ref">
21+
<ngt-primitive *args="[skydiverGltf.nodes['mixamorigHips']]" />
22+
<ngt-skinned-mesh
23+
[geometry]="skydiverGltf.nodes['skydiver_2'].geometry"
24+
[skeleton]="skydiverGltf.nodes['skydiver_2'].skeleton"
25+
>
26+
<ngt-mesh-standard-material
27+
[side]="DoubleSide"
28+
[map]="textures()?.baseColor"
29+
[roughnessMap]="textures()?.roughness"
30+
[normalMap]="textures()?.normal"
31+
[metalnessMap]="textures()?.metallic"
32+
[normalScale]="[-0.2, 0.2]"
33+
[envMapIntensity]="0.8"
34+
[toneMapped]="false"
35+
[uniforms]="{ uTime: { value: 0 } }"
36+
(afterAttach)="$event.node.onBeforeCompile = onBeforeCompile"
37+
/>
38+
</ngt-skinned-mesh>
39+
</ngt-group>
40+
</ngt-group>
41+
`,
42+
imports: [NgtArgs, NgIf],
43+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
44+
})
45+
export class Model {
46+
DoubleSide = THREE.DoubleSide;
47+
textures = injectNgtsTextureLoader(
48+
() => ({
49+
baseColor: 'assets/texture/skydiver_BaseColor.webp',
50+
roughness: 'assets/texture/skydiver_Roughness.webp',
51+
metallic: 'assets/texture/skydiver_Metallic.webp',
52+
normal: 'assets/texture/skydiver_Normal.webp',
53+
clothes: 'assets/texture/skydiver_Clothes.webp',
54+
}),
55+
{
56+
onLoad: (textures) => {
57+
if (Array.isArray(textures)) {
58+
const [baseColor, roughness, metallic, normal, clothes] = textures;
59+
baseColor.flipY = roughness.flipY = metallic.flipY = normal.flipY = clothes.flipY = false;
60+
}
61+
},
62+
},
63+
);
64+
65+
skydiverGltf = injectNgtsGLTFLoader(() => 'assets/skydiver.glb') as Signal<SkydiverGLTF>;
66+
67+
animations = injectNgtsAnimations(() => this.skydiverGltf()?.animations || [], { playFirstClip: false });
68+
69+
onBeforeCompile: THREE.MeshStandardMaterial['onBeforeCompile'] = (shader) => {
70+
Object.assign(shader.uniforms, {
71+
...((this.skydiverGltf()?.nodes['skydiver_2']).material.uniforms || {}),
72+
});
73+
shader.vertexShader = `
74+
uniform float uTime;
75+
uniform sampler2D uClothes;
76+
${shader.vertexShader}
77+
`;
78+
shader.vertexShader = shader.vertexShader.replace(
79+
`#include <begin_vertex>`,
80+
`
81+
vec3 clothesTexture = vec3(texture2D(uClothes, vUv));
82+
float circleTime = 2.0;
83+
float amplitude = 30.0;
84+
float circleTimeParam = mod(uTime, circleTime);
85+
vec3 transformed = vec3( position );
86+
transformed.y += min(clothesTexture.y * sin( circleTimeParam * amplitude * (PI / circleTime)) * 0.025, 0.5);
87+
`,
88+
);
89+
};
90+
91+
constructor() {
92+
effect(() => {
93+
const ready = this.animations.ready();
94+
if (!ready) return;
95+
const { actions } = this.animations;
96+
actions['animation_0'].reset().play();
97+
98+
const [textures, gltf] = [this.textures(), this.skydiverGltf()];
99+
if (!textures || !gltf) return;
100+
gltf.nodes['skydiver_2'].material.uniforms = {
101+
uTime: { value: 0 },
102+
uClothes: { value: textures.clothes },
103+
};
104+
});
105+
106+
injectBeforeRender(({ clock }) => {
107+
const skydiver = this.skydiverGltf()?.nodes['skydiver_2'];
108+
if (skydiver?.material.uniforms?.['uTime']) {
109+
skydiver.material.uniforms['uTime'].value = clock.getElapsedTime();
110+
}
111+
});
112+
}
113+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component, computed } from '@angular/core';
2+
import { NgtArgs, NgtRepeat, injectBeforeRender, injectNgtRef, injectNgtStore } from 'angular-three';
3+
import { NgtsInstance, NgtsInstances, PositionMesh } from 'angular-three-soba/performances';
4+
import { NgtsSobaContent } from 'angular-three-soba/utils';
5+
import * as THREE from 'three';
6+
7+
@Component({
8+
selector: 'app-wind-shape',
9+
standalone: true,
10+
template: ` <ngts-instance color="white" [instanceRef]="ref" [position]="randomPosition" /> `,
11+
imports: [NgtsInstance],
12+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
13+
})
14+
export class WindShape {
15+
randomPosition = [
16+
THREE.MathUtils.randFloatSpread(8),
17+
THREE.MathUtils.randFloatSpread(5),
18+
THREE.MathUtils.randFloatSpread(8),
19+
];
20+
private v3 = new THREE.Vector3();
21+
private randomSpeed = THREE.MathUtils.randFloat(0.05, 0.5);
22+
23+
ref = injectNgtRef<PositionMesh>();
24+
25+
private store = injectNgtStore();
26+
private getCurrentViewport = this.store.get('viewport', 'getCurrentViewport');
27+
28+
private height = computed(() => this.getCurrentViewport().height);
29+
30+
constructor() {
31+
injectBeforeRender(({ camera }) => {
32+
const instance = this.ref.nativeElement;
33+
if (instance) {
34+
const { height: elHeight } = (instance.instance.nativeElement?.geometry as any)['parameters'];
35+
const worldPosition = instance.getWorldPosition(this.v3);
36+
const limitPos = this.height() - (worldPosition.y + elHeight / 2);
37+
if (limitPos < 0) {
38+
instance.position.y = -(this.height() + elHeight / 2);
39+
}
40+
instance.position.y += this.randomSpeed;
41+
instance.rotation.y = camera.rotation.y;
42+
}
43+
});
44+
}
45+
}
46+
47+
@Component({
48+
selector: 'app-wind-effect',
49+
standalone: true,
50+
template: `
51+
<ngt-group>
52+
<ngts-instances>
53+
<ng-template ngtsSobaContent>
54+
<ngt-plane-geometry *args="[0.0135, 1.2]" />
55+
<ngt-mesh-basic-material
56+
[side]="DoubleSide"
57+
[blending]="AdditiveBlending"
58+
[opacity]="0.15"
59+
[transparent]="true"
60+
/>
61+
<app-wind-shape *ngFor="let i; repeat: 130" />
62+
</ng-template>
63+
</ngts-instances>
64+
</ngt-group>
65+
`,
66+
imports: [NgtsInstances, NgtsSobaContent, NgtArgs, WindShape, NgtRepeat],
67+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
68+
})
69+
export class WindEffect {
70+
DoubleSide = THREE.DoubleSide;
71+
AdditiveBlending = THREE.AdditiveBlending;
72+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
2+
import { NgtArgs } from 'angular-three';
3+
import { injectNgtsTextureLoader } from 'angular-three-soba/loaders';
4+
import * as THREE from 'three';
5+
6+
@Component({
7+
selector: 'app-world',
8+
standalone: true,
9+
template: `
10+
<ngt-mesh>
11+
<ngt-sphere-geometry *args="[5, 60, 60]" />
12+
<ngt-mesh-basic-material [toneMapped]="false" [map]="skyTexture()" [side]="BackSide" />
13+
</ngt-mesh>
14+
`,
15+
imports: [NgtArgs],
16+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
17+
})
18+
export class World {
19+
BackSide = THREE.BackSide;
20+
skyTexture = injectNgtsTextureLoader(() => 'assets/sky-texture.jpg');
21+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
2+
import { NgtpBloom, NgtpEffectComposer, NgtpVignette } from 'angular-three-postprocessing';
3+
import { NgtsOrbitControls } from 'angular-three-soba/controls';
4+
import { NgtsCameraShake, NgtsEnvironment } from 'angular-three-soba/staging';
5+
import { Model } from './components/model.component';
6+
import { WindEffect } from './components/wind-effect.component';
7+
import { World } from './components/world.component';
8+
9+
@Component({
10+
standalone: true,
11+
template: `
12+
<ngts-environment [frames]="1" path="assets/env" [resolution]="256" />
13+
14+
<app-world />
15+
<app-wind-effect />
16+
<app-model />
17+
18+
<ngts-camera-shake [yawFrequency]="4" [pitchFrequency]="4" [rollFrequency]="5" [intensity]="0.3" />
19+
20+
<ngt-ambient-light [intensity]="0.2 * Math.PI" />
21+
<ngt-directional-light [intensity]="2" [position]="[-0.15, -2, 0]" />
22+
23+
<ngts-orbit-controls [makeDefault]="true" />
24+
25+
<ngtp-effect-composer>
26+
<ngtp-bloom [luminanceThreshold]="0.6" [luminanceSmoothing]="0.5" [intensity]="1.2" [mipmapBlur]="true" />
27+
<ngtp-vignette [offset]="0.5" [darkness]="0.5" />
28+
</ngtp-effect-composer>
29+
`,
30+
imports: [
31+
NgtsEnvironment,
32+
NgtsCameraShake,
33+
NgtsOrbitControls,
34+
NgtpEffectComposer,
35+
NgtpBloom,
36+
NgtpVignette,
37+
World,
38+
Model,
39+
WindEffect,
40+
],
41+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
42+
})
43+
export class SkyDivingScene {
44+
Math = Math;
45+
}

apps/sandbox/src/assets/env/nx.png

486 KB
Loading

apps/sandbox/src/assets/env/ny.png

661 KB
Loading

apps/sandbox/src/assets/env/nz.png

475 KB
Loading

apps/sandbox/src/assets/env/px.png

457 KB
Loading

apps/sandbox/src/assets/env/py.png

378 KB
Loading

apps/sandbox/src/assets/env/pz.png

463 KB
Loading
400 KB
Loading

apps/sandbox/src/assets/skydiver.glb

944 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

apps/sandbox/src/styles.css

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
11
/* You can add global styles to this file, and also import other style files */
2+
3+
* {
4+
box-sizing: border-box;
5+
}
6+
27
html,
38
body {
4-
height: 100%;
59
width: 100%;
10+
height: 100%;
611
margin: 0;
12+
padding: 0;
13+
background-color: #272727;
14+
-webkit-touch-callout: none;
15+
-webkit-user-select: none;
16+
-khtml-user-select: none;
17+
-moz-user-select: none;
18+
-ms-user-select: none;
19+
user-select: none;
20+
overflow: hidden;
21+
}
22+
23+
body {
24+
position: fixed;
25+
overflow: hidden;
26+
overscroll-behavior-y: none;
27+
font-family:
28+
helvetica neue,
29+
helvetica,
30+
arial,
31+
sans-serif;
32+
color: black;
33+
-webkit-font-smoothing: antialiased;
734
}

0 commit comments

Comments
 (0)