-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ce8d02d
commit 3be368f
Showing
19 changed files
with
834 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<script lang="ts"> | ||
import type { RigidBody } from '@dimforge/rapier3d-compat' | ||
import { Mesh, useFrame, Object3DInstance, type Position } from '@threlte/core' | ||
import { SphereBufferGeometry, MeshBasicMaterial, Vector3, Object3D } from 'three' | ||
import { useRapier } from '@threlte/rapier' | ||
export let position: Position = {}; | ||
export let strength: number = 1 | ||
export let range: number = 50 | ||
export let showHelper: boolean = false | ||
export let gravityType: 'static' | 'linear' | 'newtonian' = 'static' | ||
export let gravitationalConstant: number = 6.673e-11 | ||
const { world } = useRapier() | ||
const gravitySource = new Vector3() | ||
let obj = new Object3D() | ||
const calcForceByType = { | ||
static: (s: number, m2: number, r: number, d: number, G: number): number => s, | ||
linear: (s: number, m2: number, r: number, d: number, G: number) => s * (d / r), | ||
newtonian: (s: number, m2: number, r: number, d: number, G: number) => | ||
(G * s * m2) / Math.pow(d, 2) | ||
} | ||
function applyImpulseToBodiesInRange() { | ||
const impulseVector = new Vector3() | ||
obj.getWorldPosition(gravitySource) | ||
world.forEachRigidBody((body: RigidBody) => { | ||
const { x, y, z } = body.translation() | ||
const bodyV3: Vector3 = new Vector3(x, y, z) | ||
const distance: number = gravitySource.distanceTo(bodyV3) | ||
if (distance < range) { | ||
let force = calcForceByType[gravityType]( | ||
strength, | ||
body.mass(), | ||
range, | ||
distance, | ||
gravitationalConstant | ||
) | ||
// Prevent wild forces when Attractors collide | ||
force = force === Infinity ? strength : force | ||
impulseVector.subVectors(gravitySource, bodyV3).normalize().multiplyScalar(force) | ||
body.applyImpulse(impulseVector, true) | ||
} | ||
}) | ||
} | ||
useFrame(() => { | ||
applyImpulseToBodiesInRange() | ||
}) | ||
</script> | ||
|
||
<Object3DInstance bind:object={obj} {position} /> | ||
|
||
{#if showHelper} | ||
<Mesh | ||
geometry={new SphereBufferGeometry(range)} | ||
material={new MeshBasicMaterial({ wireframe: true })} | ||
{position} | ||
/> | ||
<Mesh | ||
geometry={new SphereBufferGeometry()} | ||
material={new MeshBasicMaterial({ color: 'black', wireframe: false })} | ||
{position} | ||
/> | ||
{/if} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<script> | ||
import { Canvas } from '@threlte/core'; | ||
import { World, } from '@threlte/rapier'; | ||
import { dynamicAttractors, } from './_routeLib/systemState'; | ||
import Scene from './_routeLib/Scene.svelte'; | ||
</script> | ||
|
||
<div class="wrapper"> | ||
<Canvas> | ||
<World gravity={{y: 0}}> | ||
<Scene /> | ||
</World> | ||
</Canvas> | ||
<div class="controls"> | ||
<label> | ||
Dynamic Attractors | ||
<input | ||
type="checkbox" | ||
bind:checked={$dynamicAttractors} /> | ||
</label> | ||
</div> | ||
</div> | ||
|
||
<style> | ||
.wrapper { | ||
position: fixed; | ||
width: 100%; | ||
height: 100%; | ||
background: black; | ||
} | ||
.controls { | ||
position: absolute; | ||
top: 1rem; | ||
left: 1rem; | ||
background: #00000088; | ||
color: #fafbfc; | ||
} | ||
</style> |
103 changes: 103 additions & 0 deletions
103
src/routes/showcase/attractors/_routeLib/Objects.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
<script lang="ts"> | ||
import { MeshStandardMaterial, Vector3, SphereGeometry, MeshBasicMaterial } from 'three'; | ||
import { Collider, RigidBody } from '@threlte/rapier'; | ||
import { Mesh } from '@threlte/core'; | ||
import { nbodyCount, dynamicAttractors } from './systemState'; | ||
import Attractor from '$lib/components/Attractor.svelte'; | ||
import { randomVec3 } from '$lib'; | ||
$: attractors = [ | ||
{ | ||
id: 'LEFT', | ||
pos: { x: -100 }, | ||
m: 15, | ||
range: 75 | ||
}, | ||
{ | ||
id: 'CENTER', | ||
pos: { x: 0, y: 50 }, | ||
m: 15, | ||
range: 75 | ||
}, | ||
{ | ||
id: 'RIGHT', | ||
pos: { x: 100 }, | ||
m: 15, | ||
range: 75 | ||
} | ||
]; | ||
$: randomBodies = Array($nbodyCount) | ||
.fill('x') | ||
.map((_) => { | ||
return { | ||
id: Date.now() + Math.random(), | ||
pos: randomVec3({ x: [-75, 75], y: [25, 50], z: [-40, 40] }), | ||
lv: randomVec3({ x: [-2, 2], y: [0, -5], z: [-2, 2] }) | ||
}; | ||
}); | ||
</script> | ||
|
||
{#each attractors as body (body.id)} | ||
{#if $dynamicAttractors} | ||
<RigidBody position={body.pos}> | ||
<Collider shape="ball" args={[0.5]} mass={body.m * 2} /> | ||
<Mesh | ||
geometry={new SphereGeometry()} | ||
scale={0.5} | ||
material={new MeshBasicMaterial({ color: 'gold' })} | ||
/> | ||
<Mesh | ||
geometry={new SphereGeometry()} | ||
scale={body.range * 1.5} | ||
material={new MeshBasicMaterial({ | ||
wireframe: true, | ||
transparent: true, | ||
opacity: 0.125, | ||
color: 'cyan' | ||
})} | ||
/> | ||
|
||
<Attractor | ||
strength={body.m} | ||
gravityType="linear" | ||
gravitationalConstant={0.26674} | ||
range={body.range * 1.5} | ||
/> | ||
</RigidBody> | ||
{:else} | ||
<Mesh | ||
position={body.pos} | ||
geometry={new SphereGeometry(0.5)} | ||
material={new MeshBasicMaterial({ color: 'gold' })} | ||
> | ||
<Mesh | ||
geometry={new SphereGeometry()} | ||
scale={body.range} | ||
material={new MeshBasicMaterial({ | ||
wireframe: true, | ||
transparent: true, | ||
opacity: 0.125, | ||
color: 'cyan' | ||
})} | ||
/> | ||
|
||
<Attractor | ||
strength={body.m} | ||
gravityType="linear" | ||
gravitationalConstant={0.26674} | ||
range={body.range} | ||
/> | ||
</Mesh> | ||
{/if} | ||
{/each} | ||
|
||
{#each randomBodies as body} | ||
<RigidBody position={body.pos.multiplyScalar(3)} linearVelocity={body.lv}> | ||
<Collider shape="ball" args={[1]} mass={1} /> | ||
<Mesh | ||
geometry={new SphereGeometry()} | ||
scale={1} | ||
material={new MeshBasicMaterial({ color: 'red' })} | ||
/> | ||
</RigidBody> | ||
{/each} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<script> | ||
import { | ||
PerspectiveCamera, | ||
DirectionalLight, | ||
AmbientLight, | ||
OrbitControls, | ||
} from '@threlte/core'; | ||
import Objects from "./Objects.svelte"; | ||
</script> | ||
|
||
<PerspectiveCamera position={{ x: 0, y: 0, z: 400, }} fov={65} far="{1000}"> | ||
<OrbitControls /> | ||
</PerspectiveCamera> | ||
|
||
<DirectionalLight position={{x: 10, y: 10, z: 0}}/> | ||
<AmbientLight intensity={0.35} /> | ||
|
||
<Objects /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { writable } from "svelte/store"; | ||
|
||
export const nbodyCount = writable(100); | ||
export const attractorCount = writable(3); | ||
export const dynamicAttractors = writable(false); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<script> | ||
// My attempt to recreate the game from Bruno Simon's tweet: https://twitter.com/bruno_simon/status/1572301729894666240 | ||
import { Canvas } from '@threlte/core'; | ||
import { World, Debug } from '@threlte/rapier'; | ||
import { status, time } from './_routeLib/state'; | ||
import { slide } from "svelte/transition"; | ||
import Scene from './_routeLib/Scene.svelte'; | ||
import Timer from './_routeLib/Timer.svelte'; | ||
</script> | ||
|
||
<Timer /> | ||
<div class="wrapper"> | ||
<Canvas> | ||
<World> | ||
<!-- <Debug /> --> | ||
<Scene /> | ||
</World> | ||
</Canvas> | ||
<div class="timer row"> | ||
<span> | ||
{$time} | ||
</span> | ||
</div> | ||
<div class="action row"> | ||
{#if $status === "IDLE"} | ||
<button on:click={() => $status = "PLAYING"} transition:slide> | ||
PLAY | ||
</button> | ||
{:else if $status === "DONE"} | ||
<button on:click={() => $status = "IDLE"} in:slide> | ||
RESET | ||
</button> | ||
{/if} | ||
</div> | ||
</div> | ||
|
||
<style> | ||
* { | ||
font-family: Verdana; | ||
} | ||
.wrapper { | ||
position: fixed; | ||
width: 100%; | ||
height: 100%; | ||
background-color: cornsilk; | ||
} | ||
.row { | ||
position: absolute; | ||
left: 0; | ||
width: 100%; | ||
} | ||
.timer.row { | ||
top: 10%; | ||
width: 100%; | ||
margin: 0; | ||
background: #00000055; | ||
color: white; | ||
padding: 1rem 0; | ||
text-align: center; | ||
font-variant-numeric: tabular-nums; | ||
font-weight: 600; | ||
} | ||
.action.row { | ||
top: 40%; | ||
} | ||
.action.row button { | ||
width: 100%; | ||
height: 100%; | ||
margin: 0; | ||
background: #00000055; | ||
color: white; | ||
padding: 1rem 0; | ||
cursor: pointer; | ||
font-weight: 600; | ||
border: 2px solid transparent; | ||
transition: all 200ms ease; | ||
} | ||
.action.row button:hover { | ||
border: 2px solid white; | ||
background: #000000a1; | ||
} | ||
</style> |
42 changes: 42 additions & 0 deletions
42
src/routes/showcase/roll-the-ball/_routeLib/CameraRig.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<script> | ||
import { PerspectiveCamera, OrbitControls } from "@threlte/core"; | ||
import { tweened } from "svelte/motion"; | ||
import { quintOut } from "svelte/easing"; | ||
import { selectRigidBodyPos, rbp } from "./state"; | ||
const initialCameraPosition = { | ||
x: 0, | ||
y: 10, | ||
z: 15, | ||
} | ||
const x = selectRigidBodyPos('x'); | ||
const y = selectRigidBodyPos('y'); | ||
const z = selectRigidBodyPos('z'); | ||
const camX = tweened($x, { duration: 2000, easing: quintOut }); | ||
const camY = tweened($y, { duration: 2000, easing: quintOut }); | ||
const camZ = tweened($z, { duration: 1000, easing: quintOut }); | ||
$: if (isNaN($x)){ | ||
camX.set(initialCameraPosition.x, { duration: 4000 }); | ||
} else { | ||
camX.set($x); | ||
} | ||
$: if (isNaN($y)){ | ||
camY.set(initialCameraPosition.y, { duration: 4000 }); | ||
} else { | ||
camY.set($y + 5); | ||
} | ||
$: if (isNaN($z)){ | ||
camZ.set(initialCameraPosition.z, { duration: 4000 }); | ||
} else { | ||
camZ.set($z + 20); | ||
} | ||
</script> | ||
|
||
<PerspectiveCamera | ||
position={{x: $camX, y: $camY, z: $camZ}} | ||
lookAt={{x: $camX, y: $camY - 5, z: $camZ - 20}} | ||
fov={55} | ||
/> |
Oops, something went wrong.