Tresjs / tres

Declarative ThreeJS using Vue Components
https://tresjs.org
MIT License
1.93k stars 86 forks source link

[v4] - Animations with onLoop doesn't trigger on-demand rendering #639

Closed alvarosabu closed 2 months ago

alvarosabu commented 2 months ago

Describe the bug

Properties changed (animated) through template ref modification are not triggering invalidate on render-mode="on-demand"

Reproduction

http://localhost:5173/perf/on-demand

Steps to reproduce

On core playground go to http://localhost:5173/perf/on-demand and animate an object in the following way:

const { onLoop } = useRenderLoop()

onLoop(({ elapsed }) => {
  if (!sphereRef.value) { return }
  sphereRef.value.position.x += Math.sin(elapsed) * 0.01
})

Notice that this doesn't trigger a render

System Info

System:
    OS: macOS 14.3.1
    CPU: (8) arm64 Apple M1 Pro
    Memory: 69.72 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.14.1 - ~/.nvm/versions/node/v18.14.1/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 9.3.1 - ~/.nvm/versions/node/v18.14.1/bin/npm
  Browsers:
    Brave Browser: 120.1.61.116
    Chrome: 123.0.6312.123
    Firefox: 121.0.1
    Safari: 17.3.1
  npmPackages:
    @tresjs/cientos: 3.6.0 => 3.6.0 
    @tresjs/core: workspace:^ => 4.0.0-next.2 
    @tresjs/leches: 0.15.0-next.3 => 0.15.0-next.3 

Used Package Manager

pnpm

Code of Conduct

andretchen0 commented 2 months ago

Per #140 , on-demand works through Vue renderer prop patching.

In your example, you're bypassing the Vue renderer and modifying the position directly:

sphereRef.value.position.x += Math.sin(elapsed) * 0.01

If instead you do this, things work as expected:

<script setup lang="ts">
import { TresCanvas, useRenderLoop } from '@tresjs/core'
import BlenderCube from '../../components/BlenderCube.vue'
import GraphPane from '../../components/GraphPane.vue'
import RenderingLogger from '../../components/RenderingLogger.vue'

const { onLoop } = useRenderLoop()
const x = shallowRef(0)
onLoop(({ elapsed }) => {
  x.value = Math.sin(elapsed)
})
</script>

<template>
  <GraphPane />
  <TresCanvas
    render-mode="on-demand"
    clear-color="#82DBC5"
    @render="onRender"
  >
    <TresPerspectiveCamera
      :position="[5, 5, 5]"
      :look-at="[0, 0, 0]"
    />
    <Suspense>
      <BlenderCube :position-x="x" />
    </Suspense>
    <TresGridHelper />
    <RenderingLogger />
    <TresAmbientLight :intensity="1" />
    <TresDirectionalLight
      :position="[0, 8, 4]"
      :intensity="0.7"
    />
  </TresCanvas>
</template>
alvarosabu commented 2 months ago

Hey @andretchen0 , yes, it will work with patch props because of the invalidate function we have inside. However, for performance reasons, we recommend always editing the template ref.

If so, user would need to manually invalidate like this:

const { invalidate } = useTres()

useLoop(({ elapsed }) => {
  if (!sphereRef.value) { return }
  sphereRef.value.position.y += Math.sin(elapsed) * 0.01
  invalidate()
})

Similar to R3F https://docs.pmnd.rs/react-three-fiber/advanced/scaling-performance#triggering-manual-frames

I will add this to the documentation.