Closed RobRendell closed 4 years ago
you're right, it should call invalidate. but it's a bit tricky atm because use-cannon itself is useFrame based. i did try to bring the mutation stuff into the main loop, but got into some scope problems, there's data which only the hooks have. but i think it could call invalidate in the main loop if it detects a change. @codynova is it possible to know outright if anything was moving without going through complex vector old-new checks?
The World's internalStep
function always sets the dirty
flag on the Broadphase, so as far as the Broadphase is concerned, the simulation is always running. I think the easiest way to check for movement would be something like this:
world.bodies.some(body => body.sleepState !== Body.SLEEPING)
which could be used in conjunction with sleepTimeLimit
and sleepSpeedLimit
for greater control.
it seems to work, that was easier than i thought it would be :-)
Published in 0.2.8
my only worry is, what if we're dealing with hundredes (thousands?) of objects. it would have to climb through that loop every frame. is there maybe a subscription at the body level we can use to flag active state globally?
There's no reason we couldn't add one. The World (and sometimes the Solver) already iterate through every body at least one on each frame.
What do you think makes the most sense? The most basic solution would be having bodies add/remove themselves from a World.activeBodies
array (or Map), then just checking that length > 0
.
I wonder what effect that would have on memory? Schteppe did quite a bit of memory optimization, so if there is a more memory-efficient solution I'd love to know!
that sounds great! yeah, i tend to forget, we can change cannon now. :-D i think mem impact for a bunch of flags won't be a problem.
I was wondering more about the memory impact of the array of bodies, but I'm not really sure how that works since it's all pass-by-reference, I'd need to actually test it. I will take a stab at the solution I proposed above...
Newer frame validation which simply checks the length of World.activeBodies
each frame by referencing getter World.hasActiveBodies
released in 0.2.9
nice one! thanks a lot
Happy to do it! There is probably a more efficient way, by assuming hasActiveBodies = false
and checking for non-sleeping bodies in the World step - but it's a little trickier to figure out when it's safe to read a body's sleepState
in the step
. I may try to revisit it a little later, while keeping the forward-facing API the same (World.hasActiveBodies
)...
You guys are amazingly quick! I get up this morning to find all this extra activity :)
When I started reading the reopening comments, my thought was that if you had memory concerns about the array of active bodies, then a simple counter would also work for detecting any activity... each body would increment it when it transitions from asleep to awake (or when it's added in an awake state) and decrement it when it transitions from awake to asleep.
However, I imagine the array of active bodies will be useful for other optimisations too.
@RobRendell That's a good idea. The counter is closer to what I was imagining doing in the World step
. I think we could push it one step further. Rather than keeping a count, just check the body's sleepState
in the World step and switch the flag to true if any body is awake - otherwise default to false. The counter would be easier to implement because we can simply call it from the body's wakeUp
or sleep
methods. The flag is probably (slightly) more performant, but it requires being more careful during the integration with the World step
method. I'm wondering if we can come up with any other potential uses for the activeBodies
- I can't think of a good reason to keep it.
Revisit for performance improvements
Well, presumably you only need to iterate over the active bodies when simulating updates? If the scene has lots of sleeping physics objects, not having to iterate over the entire list would be more efficient. I imagine that in "real world" type scenes, most physics objects are at rest...
The World already has to iterate over every body multiple times in each step to do integrations, solve contact equations, evaluate and alter waking state, etc.
The new method of updating flags in the World step was released in cannon-es@0.8.7
and use-cannon@0.2.10
. I'll wait to close this time until we confirm it's working properly :)
Seems to work fine based on testing in CompoundBody demo
@codynova Sorry to bug you yet again, but this appears to have regressed (checked with both versions 0.2.11 and 0.3.0). The invalidate
function is being called all the time, even after the bodies are sleeping. I even checked by adding a useFrame() callback to the CompoundBody demo, and the callback continues to be invoked, apparently indefinitely.
@RobRendell Do you mind creating a minimum viable example codesandbox? I just tested locally with invalidateFrameloop
on the Canvas
and a callback useFrame(() => console.log('test'))
- and it seems to work fine, unless I'm just missing something
maybe you forgot to build? that is, if you're working with the examples repo. that happens to me all the time. simply updating it wont do.
I didn't find the examples repo, so I pulled this repo and copy/pasted the CompoundBody demo into my project.
However, I discovered that the problem was with my configuration - I misunderstood the sleepTimeLimit parameter, and assumed it was in milliseconds. The simulation was able to sleep, I just wasn't waiting long enough :)
Apologies for the false report! Thank you so much for being so responsive!
My app uses
<Canvas invalidateFrameloop={true}/>
in react-three-fiber to avoid rendering every frame, because most of the time the scene is static (it's a virtual gaming space for tabletop roleplaying games, and if no-one is moving anything around, there's no need to consume power by re-rendering the scene).I'm adding physics-based dice rolling using use-cannon, but obviously when the physics simulation is running the app needs to re-render every frame. At the moment, the dice's
render()
methods calluseFrame(({invalidate}) => {invalidate();});
so they cause the app to render every frame while they are mounted.use-cannon
defaults touseSleep={true}
, so presumably when the die/dice settle, the physics simulation goes to sleep. However, I can't find a way to query the sleep state of the simulation from user-land, so my useFrame call can stop callinginvalidate()
. If such a getter is not available, I'd like to request that it be added (presumably to the WorkerAPI).Given that your physics state is hidden away in the workers, it might be hard to make a getter of the sleep status available through the API. As an alternative, since
invalidateFrameloop
is a standard (if seldom used) feature of react-three-fiber, perhaps a similar prop could be added to<Physics/>
, defaulting to false, but if true, use-cannon itself could callinvalidate()
each frame that the physics sim is not sleeping, and stop calling it when it sleeps.