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

Can I disable worker with config parameter? #63

Open
sidealice opened this issue Apr 5, 2020 · 21 comments
Open

Can I disable worker with config parameter? #63

sidealice opened this issue Apr 5, 2020 · 21 comments
Labels
invalid This doesn't seem right wontfix This will not be worked on

Comments

@sidealice
Copy link

I got a problem that in some environment javascript worker api should be disabled.

Shall I disable it in cannon manually?

@codynova
Copy link
Member

codynova commented Apr 5, 2020

The web worker is not native to Cannon, and the Cannon lib can be used without the worker (cannon-es).

However the worker is a core component of this use-cannon hooks abstraction and this lib cannot be used without the worker - too much logic is dependent upon it.

It's possible to create your own useCannon hook which does not utilize the web worker, but that's outside the scope of this lib. @drcmda made a solid version of a basic useCannon hook in some of his react-three-fiber examples:

import React, { createContext, useEffect, useRef, useContext, useState } from 'react'
import { useFrame } from 'react-three-fiber'
import * as CANNON from 'cannon'

const WorldContext = createContext({})

export const PhysicsProvider = ({ children }) => {
  const [ world ] = useState(() => new CANNON.World())

  useEffect(() => {
    world.broadphase = new CANNON.NaiveBroadphase()
    world.solver.iterations = 10
    world.gravity.set(0, 0, -10)
  }, [ world ])

  // Run world stepper every frame
  useFrame(() => world.step(1 / 60))

  return (
    <WorldContext.Provider value={world}>
      {children}
    </WorldContext.Provider>
  )
}

export const useCannon = ({ ...props }, fn, deps = []) => {
  const ref = useRef()
  const world = useContext(WorldContext)
  const [ body ] = useState(() => new CANNON.Body(props))

  useEffect(() => {
    // Call function so the user can add shapes, positions, etc. to the body
    fn(body)
    world.addBody(body)
    return () => world.remove(body)
  }, deps)

  useFrame(() => {
    if (ref.current) {
      // Transport cannon physics into the referenced threejs object
      const { position, quaternion } = body
      const { x: px, y: py, z: pz } = position
      const { x: qx, y: qy, z: qz, w: qw } = quaternion
      ref.current.position.copy(new Vector3(px, py, pz))
      ref.current.quaternion.copy(new Quaternion(qx, qy, qz, qw))
    }
  })

  return ref
}

then to consume:

import React from 'react'
import { Canvas} from 'react-three-fiber'
import { useCannon } from './index'

const Box = ({ position, width, height, depth }) => {
  const ref = useCannon({ mass: 10 }, body => {
    body.addShape(
      new CANNON.Box(
        new CANNON.Vec3(width * 0.5, height * 0.5, depth * 0.5)
      )
    )
    body.position.set(...position)
  })
  return (
    <mesh ref={ref}>
      <boxGeometry attach="geometry" args={[ width, height, depth ]} />
      <meshStandardMaterial attach="material" />
    </mesh>
  )
}

const App = () => (
  <Canvas>
    <PhysicsProvider>
      <Box position={[ 0, 0, 5 ]} width={2} height={2} depth={2} />
    </PhysicsProvider>
  </Canvas>
)

Note that you will want a second physics object in the scene (such as a floor plane) for your box to collide with. You can combine this with a wireframe debugger (found here) like so:

import { PhysicsBodyWireframes } from './debug'

const App = () => (
  <Canvas>
    <PhysicsProvider>
      <PhysicsBodyWireframes />
      <Box position={[ 0, 0, 5 ]} width={2} height={2} depth={2} />
    </PhysicsProvider>
  </Canvas>
)

@codynova codynova closed this as completed Apr 5, 2020
@codynova
Copy link
Member

codynova commented Apr 5, 2020

I would be curious to know what environment you can't use web workers in? They are supported pretty well even in older browsers, although I'm not sure if the API might be different in old versions of IE

@codynova codynova added invalid This doesn't seem right wontfix This will not be worked on labels Apr 5, 2020
@codynova
Copy link
Member

codynova commented Apr 5, 2020

If you emulate the API of a web worker with a class on the main thread I guess it might technically be possible to run use-cannon without a web worker but at that point the abstraction is not ideal for performance

@drcmda
Copy link
Member

drcmda commented Apr 6, 2020

enabled could also be a flag on the Physics provider, i think i would also use this in my projects when objects are outside the frustrum.

@codynova
Copy link
Member

codynova commented Apr 6, 2020

