diff --git a/src/components/canvases/GhostCanvas.vue b/src/components/canvases/GhostCanvas.vue index 505ad781..2e1dc16f 100644 --- a/src/components/canvases/GhostCanvas.vue +++ b/src/components/canvases/GhostCanvas.vue @@ -34,7 +34,16 @@ ground, grid } = createGhostScene() - const { ghosts, totalDuration } = await createGhosts(scene, ghostUrls) + + const hasStartedAnimating = ref(false) + const hasFinishedAnimating = ref(false) + const loadedGhosts = ref(0) + + const { ghosts, totalDuration } = await createGhosts( + scene, + ghostUrls, + loadedGhosts + ) let longestGhost = ghosts[0] for (const ghost of ghosts) { @@ -73,17 +82,15 @@ directionalLight.position.y += 50 ground.position.copy(center) - ground.position.y = 0 + ground.position.y = -4 grid.position.copy(center) - grid.position.y = 0.01 + grid.position.y = -4 if (IS_DEV) { stats = new Stats() document.body.append(stats.dom) } - - animate() }) onUnmounted(() => { @@ -92,10 +99,7 @@ }) const animateDrawing = () => { - const elapsedTime = clock.getElapsedTime() - currentTime.value = - longestGhost.ghost.frames[0].time + - (elapsedTime / totalDuration) * totalDuration + currentTime.value = clock.getElapsedTime() while ( currentFrame < longestGhost.ghost.frames.length - 1 && @@ -143,6 +147,7 @@ const hasNextFrame = currentFrame < longestGhost.ghost.frames.length - 1 if (hasNextFrame) animateDrawing() + else hasFinishedAnimating.value = true controls.enableZoom = true controls.enableRotate = true @@ -156,16 +161,51 @@ requestAnimationFrame(animate) } + + const onStart = () => { + if (hasStartedAnimating.value) return + hasStartedAnimating.value = true + + animate() + } + + const onReplay = () => { + clock.startTime = 0 + clock.elapsedTime = 0 + + currentFrame = 0 + currentTime.value = 0 + + for (const { geometry, material } of ghosts) { + material.visible = false + geometry.setDrawRange(0, 0) + } + + onStart() + } diff --git a/src/components/modals/GhostModal.vue b/src/components/modals/GhostModal.vue index 2e98cb40..1a8a9fc0 100644 --- a/src/components/modals/GhostModal.vue +++ b/src/components/modals/GhostModal.vue @@ -25,7 +25,7 @@

Ghost Explorer (ALPHA)

- +
@@ -67,7 +67,7 @@ height: unset; } - button { + button.close { position: absolute; top: 1rem; right: 1rem; diff --git a/src/utils/three/createGhosts.ts b/src/utils/three/createGhosts.ts index edcc312f..c5cbebec 100644 --- a/src/utils/three/createGhosts.ts +++ b/src/utils/three/createGhosts.ts @@ -10,10 +10,11 @@ import { Vector3 } from 'three' import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js' +import { type Ref } from 'vue' import soapboxUrl from '~/assets/models/combined_soapbox.stl?url' -interface GhostInstance { +export interface GhostInstance { ghost: Ghost points: Vector3[] curve: CatmullRomCurve3 @@ -23,7 +24,11 @@ interface GhostInstance { soapbox?: Mesh } -export const createGhosts = async (scene: Scene, urls: string[]) => { +export const createGhosts = async ( + scene: Scene, + urls: string[], + loadedGhosts: Ref +) => { const ghosts: (GhostInstance | undefined)[] = [] let totalDuration = 0 @@ -38,6 +43,8 @@ export const createGhosts = async (scene: Scene, urls: string[]) => { ) ghosts.push(undefined) continue + } else { + console.log(`Ghost ${index} uses version ${ghost.version}`) } totalDuration = Math.max(totalDuration, ghost.frames.at(-1)?.time ?? 0) @@ -85,6 +92,8 @@ export const createGhosts = async (scene: Scene, urls: string[]) => { scene.add(line) + loadedGhosts.value++ + ghosts.push({ ghost, points,