feat: added threlte & threejs implementations

This commit is contained in:
Bart van der Braak 2023-07-29 14:32:25 +02:00
parent 908499271c
commit d199ec5305
10 changed files with 198 additions and 109 deletions

Binary file not shown.

View file

@ -0,0 +1 @@
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>

After

Width:  |  Height:  |  Size: 963 B

View file

@ -0,0 +1,29 @@
<!--
Auto-generated by: https://github.com/threlte/threlte/tree/main/packages/gltf
Command: npx @threlte/gltf@1.0.0-next.13 ./src/lib/assets/vectors/github.glb --transform
-->
<script>
import { Group } from 'three';
import { T, forwardEventHandlers } from '@threlte/core';
import { useGltf } from '@threlte/extras';
export const ref = new Group();
const gltf = useGltf('./github-transformed.glb', { useDraco: true });
const component = forwardEventHandlers();
</script>
<T is={ref} dispose={false} {...$$restProps} bind:this={$component}>
{#await gltf}
<slot name="fallback" />
{:then gltf}
<T.Mesh geometry={gltf.nodes.Github_Mesh.geometry}
><T.MeshPhysicalMaterial color={[0,0,0]} /></T.Mesh
>
{:catch error}
<slot name="error" {error} />
{/await}
<slot {ref} />
</T>

45
src/lib/extrude-svg.ts Normal file
View file

@ -0,0 +1,45 @@
import type * as THREE from 'three';
import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader';
import type { SVGResult, SVGResultPaths } from 'three/examples/jsm/loaders/SVGLoader';
import { Mesh } from 'three/src/objects/Mesh';
import { Group } from 'three/src/objects/Group';
import { MeshNormalMaterial } from 'three/src/materials/MeshNormalMaterial';
import { ExtrudeGeometry } from 'three/src/geometries/ExtrudeGeometry';
/**
* Parses the provided SVG markup and extrudes it into a 3D model using THREE.js.
* @param svgMarkup - SVG markup to extrude.
* @return Group containing all of the extruded SVG paths.
* @throws Error If the SVG markup is empty.
*/
export function extrudeSvg(svgMarkup: string): Group {
if (!svgMarkup) {
throw new Error('SVG markup is empty');
}
const svgData: SVGResult = new SVGLoader().parse(svgMarkup);
const material: MeshNormalMaterial = new MeshNormalMaterial();
const svgGroup: Mesh[][] = svgData.paths.map(createShapeFromPath);
const group = new Group();
svgGroup.flat().forEach(mesh => group.add(mesh));
return group;
function createShapeFromPath(path: SVGResultPaths): Mesh[] {
const shapes: THREE.Shape[] = path.toShapes(true);
return shapes.map(shape => extrudeShape(shape, material));
}
function extrudeShape(shape: THREE.Shape, material: MeshNormalMaterial): Mesh {
const geometry = new ExtrudeGeometry(shape, {
depth: 20,
bevelEnabled: false
});
return new Mesh(geometry, material);
}
}

View file

@ -1,98 +1,7 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { browser } from '$app/environment';
import * as THREE from 'three';
import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader.js';
const svgGitHub = `<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg>`;
const loader = new SVGLoader();
let canvasContainer: HTMLDivElement;
let scene: THREE.Scene;
let camera: THREE.PerspectiveCamera;
let renderer: THREE.WebGLRenderer;
onMount(() => {
if (!browser) return; // Ensure it runs only in the browser environment
initThreeJS();
animate();
});
function initThreeJS() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
75,
canvasContainer.clientWidth / canvasContainer.clientHeight,
0.1,
1000
);
camera.position.z = 5;
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(canvasContainer.clientWidth, canvasContainer.clientHeight);
renderer.setClearColor(0x000000, 0); // Transparent background
const svgData = loader.parse(svgGitHub);
const svgGroup = new THREE.Group();
const material = new THREE.MeshNormalMaterial();
// Loop through all of the parsed paths
svgData.paths.forEach((path, i) => {
const shapes = path.toShapes(true);
// Each path has array of shapes
shapes.forEach((shape, j) => {
// Finally we can take each shape and extrude it
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: 20,
bevelEnabled: false
});
// Create a mesh and add it to the group
const mesh = new THREE.Mesh(geometry, material);
svgGroup.add(mesh);
});
});
// Add our group to the scene (you'll need to create a scene)
scene.add(svgGroup);
canvasContainer.appendChild(renderer.domElement);
// Handle window resizing
window.addEventListener('resize', onWindowResize);
}
function onWindowResize() {
if (!camera || !renderer) return;
camera.aspect = canvasContainer.clientWidth / canvasContainer.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(canvasContainer.clientWidth, canvasContainer.clientHeight);
}
function animate() {
if (!browser) return; // Ensure it runs only in the browser environment
requestAnimationFrame(animate);
// Add animations or changes to the scene here if needed
if (scene && camera && renderer) {
renderer.render(scene, camera);
}
}
// Cleanup function
function cleanup() {
if (!browser) return; // Ensure it runs only in the browser environment
window.removeEventListener('resize', onWindowResize);
renderer?.dispose();
}
// Cleanup when the component is destroyed
onDestroy(cleanup);
import { Canvas } from '@threlte/core';
import Scene from './Scene.svelte';
import Github3d from '$lib/components/gltf/Github3d.svelte';
</script>
<svelte:head>
@ -101,13 +10,9 @@
<main class="container mx-auto px-4 py-8 text-left">
<h2 class="text-3xl font-bold mb-8">Tools</h2>
<Canvas>
<Scene />
<Github3d />
</Canvas>
</main>
<div bind:this={canvasContainer} class="canvas-container" />
<style>
.canvas-container {
width: 100%;
height: 100%;
}
</style>

View file

@ -0,0 +1,42 @@
<script>
import Github3d from '$lib/components/gltf/Github3d.svelte';
import { T, useFrame } from '@threlte/core';
import { interactivity } from '@threlte/extras';
import { spring } from 'svelte/motion';
interactivity();
const scale = spring(1);
let rotation = 0;
useFrame((_state, delta) => {
rotation += delta;
});
</script>
<T.PerspectiveCamera
makeDefault
position={[10, 10, 10]}
on:create={({ ref }) => {
ref.lookAt(0, 1, 0);
}}
/>
<T.Mesh
rotation.y={rotation}
position.y={1}
scale={$scale}
on:pointerenter={() => scale.set(1.5)}
on:pointerleave={() => scale.set(1)}
on:click={() => scale.set(3)}
>
<T.BoxGeometry args={[1, 2, 1]} />
<T.MeshBasicMaterial color={[0, 0, 0]} />
</T.Mesh>
<Github3d
position={[0, 0, 0]}
scale={$scale}
on:pointerenter={() => scale.set(1.5)}
on:pointerleave={() => scale.set(1)}
on:click={() => scale.set(3)}
/>