@threlte/extras
<HUD>
Renders a heads-up-display (HUD). Each HUD creates a new scene rendered on top of the main scene with a separate Threlte context and camera.
The HUD component creates a partially new Threlte context, specifically a new scene and camera.
Everything else in useThrelte
is preserved and reused.
<script lang="ts">
import { Canvas } from '@threlte/core'
import Scene from './Scene.svelte'
</script>
<div>
<Canvas>
<Scene />
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
import { T, useTask } from '@threlte/core'
import { interactivity, useCursor, useViewport } from '@threlte/extras'
import { Mesh, Quaternion } from 'three'
interface Props {
quaternion: Quaternion
onselect: (arg: string) => void
}
let { quaternion, onselect }: Props = $props()
const viewport = useViewport()
let meshes: [Mesh, Mesh, Mesh] = [null!, null!, null!]
const boxCursor = useCursor('pointer')
const torusCursor = useCursor('pointer')
const torusKnotCursor = useCursor('pointer')
interactivity()
useTask(
() => {
for (const mesh of meshes) {
mesh.quaternion.copy(quaternion)
}
},
{ autoInvalidate: false }
)
const boxHovering = boxCursor.hovering
const torusHovering = torusCursor.hovering
const torusKnotHovering = torusKnotCursor.hovering
</script>
<T.OrthographicCamera
makeDefault
zoom={80}
position={[0, 0, 10]}
/>
<T.AmbientLight intensity={Math.PI / 2} />
<T.PointLight
position={[10, 10, 10]}
decay={0}
intensity={Math.PI * 2}
/>
<T.Mesh
bind:ref={meshes[0]}
position={[$viewport.width / 2 - 1, $viewport.height / 2 - 1, 0]}
onpointerenter={boxCursor.onPointerEnter}
onpointerleave={boxCursor.onPointerLeave}
onclick={() => onselect('box')}
scale={$boxHovering ? 1.1 : 1}
>
<T.BoxGeometry args={[0.5, 0.5, 0.5]} />
<T.MeshToonMaterial color={$boxHovering ? 'hotpink' : 'gray'} />
</T.Mesh>
<T.Mesh
bind:ref={meshes[1]}
position={[$viewport.width / 2 - 2, $viewport.height / 2 - 1, 0]}
onpointerenter={torusCursor.onPointerEnter}
onpointerleave={torusCursor.onPointerLeave}
onclick={() => onselect('torus')}
scale={$torusHovering ? 1.1 : 1}
>
<T.TorusGeometry args={[0.25, 0.1]} />
<T.MeshToonMaterial color={$torusHovering ? 'hotpink' : 'gray'} />
</T.Mesh>
<T.Mesh
bind:ref={meshes[2]}
position={[$viewport.width / 2 - 3, $viewport.height / 2 - 1, 0]}
onpointerover={torusKnotCursor.onPointerEnter}
onpointerleave={torusKnotCursor.onPointerLeave}
onclick={() => onselect('torusknot')}
scale={$torusKnotHovering ? 1.1 : 1}
>
<T.TorusKnotGeometry args={[0.215, 0.08, 256]} />
<T.MeshToonMaterial color={$torusKnotHovering ? 'hotpink' : 'gray'} />
</T.Mesh>
<script lang="ts">
import { T, useTask, useThrelte } from '@threlte/core'
import { Float, OrbitControls, HUD } from '@threlte/extras'
import { Quaternion } from 'three'
import HudScene from './HudScene.svelte'
let selected = $state('box')
let rotation = $state(0)
const quaternion = new Quaternion()
const { camera } = useThrelte()
useTask((delta) => {
rotation += delta
// Spin mesh to the inverse of the default cameras matrix
quaternion.copy(camera.current.quaternion).invert()
}, { autoInvalidate: false })
</script>
<T.PerspectiveCamera
position={[11, 5, 11]}
makeDefault
fov={30}
>
<OrbitControls enableZoom={false} />
</T.PerspectiveCamera>
<T.DirectionalLight position={[0, 10, 10]} />
<T.AmbientLight intensity={0.6} />
<T.GridHelper args={[5]} />
<HUD>
<HudScene
{quaternion}
onselect={(arg) => {
selected = arg
}}
/>
</HUD>
<Float
speed={8}
rotation.y={rotation}
>
{#if selected === 'box'}
<T.Mesh
position.y={0.8}
scale={2}
>
<T.BoxGeometry args={[0.5, 0.5, 0.5]} />
<T.MeshToonMaterial color="turquoise" />
</T.Mesh>
{:else if selected === 'torus'}
<T.Mesh
position.y={0.8}
scale={1.8}
>
<T.TorusGeometry args={[0.25, 0.1]} />
<T.MeshToonMaterial color="turquoise" />
</T.Mesh>
{:else if selected === 'torusknot'}
<T.Mesh
position.y={0.8}
scale={1.8}
>
<T.TorusKnotGeometry args={[0.215, 0.08, 256]} />
<T.MeshToonMaterial color="turquoise" />
</T.Mesh>
{/if}
</Float>
Because creating a <HUD>
is somewhat similar to creating a <Canvas>
, it is recommended to use the same best practices and
place all objects you want in the HUD within a new Scene
component:
MyHUD.svelte
<script>
import Scene from './Scene.svelte'
</script>
<HUD>
<Scene />
</HUD>
Scene.svelte
<script>
import { T } from '@threlte/core'
</script>
<T.PerspectiveCamera
makeDefault
position={[0, 0, 0]}
on:create={({ ref }) => ref.lookAt(0, 0, 0)}
/>
<T.AmbientLight />
<T.Mesh>
<T.BoxGeometry />
<T.MeshStandardMaterial />
</T.Mesh>