Skip to content

NgtsSampler / surfaceSampler

NgtsSampler distributes instances across a mesh surface using THREE.js MeshSurfaceSampler. It samples points from a mesh and automatically updates an InstancedMesh with the sampled transforms.

Usage

With Content Children

<ngts-sampler [options]="{ weight: 'normal', transform: transformPoint, count: 500 }">
<ngt-mesh>
<ngt-sphere-geometry *args="[2]" />
</ngt-mesh>
<ngt-instanced-mesh *args="[undefined, undefined, 500]">
<ngt-sphere-geometry *args="[0.1]" />
</ngt-instanced-mesh>
</ngts-sampler>

Transform Function

The transform function receives sample data and should mutate payload.dummy to set position, rotation, and scale:

const transformPoint = ({ dummy, position, normal }: TransformPayload) => {
dummy.position.copy(position);
dummy.lookAt(position.clone().add(normal));
dummy.scale.setScalar(Math.random() * 0.5 + 0.5);
};

surfaceSampler Function

A reactive function that creates a computed signal sampling points on a mesh surface:

import { surfaceSampler } from 'angular-three-soba/misc';
const samples = surfaceSampler(() => meshRef()?.nativeElement, {
count: () => 1000,
instancedMesh: () => instancesRef()?.nativeElement,
transform: () => ({ dummy, position, normal }) => {
dummy.position.copy(position);
dummy.lookAt(position.clone().add(normal));
}
});

Options

Properties

name type description
count number Number of samples to distribute across the mesh surface. Default: 16
weight string Name of a vertex attribute for weighted sampling. Higher values = more likely to be sampled
transform TransformFn Custom transform function applied to each sampled instance

Utilities for distributing instances across a mesh surface using THREE.js MeshSurfaceSampler.

A component that distributes instances across a mesh surface. It samples points from a mesh and automatically updates an InstancedMesh with the sampled transforms. Both the source mesh and target instances can be provided as inputs or as children.

<ngts-sampler [options]="{ weight: 'normal', transform: transformPoint, count: 500 }">
<ngt-mesh>
<ngt-sphere-geometry *args="[2]" />
</ngt-mesh>
<ngt-instanced-mesh *args="[undefined, undefined, 500]">
<ngt-sphere-geometry *args="[0.1]" />
</ngt-instanced-mesh>
</ngts-sampler>
@Component({
template: `
<ngts-sampler [instances]="instancedRef()" [mesh]="mesh()" [options]="{ count: 500 }" />
<ngt-instanced-mesh #instanced *args="[undefined, undefined, 500]">
<!-- content -->
</ngt-instanced-mesh>
`,
})
class MyComponent {
instancedRef = viewChild<ElementRef<InstancedMesh>>('instanced');
gltf = gltfResource(() => 'my/mesh/url');
mesh = computed(() => this.gltf.value()?.scene || null);
}
PropertyDescriptionDefault
meshThe mesh to sample points from. If not provided, uses the first Mesh childnull
instancesThe InstancedMesh to update with sampled transforms. If not provided, uses first childnull
optionsSampler configuration object-
PropertyDescriptionDefault
weightName of a vertex attribute for weighted sampling. Higher values = more likely to be sampledundefined
transformCustom transform function applied to each sampled instanceundefined
countNumber of samples to distribute across the mesh surface16

The transform function receives sample data and should mutate payload.dummy to set position, rotation, and scale:

const transformPoint = ({ dummy, position, normal }: TransformPayload) => {
dummy.position.copy(position);
dummy.lookAt(position.clone().add(normal));
dummy.scale.setScalar(Math.random() * 0.5 + 0.5);
};

A function that creates a computed signal sampling points on a mesh surface. Returns an InstancedBufferAttribute containing transform matrices for each sample, suitable for use with InstancedMesh or custom instancing solutions.

function surfaceSampler(
mesh: () => ElementRef<THREE.Mesh> | THREE.Mesh | null | undefined,
options?: {
count?: () => number;
transform?: () => TransformFn | undefined;
weight?: () => string | undefined;
instancedMesh?: () => ElementRef<THREE.InstancedMesh> | THREE.InstancedMesh | null | undefined;
},
): Signal<THREE.InstancedBufferAttribute>;
@Component({
template: `
<ngt-mesh #mesh>
<ngt-sphere-geometry />
</ngt-mesh>
<ngt-instanced-mesh #instances *args="[undefined, undefined, 1000]">
<ngt-box-geometry *args="[0.1, 0.1, 0.1]" />
</ngt-instanced-mesh>
`,
})
class MyComponent {
meshRef = viewChild<ElementRef<THREE.Mesh>>('mesh');
instancesRef = viewChild<ElementRef<THREE.InstancedMesh>>('instances');
samples = surfaceSampler(() => this.meshRef()?.nativeElement, {
count: () => 1000,
instancedMesh: () => this.instancesRef()?.nativeElement,
transform:
() =>
({ dummy, position, normal }) => {
dummy.position.copy(position);
dummy.lookAt(position.clone().add(normal));
dummy.scale.setScalar(Math.random() * 0.5 + 0.5);
},
});
}
ParameterTypeDescription
mesh() => ElementRef<Mesh> | Mesh | null | undefinedSignal of the mesh to sample from
optionsObject (see below)Configuration options
PropertyTypeDescription
count() => numberSignal of sample count
transform() => TransformFn | undefinedSignal of transform function
weight() => string | undefinedSignal of vertex attribute name
instancedMesh() => ElementRef<InstancedMesh> | InstancedMesh | nullSignal of target instanced mesh

Returns a Signal<THREE.InstancedBufferAttribute> containing the transform matrices.