Giter VIP home page Giter VIP logo

Comments (16)

drcmda avatar drcmda commented on May 18, 2024 3

The timetag is merged, next patch you can get it like this:

useRender((state, t) => ...)

from react-three-fiber.

drcmda avatar drcmda commented on May 18, 2024 1

you don't have to use the default camera, you can useRender(..., true) and render your own scene and your own camera, at that point the defaults dont matter: https://github.com/drcmda/react-three-fiber#heads-up-display-rendering-multiple-scenes

Also the frameloop is optional, even the renderer: https://codesandbox.io/s/yq90n32zmx

But, this isn't what most people would go for. Auto frameloop is far more effective and it meshes well with other libs like react-spring, etc. I couldn't imagine a single reason why you'd want to be the one that calls requestAnimationLoop. There's no benefit, just downsides. useRender(..., true) essentially empties everything that would be done internally, letting you define your own update effects (camera.update(), etc) and render calls. But you could of course skip all of this and just use render/unmountComponentAtNode.

The thing about useRender is that it allows components to affect the render cycle in a uniform way, so that you can compose. This isn't easily possible in Threejs. Even simple stuff like

function Effects({ factor }) {
  const { gl, scene, camera, size } = useThree()
  const composer = useRef()
  useEffect(() => void composer.current.setSize(size.width, size.height), [size])
  // This takes over as the main render-loop (when 2nd arg is set to true)
  useRender(() => composer.current.render(), true)
  return (
    <effectComposer ref={composer} args={[gl]}>
      <renderPass attachArray="passes" args={[scene, camera]} />
      <glitchPass attachArray="passes" factor={factor} renderToScreen />
    </effectComposer>
  )
}

