@threlte/xr
<VRButton>
<VRButton /> is an HTML <button /> that can be used to init a VR session. It will also display info about browser support.
<script lang="ts">
import { Canvas } from '@threlte/core'
import { VRButton } from '@threlte/xr'
import Scene from './Scene.svelte'
</script>
<div>
<Canvas>
<Scene />
</Canvas>
<VRButton />
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
// The following components started as copies from https://fun-bit.vercel.app/
import BirchTrees from './assets/birch.svelte'
import Trees from './assets/tree.svelte'
import Bushes from './assets/bush.svelte'
import Rocks from './assets/rock.svelte'
const numberOfObjects = 50
const distinctObjects = 4
const commonRatio = 0.5
function calculateExponentialSumValues(
total: number,
numberOfValues: number,
commonRatio: number
): number[] {
let result = []
let remainingTotal = total
for (let i = 0; i < numberOfValues - 1; i++) {
let term = Math.ceil(remainingTotal * (1 - commonRatio))
result.push(term)
remainingTotal -= term
}
// The last term to ensure the sum is exactly equal to the total
result.push(remainingTotal)
return result
}
const data = $derived.by(() => {
const exponentialSumValues = calculateExponentialSumValues(
numberOfObjects,
distinctObjects,
commonRatio
)
const totalBushes = exponentialSumValues[0] ?? 0
const totalTrees = exponentialSumValues[1] ?? 0
const totalBirchTrees = exponentialSumValues[2] ?? 0
const totalRocks = exponentialSumValues[3] ?? 0
const randomBushes = []
const randomTrees = []
const randomBirchTrees = []
const randomRocks = []
for (let i = 0; i < totalBushes; i++) {
randomBushes.push([Math.random(), Math.random(), Math.random(), Math.random()])
if (i < totalTrees) {
randomTrees.push([Math.random(), Math.random(), Math.random(), Math.random()])
}
if (i < totalBirchTrees) {
randomBirchTrees.push([Math.random(), Math.random(), Math.random(), Math.random()])
}
if (i < totalRocks) {
randomRocks.push([Math.random(), Math.random(), Math.random(), Math.random()])
}
}
return {
randomBushes,
randomTrees,
randomBirchTrees,
randomRocks
}
})
</script>
<Bushes transformData={data.randomBushes as [number, number, number, number][]} />
<BirchTrees transformData={data.randomBirchTrees as [number, number, number, number][]} />
<Trees transformData={data.randomTrees as [number, number, number, number][]} />
<Rocks transformData={data.randomRocks as [number, number, number, number][]} />
<script lang="ts">
import { T } from '@threlte/core'
import { OrbitControls } from '@threlte/extras'
import { XR } from '@threlte/xr'
import Random from './Random.svelte'
</script>
<XR />
<T.PerspectiveCamera
makeDefault
position={[20, 20, 20]}
>
<OrbitControls maxPolarAngle={1.56} />
</T.PerspectiveCamera>
<T.DirectionalLight
position={[3, 10, 7]}
castShadow
shadow.camera.top={10}
shadow.camera.left={-10}
shadow.camera.right={10}
shadow.camera.bottom={-10}
/>
<T.AmbientLight />
<T.Mesh
rotation.x={-Math.PI / 2}
receiveShadow
>
<T.PlaneGeometry args={[20, 20, 1, 1]} />
<T.MeshStandardMaterial color="green" />
</T.Mesh>
<Random />
<script lang="ts">
import { Mesh, MeshStandardMaterial, RepeatWrapping, DoubleSide } from 'three'
import { T } from '@threlte/core'
import { useGltf, useTexture, InstancedMesh, Instance } from '@threlte/extras'
interface Props {
transformData?: [number, number, number, number][]
}
let { transformData = [] }: Props = $props()
type GLTFResult = {
nodes: {
Cube004: Mesh
Cube004_1: Mesh
}
materials: {
BirchTree_Bark: MeshStandardMaterial
BirchTree_Leaves: MeshStandardMaterial
}
}
const assets = Promise.all([
useGltf<GLTFResult>('https://fun-bit.vercel.app/Ultimate-Stylized-Nature/BirchTree_1.gltf'),
useTexture('https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Textures/BirchTree_Bark.png'),
useTexture('https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Textures/BirchTree_Leaves.png'),
useTexture(
'https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Textures/BirchTree_Bark_Normal.png'
)
])
</script>
{#await assets then [$gltf, $texture1, $texture2, $normalMap1]}
<InstancedMesh castShadow>
<T is={$gltf.nodes.Cube004.geometry} />
<T.MeshStandardMaterial
map={$texture1}
map.wrapS={RepeatWrapping}
map.wrapT={RepeatWrapping}
normalMap={$normalMap1}
normalMap.wrapS={RepeatWrapping}
normalMap.wrapT={RepeatWrapping}
/>
{#each transformData as randomValues}
{@const x = randomValues[0] * 20 - 10}
{@const z = randomValues[1] * 20 - 10}
{@const rot = randomValues[2] * Math.PI * 2}
{@const scale = randomValues[3] * 2 + 1}
<Instance
position.x={x}
position.z={z}
rotation.y={rot}
{scale}
/>
{/each}
</InstancedMesh>
<InstancedMesh castShadow>
<T is={$gltf.nodes.Cube004_1.geometry} />
<T.MeshStandardMaterial
map={$texture2}
side={DoubleSide}
alphaTest={0.5}
/>
{#each transformData as randomValues}
{@const x = randomValues[0] * 20 - 10}
{@const z = randomValues[1] * 20 - 10}
{@const rot = randomValues[2] * Math.PI * 2}
{@const scale = randomValues[3] * 2 + 1}
<Instance
position.x={x}
position.z={z}
rotation.y={rot}
{scale}
/>
{/each}
</InstancedMesh>
{/await}
<script lang="ts">
import * as THREE from 'three'
import { T } from '@threlte/core'
import { useGltf, useTexture, InstancedMesh, Instance } from '@threlte/extras'
interface Props {
transformData?: [number, number, number, number][]
}
let { transformData = [] }: Props = $props()
type GLTFResult = {
nodes: {
Bush: THREE.Mesh
}
materials: {
Bush_Leaves: THREE.MeshStandardMaterial
}
}
const gltf = useGltf<GLTFResult>('https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Bush.gltf')
const texture1 = useTexture(
'https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Textures/Bush_Leaves.png'
)
const assets = Promise.all([gltf, texture1])
</script>
{#await assets then [$gltf, $texture1]}
<InstancedMesh
castShadow
receiveShadow
>
<T is={$gltf.nodes.Bush.geometry} />
<T.MeshStandardMaterial
map={$texture1}
alphaTest={0.2}
/>
{#each transformData as randomValues}
{@const x = randomValues[0] * 20 - 10}
{@const z = randomValues[1] * 20 - 10}
{@const rot = randomValues[2] * Math.PI * 2}
{@const scale = randomValues[3] * 2 + 0.5}
<T.Group
position.x={x}
position.z={z}
rotation.y={rot}
{scale}
>
<Instance rotation={[1.96, -0.48, -0.85]} />
</T.Group>
{/each}
</InstancedMesh>
{/await}
<script lang="ts">
import * as THREE from 'three'
import { T } from '@threlte/core'
import { useGltf, InstancedMesh, Instance } from '@threlte/extras'
interface Props {
transformData?: [number, number, number, number][]
}
let { transformData = [] }: Props = $props()
type GLTFResult = {
nodes: {
Rock_2: THREE.Mesh
}
materials: {
Rock: THREE.MeshStandardMaterial
}
}
const gltf = useGltf<GLTFResult>(
'https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Rock_2.gltf'
)
</script>
{#if $gltf}
<InstancedMesh
castShadow
receiveShadow
>
<T is={$gltf.nodes.Rock_2.geometry} />
<T.MeshStandardMaterial color="grey" />
{#each transformData as randomValues}
{@const x = randomValues[0] * 20 - 10}
{@const z = randomValues[1] * 20 - 10}
{@const rot = randomValues[2] * Math.PI * 2}
{@const scale = randomValues[3] + 0.5}
<Instance
position.x={x}
position.z={z}
rotation.y={rot}
{scale}
/>
{/each}
</InstancedMesh>
{/if}
<script lang="ts">
import * as THREE from 'three'
import { T } from '@threlte/core'
import { useGltf, useTexture, InstancedMesh, Instance } from '@threlte/extras'
interface Props {
transformData?: [number, number, number, number][]
}
let { transformData = [] }: Props = $props()
type GLTFResult = {
nodes: {
Cylinder001: THREE.Mesh
Cylinder001_1: THREE.Mesh
}
materials: {
NormalTree_Bark: THREE.MeshStandardMaterial
NormalTree_Leaves: THREE.MeshStandardMaterial
}
}
const assets = Promise.all([
useGltf<GLTFResult>('https://fun-bit.vercel.app/Ultimate-Stylized-Nature/NormalTree_1.gltf'),
useTexture('https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Textures/NormalTree_Bark.png'),
useTexture(
'https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Textures/NormalTree_Leaves.png'
),
useTexture(
'https://fun-bit.vercel.app/Ultimate-Stylized-Nature/Textures/NormalTree_Bark_Normal.png'
)
])
</script>
{#await assets then [$gltf, $texture1, $texture2, $normalMap1]}
<InstancedMesh castShadow>
<T is={$gltf.nodes.Cylinder001.geometry} />
<T.MeshStandardMaterial
map={$texture1}
map.wrapS={THREE.RepeatWrapping}
map.wrapT={THREE.RepeatWrapping}
normalMap={$normalMap1}
normalMap.wrapS={THREE.RepeatWrapping}
normalMap.wrapT={THREE.RepeatWrapping}
/>
{#each transformData as randomValues}
{@const x = randomValues[0] * 20 - 10}
{@const z = randomValues[1] * 20 - 10}
{@const rot = randomValues[2] * Math.PI * 2}
{@const scale = randomValues[3] * 2 + 1}
<Instance
position.x={x}
position.z={z}
rotation.y={rot}
{scale}
/>
{/each}
</InstancedMesh>
<InstancedMesh castShadow>
<T is={$gltf.nodes.Cylinder001_1.geometry} />
<T.MeshStandardMaterial
map={$texture2}
side={THREE.DoubleSide}
alphaTest={0.5}
/>
{#each transformData as randomValues}
{@const x = randomValues[0] * 20 - 10}
{@const z = randomValues[1] * 20 - 10}
{@const rot = randomValues[2] * Math.PI * 2}
{@const scale = randomValues[3] * 2 + 1}
<Instance
position.x={x}
position.z={z}
rotation.y={rot}
{scale}
/>
{/each}
</InstancedMesh>
{/await}