Closed callumacrae closed 5 months ago
Reviving this.
I agree with @callumacrae that each useRenderLoop
should have its own state.
Here's a StackBlitz showcasing the bug.
Most uses I've come across of useRenderLoop
are for animating attributes. In that context, calling pause()
and then not rerendering, e.g., when the screen is resized is counter to my intuition.
It seems like we need a separate update loop.
useRenderLoop
useRenderLoop
distinct per TresCanvas
:on-update-loop="(clock)=>{}"
Here's a class-based code example from a game library (in the Haxe language) that implements these features. (It's from one of the guys who worked on the game Dead Cells, fwiw. And it's a really great library for cranking out 2D games.)
Here, Process.hx
corresponds to something like our Object3D
or a VNode
. Like those, a Process
is a node in a graph. It might have a parent and might have children. What we need is simple graph traversal.
Fwiw, Haxe compiles to JS to run in the browser. It does several tree traversals at 30 fps and 60 fps in the browser without a problem.
Another shared state issue here: #560
Hi @callumacrae @andretchen0 to give a couple of thoughts regarding useRenderLoop
The original idea of the composable was to provide the user with an abstraction to replace the internal clock of ThreeJS that uses performance.now and make it somehow reactive with useRafFn which uses requestAnimationFrame
, so any abstraction in the ecosystem (cientos, postprocessing, physics) can react to changes and animate their objects or scenes. Any Tres
component inside a unique TresCanvas
should have the exact same loop`
Ex: If I have 2 sub-components called SphereA.vue
and SphereB.vue
, both uses the onLoop
which triggers every frame.
What's certainly a bug is that useRenderLoop
should be distinct per TresCanvas, similar to #560 which I think would be easy to fix. Which I think is what you are mentioning in this thread @callumacrae
Create a separate update loop that is distinct per user component
@andretchen0 The thing is, ThreeJS renders all the scene graph at once when using renderer.render(scene, camera)
and that method is called with onLoop
every frame. To achieve the level of granularity where you can pause individual components from rendering, we would need to create a onLoop
instance per object, attach it to their localstate and then overwrite the onBeforeRender
method of the THREE.object class to conditionally render based on the paused state https://threejs.org/docs/#api/en/core/Object3D.onBeforeRender
This is certainly something I'm not eager to do because it adds a level of complexity and possible side effects that would make it really difficult to mantain, but to consider it I would like to understand how the end user would benefit from having individual loops and if there are not end-user alternatives, for example
Imagine a scene with 100 of objects, all of them with event listeners to handle.
Most uses I've come across of useRenderLoop are for animating attributes. In that context, calling pause() and then not rerendering, e.g., when the screen is resized is counter to my intuition.
Wouldn't be easier to just not render the object with a v-if
instead of using the individual onLoop
to stop the rendering?
isPaused = ref(false)
<Box v-if="isPaused" />
Allow a user component to specify a speed multiplier on self and children – this is necessary if, say, we have a physics simulation we want to slow down, but we want to keep the GUI running at normal speed
Physics simulations are in the scope of the @tresjs/rapier
package, but the speed modifier couldn't be just a ref on the end user component?
const speedFactor = ref(1)
onLoop(({ delta, elapsed}) => {
boxRef.value.position.x += delta * speedFactor
})
Looking forward for your feedback
Multiple loops on the scene graph
A related issue of shared state that it would be handy to fix: "elapsed" is shared.
The value for "elapsed" starts ticking up whenever the first TresCanvas or subcomponent does useRenderLoop
. It then shares that same "elapsed" value with other TresCanvases, even if they were created later.
This functionality is now available by using the new useLoop
composable which is bound to the state https://docs.tresjs.org/api/composables.html#useloop
Is your feature request related to a problem? Please describe.
Use case is having multiple
<TresCanvas>
instances on one page which are individually pausable. Currently, pausing one instance results in all instances being paused.Describe the solution you'd like
I think there's two possible solutions here:
useRenderLoop()
instance has its own state. It looks like this would have implications on the raycasting functionality, but that might be desirable?<TresCanvas>
has its own state and providers or refs are used to telluseRenderLoop
which to useEach approach has their own pros and cons. I think each
<TresCanvas>
could have its own problems when third party Tres components try to do their own thing and affect the main app behaviour. It's also harder to implement and possibly involves an API change!Happy to contribute a fix, especially if the first solution is chosen as it's a lot easier :D