// Mount effects conditionally
{effectsActive && <Effects />

would mean so much complexity in Threejs. But here the effects component is self managed and controls its own render logic. But once it's unmounted everything goes back to normal. In Threejs your render loop would have to know about effects in order to switch it on/off, which creates a dependency you don't want to have.

from react-three-fiber.

AndrewRayCode avatar AndrewRayCode commented on May 18, 2024

I couldn't imagine a single reason why you'd want to be the one that calls requestAnimationLoop.

You don't need to imagine it, it's what I already do! πŸ˜ƒ I pause my animation loop on window blur, and on focus, start it up again and track the elapsed time delta. I also use the value passed to the rAF callback, which I can't do with the current loop because it's abstracted away.

In your example of manual looping, you have to drop into more imperative code to achieve it, which I'd prefer not to do if there's a declarative component that can do it (but that doesn't introduce default behavior). It also requires exposing the guts of the library to the user. React-three-render achieved this with a parameter to a trigger created callback.

from react-three-fiber.

drcmda avatar drcmda commented on May 18, 2024

I pause my animation loop on window blur, and on focus, start it up again and track the elapsed time delta

But can't you do this with the invalidate function? Im not so familiar with three-renderer, but that trigger looks familar.

<Canvas invalidateFrameloop={state.paused}>
  <SomeComponent />
</Canvas>

function SomeComponent() {
  const { invalidate } = useThree()

  useEffect(() => {
    // trigger a single frame, if invalidateFrameloop is true rendering is manual
    invalidate()
  }, [])

  return ...
}

There's also a universal one, which would render all canvases (if you had multiple) for a single frame.

import { invalidate } from 'react-three-fiber'

invalidate()

Another (perhaps cleaner?) way would be:

useRender(({ gl, camera, scene }) => {
  if (!state.pause) gl.render(camera.scene)
}, true)

I also use the value passed to the rAF callback, which I can't do with the current loop because it's abstracted away.

That's no problem, i'll add it.

from react-three-fiber.

AndrewRayCode avatar AndrewRayCode commented on May 18, 2024

I want to rAF outside of the canvas component because I need to update many UI elements surrounding the canvas as part of my game state, like overlayed vanilla html menus. I have a big game state object, I run it through one frame (like physics), then I pass all that data down to UI and <Canvas /> elements so everything updates, then I re-render.

I tried to recreate this scenario in a codesandbox, where <Game /> is that top level controlling component. I can't figure out why the renderFn() method on line 83 never gets set. It seems unlikely this is from a bug in r3f, but I'm stuck. Also do you have any input on this pattern? I think it suits my purpose of controlling the render loop, but I don't know if there's a simpler way.

from react-three-fiber.

drcmda avatar drcmda commented on May 18, 2024

You can try using the global invalidate, don't need to pass the local one up. Maybe that makes it easier.

import { invalidate } from 'react-three-fiber'

Here's the codesandbox with global invalidate: https://codesandbox.io/s/zn7o3z0k33

What i don't like about the current solution is that it seems to put React through the update loop with that useState trigger, it re-renders the Game component 60fps. I would avoid that at all cost. Even if some UI elements have to reflect something, can't this be reduced? Or written outside of React? Running React in a game loop is a bit controversial i think.

That's the whole purpose, if the rotation would be in useRender with a clean ongoing game loop it would be butter smooth. But "animating" that rotation through React will kill performance.

Personally i would prefer the loop to be in canvas. I'd communicate change to the surrounding UI in there, probably via animatable values (react-spring), because they don't re-render anything.

from react-three-fiber.

AndrewRayCode avatar AndrewRayCode commented on May 18, 2024

I'll try the global invalidate, I generally prefer to keep things "in the tree" so I'll also try to figure out why my demo isn't working.

What I do now for Charisma is steps a physics engine (p2.js) which updates all in-game positions, then I pass down those positions to the respective components as props. I loop over all "dynamic" entity data in one component and re-render each child, like <Player />, with the updated <Player position={physicsPosition} /> data. I'm new to hooks, is there a way to do this without passing props? I understand how the hooks work for fairly static scenes, like the demo scenes in this library, but I need to update most game data which affects many components at 60fps.

from react-three-fiber.

drcmda avatar drcmda commented on May 18, 2024

no, if you need realtime it will come down to prop passing. i just hope physics won't take too long to computer, because three + p2 + react + dom paint all in a 15ms window isn't much time to do things. If you use react-spring for positioning, it will at least be outside of react, you would pass down props once and change them in the parent, which informs the views that cling to it.

from react-three-fiber.

AndrewRayCode avatar AndrewRayCode commented on May 18, 2024

I've found two things:

My setup doesn't work if useRender() is called inside the scene component. As in this works:

      <Canvas>
        <ExposeContextToParent
          setRenderTrigger={setRenderTrigger}
        />
        <scene ref={sceneRef}>

but this doesn't:

      <Canvas>
        <scene ref={sceneRef}>
          <ExposeContextToParent
            setRenderTrigger={setRenderTrigger}
          />

Secondly, there doesn't appear to be a way to fully control the render loop? If I use invalidateFrameLoop and useRender(..., true) then setting props on my scene elements still triggers a re-render.

Are either of these bugs?

from react-three-fiber.

drcmda avatar drcmda commented on May 18, 2024

invalidateFrameLoop means that it only renders on prop changes or manual trigger (by calling invalidate). if you need a fully manual loop, perhaps we can expose some of the code around canvas and make it re-usable so that people can build whatever they like around the basic render/unmountComponentAtNode reconciler functionality. can i interest you in looking into it, making a PR?

from react-three-fiber.

AndrewRayCode avatar AndrewRayCode commented on May 18, 2024

I'm still struggling with this. I put my game state on the useRender callback state ref, and I can update my game state inside the first/"top level" useRender callback and things are working.

But now I want to click on a <button> outside my canvas and have it affect the game state. Because I don't have access to the context outside of the canvas I can't do useRender(). I need the opposite too, some action inside the canvas needs to affect state that the <button> uses.

Do you have a suggestion of how to approach this? Is there a way to move the <stateContext.Provider> higher up / into userland, so I can use useRender() outside of r3f? That way the existing renderloop logic could still run, but non-canvas components could play with the state as well, and branch if gl/camera aren't created yet

from react-three-fiber.

drcmda avatar drcmda commented on May 18, 2024

You could use a global effect. Check the exports, it’s among them. This can be done outside of canvas.

from react-three-fiber.

linonetwo avatar linonetwo commented on May 18, 2024

re-renders the Game component 60fps

Why is this bad? How to redraw things if you don't rerender react component? I also need a better way to do this.

I'm writing a binding to an ECS framework, upon drawing, I sync components to react, and trigger rerender:

https://github.com/linonetwo/react-encompass-ecs/blob/4748245cd85801918f55e2db2eaadec9a2ce9c9c/src/updator.ts#L6-L8

I can see reconciler takes a long time, while gl drawing is very fast.

from react-three-fiber.

drcmda avatar drcmda commented on May 18, 2024

See: https://twitter.com/0xca0a/status/1135465164504031233

And: https://twitter.com/0xca0a/status/1133329007800397824 (this one's using three-fiber)

It all depends on how big the rendering effort is. In edge cases you can use useRender, react-spring, or something like zustand. All three go outside of React.

from react-three-fiber.

linonetwo avatar linonetwo commented on May 18, 2024

Thanks, your example helps, I've already got the reference to component (the component in ECS) which gets transient updates by ECS framework (I guess "transient updates" means the reference to the data object is not changed so it won't trigger rerender, but the data inside object change rapidly, am I right?)

The key here is <mesh ref={mesh}> and useRender(() => mesh.current && mesh.current.rotation.set(...coords.current)), so the only way to avoid performance issue is update things imperatively. There is no way to fastly do declearative update.

Also, hope you can expand https://github.com/react-spring/zustand#transient-updates-for-often-occuring-state-changes to describe what does "transient updates" means...

from react-three-fiber.

drcmda avatar drcmda commented on May 18, 2024

well, yes, it means what you said. you are basically notified of a state change via callback and now you can apply updates to the component by directly mutating the view "transiently".

react-spring solves this in a purely declarative way. but it's focussed on animation, which probably isn't what you need. so zustand + useRender cuts a good middleground, because the api is formed in a way that suits useEffect, so everything gets cleaned up after the component unmounts.

also interesting, react will get something like this officially. dan abramov has mentioned this on twitter. something like a fast path-through mode where, if you agree to not change the structure of the view, you can blow props changes through React without diffing, which would be as fast as applying them natively.

from react-three-fiber.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.