Threlte first scene

Shoe

Efficiently configure Threlte scene

Threlte is a 3D framework for the web. Built on top of SvelteandThree.js.

Threlte first scene

Installation & Configuration

1. Install required packagesĀ 

terminal

npm install three @threlte/core \
            @threlte/extras \
            @threlte/rapier @dimforge/rapier3d-compat \
            @threlte/theatre @theatre/core @theatre/studio \
            @threlte/xr 

This will install all necessary packages for threlte

2. Configure vite.config.js

vite.config.js

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';

export default defineConfig({
	plugins: [sveltekit()],
	test: {
		include: ['src/**/*.{test,spec}.{js,ts}']
	},
	ssr: {
		noExternal: ['three']
	}
});

File structure

3. Every threlte consists of a couple of components

3a. Route page aka the ā€œ+page.svelteā€ [Contains Canvas from threlte core]

3b. 3d Model File, named like 3dModelName.svelte [normally under lib folder, this file is dedicated to the model configuration, material, texture, model loader, etc. ]

3c. Scene File named like Scene3.svelte [HDR, Camera, Grid & import the model file here]

Create svelte threlte component from GLB fileĀ 

1. Export the glb model from blender (eg. PaperCup_Tutorial.glb)

2. Copy the glb model to the static folder

3. Open terminal from the sveltekit project folder, using the terminal change your directory to the model folder.

4. Run

terminal

npx @threlte/gltf@next ./PaperCup_Tutorial.glb

5. This will create a svelte component named PaperCup_Tutorial.svelte

Creating the scene file

Here is an example of a scene file, Usually I create this file under "Scenes" folder inside the "lib" folder Check the provided code, I have tried to include the required snippet, you must update the code based on your requirements. You must update the Environment path and files name (In my case, I have a HDR file named "stadium_01_2k.hdr" resides under static/hdr folder, the hdr file downloaded from hdrheaven)

scene.svelte

<script>
	import { T } from '@threlte/core';
	import {
		Environment,
		OrbitControls,
		TransformControls,
		interactivity,
		ContactShadows,
		SoftShadows,
		HTML
	} from '@threlte/extras';
	import { BoxGeometry, DirectionalLightHelper, MeshStandardMaterial } from 'three';
	import { spring, tweened } from 'svelte/motion';
</script>

<!-- This requires a .hdr file inside static folder -->
<Environment path="/hdr/" files="stadium_01_2k.hdr" isBackground={false} />

<T.PerspectiveCamera makeDefault position={[3, 3, 10]} lookAt.y={0.0} fov={30}>
	<OrbitControls enableDamping />
</T.PerspectiveCamera>

<T.GridHelper args={[10, 10]} />

<TransformControls>
	<T.Mesh position={[0, 0.5, 0]} scale={7}>
		<T.SphereGeometry />
		<T.MeshStandardMaterial roughness={0} color="gray" side={0} />
	</T.Mesh>
</TransformControls>

<HTML position.y={2} scale={0.5} transform occlude={false} sprite={false} center>
	<button class="btn btn-primary"> Hello Threlte </button>
</HTML>

<ContactShadows scale={20} blur={2} far={2.5} opacity={1} position={[0, -1, 0]} />

Creating the route file

1. Open +page.svelte file, Import Canvas and sceneOne (this is the file where we will build the scene)file from library

import { Canvas } from '@threlte/core';
import SceneOne from '../lib/SceneOne.svelte';

2. Inside +page.svelte file, Create a section where we want the canvas to live, the canvas is our placeholder to render all three js objects and interactivitiesĀ 

+page.svelte

<section class="p-5">
	<div class="canvas-wrapper ring h-[500px]">
		<Canvas>
			<SceneOne />
		</Canvas>
	</div>
</section>

3. Let’s update the sceneOne.svelte file, in my case, resides under the lib folder which is under the src folder.

Import required packages

sceneOne.svelte

import { PerspectiveCamera, DirectionalLight, AmbientLight, Mesh, useFrame } from '@threlte/core';
import { MeshStandardMaterial, IcosahedronGeometry } from 'three';
import { tweened } from 'svelte/motion';

// tweened take a new number and go from defined number (which is 3 in this case) to that new number
	// within defined duration (which is 2000 in our case)
	const t = tweened(0, { duration: 2000 });
	const t2 = tweened(1, { duration: 50 });
	// setTimeout(() => {
	// 	t.set(-3);
	// });

	let rotation = 0;
	useFrame(() => {
		rotation += 0.01;
	});

4. Now create camera, light, and mesh

<PerspectiveCamera position={{ y: 0, z: 20 }} lookAt={{ x: 0, y: 0, z: 0 }} />

<DirectionalLight />
<AmbientLight />

<!-- when we want to interact with any 3d object we must set the properties to 'interactive' -->
<Mesh
	geometry={new IcosahedronGeometry()}
	material={new MeshStandardMaterial({ color: 'seagreen' })}
	position={{ x: $t, y: 0, z: 15 }}
	rotation={{ x: rotation, y: 0, z: rotation }}
	scale={$t2}
	interactive
	on:pointerleave={() => {
		$t2 = 1;
		// console.log('reverse pointer enter');
	}}
	on:pointerenter={() => {
		$t2 = 1.3;
		// console.log('pointer enter');
	}}
	on:click={() => {
		$t = $t > 0 ? -4 : 4;
	}}
/>

That’s pretty much it