@drcmda do you just mean a way to pause the World simulation, or are we talking about a second Provider whose context isnt a web worker?

@sidealice
Copy link
Author

Thank you for the detailed explaination.
The environment which I used is a 3rd party open platform.
They forbidden to use service-worker api for the reason of security.(I think they may crorrect to ban the api, because many open platforms banned this api.)

@drcmda
Copy link
Member

drcmda commented Apr 6, 2020

i think pausing would be a nice feature. similar to enabled/disabled on threejs controls. that would also solve @sidealice usecase since it's requesting the webworker runtime.

@codynova
Copy link
Member

codynova commented Apr 6, 2020

I'm missing how pausing the simulation would help with the worker runtime, but I know we can implement pausing - it should be as easy as skipping the world.step call when we want to pause 😺

@codynova codynova reopened this Apr 6, 2020
@drcmda
Copy link
Member

drcmda commented Apr 6, 2020

if it's not enabled, maybe it wouldn't have to create the worker. then again, there would be lots of awkward rewiring, looking over the code it's probably not worth it because everything expects the worker to be in place - if we change that it would affect everything. better yet to not add Physics to the scene graph at all, and that can easily be made conditional.

@codynova
Copy link
Member

codynova commented Apr 6, 2020

I played around with a substitute for the worker API on the main thread the other day (specifically to address this issue). What would be the equivalent of worker.terminate()? I'm not sure of the most elegant way to release the resource references

@jwrubel
Copy link

jwrubel commented Jul 28, 2020

I would be curious to know what environment you can't use web workers in? They are supported pretty well even in older browsers, although I'm not sure if the API might be different in old versions of IE

Hi from React Native land... I've been using the useCannon hook from the examples folder but having access to the full api would be awesome.

@codynova
Copy link
Member

codynova commented Jul 28, 2020

@jwrubel You technically have access to more of the Cannon API with that example hook than you do within use-cannon. Because use-cannon wraps the physics simulation in a web worker, access to the Cannon API has to be manually abstracted. Since the example hook does not implement a web worker, you have access to the entire Cannon simulation at runtime between your physics Provider and Body hook. However, use-cannon comes with other benefits, such as not blocking the main thread with the physics simulation, and a more declarative API.

Making use-cannon work in environments that don't support web workers would remove the primary benefit of use-cannon in my opinion. You could probably build a much more ergonomic API and better DX if you were to reconsider what a declarative API would look like for Cannon outside of a web worker.

@jwrubel
Copy link

jwrubel commented Jul 28, 2020

It's possible I'm getting lost in the code a bit. The hook only returns ref whereas the use-cannon package returns an api that the docs show being referenced in useFrame, etc. When I use the hook like:

