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
/>
);
};