Skip to content

Commit 4c61180

Browse files
committed
lod/detailed/statsgl
1 parent 4470a8e commit 4c61180

File tree

20 files changed

+295
-7
lines changed

20 files changed

+295
-7
lines changed

apps/kitchen-sink/project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
}
2222
],
2323
"styles": ["apps/kitchen-sink/src/styles.css"],
24-
"scripts": [],
25-
"externalDependencies": ["three/examples/jsm/utils/BufferGeometryUtils", "three/examples/jsm/postprocessing/Pass"]
24+
"scripts": []
2625
},
2726
"configurations": {
2827
"production": {
@@ -38,6 +37,7 @@
3837
"maximumError": "4kb"
3938
}
4039
],
40+
"externalDependencies": ["three/examples/jsm/utils/BufferGeometryUtils", "three/examples/jsm/postprocessing/Pass"],
4141
"outputHashing": "all"
4242
},
4343
"development": {

apps/kitchen-sink/public/bust-1-d.glb

529 KB
Binary file not shown.

apps/kitchen-sink/public/bust-2-d.glb

15.7 KB
Binary file not shown.

apps/kitchen-sink/public/bust-3-d.glb

5.39 KB
Binary file not shown.

apps/kitchen-sink/public/bust-4-d.glb

2.32 KB
Binary file not shown.

apps/kitchen-sink/public/bust-5-d.glb

1.27 KB
Binary file not shown.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, input, Signal } from '@angular/core';
2+
import { NgtEuler, NgtVector3 } from 'angular-three';
3+
import { NgtsOrbitControls } from 'angular-three-soba/controls';
4+
import { injectGLTF } from 'angular-three-soba/loaders';
5+
import { NgtsBakeShadows } from 'angular-three-soba/misc';
6+
import { NgtsDetailed } from 'angular-three-soba/performances';
7+
import { NgtsEnvironment } from 'angular-three-soba/staging';
8+
import { Mesh, MeshStandardMaterial } from 'three';
9+
import { GLTF } from 'three-stdlib';
10+
11+
const positions = [...Array(800)].map(() => ({
12+
position: [40 - Math.random() * 80, 40 - Math.random() * 80, 40 - Math.random() * 80],
13+
rotation: [Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2],
14+
})) as Array<{ position: [number, number, number]; rotation: [number, number, number] }>;
15+
16+
interface BustGLTF extends GLTF {
17+
nodes: { Mesh_0001: Mesh };
18+
materials: { default: MeshStandardMaterial };
19+
}
20+
21+
@Component({
22+
selector: 'app-bust',
23+
standalone: true,
24+
template: `
25+
<ngts-detailed [distances]="[0, 15, 25, 35, 100]" [options]="{ position: position(), rotation: rotation() }">
26+
@for (level of gltfs() || []; track $index) {
27+
<ngt-mesh
28+
[receiveShadow]="true"
29+
[castShadow]="true"
30+
[geometry]="level.nodes.Mesh_0001.geometry"
31+
[material]="level.materials.default"
32+
>
33+
<ngt-value [rawValue]="0.25" attach="material.envMapIntensity" />
34+
</ngt-mesh>
35+
}
36+
</ngts-detailed>
37+
`,
38+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
39+
changeDetection: ChangeDetectionStrategy.OnPush,
40+
imports: [NgtsDetailed],
41+
})
42+
export class LODBust {
43+
position = input<NgtVector3>([0, 0, 0]);
44+
rotation = input<NgtEuler>([0, 0, 0]);
45+
46+
gltfs = injectGLTF(() => [
47+
'./bust-1-d.glb',
48+
'./bust-2-d.glb',
49+
'./bust-3-d.glb',
50+
'./bust-4-d.glb',
51+
'./bust-5-d.glb',
52+
]) as Signal<BustGLTF[] | null>;
53+
}
54+
55+
@Component({
56+
standalone: true,
57+
template: `
58+
@for (p of positions; track $index) {
59+
<app-bust [position]="p.position" [rotation]="p.rotation" />
60+
}
61+
<ngts-orbit-controls [options]="{ autoRotate: true, autoRotateSpeed: 0.5, zoomSpeed: 0.075 }" />
62+
<ngt-point-light [intensity]="0.5 * Math.PI" [decay]="0" />
63+
<ngt-spot-light [position]="50" [intensity]="1.5 * Math.PI" [castShadow]="true" [decay]="0" />
64+
<ngts-environment [options]="{ preset: 'city' }" />
65+
<ngts-bake-shadows />
66+
`,
67+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
68+
changeDetection: ChangeDetectionStrategy.OnPush,
69+
imports: [NgtsBakeShadows, NgtsEnvironment, NgtsOrbitControls, LODBust],
70+
})
71+
export class Experience {
72+
protected readonly Math = Math;
73+
protected readonly positions = positions;
74+
}
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 { NgtCanvas } from 'angular-three';
3+
import { NgtsLoader } from 'angular-three-soba/loaders';
4+
import { NgtsStats } from 'angular-three-soba/stats';
5+
import { Experience } from './experience';
6+
7+
@Component({
8+
standalone: true,
9+
template: `
10+
<ngt-canvas stats [sceneGraph]="scene" frameloop="demand" [camera]="{ position: [0, 0, 40] }" />
11+
<ngts-loader />
12+
`,
13+
changeDetection: ChangeDetectionStrategy.OnPush,
14+
imports: [NgtCanvas, NgtsLoader, NgtsStats],
15+
})
16+
export default class LOD {
17+
scene = Experience;
18+
}

