Skip to content

Commit 4840344

Browse files
Chau TranChau Tran
Chau Tran
authored and
Chau Tran
committed
fix(core): adjust timing on effects that read computed inputs
1 parent 97fe376 commit 4840344

File tree

23 files changed

+861
-145
lines changed

23 files changed

+861
-145
lines changed

libs/angular-three/src/lib/di/before-render.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export function injectBeforeRender(
88
{ priority = 0, injector }: { priority?: number; injector?: Injector } = {}
99
) {
1010
injector = assertInjectionContext(injectBeforeRender, injector);
11-
1211
return runInInjectionContext(injector, () => {
1312
const store = inject(NgtStore);
1413
const sub = store.get('internal').subscribe(cb, priority, store);

libs/angular-three/src/lib/di/ref.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@ export function injectNgtRef<TElement>(
2626
injector?: Injector
2727
): NgtInjectedRef<TElement> {
2828
injector = assertInjectionContext(injectNgtRef, injector);
29-
3029
return runInInjectionContext(injector, () => {
3130
const cdr = inject(ChangeDetectorRef);
32-
3331
const ref = is.ref(initial) ? initial : new ElementRef<TElement>(initial as TElement);
3432
const signalRef = createSignal(ref.nativeElement);
3533
const readonlySignal = signalRef.asReadonly();

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

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -107,24 +107,29 @@ export function injectNgtLoader<
107107
const response = signal<NgtLoaderResults<TUrl, NgtBranchingReturn<TReturn, GLTF, GLTF & NgtObjectMap>>>(null!);
108108
const effector = load(loaderConstructorFactory, inputs, { extensions, onProgress });
109109

110-
effect(() => {
111-
const originalUrls = untracked(inputs);
112-
Promise.all(effector())
113-
.then((results) => {
114-
if (Array.isArray(originalUrls)) return results;
115-
if (typeof originalUrls === 'string') return results[0];
116-
const keys = Object.keys(originalUrls);
117-
return keys.reduce((result, key) => {
118-
(result as NgtAnyRecord)[key] = results[keys.indexOf(key)];
119-
return result;
120-
}, {} as { [key in keyof TUrl]: NgtBranchingReturn<TReturn, GLTF, GLTF & NgtObjectMap> });
121-
})
122-
.then((value) => {
123-
response.set(
124-
value as NgtLoaderResults<TUrl, NgtBranchingReturn<TReturn, GLTF, GLTF & NgtObjectMap>>
125-
);
126-
safeDetectChanges(cdr);
127-
});
110+
requestAnimationFrame(() => {
111+
effect(
112+
() => {
113+
const originalUrls = untracked(inputs);
114+
Promise.all(effector())
115+
.then((results) => {
116+
if (Array.isArray(originalUrls)) return results;
117+
if (typeof originalUrls === 'string') return results[0];
118+
const keys = Object.keys(originalUrls);
119+
return keys.reduce((result, key) => {
120+
(result as NgtAnyRecord)[key] = results[keys.indexOf(key)];
121+
return result;
122+
}, {} as { [key in keyof TUrl]: NgtBranchingReturn<TReturn, GLTF, GLTF & NgtObjectMap> });
123+
})
124+
.then((value) => {
125+
response.set(
126+
value as NgtLoaderResults<TUrl, NgtBranchingReturn<TReturn, GLTF, GLTF & NgtObjectMap>>
127+
);
128+
safeDetectChanges(cdr);
129+
});
130+
},
131+
{ injector }
132+
);
128133
});
129134

130135
return response.asReadonly();

libs/soba/abstractions/src/line/line.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ declare global {
4040
export class NgtsLine extends NgtsLineInputs {
4141
@Input() lineRef = injectNgtRef<LineSegments2 | Line2>();
4242

43-
@Input() set points(
43+
@Input({ required: true }) set points(
4444
points: Array<THREE.Vector3 | THREE.Vector2 | [number, number, number] | [number, number] | number>
4545
) {
4646
this.set({ points });

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,7 @@ export class NgtsOrbitControls extends NgtSignalStore<NgtsOrbitControlsState> {
131131

132132
#makeControlsDefault() {
133133
const makeDefault = this.select('makeDefault');
134-
const trigger = computed(() => {
135-
return { controls: this.controlsRef.nativeElement, makeDefault: makeDefault() };
136-
});
134+
const trigger = computed(() => ({ controls: this.controlsRef.nativeElement, makeDefault: makeDefault() }));
137135

138136
effect(
139137
(onCleanup) => {
@@ -154,14 +152,12 @@ export class NgtsOrbitControls extends NgtSignalStore<NgtsOrbitControlsState> {
154152
const performance = this.#store.select('performance');
155153
const regress = this.select('regress');
156154

157-
const trigger = computed(() => {
158-
return {
159-
invalidate: invalidate(),
160-
performance: performance(),
161-
regress: regress(),
162-
controls: this.controlsRef.nativeElement,
163-
};
164-
});
155+
const trigger = computed(() => ({
156+
invalidate: invalidate(),
157+
performance: performance(),
158+
regress: regress(),
159+
controls: this.controlsRef.nativeElement,
160+
}));
165161
effect((onCleanup) => {
166162
const { controls, invalidate, performance, regress } = trigger();
167163
if (!controls) return;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.ngts-loader-container {
2+
--ngts-loader-container-opacity: 0;
3+
position: absolute;
4+
top: 0;
5+
left: 0;
6+
width: 100%;
7+
height: 100%;
8+
background: #171717;
9+
display: flex;
10+
align-items: center;
11+
justify-content: center;
12+
transition: opacity 300ms ease;
13+
z-index: 1000;
14+
opacity: var(--ngts-loader-container-opacity);
15+
}
16+
17+
.ngts-loader-inner {
18+
width: 100px;
19+
height: 3px;
20+
background: #272727;
21+
text-align: center;
22+
}
23+
24+
.ngts-loader-bar {
25+
--ngts-loader-bar-scale: 0;
26+
height: 3px;
27+
width: 100px;
28+
background: white;
29+
transition: transform 200ms;
30+
transform-origin: left center;
31+
transform: scaleX(var(--ngts-loader-bar-scale));
32+
}
33+
34+
.ngts-loader-data {
35+
display: inline-block;
36+
position: relative;
37+
font-variant-numeric: tabular-nums;
38+
margin-top: 0.8em;
39+
color: #f0f0f0;
40+
font-size: 0.6em;
41+
font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, Roboto,
42+
Ubuntu, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
43+
white-space: nowrap;
44+
}

libs/soba/misc/src/animations/animations.ts

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import * as THREE from 'three';
44

55
export function injectNgtsAnimations(
66
animationsFactory: () => THREE.AnimationClip[],
7-
{
8-
ref,
9-
injector,
10-
}: {
11-
ref?: NgtInjectedRef<THREE.Object3D> | THREE.Object3D;
12-
injector?: Injector;
13-
}
7+
{ ref, injector }: { ref?: NgtInjectedRef<THREE.Object3D> | THREE.Object3D; injector?: Injector }
148
) {
159
injector = assertInjectionContext(injectNgtsAnimations, injector);
1610
return runInInjectionContext(injector, () => {
@@ -44,27 +38,32 @@ export function injectNgtsAnimations(
4438

4539
injectBeforeRender(({ delta }) => mixer.update(delta));
4640

47-
effect(() => {
48-
const actual = actualRef.nativeElement;
49-
const animations = animationsFactory();
41+
requestAnimationFrame(() => {
42+
effect(
43+
() => {
44+
const actual = actualRef.nativeElement;
45+
const animations = animationsFactory();
5046

51-
for (let i = 0; i < animations.length; i++) {
52-
const clip = animations[i];
47+
for (let i = 0; i < animations.length; i++) {
48+
const clip = animations[i];
5349

54-
names.push(clip.name);
55-
clips.push(clip);
50+
names.push(clip.name);
51+
clips.push(clip);
5652

57-
Object.defineProperty(actions, clip.name, {
58-
enumerable: true,
59-
get: () => {
60-
return cached[clip.name] || (cached[clip.name] = mixer.clipAction(clip, actual));
61-
},
62-
});
53+
Object.defineProperty(actions, clip.name, {
54+
enumerable: true,
55+
get: () => {
56+
return cached[clip.name] || (cached[clip.name] = mixer.clipAction(clip, actual));
57+
},
58+
});
6359

64-
if (i === 0) {
65-
actions[clip.name].play();
66-
}
67-
}
60+
if (i === 0) {
61+
actions[clip.name].play();
62+
}
63+
}
64+
},
65+
{ injector }
66+
);
6867
});
6968

7069
return { ref: actualRef, actions, mixer, names, clips };

libs/soba/misc/src/depth-buffer/depth-buffer.ts

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,39 +21,44 @@ export function injectNgtsDepthBuffer(
2121
const size = store.select('size');
2222
const dpr = store.select('viewport', 'dpr');
2323

24-
const fboParams = computed(() => {
25-
const params = { size: 256, frames: Infinity, ...paramsFactory() };
26-
const width = params.size || size().width * dpr();
27-
const height = params.size || size().height * dpr();
28-
const depthTexture = new THREE.DepthTexture(width, height);
29-
depthTexture.format = THREE.DepthFormat;
30-
depthTexture.type = THREE.UnsignedShortType;
31-
return { width, height, settings: { depthTexture } };
32-
});
24+
requestAnimationFrame(() => {
25+
const fboParams = computed(() => {
26+
const params = { size: 256, frames: Infinity, ...paramsFactory() };
27+
const width = params.size || size().width * dpr();
28+
const height = params.size || size().height * dpr();
29+
const depthTexture = new THREE.DepthTexture(width, height);
30+
depthTexture.format = THREE.DepthFormat;
31+
depthTexture.type = THREE.UnsignedShortType;
32+
return { width, height, settings: { depthTexture } };
33+
});
3334

34-
const fboRef = injectNgtsFBO(fboParams);
35-
36-
let count = 0;
37-
injectBeforeRender(({ gl, scene, camera }) => {
38-
const params = { size: 256, frames: Infinity, ...paramsFactory() };
39-
if ((params.frames === Infinity || count < params.frames) && fboRef.untracked) {
40-
gl.setRenderTarget(fboRef.untracked);
41-
gl.render(scene, camera);
42-
gl.setRenderTarget(null);
43-
count++;
44-
}
45-
});
35+
const fboRef = injectNgtsFBO(fboParams, { injector });
4636

47-
effect(
48-
() => {
49-
const fbo = fboRef.nativeElement;
50-
if (fbo) {
51-
depthBufferRef.nativeElement = fbo.depthTexture;
52-
safeDetectChanges(cdr);
53-
}
54-
},
55-
{ allowSignalWrites: true }
56-
);
37+
effect(
38+
() => {
39+
const fbo = fboRef.nativeElement;
40+
if (fbo) {
41+
depthBufferRef.nativeElement = fbo.depthTexture;
42+
safeDetectChanges(cdr);
43+
}
44+
},
45+
{ allowSignalWrites: true, injector }
46+
);
47+
48+
let count = 0;
49+
injectBeforeRender(
50+
({ gl, scene, camera }) => {
51+
const params = { size: 256, frames: Infinity, ...paramsFactory() };
52+
if ((params.frames === Infinity || count < params.frames) && fboRef.untracked) {
53+
gl.setRenderTarget(fboRef.untracked);
54+
gl.render(scene, camera);
55+
gl.setRenderTarget(null);
56+
count++;
57+
}
58+
},
59+
{ injector }
60+
);
61+
});
5762

5863
return depthBufferRef;
5964
});

libs/soba/misc/src/fbo/fbo.ts

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,40 +33,42 @@ export function injectNgtsFBO(fboParams: () => NgtsFBOParams, { injector }: { in
3333

3434
inject(DestroyRef).onDestroy(() => targetRef.untracked.dispose());
3535

36-
const size = store.select('size');
37-
const dpr = store.select('viewport', 'dpr');
38-
const trigger = computed(() => {
39-
const { width, height, settings } = fboParams();
40-
const _width = typeof width === 'number' ? width : size().width * dpr();
41-
const _height = typeof height === 'number' ? height : size().height * dpr();
42-
const _settings = (typeof width === 'number' ? settings : (width as FBOSettings)) || {};
36+
requestAnimationFrame(() => {
37+
const size = store.select('size');
38+
const dpr = store.select('viewport', 'dpr');
39+
const trigger = computed(() => {
40+
const { width, height, settings } = fboParams();
41+
const _width = typeof width === 'number' ? width : size().width * dpr();
42+
const _height = typeof height === 'number' ? height : size().height * dpr();
43+
const _settings = (typeof width === 'number' ? settings : (width as FBOSettings)) || {};
4344

44-
return { width: _width, height: _height, settings: _settings };
45-
});
45+
return { width: _width, height: _height, settings: _settings };
46+
});
4647

47-
effect(
48-
() => {
49-
const { width, height, settings } = trigger();
50-
const { samples = 0, depth, ...targetSettings } = settings;
51-
if (!targetRef.untracked) {
52-
const target = new THREE.WebGLRenderTarget(width, height, {
53-
minFilter: THREE.LinearFilter,
54-
magFilter: THREE.LinearFilter,
55-
type: THREE.HalfFloatType,
56-
...targetSettings,
57-
});
58-
if (depth) target.depthTexture = new THREE.DepthTexture(width, height, THREE.FloatType);
48+
effect(
49+
() => {
50+
const { width, height, settings } = trigger();
51+
const { samples = 0, depth, ...targetSettings } = settings;
52+
if (!targetRef.untracked) {
53+
const target = new THREE.WebGLRenderTarget(width, height, {
54+
minFilter: THREE.LinearFilter,
55+
magFilter: THREE.LinearFilter,
56+
type: THREE.HalfFloatType,
57+
...targetSettings,
58+
});
59+
if (depth) target.depthTexture = new THREE.DepthTexture(width, height, THREE.FloatType);
5960

60-
target.samples = samples;
61-
targetRef.nativeElement = target;
62-
}
61+
target.samples = samples;
62+
targetRef.nativeElement = target;
63+
}
6364

64-
targetRef.untracked.setSize(width, height);
65-
if (samples) targetRef.nativeElement.samples = samples;
66-
safeDetectChanges(cdr);
67-
},
68-
{ allowSignalWrites: true }
69-
);
65+
targetRef.untracked.setSize(width, height);
66+
if (samples) targetRef.nativeElement.samples = samples;
67+
safeDetectChanges(cdr);
68+
},
69+
{ allowSignalWrites: true, injector }
70+
);
71+
});
7072

7173
return targetRef;
7274
});

libs/soba/src/performance/adaptive.stories.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export default {
7373
decorators: [moduleMetadata({ imports: [StorybookSetup] })],
7474
} as Meta;
7575

76+
// TODO: mousemove doesn't seem to trigger change detection on mouse up
7677
export const Default = makeStoryFunction(DefaultAdaptiveStory, {
7778
camera: { position: [0, 0, 30], fov: 50 },
7879
controls: false,

libs/soba/src/setup-canvas.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,14 @@ export function makeStoryObject(
243243
return { render, args, argTypes };
244244
}
245245

246+
export function number(defaultValue: number): number;
247+
export function number(
248+
defaultValue: number,
249+
options: { min?: number; max?: number; step?: number; range?: true }
250+
): {
251+
defaultValue: number;
252+
control: { control: { type: 'range' | 'number'; min?: number; max?: number; step?: number } };
253+
};
246254
export function number(
247255
defaultValue: number,
248256
options: { min?: number; max?: number; step?: number; range?: true } = {}

0 commit comments

Comments
 (0)