Apply Physics to Individual R3F Rapier Instance using instancedRigidBodies and instancedMesh

Hi everyone, I am using r3f and rapier to create instanced meshes and I wanted to apply an impulse to the individual meshes when clicked. I tried to achieve this using raycaster to get the id of the mesh and use the id to apply impulse to that specific rigidbody.

I am having trouble with the raycaster getting the object onclick. It gets them for some objects but not for others. Could you please help me figure out what I am doing wrong? Is this a good approach?

Thanks

Here is the sandbox: https://codesandbox.io/p/sandbox/instancedmeshesphysics-szqc4r

Hi @Nate_A you can have a look at this example for a detailed insight into how this can be done

at line 61 you could start by changing <InstanceMesh> to something like the following…

<instancedMesh
  receiveShadow
  castShadow
  args={[undefined, undefined, count]}
  dispose={null}
  onClick={(e) => {
    console.log(e.instanceId)
  }}
>

whereby you could create / apply some sort of directional velocity ( unsure what you mean by “impulse”, are you refering to scale, color, position? ) to the particular instanceId that’s been clicked…

1 Like

also, GitHub - pmndrs/react-three-rapier: 🤺 Rapier physics in React

2 Likes

Nice, i think the main problem in @Nate_A’s sandbox is the somewhat unnessicary creation of raycaster and (unproperly initialized) window click events ( fwiu you’d initialize an eventListener in a useEffect and return removeEventListener at the end of that ). r3f already has an internal event system that uses raycaster, all you need to do is add the required pointer event to the relative 3D components eg… onClick={(e)=>{ // do stuff on click }}, this simplifies the use of pointer events in 3D space and cuts out having to manually set this up like in @Nate_A’s original sandbox…

1 Like

Thank you for your help and time. I’m still having issues with getting the instanceId every time. It’s weird. It works for few but not for most. I can’t figure out why. That’s the reason I tried my ill-conceived raycaster instance. I can manipulate each Instanced Rigidbody if I get the instanceId from the mesh onClick. But it is not getting them every time for some reason. Thanks again!

Hey @Nate_A , try this…
https://codesandbox.io/p/sandbox/instancedmeshesphysics-forked-fddnvj?file=%2Fsrc%2FExperience.js

it looked like your useMemo instances and returned array instances were getting somewhat confused having the same name, also added e.stopPropagation() to prevent the click event from passing through the first hit object, meaning only the first hit object is affected not every other object behind within the rays trajectory.

Not able to open your fork. I did make the changes you suggested. Still unable to get all of them. Is it working for you? Thank you.

just updated the sandbox to be publicly listed https://codesandbox.io/p/sandbox/instancedmeshesphysics-forked-fddnvj?file=%2Fsrc%2FExperience.js

here’s the code snippet for experience.js if you still can’t access it

import {
  Physics,
  RigidBody,
  CuboidCollider,
  InstancedRigidBodies,
  useRapier,
} from "@react-three/rapier";
import { useMemo, useRef, useEffect } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { Vector2, Vector3 } from "three";

export default function Experience() {
  const cubes = useRef();
  const rigidBodies = useRef();
  const cubesCount = 300;
  const instancess = useMemo(() => {
    const instances = [];
    for (let i = 0; i < cubesCount; i++) {
      const angle = Math.random() * Math.PI * 2;
      const radius = 2 + Math.random() * 50;
      const x = Math.cos(angle) * radius;
      const z = Math.sin(angle) * radius;
      instances.push({
        key: "instance_" + i,
        position: [x, Math.random() * 5, z],
        rotation: [Math.random(), Math.random(), Math.random()],
      });
    }

    return instances;
  }, []);

  return (
    <>
      <Physics>
        <InstancedRigidBodies
          instances={instancess}
          type="dynamic"
          gravityScale={0}
          ref={rigidBodies}
          // canSleep={true}
        >
          <instancedMesh
            ref={cubes}
            args={[null, null, cubesCount]}
            dispose={null}
            onClick={(e) => {
              e.stopPropagation();
              console.log(e.instanceId);
              rigidBodies.current[e.instanceId].applyImpulse(
                {
                  x: Math.random(),
                  y: Math.random(),
                  z: Math.random(),
                },
                true
              );
            }}
          >
            <boxGeometry args={[1]} />
            <meshNormalMaterial />
          </instancedMesh>
        </InstancedRigidBodies>
      </Physics>
    </>
  );
}

2 Likes

Thanks again for your help. Still having issue on getting the event each time onClick. e.stopPropagation() is a good solution though after it gets the event but it’s not getting the event all of the time.

Your original sandbox is not accessible any more, just to see, do you have console open and logging lots of data on click? If so can you try remove all console logs or close the console to see if that improves things?