@threlte/extras
meshBounds
A raycast function that will first check for collision with the object’s bounding sphere, then it will check against the object’s bounding box but only if the bounding box has been previously computed. You can use this function if you want to trade pointer precision for an increase in performance.
Usage
Basic Usage
meshBounds
can simply be passed to an object’s
raycast
prop.
<script>
import { meshBounds } from '@threlte/extras'
</script>
<T.Mesh raycast={meshBounds}>
<T.BoxGeometry />
</T.Mesh>
If your meshes or models aren’t very complex in terms of geometry, meshBounds
won’t provide much
of a performance boost.
<script lang="ts">
import Scene from './Scene.svelte'
import { Canvas } from '@threlte/core'
import { Checkbox, Pane } from 'svelte-tweakpane-ui'
let showBounds = $state(false)
</script>
<Pane
title="meshBounds"
position="fixed"
>
<Checkbox
bind:value={showBounds}
label="show bounds"
/>
</Pane>
<div>
<Canvas>
<Scene {showBounds} />
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style>
import type { Vector3Tuple } from 'three'
export class BoundsMesh {
wireframe = $state(true)
constructor(readonly position: Vector3Tuple) {}
}
<script lang="ts">
import { BoundsMesh } from './BoundsMesh.svelte.ts'
import { OrbitControls } from '@threlte/extras'
import { T } from '@threlte/core'
import { interactivity, meshBounds } from '@threlte/extras'
import type { Vector3Tuple } from 'three'
type Props = {
showBounds: boolean
}
let { showBounds }: Props = $props()
interactivity()
const positions: Vector3Tuple[] = [
[0, 1, 0],
[1, -1, 0],
[-1, -1, 0]
]
const meshes = positions.map((position) => new BoundsMesh(position))
const size = 1
// half of the box's diagonal === radius of the bounding sphere
const radius = 0.5 * size * Math.sqrt(3)
</script>
<T.AmbientLight />
<T.PerspectiveCamera
makeDefault
position.z={5}
>
<OrbitControls />
</T.PerspectiveCamera>
{#each meshes as mesh}
<T.Mesh
raycast={meshBounds}
onpointerenter={() => {
mesh.wireframe = false
}}
onpointerleave={() => {
mesh.wireframe = true
}}
position={mesh.position}
>
<T.BoxGeometry args={[size, size, size]} />
<T.MeshStandardMaterial
color="hotpink"
wireframe={mesh.wireframe}
/>
</T.Mesh>
{/each}
<T.Group visible={showBounds}>
{#each meshes as { position }}
<T.Mesh {position}>
<T.SphereGeometry args={[radius]} />
<T.MeshStandardMaterial
transparent
opacity={0.25}
/>
</T.Mesh>
{/each}
</T.Group>
Creating a plugin
Instead of manually applying the meshBounds
raycast function to each mesh, you
can use a Threlte plugin to automatically
apply the meshBounds
raycast function to all meshes in your scene.
<script>
import { T, injectPlugin, isInstanceOf } from '@threlte/core'
import { meshBounds } from '@threlte/extras'
injectPlugin('mesh-bounds-plugin', (args) => {
if (isInstanceOf(args.ref, 'Mesh')) {
args.ref.raycast = meshBounds
}
})
</script>
<!-- No need to manually apply the meshBounds raycast function -->
<T.Mesh>
<T.MeshBasicMaterial color="hotpink" />
<T.BoxGeometry />
</T.Mesh>
To selectively apply the meshBounds
raycast function to only certain meshes,
you can listen to a prop (e.g. raycastMeshBounds
) and conditionally apply the
function.
<script>
import { T, injectPlugin, isInstanceOf } from '@threlte/core'
import { meshBounds } from '@threlte/extras'
import { onDestroy } from 'svelte'
injectPlugin('mesh-bounds-plugin', (args) => {
if (!isInstanceOf(args.ref, 'Mesh')) return
const originalRaycast = args.ref.raycast
$effect(() => {
if (!!args.props.raycastMeshBounds) {
args.ref.raycast = meshBounds
} else {
args.ref.raycast = originalRaycast
}
})
onDestroy(() => {
args.ref.raycast = originalRaycast
})
return {
pluginProps: ['raycastMeshBounds']
}
})
</script>
<!-- Regular raycasting -->
<T.Mesh>
<T.MeshBasicMaterial color="hotpink" />
<T.BoxGeometry />
</T.Mesh>
<!-- meshBounds raycasting -->
<T.Mesh raycastMeshBounds>
<T.MeshBasicMaterial color="hotpink" />
<T.BoxGeometry />
</T.Mesh>