apps/kitchen-sink/src/app/soba/shaky/shaky.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import { ChangeDetectionStrategy, Component } from '@angular/core';
22
import { NgtCanvas } from 'angular-three';
3+
import { NgtsStats } from 'angular-three-soba/stats';
34
import { Experience } from './experience';
45

56
@Component({
67
standalone: true,
78
template: `
8-
<ngt-canvas [sceneGraph]="scene" [shadows]="true" [camera]="{ position: [0, 160, 160], fov: 20 }" [dpr]="[1, 2]" />
9+
<ngt-canvas
10+
[stats]="{ minimal: true }"
11+
[sceneGraph]="scene"
12+
[shadows]="true"
13+
[camera]="{ position: [0, 160, 160], fov: 20 }"
14+
[dpr]="[1, 2]"
15+
/>
916
`,
10-
imports: [NgtCanvas],
17+
imports: [NgtCanvas, NgtsStats],
1118
changeDetection: ChangeDetectionStrategy.OnPush,
1219
host: { class: 'shaky-soba' },
1320
styles: `

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ const routes: Routes = [
1717
path: 'shaky',
1818
loadComponent: () => import('./shaky/shaky'),
1919
},
20+
{
21+
path: 'lod',
22+
loadComponent: () => import('./lod/lod'),
23+
},
2024
{
2125
path: '',
2226
redirectTo: 'basic',

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
2929
host: { class: 'soba' },
3030
})
3131
export default class Soba {
32-
examples = ['basic', 'hud', 'render-texture', 'shaky'];
32+
examples = ['basic', 'hud', 'render-texture', 'shaky', 'lod'];
3333
}

libs/soba/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@
3333
"camera-controls": "^2.8.0",
3434
"hls.js": "^1.5.0",
3535
"meshline": "^3.1.0",
36-
"stats-gl": "^1.0.0",
37-
"stats.js": "^0.17.0",
36+
"stats-gl": "^2.0.0",
3837
"three": ">=0.148.0",
3938
"three-custom-shader-material": "^5.4.0",
4039
"three-mesh-bvh": "^0.5.0 || ^0.6.0",

libs/soba/performances/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './lib/adaptive-dpr';
22
export * from './lib/adaptive-events';
3+
export * from './lib/detailed';
34
export * from './lib/instances/instances';
45
export * from './lib/points/points';
56
export * from './lib/segments/segments';
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {
2+
afterNextRender,
3+
ChangeDetectionStrategy,
4+
Component,
5+
CUSTOM_ELEMENTS_SCHEMA,
6+
ElementRef,
7+
input,
8+
viewChild,
9+
} from '@angular/core';
10+
import { extend, getLocalState, injectBeforeRender, NgtLOD, omit, pick } from 'angular-three';
11+
import { injectAutoEffect } from 'ngxtension/auto-effect';
12+
import { mergeInputs } from 'ngxtension/inject-inputs';
13+
import { LOD } from 'three';
14+
15+
export interface NgtsDetailedOptions extends Partial<NgtLOD> {
16+
hysteresis: number;
17+
}
18+
19+
const defaultOptions: NgtsDetailedOptions = {
20+
hysteresis: 0,
21+
};
22+
23+
@Component({
24+
selector: 'ngts-detailed',
25+
standalone: true,
26+
template: `
27+
<ngt-lOD #lod [parameters]="parameters()">
28+
<ng-content />
29+
</ngt-lOD>
30+
`,
31+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
32+
changeDetection: ChangeDetectionStrategy.OnPush,
33+
})
34+
export class NgtsDetailed {
35+
distances = input.required<number[]>();
36+
options = input(defaultOptions, { transform: mergeInputs(defaultOptions) });
37+
parameters = omit(this.options, ['hysteresis']);
38+
39+
lodRef = viewChild.required<ElementRef<LOD>>('lod');
40+
private hysteresis = pick(this.options, 'hysteresis');
41+
42+
constructor() {
43+
extend({ LOD });
44+
45+
const autoEffect = injectAutoEffect();
46+
47+
afterNextRender(() => {
48+
autoEffect(() => {
49+
const lod = this.lodRef().nativeElement;
50+
const localState = getLocalState(lod);
51+
if (!localState) return;
52+
const [, distances, hysteresis] = [localState.objects(), this.distances(), this.hysteresis()];
53+
lod.levels.length = 0;
54+
lod.children.forEach((object, index) => {
55+
lod.levels.push({ object, distance: distances[index], hysteresis });
56+
});
57+
});
58+
});
59+
60+
injectBeforeRender(({ camera }) => {
61+
this.lodRef().nativeElement.update(camera);
62+
});
63+
}
64+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2+
import { Meta } from '@storybook/angular';
3+
import { NgtArgs } from 'angular-three';
4+
import { NgtsOrbitControls } from 'angular-three-soba/controls';
5+
import { NgtsDetailed } from 'angular-three-soba/performances';
6+
import { makeDecorators, makeStoryFunction } from '../setup-canvas';
7+
8+
@Component({
9+
standalone: true,
10+
template: `
11+
<ngts-detailed [distances]="[0, 50, 150]">
12+
<ngt-mesh>
13+
<ngt-icosahedron-geometry *args="[10, 3]" />
14+
<ngt-mesh-basic-material color="hotpink" [wireframe]="true" />
15+
</ngt-mesh>
16+
17+
<ngt-mesh>
18+
<ngt-icosahedron-geometry *args="[10, 2]" />
19+
<ngt-mesh-basic-material color="lightgreen" [wireframe]="true" />
20+
</ngt-mesh>
21+
22+
<ngt-mesh>
23+
<ngt-icosahedron-geometry *args="[10, 1]" />
24+
<ngt-mesh-basic-material color="lightblue" [wireframe]="true" />
25+
</ngt-mesh>
26+
</ngts-detailed>
27+
28+
<ngts-orbit-controls [options]="{ enablePan: false, enableRotate: false, zoomSpeed: 0.5 }" />
29+
`,
30+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
31+
changeDetection: ChangeDetectionStrategy.OnPush,
32+
imports: [NgtsDetailed, NgtArgs, NgtsOrbitControls],
33+
})
34+
class DefaultDetailedStory {}
35+
36+
export default {
37+
title: 'Performances/Detailed',
38+
decorators: makeDecorators(),
39+
} as Meta;
40+
41+
export const Default = makeStoryFunction(DefaultDetailedStory, {
42+
camera: { position: [0, 0, 100] },
43+
controls: false,
44+
});

libs/soba/stats/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# angular-three-soba/stats
2+
3+
Secondary entry point of `angular-three-soba`. It can be used by importing from `angular-three-soba/stats`.

libs/soba/stats/ng-package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"lib": {
3+
"entryFile": "src/index.ts"
4+
}
5+
}

libs/soba/stats/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/stats';

libs/soba/stats/src/lib/stats.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { DOCUMENT } from '@angular/common';
2+
import { afterNextRender, computed, Directive, ElementRef, inject, input, untracked } from '@angular/core';
3+
import { addAfterEffect, injectStore, omit, pick, resolveRef } from 'angular-three';
4+
import { injectAutoEffect } from 'ngxtension/auto-effect';
5+
import { mergeInputs } from 'ngxtension/inject-inputs';
6+
import Stats from 'stats-gl';
7+
8+
export type NgtsStatsOptions = Partial<Stats> & {
9+
showPanel?: number;
10+
domClass: string;
11+
parent: ElementRef<HTMLElement> | HTMLElement | null | undefined;
12+
};
13+
14+
const defaultOptions: NgtsStatsOptions = {
15+
domClass: '',
16+
parent: null,
17+
};
18+
19+
@Directive({
20+
selector: 'ngt-canvas[stats]',
21+
standalone: true,
22+
})
23+
export class NgtsStats {
24+
options = input(defaultOptions, { transform: mergeInputs(defaultOptions), alias: 'stats' });
25+
26+
constructor() {
27+
const statsOptions = omit(this.options, ['parent', 'domClass']);
28+
const parent = pick(this.options, 'parent');
29+
const domClass = pick(this.options, 'domClass');
30+
31+
const document = inject(DOCUMENT);
32+
const autoEffect = injectAutoEffect();
33+
const store = injectStore();
34+
const gl = store.select('gl');
35+
36+
const stats = computed(() => {
37+
const _gl = gl();
38+
if (!_gl) return null;
39+
40+
const stats = new Stats({
41+
...untracked(statsOptions),
42+
});
43+
void stats.init(_gl);
44+
return stats;
45+
});
46+
47+
afterNextRender(() => {
48+
autoEffect(() => {
49+
const _stats = stats();
50+
if (!_stats) return;
51+
52+
const [_parent, _domClass] = [resolveRef(parent()), domClass()];
53+
const target = _parent ?? document.body;
54+
target.appendChild(_stats.dom);
55+
const classList = _domClass.split(' ').filter(Boolean);
56+
if (classList.length) _stats.dom.classList.add(...classList);
57+
const end = addAfterEffect(() => _stats.update());
58+
59+
return () => {
60+
if (classList.length) _stats.dom.classList.remove(...classList);
61+
target.removeChild(_stats.dom);
62+
end();
63+
};
64+
});
65+
});
66+
}
67+
}

tsconfig.base.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"angular-three-soba/performances": ["libs/soba/performances/src/index.ts"],
3232
"angular-three-soba/shaders": ["libs/soba/shaders/src/index.ts"],
3333
"angular-three-soba/staging": ["libs/soba/staging/src/index.ts"],
34+
"angular-three-soba/stats": ["libs/soba/stats/src/index.ts"],
3435
"angular-three-soba/vanilla-exports": ["libs/soba/vanilla-exports/src/index.ts"],
3536
"plugin": ["libs/plugin/src/index.ts"]
3637
}

0 commit comments

Comments
 (0)