I’ve also asked this question on Stack Overflow.
I’m trying to add grabbing functionality to my scene, with physics as well.
So far, the closest I’ve got was this CodeSandbox by gallant-sunset-6fjdgg, coming from this answer.
The problems/limitations with that sandbox are:
- It doesn’t feature physics
- The object only moves around the xy axes.
Does anyone know how to add physics and a movement across the z axis to it?
Here’s the code for reference:
App.jsx
import React, { useState } from "react";
import { Canvas } from "@react-three/fiber";
import Obj from "./Obj.jsx";
import { OrthographicCamera, OrbitControls } from "@react-three/drei";
import * as THREE from "three";
export default function App() {
const [isDragging, setIsDragging] = useState(false);
const floorPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
return (
<Canvas style={{ background: "white" }} shadows dpr={[1, 2]}>
<ambientLight intensity={0.5} />
<directionalLight
intensity={0.5}
castShadow
shadow-mapSize-height={1512}
shadow-mapSize-width={1512}
/>
<mesh
rotation={[-Math.PI / 2, 0, 0]}
position={[0, -0.1, 0]}
receiveShadow
>
<planeBufferGeometry attach="geometry" args={[30, 30]} receiveShadow />
<meshPhongMaterial
attach="material"
color="#ccc"
side={THREE.DoubleSide}
receiveShadow
/>
</mesh>
<planeHelper args={[floorPlane, 5, "red"]} />
<gridHelper args={[100, 100]} />
<Obj setIsDragging={setIsDragging} floorPlane={floorPlane} />
<OrthographicCamera makeDefault zoom={50} position={[0, 40, 200]} />
<OrbitControls minZoom={10} maxZoom={50} enabled={!isDragging} />
</Canvas>
);
}
Obj.jsx
import React, { useState, useRef } from "react";
import { useDrag } from "@use-gesture/react";
import { animated, useSpring } from "@react-spring/three";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
function Obj({ setIsDragging, floorPlane }) {
const [pos, setPos] = useState([0, 1, 0]);
const { size, viewport } = useThree();
const aspect = size.width / viewport.width;
let planeIntersectPoint = new THREE.Vector3();
const dragObjectRef = useRef();
const [spring, api] = useSpring(() => ({
// position: [0, 0, 0],
position: pos,
scale: 1,
rotation: [0, 0, 0],
config: { friction: 10 },
}));
const bind = useDrag(
({ active, movement: [x, y], timeStamp, event }) => {
if (active) {
event.ray.intersectPlane(floorPlane, planeIntersectPoint);
setPos([planeIntersectPoint.x, 1.5, planeIntersectPoint.z]);
}
setIsDragging(active);
api.start({
// position: active ? [x / aspect, -y / aspect, 0] : [0, 0, 0],
position: pos,
scale: active ? 1.2 : 1,
rotation: [y / aspect, x / aspect, 0],
});
return timeStamp;
},
{ delay: true }
);
return (
<animated.mesh {...spring} {...bind()} castShadow>
<dodecahedronBufferGeometry
ref={dragObjectRef}
attach="geometry"
args={[1.4, 0]}
/>
<meshNormalMaterial attach="material" />
</animated.mesh>
);
}
export default Obj;