Skip to content

Commit

Permalink
feat: add useTresReady (#712)
Browse files Browse the repository at this point in the history
* add onTresReady hook
* add @ready emit to TresCanvas
* start loop onTresReady

Co-authored-by: Alvaro Saburido <[email protected]>
  • Loading branch information
andretchen0 and alvarosabu authored Jun 15, 2024
1 parent 74a2c47 commit 15e3f07
Show file tree
Hide file tree
Showing 11 changed files with 636 additions and 2 deletions.
62 changes: 62 additions & 0 deletions playground/src/pages/basic/ready/LoopCallbackWatcher.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script setup lang="ts">
import { useLoop } from '@tresjs/core'
import { type ShallowRef, ref, shallowRef } from 'vue'
const isCalled = ref(false)
interface TestResult { passed: boolean, msg: string }
const messages = shallowRef([
{
passed: false,
msg: 'callback was not called',
},
]) as ShallowRef<TestResult[]>
const captureCallback = (renderer: any, _elapsed: number) => {
if (!isCalled.value) {
isCalled.value = true
const isRendererOk = !!renderer
const domElement = renderer?.domElement
const isDomElementOk = !!(domElement) && domElement.width > 0 && domElement.height > 0
messages.value = [
{
passed: true,
msg: 'When the callback was called for the first time ...',
},
{
passed: isRendererOk,
msg: isRendererOk ? '... the renderer existed.' : '... the renderer did not exist.',
},
{
passed: !!domElement,
msg: domElement ? '... the canvas existed.' : '... the canvas did not exist.',
},
{
passed: isDomElementOk,
msg: isDomElementOk
? `... the canvas was not degenerate: ${domElement.width} px × ${domElement.height} px.`
: `... the canvas was degenerate.`,
},
]
}
}
useLoop().onBeforeRender(({ elapsed: _elapsed, renderer }) => {
captureCallback(renderer, _elapsed)
})
useLoop().render(({ elapsed: _elapsed, renderer, scene, camera }) => {
captureCallback(renderer, _elapsed)
renderer.render(scene, camera)
})
useLoop().onAfterRender(({ elapsed: _elapsed, renderer }) => {
captureCallback(renderer, _elapsed)
})
defineExpose({
isCalled,
messages,
})
</script>
69 changes: 69 additions & 0 deletions playground/src/pages/basic/ready/OnTresReadyWatcher.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script setup lang="ts">
import { type TresContext, onTresReady } from '@tresjs/core'
import { type ShallowRef, ref, shallowRef } from 'vue'
const isCalled = ref(false)
interface TestResult { passed: boolean, msg: string }
const messages = shallowRef([
{
passed: false,
msg: 'callback was not called',
},
]) as ShallowRef<TestResult[]>
const captureCallback = (ctx: TresContext) => {
if (isCalled.value) {
messages.value = [
{
passed: false,
msg: 'Callback was called twice.',
},
]
}
if (!isCalled.value) {
isCalled.value = true
const isCtxOk = !!(ctx && 'renderer' in ctx && 'scene' in ctx)
const renderer = ctx.renderer.value
const isRendererOk = !!renderer
const domElement = renderer?.domElement
const isDomElementOk = !!(domElement) && domElement.width > 0 && domElement.height > 0
messages.value = [
{
passed: true,
msg: 'When the callback was called ...',
},
{
passed: true,
msg: '... it had not previously been called.',
},
{
passed: isCtxOk,
msg: isCtxOk ? '... TresContext was passed.' : '... TresContext was not passed.',
},
{
passed: isRendererOk,
msg: isRendererOk ? '... the renderer existed.' : '... the renderer did not exist.',
},
{
passed: !!domElement,
msg: domElement ? '... the canvas existed.' : '... the canvas did not exist.',
},
{
passed: isDomElementOk,
msg: isDomElementOk
? `... the canvas was not degenerate: ${domElement.width} px × ${domElement.height} px.`
: `... the canvas was degenerate.`,
},
]
}
}
onTresReady(captureCallback)
defineExpose({
isCalled,
messages,
})
</script>
163 changes: 163 additions & 0 deletions playground/src/pages/basic/ready/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<script setup lang="ts">
import type { TresContext } from '@tresjs/core'
import { TresCanvas } from '@tresjs/core'
import type { ShallowRef } from 'vue'
import { ref } from 'vue'
import LoopCallbackWatcher from './LoopCallbackWatcher.vue'
import OnTresReadyWatcher from './OnTresReadyWatcher.vue'
interface TestResult { passed: boolean, msg: string }
const onReadyMessages = shallowRef([
{
passed: false,
msg: '@ready callback was not called',
},
]) as ShallowRef<TestResult[]>
let numOnReadyCalls = 0
const onReady = function (ctx: TresContext) {
numOnReadyCalls++
const renderer = ctx.renderer.value
const domElement = renderer?.domElement
const isPassedCanvas = domElement.width > 0 && domElement.width > 0
const isPassedCtx = !!renderer && 'camera' in ctx && !!(ctx.camera.value)
onReadyMessages.value = [
{
passed: true,
msg: 'When the callback was called ...',
},
{
passed: numOnReadyCalls === 1,
msg: '... it had not previously been called.',
},
{
passed: isPassedCtx,
msg: isPassedCtx ? '... TresContext was passed.' : '... TresContext was not passed or was missing elements',
},
{
passed: !!renderer,
msg: renderer ? '... the renderer existed.' : '... the renderer did not exist.',
},
{
passed: !!domElement,
msg: domElement ? '... the canvas existed.' : '... the canvas did not exist.',
},
{
passed: isPassedCanvas,
msg: isPassedCanvas
? `... the canvas was not degenerate: ${domElement.width} px × ${domElement.height} px.`
: `... the canvas was degenerate.`,
},
]
}
const onTresReadyWatcherRef = ref({
isCalled: false,
messages: [] as TestResult[],
})
const loopCallbackWatcherRef = ref({
isCalled: false,
messages: [] as TestResult[],
})
</script>

<template>
<div class="overlay">
<h1>When is Tres ready?</h1>
<p>
Tres is "ready" if either:
</p>
<ul>
<li>
The scene can be meaningfully rendered.
</li>
<ul>
<li>the renderer exists</li>
<li>the canvas width and height are > 0</li>
</ul>
<li>Tres has waited 100 ms - assumes setup is intentionally degenerate.</li>
</ul>
<hr />
<h1>"ready" in user space</h1>
<h2><code>&lt;TresCanvas @ready="(ctx:TresContext) => {}"&gt;</code></h2>
<p>A callback can be defined in the <code>&lt;script setup /&gt;</code> of a &lt;TresCanvas&gt;.</p>
<ul>
<li
v-for="({ passed, msg }, i) of onReadyMessages"
:key="i"
:class="passed ? 'pass' : 'fail'"
>
<span>{{ passed ? "" : "" }} {{ msg }}</span>
</li>
</ul>
<h2><code>onTresReady((ctx:TresContext) => {})</code></h2>
<p><code>onTresReady</code> can only be called in a child component.</p>
<ul>
<li
v-for="({ passed, msg }, i) of onTresReadyWatcherRef.messages"
:key="i"
:class="passed ? 'pass' : 'fail'"
>
<span>{{ passed ? "" : "" }} {{ msg }}</span>
</li>
</ul>
<h2><code>useLoop()...(callback)</code></h2>
<p><code>useLoop</code> can only be called in a child component.</p>
<ul>
<li
v-for="({ passed, msg }, i) of loopCallbackWatcherRef.messages"
:key="i"
:class="passed ? 'pass' : 'fail'"
>
<span>{{ passed ? "" : "" }} {{ msg }}</span>
</li>
</ul>
<hr />
<h1>Context</h1>
<p>
<a href="https://github.com/Tresjs/tres/issues/595">See this Github issue for further explanation.</a>
</p>
</div>
<TresCanvas clear-color="gray" @ready="onReady">
<LoopCallbackWatcher ref="loopCallbackWatcherRef" />
<OnTresReadyWatcher ref="onTresReadyWatcherRef" />
<TresMesh>
<TresBoxGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</TresCanvas>
</template>

<style scoped>
.overlay {
position: fixed;
z-index: 1000;
margin: 10px;
padding: 10px;
border-radius: 6px;
max-width: 400px;
font-family: sans-serif;
font-size: small;
background-color: white;
}
.overlay .pass {
color: green;
}
.overlay .fail {
color: red;
}
.overlay li {
padding-left: 0;
margin-left: 0;
}
.overlay ul {
padding-left: 0;
margin-left: 1.5em;
}
</style>
5 changes: 5 additions & 0 deletions playground/src/router/routes/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,9 @@ export const basicRoutes = [
name: 'Pierced Props',
component: () => import('../../pages/basic/PiercedProps.vue'),
},
{
path: '/basic/ready',
name: '@ready',
component: () => import('../../pages/basic/ready/index.vue'),
},
]
1 change: 1 addition & 0 deletions src/components/TresCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const emit = defineEmits([
'pointer-out',
'pointer-missed',
'wheel',
'ready',
])
const slots = defineSlots<{
Expand Down
1 change: 1 addition & 0 deletions src/composables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './usePointerEventHandler'
export * from './useTresContextProvider'
export * from './useLoop'
export * from './useTresEventManager'
export { onTresReady } from './useTresReady'
9 changes: 8 additions & 1 deletion src/composables/useTresContextProvider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { TresEventManager } from '../useTresEventManager'
import useSizes, { type SizesType } from '../useSizes'
import type { RendererLoop } from '../../core/loop'
import { createRenderLoop } from '../../core/loop'
import { useTresReady } from '../useTresReady'

export interface InternalState {
priority: Ref<number>
Expand Down Expand Up @@ -209,9 +210,15 @@ export function useTresContextProvider({
}
}, 'render')

ctx.loop.start()
const { on: onTresReady, cancel: cancelTresReady } = useTresReady(ctx)!

onTresReady(() => {
emit('ready', ctx)
ctx.loop.start()
})

onUnmounted(() => {
cancelTresReady()
ctx.loop.stop()
})

Expand Down
Loading

0 comments on commit 15e3f07

Please sign in to comment.