Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add useTresReady #712

Merged
merged 3 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading