Skip to content

Commit aef9617

Browse files
committed
docs(examples): update skydive routes
1 parent e33ce87 commit aef9617

File tree

6 files changed

+295
-10
lines changed

6 files changed

+295
-10
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2+
import { extend } from 'angular-three';
3+
import { NgtpBloom, NgtpEffectComposer, NgtpVignette } from 'angular-three-postprocessing';
4+
import { NgtsOrbitControls } from 'angular-three-soba/controls';
5+
import { NgtsCameraShake, NgtsEnvironment } from 'angular-three-soba/staging';
6+
import * as THREE from 'three';
7+
import { Skydiver } from './skydiver';
8+
import { Winds } from './winds';
9+
import { World } from './world';
10+
11+
extend(THREE);
12+
13+
@Component({
14+
selector: 'app-scene-graph',
15+
template: `
16+
<ngts-environment [options]="{ frames: 1, path: './env/skydiving', resolution: 256 }" />
17+
18+
<app-world />
19+
<app-skydiver />
20+
<app-winds />
21+
22+
<ngts-camera-shake [options]="{ yawFrequency: 2, pitchFrequency: 2, rollFrequency: 2, intensity: 0.3 }" />
23+
24+
<ngt-ambient-light [intensity]="0.2 * Math.PI" />
25+
<ngt-directional-light [intensity]="2 * Math.PI" [position]="[-0.15, -2, 0]" />
26+
27+
<ngts-orbit-controls [options]="{ makeDefault: true, maxDistance: 5 }" />
28+
29+
<ngtp-effect-composer>
30+
<ngtp-bloom [options]="{ luminanceThreshold: 0.6, luminanceSmoothing: 0.5, intensity: 1.2, mipmapBlur: true }" />
31+
<ngtp-vignette [options]="{ offset: 0.5, darkness: 0.5 }" />
32+
</ngtp-effect-composer>
33+
`,
34+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
35+
changeDetection: ChangeDetectionStrategy.OnPush,
36+
imports: [
37+
NgtsEnvironment,
38+
NgtsCameraShake,
39+
NgtsOrbitControls,
40+
NgtpEffectComposer,
41+
NgtpBloom,
42+
NgtpVignette,
43+
Skydiver,
44+
World,
45+
Winds,
46+
],
47+
})
48+
export class SceneGraph {
49+
protected readonly Math = Math;
50+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
CUSTOM_ELEMENTS_SCHEMA,
5+
effect,
6+
ElementRef,
7+
viewChild,
8+
} from '@angular/core';
9+
import { injectBeforeRender, NgtArgs } from 'angular-three';
10+
import { injectGLTF, injectTexture } from 'angular-three-soba/loaders';
11+
import { injectAnimations } from 'angular-three-soba/misc';
12+
import {
13+
Bone,
14+
BufferGeometry,
15+
DoubleSide,
16+
Mesh,
17+
MeshStandardMaterial,
18+
ShaderMaterial,
19+
SkinnedMesh,
20+
SRGBColorSpace,
21+
} from 'three';
22+
import { GLTF } from 'three-stdlib';
23+
24+
type SkydiverGLTF = GLTF & {
25+
nodes: {
26+
skydiver_2: SkinnedMesh<BufferGeometry, ShaderMaterial>;
27+
mixamorigHips: Mesh;
28+
};
29+
materials: {};
30+
};
31+
32+
@Component({
33+
selector: 'app-skydiver',
34+
template: `
35+
<ngt-group [dispose]="null">
36+
@if (gltf(); as gltf) {
37+
<ngt-group>
38+
<ngt-primitive #bone *args="[gltf.nodes.mixamorigHips]" />
39+
<ngt-skinned-mesh [geometry]="gltf.nodes.skydiver_2.geometry" [skeleton]="gltf.nodes.skydiver_2.skeleton">
40+
@if (textures(); as textures) {
41+
<ngt-mesh-standard-material
42+
[parameters]="{
43+
uniforms: { uTime: { value: 0 }, uClothes: { value: textures.clothes } },
44+
onBeforeCompile,
45+
toneMapped: false,
46+
envMapIntensity: 0.8,
47+
metalnessMap: textures.metallic,
48+
normalMap: textures.normal,
49+
roughnessMap: textures.roughness,
50+
map: textures.baseColor,
51+
normalScale: [-0.2, 0.2],
52+
side: DoubleSide,
53+
}"
54+
/>
55+
}
56+
</ngt-skinned-mesh>
57+
</ngt-group>
58+
}
59+
</ngt-group>
60+
`,
61+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
62+
changeDetection: ChangeDetectionStrategy.OnPush,
63+
imports: [NgtArgs],
64+
})
65+
export class Skydiver {
66+
protected DoubleSide = DoubleSide;
67+
68+
protected gltf = injectGLTF<SkydiverGLTF>(() => './skydiver.glb');
69+
protected textures = injectTexture(
70+
() => ({
71+
baseColor: './texture/skydiver_BaseColor.webp',
72+
roughness: './texture/skydiver_Roughness.webp',
73+
metallic: './texture/skydiver_Metallic.webp',
74+
normal: './texture/skydiver_Normal.webp',
75+
clothes: './texture/skydiver_Clothes.webp',
76+
}),
77+
{
78+
onLoad: (textures) => {
79+
textures.forEach((texture) => {
80+
texture.flipY = false;
81+
texture.colorSpace = SRGBColorSpace;
82+
});
83+
},
84+
},
85+
);
86+
87+
private boneRef = viewChild<ElementRef<Bone>>('bone');
88+
private animations = injectAnimations(this.gltf, this.boneRef);
89+
90+
protected onBeforeCompile: MeshStandardMaterial['onBeforeCompile'] = (shader) => {
91+
const gltf = this.gltf();
92+
93+
Object.assign(shader.uniforms, {
94+
...(gltf?.nodes['skydiver_2'].material?.uniforms || {}),
95+
});
96+
97+
shader.vertexShader = /* language=glsl glsl */ `
98+
uniform float uTime;
99+
uniform sampler2D uClothes;
100+
${shader.vertexShader}
101+
`;
102+
shader.vertexShader = shader.vertexShader.replace(
103+
`#include <begin_vertex>`,
104+
`
105+
vec3 clothesTexture = vec3(texture2D(uClothes, vMapUv));
106+
float circleTime = 2.0;
107+
float amplitude = 30.0;
108+
float circleTimeParam = mod(uTime, circleTime);
109+
vec3 transformed = vec3( position );
110+
transformed.y += min(clothesTexture.y * sin( circleTimeParam * amplitude * (PI / circleTime)) * 0.025, 0.5);
111+
`,
112+
);
113+
};
114+
115+
constructor() {
116+
effect((onCleanup) => {
117+
if (!this.animations.isReady) return;
118+
const { actions } = this.animations;
119+
actions['animation_0']?.reset().play();
120+
onCleanup(() => actions['animation_0']?.stop());
121+
});
122+
123+
injectBeforeRender(({ clock }) => {
124+
const skydiver = this.gltf()?.nodes['skydiver_2'];
125+
if (skydiver?.material.uniforms?.['uTime']) {
126+
skydiver.material.uniforms['uTime'].value = clock.getElapsedTime();
127+
}
128+
});
129+
}
130+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { NgtsLoader } from 'angular-three-soba/loaders';
3+
import { NgtCanvas, NgtCanvasContent } from 'angular-three/dom';
4+
import { SceneGraph } from './scene-graph';
5+
6+
@Component({
7+
selector: 'app-sky-diving',
8+
template: `
9+
<ngt-canvas [camera]="{ position: [0, 0, 3], fov: 70 }" shadows>
10+
<app-scene-graph *canvasContent />
11+
</ngt-canvas>
12+
<ngts-loader />
13+
`,
14+
imports: [NgtCanvas, NgtsLoader, SceneGraph, NgtCanvasContent],
15+
changeDetection: ChangeDetectionStrategy.OnPush,
16+
host: { class: 'skydiving-soba' },
17+
})
18+
export default class SkyDiving {}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, viewChild } from '@angular/core';
2+
import { injectBeforeRender, NgtArgs } from 'angular-three';
3+
import { NgtsInstance, NgtsInstances } from 'angular-three-soba/performances';
4+
import { AdditiveBlending, DoubleSide, MathUtils, Vector3 } from 'three';
5+
6+
@Component({
7+
selector: 'app-wind-shape',
8+
template: `
9+
<ngts-instance [options]="{ color: 'white', position: randomPosition }" />
10+
`,
11+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
12+
changeDetection: ChangeDetectionStrategy.OnPush,
13+
imports: [NgtsInstance],
14+
})
15+
export class WindShape {
16+
protected randomPosition = [
17+
MathUtils.randFloatSpread(8),
18+
MathUtils.randFloatSpread(5),
19+
MathUtils.randFloatSpread(8),
20+
] as const;
21+
private v3 = new Vector3();
22+
private randomSpeed = MathUtils.randFloat(0.05, 0.5);
23+
24+
private instanceRef = viewChild.required(NgtsInstance);
25+
26+
constructor() {
27+
injectBeforeRender(({ camera, viewport }) => {
28+
const instance = this.instanceRef().positionMeshRef().nativeElement;
29+
if (!instance) return;
30+
31+
const { height: elHeight } = (instance.geometry as any)['parameters'];
32+
const worldPosition = instance.getWorldPosition(this.v3);
33+
const limitPos = viewport.height - (worldPosition.y + elHeight / 2);
34+
if (limitPos < 0) {
35+
instance.position.y = -(viewport.height + elHeight / 2);
36+
}
37+
instance.position.y += this.randomSpeed;
38+
instance.rotation.y = camera.rotation.y;
39+
});
40+
}
41+
}
42+
43+
@Component({
44+
selector: 'app-winds',
45+
template: `
46+
<ngt-group>
47+
<ngts-instances>
48+
<ngt-plane-geometry *args="[0.0135, 1.2]" />
49+
<ngt-mesh-basic-material [side]="DoubleSide" [blending]="AdditiveBlending" [opacity]="0.15" transparent />
50+
51+
@for (i of count; track $index) {
52+
<app-wind-shape />
53+
}
54+
</ngts-instances>
55+
</ngt-group>
56+
`,
57+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
58+
changeDetection: ChangeDetectionStrategy.OnPush,
59+
imports: [NgtsInstances, NgtArgs, WindShape],
60+
})
61+
export class Winds {
62+
protected readonly DoubleSide = DoubleSide;
63+
protected readonly AdditiveBlending = AdditiveBlending;
64+
65+
protected count = Array.from({ length: 200 }, (_, i) => i);
66+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2+
import { NgtArgs } from 'angular-three';
3+
import { injectTexture } from 'angular-three-soba/loaders';
4+
import { BackSide } from 'three';
5+
6+
@Component({
7+
selector: 'app-world',
8+
template: `
9+
<ngt-mesh>
10+
<ngt-sphere-geometry *args="[5, 60, 60]" />
11+
<ngt-mesh-basic-material [toneMapped]="false" [map]="skyTexture()" [side]="BackSide" />
12+
</ngt-mesh>
13+
`,
14+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
15+
changeDetection: ChangeDetectionStrategy.OnPush,
16+
imports: [NgtArgs],
17+
})
18+
export class World {
19+
protected readonly BackSide = BackSide;
20+
protected skyTexture = injectTexture(() => './sky-texture.jpg');
21+
}

apps/kitchen-sink-new/src/app/soba/soba.routes.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,16 @@ const routes: Routes = [
8080
// },
8181
// },
8282
// },
83-
// {
84-
// path: 'skydiving',
85-
// loadComponent: () => import('./skydiving/skydiving'),
86-
// data: {
87-
// credits: {
88-
// title: 'WebGL Skydiving',
89-
// link: 'https://github.com/sebastien-lempens/webgl-skydiving',
90-
// },
91-
// },
92-
// },
83+
{
84+
path: 'skydiving',
85+
loadComponent: () => import('./skydiving/skydiving'),
86+
data: {
87+
credits: {
88+
title: 'WebGL Skydiving',
89+
link: 'https://github.com/sebastien-lempens/webgl-skydiving',
90+
},
91+
},
92+
},
9393
{
9494
path: 'porsche',
9595
loadComponent: () => import('./porsche/porsche'),

0 commit comments

Comments
 (0)