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

fix: 686 useloop callback state missing controls #687

Merged
merged 3 commits into from
May 22, 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
66 changes: 66 additions & 0 deletions playground/auto-imports.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue')
}
31 changes: 0 additions & 31 deletions playground/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,21 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
AkuAku: typeof import('./src/components/AkuAku.vue')['default']
AnimatedModel: typeof import('./src/components/AnimatedModel.vue')['default']
AnimatedObjectUseUpdate: typeof import('./src/components/AnimatedObjectUseUpdate.vue')['default']
BlenderCube: typeof import('./src/components/BlenderCube.vue')['default']
Box: typeof import('./src/components/Box.vue')['default']
CameraOperator: typeof import('./src/components/CameraOperator.vue')['default']
Cameras: typeof import('./src/components/Cameras.vue')['default']
copy: typeof import('./src/components/TheBasic copy.vue')['default']
DanielTest: typeof import('./src/components/DanielTest.vue')['default']
DebugUI: typeof import('./src/components/DebugUI.vue')['default']
DeleteMe: typeof import('./src/components/DeleteMe.vue')['default']
DirectiveSubComponent: typeof import('./src/components/DirectiveSubComponent.vue')['default']
DynamicModel: typeof import('./src/components/DynamicModel.vue')['default']
FBOCube: typeof import('./src/components/FBOCube.vue')['default']
FBXModels: typeof import('./src/components/FBXModels.vue')['default']
Gltf: typeof import('./src/components/gltf/index.vue')['default']
GraphPane: typeof import('./src/components/GraphPane.vue')['default']
LocalOrbitControls: typeof import('./src/components/LocalOrbitControls.vue')['default']
MeshWobbleMaterial: typeof import('./src/components/meshWobbleMaterial/index.vue')['default']
MultipleCanvas: typeof import('./src/components/MultipleCanvas.vue')['default']
PortalJourney: typeof import('./src/components/portal-journey/index.vue')['default']
RenderingLogger: typeof import('./src/components/RenderingLogger.vue')['default']
Responsiveness: typeof import('./src/components/Responsiveness.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ShadersExperiment: typeof import('./src/components/shaders-experiment/index.vue')['default']
TakeOverLoopExperience: typeof import('./src/components/TakeOverLoopExperience.vue')['default']
TestSphere: typeof import('./src/components/TestSphere.vue')['default']
Text3D: typeof import('./src/components/Text3D.vue')['default']
TheBasic: typeof import('./src/components/TheBasic.vue')['default']
TheCameraOperator: typeof import('./src/components/TheCameraOperator.vue')['default']
TheConditional: typeof import('./src/components/TheConditional.vue')['default']
TheEnvironment: typeof import('./src/components/TheEnvironment.vue')['default']
TheEvents: typeof import('./src/components/TheEvents.vue')['default']
TheExperience: typeof import('./src/components/TheExperience.vue')['default']
TheFireFlies: typeof import('./src/components/portal-journey/TheFireFlies.vue')['default']
TheFirstScene: typeof import('./src/components/TheFirstScene.vue')['default']
TheGizmos: typeof import('./src/components/TheGizmos.vue')['default']
TheGroups: typeof import('./src/components/TheGroups.vue')['default']
TheModel: typeof import('./src/components/gltf/TheModel.vue')['default']
TheParticles: typeof import('./src/components/TheParticles.vue')['default']
ThePortal: typeof import('./src/components/portal-journey/ThePortal.vue')['default']
TheSmallExperience: typeof import('./src/components/TheSmallExperience.vue')['default']
TheSphere: typeof import('./src/components/TheSphere.vue')['default']
TheUSDZModel: typeof import('./src/components/udsz/TheUSDZModel.vue')['default']
TresLechesTest: typeof import('./src/components/TresLechesTest.vue')['default']
Udsz: typeof import('./src/components/udsz/index.vue')['default']
VectorSetProps: typeof import('./src/components/VectorSetProps.vue')['default']
}
}
8 changes: 4 additions & 4 deletions playground/src/components/AnimatedObjectUseUpdate.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<!-- eslint-disable no-console -->
<script setup lang="ts">
import { useLoop } from '@tresjs/core'
import { type LoopCallbackWithCtx, useLoop } from '@tresjs/core'
import { useControls } from '@tresjs/leches'
import { useThrottleFn } from '@vueuse/core'

