2

Three.js and React three fiber newbie here. I'm attempting to get ray casting to work correctly using Raycaster from three. I’ve got 2 simple mesh boxes, one static and other movable via keyboard events. Goal is to console log the static mesh when movable mesh intersects it via ray cast helper function. But intersection is not getting detected. Any help would be most appreciated.

Here are the components:

App.js

import { Canvas } from "@react-three/fiber";
import { OrbitControls, Plane } from "@react-three/drei";
import MovingBox from "./MovingBox";

export default function App() {
  return (
    <>
      <Canvas camera={{ position: [0, 7.5, -15], fov: 50 }}>
        <ambientLight intensity={0.5} />
        <directionalLight
          intensity={1}
          position={[4, 20, 0]}
          color={0xffffff}
        />
        <Plane
          receiveShadow
          rotation={[-Math.PI / 2, 0, Math.PI]}
          position={[0, 0, 0]}
          args={[200, 200]}
        >
          <meshStandardMaterial attach="material" color="green" />
        </Plane>
        <MovingBox />
        <mesh scale={[5, 5, 5]} position={[-10, 1, 0]} name="CollisionBox">
          <boxGeometry />
          <meshStandardMaterial color="hotpink" />
        </mesh>
        <OrbitControls />
      </Canvas>
    </>
  );
}

MovingBox.js

import { useRef, useMemo } from "react";
import { useHelper } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import { useKeyboardControls } from "./useKeyboardControls";
import { Vector3, Raycaster, BoxHelper } from "three";

const useForwardRaycast = (obj, collisionItem) => {
  // console.log("obj", obj);
  // console.log("collisionItem", collisionItem);
  const scene = useThree((state) => state.scene);

  const raycaster = useMemo(() => new Raycaster(), []);
  const pos = useMemo(() => new Vector3(), []);
  const dir = useMemo(() => new Vector3(), []);

  return () => {
    if (!obj.current) return [];
    raycaster.set(
      obj.current.getWorldPosition(pos),
      obj.current.getWorldDirection(dir)
    );
    return raycaster.intersectObjects(scene);
  };
};

const MovingBox = () => {
  const boxRef = useRef();
  const controls = useKeyboardControls();
  const scene = useThree((state) => state.scene);

  useHelper(boxRef, BoxHelper, "red");

  const raycast = useForwardRaycast(
    boxRef,
    scene?.children?.find((el) => el.name === "CollisionBox")
  );

  useFrame(() => {
    const { moveForward, moveBackward, moveLeft, moveRight } = controls;
    if (moveForward) {
      boxRef.current.position.z += 0.1;
    }
    if (moveBackward) {
      boxRef.current.position.z -= 0.1;
    }
    if (moveLeft) {
      boxRef.current.position.x += 0.1;
    }
    if (moveRight) {
      boxRef.current.position.x -= 0.1;
    }
  });

  useFrame(() => {
    const intersections = raycast();
    if (intersections.length > 0) {
      console.log("intersections", intersections);
    }
  });

  return (
    // <Box args={[5, 5, 5]} radius={0.05} smoothness={4} position={[0, 1, 0]} castShadow receiveShadow ref={boxRef} name="MovingBox">
    //   <meshPhongMaterial color="#f3f3f3" />
    // </Box>
    <mesh scale={[5, 5, 5]} position={[0, 0, 0]} ref={boxRef} name="MovingBox">
      <boxGeometry />
      <meshStandardMaterial color="grey" />
    </mesh>
  );
};

export default MovingBox;

Codesandbox: https://codesandbox.io/s/raycast-test-s6vvgd?file=/src/MovingBox.js:0-2041

1 Answers1

0

try to move from up to down after changing to "intersectObject" method (singular) when you want to test one object (the result will always be an array even when you test only one object). That is where you detect the intersection, as shown in the screenshot. Change the direction of the raycast if you need to experiment with custom origin and direction. Also, you might need to normalize the direction (convert to vector of length 1).

If you want to cast a ray while moving, you have to create the animate() function to test it on each frame.

Hope it helps.

Scene image