const ref = useCannon({ 
    mass: 1000
  }, body => {
    let payload = new CANNON.Sphere(1);
    body.addShape(payload);
    body.position.set(...position) 
    ...
}

I can't access ref.current in useFrame (or it's entirely possible I'm doing something wrong). Do you have an example of being able to access the ref outside the body hook?

@codynova
Copy link
Member

codynova commented Jul 28, 2020

The ref in the hook from the example code you posted is a reference to a ThreeJS mesh that will receive values from the Cannon physics body - it's not a reference to the actual Cannon physics body.

The api object that's returned by the hooks in this use-cannon package is a bridge into the web worker, to allow you to access a subset of Cannon APIs from the main thread - this is the manual abstraction that I mentioned in my last reply.

Since the hook from the example code you posted is already running on the main thread, you have access to the entire cannon-es lib (the entire Cannon API). If you want access to the body object outside the hook, you can assign it to a ref.

@donmccurdy
Copy link
Member

I would be curious to know what environment you can't use web workers in? They are supported pretty well even in older browsers, although I'm not sure if the API might be different in old versions of IE

I don't personally need this, but thought I'd share some results from having implemented cannon.js support (both with and without a Web Worker) in a past project. When running a large simulation, or a simulation that isn't highly interactive, a Web Worker is a great choice. But there's a minimum to the latency of interaction with a physics simulation running in a web worker. This is particularly noticeable with WebXR input devices, where (1) the display is likely refreshing at 90-120fps, and (2) the user's perception of their own motion is quite precise. In that scenario, with interaction wired up to WebXR controllers using Cannon.JS constraints, I found that not using a Web Worker for lightweight simulations worked really well.

Context: n5ro/aframe-physics-system#33

@abernier
Copy link

abernier commented Apr 9, 2023

got previous useCannon #63 (comment) r3f example working in this csb: https://codesandbox.io/s/cannon-es-r3f-921gwn

@abernier
Copy link

abernier commented Apr 10, 2023

I've simplified the original useCannon(options, fn, deps) signature, so it now only takes one param. No more limited to a single fn with dynamic deps args but: you can have as many fns/deps since the body is now returned by the hook, like here with different/separate effects

https://codesandbox.io/s/cannon-es-r3f-forked-66mobn?file=/src/App.js:1211-1225

@jmatsushita
Copy link

It would be great to allow use-cannon to disable the worker backend for use in react-native or for latency sensitive cases as mentioned above. Having the workaround above is nice, but we don't have all the nice @react-three/cannon api.

@itsyoboieltr
Copy link

It would be great to allow use-cannon to disable the worker backend for use in react-native or for latency sensitive cases as mentioned above. Having the workaround above is nice, but we don't have all the nice @react-three/cannon api.

Agreed, this would be great for React Native support (afaik this is the only thing blocking)

@albertocm
Copy link

The web worker is not native to Cannon, and the Cannon lib can be used without the worker (cannon-es).

However the worker is a core component of this use-cannon hooks abstraction and this lib cannot be used without the worker - too much logic is dependent upon it.

It's possible to create your own useCannon hook which does not utilize the web worker, but that's outside the scope of this lib. @drcmda made a solid version of a basic useCannon hook in some of his react-three-fiber examples:

import React, { createContext, useEffect, useRef, useContext, useState } from 'react'
import { useFrame } from 'react-three-fiber'
import * as CANNON from 'cannon'

const WorldContext = createContext({})

export const PhysicsProvider = ({ children }) => {
  const [ world ] = useState(() => new CANNON.World())

  useEffect(() => {
    world.broadphase = new CANNON.NaiveBroadphase()
    world.solver.iterations = 10
    world.gravity.set(0, 0, -10)
  }, [ world ])

  // Run world stepper every frame
  useFrame(() => world.step(1 / 60))

  return (
    <WorldContext.Provider value={world}>
      {children}
    </WorldContext.Provider>
  )
}

export const useCannon = ({ ...props }, fn, deps = []) => {
  const ref = useRef()
  const world = useContext(WorldContext)
  const [ body ] = useState(() => new CANNON.Body(props))

  useEffect(() => {
    // Call function so the user can add shapes, positions, etc. to the body
    fn(body)
    world.addBody(body)
    return () => world.remove(body)
  }, deps)

  useFrame(() => {
    if (ref.current) {
      // Transport cannon physics into the referenced threejs object
      const { position, quaternion } = body
      const { x: px, y: py, z: pz } = position
      const { x: qx, y: qy, z: qz, w: qw } = quaternion
      ref.current.position.copy(new Vector3(px, py, pz))
      ref.current.quaternion.copy(new Quaternion(qx, qy, qz, qw))
    }
  })

  return ref
}

then to consume:

import React from 'react'
import { Canvas} from 'react-three-fiber'
import { useCannon } from './index'

const Box = ({ position, width, height, depth }) => {
  const ref = useCannon({ mass: 10 }, body => {
    body.addShape(
      new CANNON.Box(
        new CANNON.Vec3(width * 0.5, height * 0.5, depth * 0.5)
      )
    )
    body.position.set(...position)
  })
  return (
    <mesh ref={ref}>
      <boxGeometry attach="geometry" args={[ width, height, depth ]} />
      <meshStandardMaterial attach="material" />
    </mesh>
  )
}

const App = () => (
  <Canvas>
    <PhysicsProvider>
      <Box position={[ 0, 0, 5 ]} width={2} height={2} depth={2} />
    </PhysicsProvider>
  </Canvas>
)

Note that you will want a second physics object in the scene (such as a floor plane) for your box to collide with. You can combine this with a wireframe debugger (found here) like so:

import { PhysicsBodyWireframes } from './debug'

const App = () => (
  <Canvas>
    <PhysicsProvider>
      <PhysicsBodyWireframes />
      <Box position={[ 0, 0, 5 ]} width={2} height={2} depth={2} />
    </PhysicsProvider>
  </Canvas>
)

I just used this solution for my project (a few objects collisioning). On web works perfect, but on Android and iOS It gets super laggy. Any solution to the poor performance?

@neciszhang
Copy link

I recently rewrote this library and removed the web worker functionality. I thought I could release another version for everyone to use directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid This doesn't seem right wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

10 participants