const sphereRef = ref()

const log = useThrottleFn(() => console.log('updating sphere'), 3000)
const log = useThrottleFn(state => console.log('updating sphere', state), 3000)
const log2 = useThrottleFn(() => console.log('this should happen before updating the sphere'), 3000)

const { onBeforeRender, pause, resume } = useLoop()

const updateCallback = (state) => {
const updateCallback = (state: LoopCallbackWithCtx) => {
if (!sphereRef.value) { return }
log()
log(state)
sphereRef.value.position.y += Math.sin(state.elapsed) * 0.01
}

Expand Down
4 changes: 2 additions & 2 deletions playground/src/components/DirectiveSubComponent.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { vAlwaysLookAt, vLightHelper } from '@tresjs/core'
import { vLightHelper } from '@tresjs/core'
</script>

<template>
<TresDirectionalLight v-light-helper v-always-look-at="[0, 0, 0]" :position="[3, 3, 3]" :intensity="1" />
<TresDirectionalLight v-light-helper :position="[3, 3, 3]" :intensity="1" />
</template>
11 changes: 9 additions & 2 deletions playground/src/components/TakeOverLoopExperience.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,18 @@ watchEffect(() => {
resumeRender()
}
})

const showGrid = ref(true)

setTimeout(() => {
showGrid.value = false
}, 10000)
</script>

<template>
<TresPerspectiveCamera :position="[3, 3, 3]" />
<OrbitControls />
<OrbitControls make-default />
<AnimatedObjectUseUpdate />
<TresAmbientLight :intensity="1" /> />
<TresGridHelper v-if="showGrid" />
<TresAmbientLight :intensity="1" />
</template>
14 changes: 13 additions & 1 deletion playground/src/pages/models/PrimitivesModel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ const gl = {
}

useControls('fpsgraph')

const modelsPositions = ref([
{
position: [0, 2, 2],
},
{
position: [0, 3, 5],
},
{
position: [0, 1, 1],
},
])
</script>

<template>
Expand All @@ -35,7 +47,7 @@ useControls('fpsgraph')
<OrbitControls />

<Suspense>
<DynamicModel />
<DynamicModel v-for="model in modelsPositions" :key="model" :position="model.position" />
</Suspense>
<TresAxesHelper :args="[1]" />
<TresDirectionalLight
Expand Down
20 changes: 9 additions & 11 deletions src/composables/useLoop/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { unref } from 'vue'
import type { Fn } from '@vueuse/core'
import type { TresCamera } from '../../types'
import { useTresContext } from '../useTresContextProvider'
import type { LoopCallbackFn } from './../../core/loop'

