feat: grab objects with closed fist raycast
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
|||||||
GRAB_THROW_BOOST_MIN,
|
GRAB_THROW_BOOST_MIN,
|
||||||
GRAB_THROW_BOOST_STEP,
|
GRAB_THROW_BOOST_STEP,
|
||||||
} from "@/data/interaction/grabConfig";
|
} from "@/data/interaction/grabConfig";
|
||||||
|
import { INTERACTION_RADIUS } from "@/data/interaction/interactionConfig";
|
||||||
import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
|
import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
|
||||||
import { useHandTrackingSnapshot } from "@/hooks/useHandTrackingSnapshot";
|
import { useHandTrackingSnapshot } from "@/hooks/useHandTrackingSnapshot";
|
||||||
import type { ColliderShape, Vector3Tuple } from "@/types/three";
|
import type { ColliderShape, Vector3Tuple } from "@/types/three";
|
||||||
@@ -46,6 +47,9 @@ const _currentPos = new THREE.Vector3();
|
|||||||
const _velocity = new THREE.Vector3();
|
const _velocity = new THREE.Vector3();
|
||||||
const _handNdc = new THREE.Vector3();
|
const _handNdc = new THREE.Vector3();
|
||||||
const _handDirection = new THREE.Vector3();
|
const _handDirection = new THREE.Vector3();
|
||||||
|
const _cameraPos = new THREE.Vector3();
|
||||||
|
const _objectPos = new THREE.Vector3();
|
||||||
|
const _handRaycaster = new THREE.Raycaster();
|
||||||
|
|
||||||
export function GrabbableObject({
|
export function GrabbableObject({
|
||||||
position,
|
position,
|
||||||
@@ -56,8 +60,10 @@ export function GrabbableObject({
|
|||||||
}: GrabbableObjectProps): React.JSX.Element {
|
}: GrabbableObjectProps): React.JSX.Element {
|
||||||
const camera = useThree((state) => state.camera);
|
const camera = useThree((state) => state.camera);
|
||||||
const { hands } = useHandTrackingSnapshot();
|
const { hands } = useHandTrackingSnapshot();
|
||||||
|
const groupRef = useRef<THREE.Group>(null);
|
||||||
const rbRef = useRef<RapierRigidBody>(null);
|
const rbRef = useRef<RapierRigidBody>(null);
|
||||||
const isHolding = useRef(false);
|
const isHolding = useRef(false);
|
||||||
|
const isHandHolding = useRef(false);
|
||||||
|
|
||||||
useDebugFolder("GrabbableObject", (folder) => {
|
useDebugFolder("GrabbableObject", (folder) => {
|
||||||
folder
|
folder
|
||||||
@@ -96,23 +102,43 @@ export function GrabbableObject({
|
|||||||
? hands.find((hand) => hand.isFist)
|
? hands.find((hand) => hand.isFist)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (!isHolding.current && !fistHand) return;
|
const t = rbRef.current.translation();
|
||||||
|
_currentPos.set(t.x, t.y, t.z);
|
||||||
|
|
||||||
if (fistHand) {
|
if (fistHand) {
|
||||||
_handNdc.set((1 - fistHand.x) * 2 - 1, -fistHand.y * 2 + 1, 0.5);
|
_handNdc.set((1 - fistHand.x) * 2 - 1, -fistHand.y * 2 + 1, 0.5);
|
||||||
_handNdc.unproject(camera);
|
_handNdc.unproject(camera);
|
||||||
_handDirection.subVectors(_handNdc, camera.position).normalize();
|
camera.getWorldPosition(_cameraPos);
|
||||||
|
_handDirection.subVectors(_handNdc, _cameraPos).normalize();
|
||||||
|
|
||||||
|
if (!isHandHolding.current) {
|
||||||
|
_objectPos.copy(_currentPos);
|
||||||
|
_handRaycaster.set(_cameraPos, _handDirection);
|
||||||
|
_handRaycaster.far = INTERACTION_RADIUS;
|
||||||
|
|
||||||
|
const isObjectInRange =
|
||||||
|
_cameraPos.distanceTo(_objectPos) <= INTERACTION_RADIUS;
|
||||||
|
const hits = groupRef.current
|
||||||
|
? _handRaycaster.intersectObject(groupRef.current, true)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
isHandHolding.current = isObjectInRange && hits.length > 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isHandHolding.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isHolding.current && !isHandHolding.current) return;
|
||||||
|
|
||||||
|
if (fistHand && isHandHolding.current) {
|
||||||
_holdTarget
|
_holdTarget
|
||||||
.copy(camera.position)
|
.copy(_cameraPos)
|
||||||
.addScaledVector(_handDirection, params.holdDistance);
|
.addScaledVector(_handDirection, params.holdDistance);
|
||||||
} else {
|
} else {
|
||||||
camera.getWorldDirection(_holdTarget);
|
camera.getWorldDirection(_holdTarget);
|
||||||
_holdTarget.multiplyScalar(params.holdDistance).add(camera.position);
|
_holdTarget.multiplyScalar(params.holdDistance).add(camera.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
const t = rbRef.current.translation();
|
|
||||||
_currentPos.set(t.x, t.y, t.z);
|
|
||||||
|
|
||||||
_velocity
|
_velocity
|
||||||
.subVectors(_holdTarget, _currentPos)
|
.subVectors(_holdTarget, _currentPos)
|
||||||
.multiplyScalar(params.stiffness);
|
.multiplyScalar(params.stiffness);
|
||||||
@@ -131,31 +157,36 @@ export function GrabbableObject({
|
|||||||
colliders={colliders}
|
colliders={colliders}
|
||||||
position={position}
|
position={position}
|
||||||
>
|
>
|
||||||
<InteractableObject
|
<group ref={groupRef}>
|
||||||
kind="grab"
|
<InteractableObject
|
||||||
label={label}
|
kind="grab"
|
||||||
position={position}
|
label={label}
|
||||||
bodyRef={rbRef}
|
position={position}
|
||||||
onPress={() => {
|
bodyRef={rbRef}
|
||||||
isHolding.current = true;
|
onPress={() => {
|
||||||
}}
|
isHolding.current = true;
|
||||||
onRelease={() => {
|
}}
|
||||||
isHolding.current = false;
|
onRelease={() => {
|
||||||
if (!rbRef.current || params.throwBoost === GRAB_THROW_BOOST_DEFAULT)
|
isHolding.current = false;
|
||||||
return;
|
if (
|
||||||
const v = rbRef.current.linvel();
|
!rbRef.current ||
|
||||||
rbRef.current.setLinvel(
|
params.throwBoost === GRAB_THROW_BOOST_DEFAULT
|
||||||
{
|
)
|
||||||
x: v.x * params.throwBoost,
|
return;
|
||||||
y: v.y * params.throwBoost,
|
const v = rbRef.current.linvel();
|
||||||
z: v.z * params.throwBoost,
|
rbRef.current.setLinvel(
|
||||||
},
|
{
|
||||||
true,
|
x: v.x * params.throwBoost,
|
||||||
);
|
y: v.y * params.throwBoost,
|
||||||
}}
|
z: v.z * params.throwBoost,
|
||||||
>
|
},
|
||||||
{children}
|
true,
|
||||||
</InteractableObject>
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</InteractableObject>
|
||||||
|
</group>
|
||||||
</RigidBody>
|
</RigidBody>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user