Giter VIP home page Giter VIP logo

Comments (9)

alexandernanberg avatar alexandernanberg commented on June 2, 2024 1

with your permission of course

Sure, feel free to take anything that is useful πŸ˜„

Hm, supposedly we could initialize the rigidbody in useLayoutEffect to get around that...

I think I had to create both the rigidbody and collider in a useLayoutEffect to avoid "layout shift" (not sure what it's called in a 3d context), was some time ago so don't remember exactly why I had to do it that way

from react-three-rapier.

wiledal avatar wiledal commented on June 2, 2024

Thanks for this! Indeed I will have to move everything into effects. It's just not nice working with current all the time, and I was hoping to avoid it wherever I could, but I was not aware of these drawbacks.

Your adaption is really nice. Would you like to help flesh out r3f/rapier?

πŸ‡ΈπŸ‡ͺπŸ‘‹

from react-three-rapier.

Glavin001 avatar Glavin001 commented on June 2, 2024

@wiledal : To make the DX nicer, what about proxying the rigid body API calls?

Before:

  • ❌ Uses useMemo
  • βœ… Nice DX without .current
// Package
export const useRigidBody = <O extends Object3D>(
  // ...
  const rigidBody = useMemo(() => {
    // ...
    const body = world.createRigidBody(rigidBodyDesc);
    return body;
  }, []);
  // ...
  return [ref as MutableRefObject<O>, rigidBody];
};

After:

  • βœ… Uses useEffect
  • βœ… Nice DX without .current
export const useRigidBody = <O extends Object3D>(
  // ...
  const rigidBodyRef = useRef();
  useEffect(() => {
    // ...
    const body = world.createRigidBody(rigidBodyDesc);
    rigidBodyRef.current.body;
  }, []);
  // ...
  const api = createBodyApi(rigidBodyRef);
  return [ref as MutableRefObject<O>, api];
};

function createBodyApi(bodyRef) {
  return {
    applyImpulse: (...args) => {
      bodyRef.current.applyImpulse(...args);
    },
    applyTorqueImpulse: (...args) => {
      bodyRef.current.applyImpulse(...args);
    },
    // ...
  };
}

Would allow the same public API.

For example:

// Usage
  const [box, rigidBody] = useCuboid(
    {
      position: [-4 + Math.random() * 8, 10, 0],
    },
    {
      args: [0.5, 0.5, 0.5],
    }
  );

  useEffect(() => {
    rigidBody.applyImpulse({ x: 0, y: 0, z: 0 }, true);
    rigidBody.applyTorqueImpulse({ x: 0, y: 0, z: Math.random() * 0.1 }, true);
  }, []);

from react-three-rapier.

alexandernanberg avatar alexandernanberg commented on June 2, 2024

Your adaption is really nice. Would you like to help flesh out r3f/rapier?

Would be fun, I'm a bit short on time right now but if I get some spare time later I'll for sure see where I can contribute!

@Glavin001 The issue I hit with that approach is that you can't pass down the rigidbody in context to the child <Collider> component (where it's needed in order to add it to the rapier world). React fires effects bottom-up, meaning the child (<Collider>) won't have access to the rigidbody from the parent. Not sure if this makes sense, kinda hard to explain 😬

from react-three-rapier.

wiledal avatar wiledal commented on June 2, 2024

Would be fun, I'm a bit short on time right now but if I get some spare time later I'll for sure see where I can contribute!

No worries! I might lift some approaches from your repo, as you have it so neatly laid out -- with your permission of course πŸ™

function createBodyApi(bodyRef) {

I was thinking of doing this, but the extra work put me off for now. But, I will probably do it anyways because interacting with the api is annoying right now as you have to convert between Rapier's Vector3, Three's Vector3 back and forth for all interactions.

you can't pass down the rigidbody in context to the child

Hm, supposedly we could initialize the rigidbody in useLayoutEffect to get around that...

from react-three-rapier.

wiledal avatar wiledal commented on June 2, 2024

Sure, feel free to take anything that is useful πŸ˜„
✨

I've been doing some experiments, and from what I can tell it works okay initializing in a useLayoutEffect and returning a proxy api.

I have one strange useLayoutEffect cleanup function that fires prematurely for some reason, but probably unrelated.

I feel there's big win to only have to deal with Three vector types, so I'll probably continue on this path.

from react-three-rapier.

wiledal avatar wiledal commented on June 2, 2024

@alexandernanberg you are right, useLayoutEffect did not work as we also needed the world to be initiated the same.

After more fiddling I believe a combination of these approaches is hitting the goldilocks sweetspot ✨

  1. Use the RefGetter (MutableRefObject<() => T>) approach for "lazy" (or rather "eager"!) initiation of objects (world, rigidbody, collider, joint), as mentioned in the original post.
  2. In a memoized proxy, use the RefGetter for all api forwarding
const createSomeApi = (refGetter) => ({
  someFunc() => refGetter.current()!.someFunc()
})

const useSomething = () => {
  const ref = useRef()
  const refGetter = useRef(() => {
    if (!ref.current) {
      ref.current = new Something()
    }
    return ref.current
  })

  const api = useMemo(() => createSomeApi(refGetter), [])

  return api
}

The result is an immediately accessible api, with eagerly initiated objects.

const [ref, api] = useRigidBody(...)

useEffect(() => {
  api.applyImpulse(new Vector3(0,0,1))
}, [])
  useFrame(() => {
    refs.current.forEach((ref) => {
      const p = ref.current.translation();
      // since p is proxied to be a Vector3, we can easily apply all Threejs math on it
      p.normalize().multiplyScalar(-0.02);
      ref.current.applyImpulse(p);
    });
  });

from react-three-rapier.

wiledal avatar wiledal commented on June 2, 2024

I went through and applied these approaches for everything.
Going to double check so that all types are in place and usable asap.

See: #14 :)

from react-three-rapier.

alexandernanberg avatar alexandernanberg commented on June 2, 2024

I'm still not convinced useMemo is the right choice here, since React can throw away the value at any time. A lazy state setter might be better (useState(() => createSomeApi(refGetter))), but technically it might still violate the React rules since you're setting/getting the ref value in render (React 18 compatibility is hard πŸ˜…)

from react-three-rapier.

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.