export function useLoop() {
const {
Expand All @@ -17,24 +15,24 @@ export function useLoop() {

// Pass context to loop
loop.setContext({
camera: unref(camera) as TresCamera,
scene: unref(scene),
renderer: unref(renderer),
raycaster: unref(raycaster),
controls: unref(controls),
camera,
scene,
renderer,
raycaster,
controls,
invalidate,
advance,
})

function onBeforeRender(cb: Fn, index = 0) {
function onBeforeRender(cb: LoopCallbackFn, index = 0) {
return loop.register(cb, 'before', index)
}

function render(cb: Fn) {
function render(cb: LoopCallbackFn) {
return loop.register(cb, 'render')
}

function onAfterRender(cb: Fn, index = 0) {
function onAfterRender(cb: LoopCallbackFn, index = 0) {
return loop.register(cb, 'after', index)
}

Expand Down
56 changes: 40 additions & 16 deletions src/core/loop.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Ref } from 'vue'
import { ref } from 'vue'
import { ref, unref } from 'vue'
import type { Camera, EventDispatcher, Raycaster, Scene, WebGLRenderer } from 'three'
import { Clock, MathUtils } from 'three'
import type { Fn } from '@vueuse/core'
import type { Callback, PriorityEventHookOn } from '../utils/createPriorityEventHook'
import type { Callback } from '../utils/createPriorityEventHook'
import { createPriorityEventHook } from '../utils/createPriorityEventHook'

export type LoopStage = 'before' | 'render' | 'after'
Expand All @@ -13,15 +14,29 @@ export interface LoopCallback {
clock: Clock
}

export interface LoopCallbackWithCtx extends LoopCallback {
camera: Camera
scene: Scene
renderer: WebGLRenderer
raycaster: Raycaster
controls: Ref<(EventDispatcher<object> & {
enabled: boolean
}) | null>
invalidate: Fn
advance: Fn
}

export type LoopCallbackFn = (params: LoopCallbackWithCtx) => void

export interface RendererLoop {
loopId: string
register: (callback: Fn, stage: LoopStage, index?: number) => Partial<PriorityEventHookOn<LoopCallback>>
start: () => void
stop: () => void
pause: () => void
resume: () => void
pauseRender: () => void
resumeRender: () => void
register: (callback: LoopCallbackFn, stage: LoopStage, index?: number) => { off: Fn }
start: Fn
stop: Fn
pause: Fn
resume: Fn
pauseRender: Fn
resumeRender: Fn
isActive: Ref<boolean>
isRenderPaused: Ref<boolean>
setContext: (newContext: Record<string, any>) => void
Expand All @@ -33,10 +48,10 @@ export function createRenderLoop(): RendererLoop {
const isRenderPaused = ref(false)
let animationFrameId: number
const loopId = MathUtils.generateUUID()
let defaultRenderFn: Callback<LoopCallback> | null = null
const subscribersBefore = createPriorityEventHook<LoopCallback>()
const subscriberRender = createPriorityEventHook<LoopCallback>()
const subscribersAfter = createPriorityEventHook<LoopCallback>()
let defaultRenderFn: Callback<LoopCallbackWithCtx> | null = null
const subscribersBefore = createPriorityEventHook<LoopCallbackWithCtx>()
const subscriberRender = createPriorityEventHook<LoopCallbackWithCtx>()
const subscribersAfter = createPriorityEventHook<LoopCallbackWithCtx>()

// Context to be passed to callbacks
let context: Record<string, any> = {}
Expand All @@ -45,7 +60,7 @@ export function createRenderLoop(): RendererLoop {
context = newContext
}

function registerCallback(callback: Callback<LoopCallback>, stage: 'before' | 'render' | 'after', index = 0): Partial<PriorityEventHookOn<LoopCallback>> {
function registerCallback(callback: LoopCallbackFn, stage: 'before' | 'render' | 'after', index = 0): { off: Fn } {
switch (stage) {
case 'before':
return subscribersBefore.on(callback, index)
Expand Down Expand Up @@ -97,7 +112,16 @@ export function createRenderLoop(): RendererLoop {
function loop() {
const delta = clock.getDelta()
const elapsed = clock.getElapsedTime()
const params = { delta, elapsed, clock, ...context }
const snapshotCtx = {
camera: unref(context.camera),
scene: unref(context.scene),
renderer: unref(context.renderer),
raycaster: unref(context.raycaster),
controls: unref(context.controls),
invalidate: context.invalidate,
advance: context.advance,
}
const params = { delta, elapsed, clock, ...snapshotCtx }

if (isActive.value) {
subscribersBefore.trigger(params)
Expand All @@ -123,7 +147,7 @@ export function createRenderLoop(): RendererLoop {

return {
loopId,
register: (callback: Fn, stage: 'before' | 'render' | 'after', index) => registerCallback(callback, stage, index),
register: (callback: LoopCallbackFn, stage: 'before' | 'render' | 'after', index) => registerCallback(callback, stage, index),
start,
stop,
pause,
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './core/catalogue'
export * from './components'
export * from './types'
export * from './directives'
export * from './core/loop'

export interface TresOptions {
extends?: Record<string, unknown>
Expand Down
Loading