Skip to content

Commit 9b16937

Browse files
Chau TranChau Tran
Chau Tran
authored and
Chau Tran
committed
feat(soba): migrate performance
1 parent 0d3245d commit 9b16937

File tree

17 files changed

+436
-6
lines changed

17 files changed

+436
-6
lines changed

libs/angular-three/src/lib/loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ injectNgtLoader['preload'] = <
140140
TLoaderConstructor extends NgtLoaderProto<TData>
141141
>(
142142
loaderConstructorFactory: (inputs: string[]) => TLoaderConstructor,
143-
inputs: Signal<TUrl>,
143+
inputs: () => TUrl,
144144
extensions?: NgtLoaderExtensions<TLoaderConstructor>
145145
) => {
146146
Promise.all(load(loaderConstructorFactory, inputs, { extensions })());

libs/soba/controls/src/orbit-controls/orbit-controls.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,18 @@ export class NgtsOrbitControls extends NgtSignalStore<NgtsOrbitControlsState> {
154154
#setEvents() {
155155
const trigger = computed(() => {
156156
const invalidate = this.#store.select('invalidate');
157-
const performance = this.#store.get('performance');
158-
return { invalidate: invalidate(), performance, controls: this.controlsRef.nativeElement };
157+
const performance = this.#store.select('performance');
158+
const regress = this.select('regress');
159+
return {
160+
invalidate: invalidate(),
161+
performance: performance(),
162+
regress: regress(),
163+
controls: this.controlsRef.nativeElement,
164+
};
159165
});
160166
effect((onCleanup) => {
161-
const { controls, invalidate, performance } = trigger();
167+
const { controls, invalidate, performance, regress } = trigger();
162168
if (!controls) return;
163-
const regress = this.get('regress');
164169
const changeCallback: (e: THREE.Event) => void = (e) => {
165170
invalidate();
166171
if (regress) performance.regress();

libs/soba/loaders/README.md

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

libs/soba/loaders/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+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Injector, Signal } from '@angular/core';
2+
import { injectNgtLoader, type NgtLoaderResults, type NgtObjectMap } from 'angular-three';
3+
// @ts-ignore
4+
import { MeshoptDecoder } from 'three-stdlib';
5+
import { DRACOLoader } from 'three-stdlib/loaders/DRACOLoader';
6+
import { GLTF, GLTFLoader } from 'three-stdlib/loaders/GLTFLoader';
7+
8+
let dracoLoader: DRACOLoader | null = null;
9+
10+
function _extensions(useDraco: boolean | string, useMeshOpt: boolean, extensions?: (loader: GLTFLoader) => void) {
11+
return (loader: THREE.Loader) => {
12+
if (extensions) {
13+
extensions(loader as GLTFLoader);
14+
}
15+
16+
if (useDraco) {
17+
if (!dracoLoader) {
18+
dracoLoader = new DRACOLoader();
19+
}
20+
21+
dracoLoader.setDecoderPath(
22+
typeof useDraco === 'string' ? useDraco : 'https://www.gstatic.com/draco/versioned/decoders/1.4.3/'
23+
);
24+
(loader as GLTFLoader).setDRACOLoader(dracoLoader);
25+
}
26+
27+
if (useMeshOpt) {
28+
(loader as GLTFLoader).setMeshoptDecoder(
29+
typeof MeshoptDecoder === 'function' ? MeshoptDecoder() : MeshoptDecoder
30+
);
31+
}
32+
};
33+
}
34+
35+
export function injectNgtsGLTFLoader<TUrl extends string | string[] | Record<string, string>>(
36+
path: () => TUrl,
37+
{
38+
useDraco = true,
39+
useMeshOpt = true,
40+
injector,
41+
extensions,
42+
}: {
43+
useDraco?: boolean | string;
44+
useMeshOpt?: boolean;
45+
injector?: Injector;
46+
extensions?: (loader: GLTFLoader) => void;
47+
} = {}
48+
): Signal<NgtLoaderResults<TUrl, GLTF & NgtObjectMap>> {
49+
return injectNgtLoader(() => GLTFLoader, path, {
50+
extensions: _extensions(useDraco, useMeshOpt, extensions),
51+
injector,
52+
});
53+
}
54+
55+
injectNgtsGLTFLoader['preload'] = <TUrl extends string | string[] | Record<string, string>>(
56+
path: () => TUrl,
57+
{
58+
useDraco = true,
59+
useMeshOpt = true,
60+
extensions,
61+
}: {
62+
useDraco?: boolean | string;
63+
useMeshOpt?: boolean;
64+
extensions?: (loader: GLTFLoader) => void;
65+
}
66+
) => {
67+
injectNgtLoader.preload(() => GLTFLoader, path, _extensions(useDraco, useMeshOpt, extensions));
68+
};

libs/soba/loaders/src/index.ts

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

libs/soba/performance/README.md

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

libs/soba/performance/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+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Directive, effect, inject, Input, signal, untracked } from '@angular/core';
2+
import { NgtStore } from 'angular-three';
3+
4+
@Directive({
5+
selector: 'ngts-adaptive-dpr',
6+
standalone: true,
7+
})
8+
export class NgtsAdaptiveDpr {
9+
#pixelated = signal(false);
10+
@Input() set pixelated(pixelated: boolean) {
11+
this.#pixelated.set(pixelated);
12+
}
13+
14+
constructor() {
15+
const store = inject(NgtStore);
16+
effect(
17+
(onCleanup) => {
18+
const domElement = store.get('gl', 'domElement');
19+
onCleanup(() => {
20+
const active = store.get('internal', 'active');
21+
const setDpr = store.get('setDpr');
22+
const initialDpr = store.get('viewport', 'initialDpr');
23+
if (active) setDpr(initialDpr);
24+
if (untracked(this.#pixelated) && domElement) domElement.style.imageRendering = 'auto';
25+
});
26+
},
27+
{ allowSignalWrites: true }
28+
);
29+
30+
effect(
31+
() => {
32+
const performanceCurrent = store.select('performance', 'current');
33+
const { gl, viewport, setDpr } = store.get();
34+
setDpr(performanceCurrent() * viewport.initialDpr);
35+
if (untracked(this.#pixelated) && gl.domElement)
36+
gl.domElement.style.imageRendering = performanceCurrent() === 1 ? 'auto' : 'pixelated';
37+
},
38+
{ allowSignalWrites: true }
39+
);
40+
}
41+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Directive, effect, inject } from '@angular/core';
2+
import { NgtStore } from 'angular-three';
3+
4+
@Directive({
5+
selector: 'ngts-adaptive-events',
6+
standalone: true,
7+
})
8+
export class NgtsAdaptiveEvents {
9+
constructor() {
10+
const store = inject(NgtStore);
11+
effect(
12+
(onCleanup) => {
13+
const enabled = store.get('events', 'enabled');
14+
onCleanup(() => {
15+
const setEvents = store.get('setEvents');
16+
setEvents({ enabled });
17+
});
18+
},
19+
{ allowSignalWrites: true }
20+
);
21+
22+
effect(
23+
() => {
24+
const performanceCurrent = store.select('performance', 'current')();
25+
const setEvents = store.get('setEvents');
26+
setEvents({ enabled: performanceCurrent === 1 });
27+
},
28+
{ allowSignalWrites: true }
29+
);
30+
}
31+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Component, computed, CUSTOM_ELEMENTS_SCHEMA, effect, Input } from '@angular/core';
2+
import { extend, injectNgtRef, NgtSignalStore, type NgtBeforeRenderEvent, type NgtLOD } from 'angular-three';
3+
import { LOD } from 'three';
4+
5+
extend({ LOD });
6+
7+
export interface NgtsDetailedState {
8+
distances: number[];
9+
}
10+
11+
declare global {
12+
interface HTMLElementTagNameMap {
13+
'ngts-detailed': NgtsDetailedState & NgtLOD;
14+
}
15+
}
16+
17+
@Component({
18+
selector: 'ngts-detailed',
19+
standalone: true,
20+
template: `
21+
<ngt-lOD ngtCompound [ref]="lodRef" (beforeRender)="onLODBeforeRender($event)">
22+
<ng-content />
23+
</ngt-lOD>
24+
`,
25+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
26+
})
27+
export class NgtsDetailed extends NgtSignalStore<NgtsDetailedState> {
28+
@Input() lodRef = injectNgtRef<LOD>();
29+
30+
@Input({ required: true }) set distances(distances: number[]) {
31+
this.set({ distances });
32+
}
33+
34+
constructor() {
35+
super();
36+
this.#updateChildren();
37+
}
38+
39+
onLODBeforeRender({ object, state }: NgtBeforeRenderEvent<THREE.LOD>) {
40+
object.update(state.camera);
41+
}
42+
43+
#updateChildren() {
44+
const trigger = computed(() => {
45+
const children = this.lodRef.children();
46+
const distances = this.select('distances');
47+
return { children: children(), distances: distances() };
48+
});
49+
effect(() => {
50+
const { children, distances } = trigger();
51+
const lod = this.lodRef.untracked;
52+
if (lod) {
53+
lod.levels.length = 0;
54+
children.forEach((child, index) => {
55+
lod.addLevel(child, distances[index]);
56+
});
57+
}
58+
});
59+
}
60+
}

libs/soba/performance/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './adaptive/adaptive-dpr';
2+
export * from './adaptive/adaptive-events';
3+
export * from './detailed/detailed';
4+
export * from './stats/stats';
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { DOCUMENT } from '@angular/common';
2+
import { computed, Directive, effect, ElementRef, inject, Input } from '@angular/core';
3+
import { addAfterEffect, addEffect, is, NgtAnyRecord, NgtSignalStore } from 'angular-three';
4+
import * as Stats from 'stats.js';
5+
6+
export interface NgtsStatsState {
7+
showPanel: number;
8+
right: boolean;
9+
parent?: HTMLElement;
10+
classes?: string;
11+
}
12+
13+
@Directive({
14+
selector: 'ngts-stats',
15+
standalone: true,
16+
})
17+
export class NgtsStats extends NgtSignalStore<NgtsStatsState> {
18+
@Input() set showPanel(showPanel: number) {
19+
this.set({ showPanel });
20+
}
21+
22+
@Input() set parent(parent: HTMLElement | ElementRef<HTMLElement>) {
23+
this.set({ parent: is.ref(parent) ? parent.nativeElement : parent });
24+
}
25+
26+
@Input() set classes(classes: string) {
27+
this.set({ classes });
28+
}
29+
30+
@Input() set right(right: boolean) {
31+
this.set({ right });
32+
}
33+
34+
constructor() {
35+
super({ showPanel: 0, right: false });
36+
37+
const document = inject(DOCUMENT);
38+
const stats: Stats = (Stats as NgtAnyRecord)['default']
39+
? new (Stats as NgtAnyRecord)['default']()
40+
: new Stats();
41+
42+
const trigger = computed(() => {
43+
const showPanel = this.select('showPanel');
44+
const right = this.select('right');
45+
const parent = this.select('parent');
46+
const classes = this.select('classes');
47+
return { showPanel: showPanel(), right: right(), parent: parent(), classes: classes() };
48+
});
49+
50+
effect((onCleanup) => {
51+
const { showPanel, right, parent, classes } = trigger();
52+
53+
const node = parent ?? document.body;
54+
stats.showPanel(showPanel);
55+
node.appendChild(stats.dom);
56+
if (classes) {
57+
stats.dom.classList.add(...classes.split(' ').filter((cls: string) => cls));
58+
}
59+
if (right) {
60+
stats.dom.style.right = '0px';
61+
stats.dom.style.left = 'inherit';
62+
} else {
63+
stats.dom.style.left = '0px';
64+
stats.dom.style.right = 'inherit';
65+
}
66+
const begin = addEffect(() => stats.begin());
67+
const end = addAfterEffect(() => stats.end());
68+
onCleanup(() => {
69+
node.removeChild(stats.dom);
70+
begin();
71+
end();
72+
});
73+
});
74+
}
75+
}

libs/soba/project.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@
5252
"libs/soba/cameras/**/*.ts",
5353
"libs/soba/cameras/**/*.html",
5454
"libs/soba/misc/**/*.ts",
55-
"libs/soba/misc/**/*.html"
55+
"libs/soba/misc/**/*.html",
56+
"libs/soba/performance/**/*.ts",
57+
"libs/soba/performance/**/*.html",
58+
"libs/soba/loaders/**/*.ts",
59+
"libs/soba/loaders/**/*.html"
5660
]
5761
}
5862
},

0 commit comments

Comments
 (0)