The @use-gpu/scene
package provides a classic 3D scene hierarchy with matrix transforms. Unlike most 3D engines, this is just one use-case of Use.GPU, which can be bypassed entirely, or composed with other parts.
A basic scene looks like:
return (
<Scene>
<Node position={[1, 2, 3]} rotation={[0, 1, 0]}>
<Mesh mesh={mesh} shaded />
</Node>
</Scene>
);
Use <Node>
to apply and nest matrix transforms, which are applied from parent to child. These take the classic position
, rotation
and scale
attributes, though you can also use quaternion
and matrix
for more advanced use.
Use <Mesh>
to render individual triangle meshes, or <Instances>
to render multiple copies of one mesh.
A mesh is a dictionary of any of the attributes that can be given to <FaceLayer>
, i.e. positions
, uvs
, indices
, ... Each is a StorageSource
. You can load these using <Data>
or any other data-driven geometry component.
There are also a handful of pre-fab geometries available:
These return a CPUGeometry
object. This is just a wrapper around attributes along with their schema
. This can be passed to <GeometryData>
to create GPUGeometry
from it.
This is best done via a <Gather>
, so you gather up related resources up-front, e.g. a texture:
const geometry = useOne(() => makeBoxGeometry({ width: 2 }));
return (
<Gather
children={[
<GeometryData geometry={geometry} />,
<ImageTexture url="/textures/test.png" />,
]}
then={([
mesh,
texture,
]) => (
<Scene>
<PBRMaterial albedoMap={texture}>
<Mesh
mesh={mesh}
shaded
/>
</PBRMaterial>
</Scene>
)
/>
);
To render many copies of the same mesh, use <Instances>
. This takes a render
prop that receives a contextual <Instance>
component type:
<Instances
mesh={mesh}
shaded
render={(Instance) => (<>
<Instance position={[1, 2, 3]} />
<Instance position={[3, 4, 5]} />
</>)
/>
Each instance can be transformed directly like a <Node>
, but you can also wrap them in additional nodes if needed.
You can "escape" from the scene model, using a <Primitive>
. This will gather the combined matrix transform and provide it as a TransformContext
to its children. Thus it can be applied to other geometry layers, or combined with non-matrix transforms.
A <Mesh>
is in fact just a convenient wrapper consisting of a <FaceLayer>
inside a <Primitive>
.
Meshes can be wrapped inside material components such as <BasicMaterial>
and <PBRMaterial>
to style them. These take properties for color, emissive, texture, normal map, etc.
These materials use the more fundamental <ShaderFlatMaterial>
and <ShaderLitMaterial>
components, for respectively unlit and lit geometry. They take custom shader functions to make arbitrary materials.