Skip to content

Commit f5262bc

Browse files
committed
fix(soba): clean up sampler; use computed instead of setting signals in effect
1 parent 3c074c5 commit f5262bc

File tree

1 file changed

+88
-95
lines changed

1 file changed

+88
-95
lines changed

libs/soba/misc/src/lib/sampler.ts

Lines changed: 88 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,17 @@ import {
22
afterNextRender,
33
ChangeDetectionStrategy,
44
Component,
5+
computed,
56
CUSTOM_ELEMENTS_SCHEMA,
67
effect,
78
ElementRef,
89
inject,
910
Injector,
1011
input,
11-
signal,
12-
untracked,
1312
viewChild,
1413
} from '@angular/core';
15-
import { checkUpdate, extend, getLocalState, NgtGroup, omit, resolveRef } from 'angular-three';
14+
import { checkUpdate, extend, getLocalState, NgtGroup, omit, pick, resolveRef } from 'angular-three';
1615
import { assertInjector } from 'ngxtension/assert-injector';
17-
import { injectAutoEffect } from 'ngxtension/auto-effect';
1816
import { mergeInputs } from 'ngxtension/inject-inputs';
1917
import { BufferGeometry, Color, Group, InstancedBufferAttribute, InstancedMesh, Mesh, Object3D, Vector3 } from 'three';
2018
import { MeshSurfaceSampler } from 'three-stdlib';
@@ -62,82 +60,80 @@ export function injectSurfaceSampler(
6260
{ injector }: { injector?: Injector } = {},
6361
) {
6462
return assertInjector(injectSurfaceSampler, injector, () => {
65-
const buffer = signal(
66-
(() => {
67-
const arr = Array.from({ length: options().count ?? 16 }, () => [
68-
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
69-
]).flat();
70-
return new InstancedBufferAttribute(Float32Array.from(arr), 16);
71-
})(),
72-
);
73-
74-
effect(
75-
() => {
76-
const currentMesh = resolveRef(mesh());
77-
if (!currentMesh) return;
78-
79-
const localState = getLocalState(currentMesh);
80-
if (!localState) return;
81-
82-
const nonObjects = localState.nonObjects();
83-
if (
84-
!nonObjects ||
85-
!nonObjects.length ||
86-
nonObjects.every((nonObject) => !(nonObject as BufferGeometry).isBufferGeometry)
87-
) {
88-
return;
63+
const initialBufferAttribute = (() => {
64+
const arr = Array.from({ length: options().count ?? 16 }, () => [
65+
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
66+
]).flat();
67+
return new InstancedBufferAttribute(Float32Array.from(arr), 16);
68+
})();
69+
70+
const buffer = computed(() => {
71+
const currentMesh = resolveRef(mesh());
72+
if (!currentMesh) return initialBufferAttribute;
73+
74+
const localState = getLocalState(currentMesh);
75+
if (!localState) return initialBufferAttribute;
76+
77+
const nonObjects = localState.nonObjects();
78+
if (
79+
!nonObjects ||
80+
!nonObjects.length ||
81+
nonObjects.every((nonObject) => !(nonObject as BufferGeometry).isBufferGeometry)
82+
) {
83+
return initialBufferAttribute;
84+
}
85+
86+
const sampler = new MeshSurfaceSampler(currentMesh);
87+
const { weight, count = 16, transform, instanceMesh } = options();
88+
89+
if (weight) {
90+
sampler.setWeightAttribute(weight);
91+
}
92+
93+
sampler.build();
94+
95+
const position = new Vector3();
96+
const normal = new Vector3();
97+
const color = new Color();
98+
const dummy = new Object3D();
99+
const instance = resolveRef(instanceMesh);
100+
101+
currentMesh.updateMatrixWorld(true);
102+
103+
for (let i = 0; i < count; i++) {
104+
sampler.sample(position, normal, color);
105+
106+
if (typeof transform === 'function') {
107+
transform({ dummy, sampledMesh: currentMesh, position, normal, color }, i);
108+
} else {
109+
dummy.position.copy(position);
89110
}
90111

91-
const sampler = new MeshSurfaceSampler(currentMesh);
112+
dummy.updateMatrix();
92113

93-
const { weight, count = 16, transform, instanceMesh } = options();
94-
95-
if (weight) {
96-
sampler.setWeightAttribute(weight);
114+
if (instance) {
115+
instance.setMatrixAt(i, dummy.matrix);
97116
}
98117

99-
sampler.build();
100-
101-
const position = new Vector3();
102-
const normal = new Vector3();
103-
const color = new Color();
104-
const dummy = new Object3D();
105-
const instance = resolveRef(instanceMesh);
106-
107-
currentMesh.updateMatrixWorld(true);
108-
109-
for (let i = 0; i < count; i++) {
110-
sampler.sample(position, normal, color);
111-
112-
if (typeof transform === 'function') {
113-
transform({ dummy, sampledMesh: currentMesh, position, normal, color }, i);
114-
} else {
115-
dummy.position.copy(position);
116-
}
117-
118-
dummy.updateMatrix();
119-
120-
if (instance) {
121-
instance.setMatrixAt(i, dummy.matrix);
122-
}
123-
124-
dummy.matrix.toArray(untracked(buffer).array, i * 16);
125-
}
118+
dummy.matrix.toArray(initialBufferAttribute.array, i * 16);
119+
}
126120

127-
if (instance) {
128-
checkUpdate(instance.instanceMatrix);
129-
}
121+
if (instance) {
122+
checkUpdate(instance.instanceMatrix);
123+
}
130124

131-
checkUpdate(buffer);
125+
checkUpdate(initialBufferAttribute);
132126

133-
buffer.set(
134-
new InstancedBufferAttribute(untracked(buffer).array, untracked(buffer).itemSize).copy(untracked(buffer)),
135-
);
136-
},
137-
{ allowSignalWrites: true },
138-
);
127+
return new InstancedBufferAttribute(initialBufferAttribute.array, initialBufferAttribute.itemSize).copy(
128+
initialBufferAttribute,
129+
);
130+
});
139131

140-
return buffer.asReadonly();
132+
return computed(() => {
133+
const _buffer = buffer();
134+
if (!_buffer) return initialBufferAttribute;
135+
return _buffer;
136+
});
141137
});
142138
}
143139

@@ -184,44 +180,41 @@ export class NgtsSampler {
184180

185181
groupRef = viewChild.required<ElementRef<Group>>('group');
186182

187-
private meshToSample = signal<Mesh | null>(null);
188-
private instancedToSample = signal<InstancedMesh | null>(null);
183+
private sampleState = computed(() => {
184+
const group = this.groupRef().nativeElement;
185+
const localState = getLocalState(group);
186+
if (!localState) return { mesh: null, instanced: null };
187+
188+
const [mesh, instances] = [resolveRef(this.mesh()), resolveRef(this.instances())];
189+
const objects = localState.objects();
190+
191+
return {
192+
mesh: mesh ?? (objects.find((c) => c.type === 'Mesh') as Mesh),
193+
instanced:
194+
instances ?? (objects.find((c) => !!Object.getOwnPropertyDescriptor(c, 'instanceMatrix')) as InstancedMesh),
195+
};
196+
});
189197

190198
constructor() {
191199
extend({ Group });
192-
const autoEffect = injectAutoEffect();
193200
const injector = inject(Injector);
194201

195202
afterNextRender(() => {
196-
autoEffect(
197-
() => {
198-
const group = this.groupRef().nativeElement;
199-
const localState = getLocalState(group);
200-
if (!localState) return;
201-
202-
const [mesh, instances] = [resolveRef(this.mesh()), resolveRef(this.instances())];
203-
204-
this.meshToSample.set(mesh ?? (localState.objects().find((c) => c.type === 'Mesh') as Mesh));
205-
this.instancedToSample.set(
206-
instances ??
207-
(localState
208-
.objects()
209-
.find((c) => !!Object.getOwnPropertyDescriptor(c, 'instanceMatrix')) as InstancedMesh),
210-
);
211-
},
212-
{ allowSignalWrites: true },
213-
);
203+
const meshToSample = pick(this.sampleState, 'mesh');
204+
const instancedToSample = pick(this.sampleState, 'instanced');
214205

215-
injectSurfaceSampler(
216-
this.meshToSample,
206+
const sampler = injectSurfaceSampler(
207+
meshToSample,
217208
() => ({
218209
count: this.options().count,
219210
transform: this.options().transform,
220211
weight: this.options().weight,
221-
instanceMesh: this.instancedToSample(),
212+
instanceMesh: instancedToSample(),
222213
}),
223214
{ injector },
224215
);
216+
217+
effect(sampler, { injector });
225218
});
226219
}
227220
}

0 commit comments

Comments
 (0)