My current challenge involves shooting multiple rays from a rotating mesh in various directions targeting points on a circle divided by the number of rays.
To assist with debugging, I have added ArrowHelpers for each ray with a goal for the arrows to turn red upon hitting something and white if not. Additionally, the length of the arrows changes based on the distance to the intersected object; remaining at the ray's max length (far) when not intersecting.
However, I am facing a problem where every ray continues to check the same forward direction of the rotating mesh. I suspect I need to determine a formula to calculate a new normalized vector based on the current rotation of the object. Despite trying approaches like object3D.localToGlobal
, Vector3.applyQuaternion
, my math skills seem to be inadequate.
For reference, the code sandbox can be accessed at: https://codesandbox.io/s/raycast-issue-bch05b
Raycasting code:
import { RefObject } from "react";
import * as THREE from "three";
import React from "react";
import { useFrame, useThree } from "@react-three/fiber";
export type RayCastResult = {
hit: boolean;
angle: number;
direction: THREE.Vector3;
distance: number;
};
export const useRaycasts = ({
count = 4,
near = 1,
far = 10,
obj
}: {
count?: number;
near?: number;
far?: number;
obj: RefObject<THREE.Mesh>;
}): { rays: RayCastResult[] } => {
const rays = React.useMemo(() => {
const rays: RayCastResult[] = [];
let angle = 0;
const step = (2 * Math.PI) / count;
for (let i = 0; i < count; i++) {
rays.push({
hit: false,
angle: angle,
direction: new THREE.Vector3(
Math.cos(angle),
0,
Math.sin(angle)
).normalize(),
distance: 10
});
angle += step;
}
return rays;
}, [count]);
const pos = React.useMemo(() => new THREE.Vector3(), []);
const dir = React.useMemo(() => new THREE.Vector3(), []);
const { scene, raycaster } = useThree();
useFrame(() => {
if (!obj.current) return;
obj.current.getWorldDirection(dir);
obj.current.getWorldPosition(pos);
rays.forEach((direction, i) => {
if (!obj.current) return;
raycaster.set(
pos,
dir
.applyAxisAngle(rays[0].direction, obj.current?.rotation.y)
.normalize()
);
raycaster.near = near;
raycaster.far = far;
const intersects = raycaster.intersectObjects(scene.children);
// ONLY check first object
if (intersects.length) {
rays[i].hit = true;
rays[i].distance = intersects[0].distance;
} else {
rays[i].hit = false;
rays[i].distance = raycaster.far;
}
});
});
return { rays };
};