Placing plumes
Position, rotation, direction, targets, and moving a nozzle from code.
The one rule
A plume leaves its nozzle's origin along the nozzle's local +Z. That is aft of a model that flies along -Z with +Y up, the convention the other packages share. Every way of placing one just decides where that origin is and which way +Z points.
In JSX
<Afterburner> takes every <group> prop. position is the exit plane's
centre. Turn it with rotation, or point it with direction:
// On a model built facing +X: exit 7 m behind its origin, exhaust towards −X
<Afterburner position={[-7, 0, 0]} direction={[-1, 0, 0]} />
// The same, as a rotation: a quarter turn about Y takes +Z to −X
<Afterburner position={[-7, 0, 0]} rotation={[0, -Math.PI / 2, 0]} />direction is any vector in the parent's frame, normalised for you. When both
are given, it wins over rotation.
The group's scale scales the plume, look and all. Usually you want
params={{ nozzleRadiusM }} instead: it rescales every length of the jet,
the way a bigger engine would.
On a node you don't own
For a node of a loaded model, or anything else outside your JSX, pass it as
target. The plume is portalled into it, and position, rotation and
direction are then in its frame:
const { nodes } = useGLTF("/fighter.glb");
<Afterburner
target={nodes.FX_Exhaust_L}
position={[0, 0, 0.08]}
direction={[0, 0, -1]}
/>;target takes any Object3D: a mesh, a group, a bone. If it comes from a ref,
keep it in state so the plume re-renders once it exists. See
the tutorial.
Moving it from code
Through the handle, without a render:
// Move and aim it, in the frame it sits in
engine.current?.setOffset({
position: [0, 0.2, -4.2],
direction: [0, -0.1, -1],
});
// Just aim it
engine.current?.setDirection([0, -0.1, -1]);
// Or write the group directly: it is a plain three.js Group
engine.current?.object?.position.set(0, 0.2, -4.2);setOffset takes an AfterburnerOffset:
| field | type |
|---|---|
position | [x, y, z] or any { x, y, z } |
rotation | an Euler, a Quaternion, [x, y, z] or [x, y, z, w] |
direction | [x, y, z] or any { x, y, z }; wins over rotation |
Fields you leave out are kept.
Without React
A core nozzle follows an object's world matrix, at an optional offset in its
frame. attach switches which object it follows:
const tail = batch.add({
object: engine_node,
offset: { position: [0, 0, -4.2], direction: [0, 0, -1] },
});
tail.setOffset({ position: [0, 0.1, -4.2] });
tail.attach(other_engine); // follow something else
tail.attach(null); // stop following, and hold where it is nowDetaching folds the last world pose into the nozzle's matrix, so the plume
doesn't jump. More in Without React.