A composition component that combines TransformControls, Select, and OrbitCamera from Drei into an easy-to-use layout helper.
npm install
npm run dev
npm run build
npm run preview
npm run sandbox
The simplest way to use LayoutControls is to parent your scene and add the autoSelect prop. This will enable layout controls for any mesh found within the children of the component.
import { Box } from "@react-three/drei";
import LayoutControls from "./LayoutControls";
const Scene = () => {
return (
<LayoutControls>
<Box position={[-1.5, 0, 0]}>
<meshNormalMaterial />
</Box>
<Box position={[0, 0, 0]}>
<meshNormalMaterial />
</Box>
<Box position={[1.5, 0, 0]}>
<meshNormalMaterial />
</Box>
</LayoutControls>
);
};Cycle through transform control modes by double-clicking an object or with the t hotkey.
Use your keyboards's copy command to add the last selected transforms prop to your clipboard. By default it will copy the values as React props, but you can change that behavior by passing "props", "arrays", or "vectors" to the 'copyFormat' prop.
If you want full control of what is transformable, pass autoSelect={false} and add a controllable prop to your mesh or group components. There just needs to be at least one mesh geometry in a group for it to be selectable.
import { Box } from "@react-three/drei";
import LayoutControls from "./LayoutControls";
const Scene = () => {
return (
<LayoutControls autoSelect={false}>
<Box position={[-1.5, 0, 0]} controllable>
<meshNormalMaterial />
</Box>
<Box position={[0, 0, 0]}>
<meshNormalMaterial />
</Box>
<Box position={[1.5, 0, 0]} controllable>
<meshNormalMaterial />
</Box>
</LayoutControls>
);
};LayoutControls uses the Select component from Drei, which uses raycasting to return a selection during the onPointerDown event. Since raycasting requires geometry, groups and other objects without geometry won't work. To fix that, just add the controllable prop to your group. This will then allow you to transform your desired object. This works by bubbling up the object tree from where the mesh geometry intersected. This will work with or without the autoSelect prop, and allow you to intentionally transform multiple meshes at once. The component will stop at the first controllable parent it finds. You can change the parent bubbling limit by overriding the layers prop.
import { Box } from "@react-three/drei";
import LayoutControls from "./LayoutControls";
const Scene = () => {
return (
<LayoutControls>
<group controllable>
<Box position={[-1.5, 0, 0]}>
<meshNormalMaterial />
</Box>
<Box position={[0, 0, 0]}>
<meshNormalMaterial />
</Box>
<Box position={[1.5, 0, 0]}>
<meshNormalMaterial />
</Box>
</group>
</LayoutControls>
);
};import LayoutControls from "./LayoutControls";
const Scene = () => {
return (
<LayoutControls
autoSelect={true} // autoSelectmatically interact with all children meshes
copyFormat={"props"} // What format to copy to the clipboard "props" || "arrays" || "vectors"
copyPoints={3} // How many decimal places to fix the transforms to
cycleKey={"t"} // Hot key for cycling transform modes,
enabled={true} // Turn LayoutControls on/off
layers={100} // Parent bubbling limit for the controllable prop
selectedModel={ref || "name"} // Takes an object ref or an object name. If searching by name, make sure it's a child, or set the scene prop.
orbit={true || ref} // Adds an orbit camera to the scene and sets it as the default camera. Pass an object ref to set a look-at target.
snap={0} // Default unit movement
scene={ref} // Pass a scene or object ref to search for object names outside of the children
// include any valid TransformControl props
/>
);
};