Skip to content

Getting started

The three examples the gallery lists under Getting started. Each block is what the gallery’s code panel shows beside that example, copied from the example itself: the lines that do it rather than the whole file, so it isn’t compiled here the way the guides’ code is. The file it came from is linked under each one.

A lit surface composited by Flutter, laid out like any other widget.

// A scene is a description, handed over whole every frame.
OrblitView(
scene: OrblitScene(
objects: [
OrblitObject(
key: 1,
transform: Matrix4.identity()..rotateY(turn),
colour: Vector3(0.72, 0.13, 0.08), // linear RGB
),
],
lights: [
OrblitLight(
key: 10,
kind: OrblitLightKind.directional,
intensity: 82000, // lux
direction: Vector3(-0.4, -1, -0.55)..normalize(),
),
],
sky: OrblitSky(colour: Vector3(0.01, 0.02, 0.03), ambient: 9000),
camera: camera,
),
)
// The key is what makes saying it again cheap. Same key, same object: the
// renderer moves what moved instead of building the scene a second time.

From surface.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='a scene as a widget' flutter run -d macos

A .oscene document parsed and drawn, in 2D and in 3D, with an edit applied as a diff rather than a rebuild.

// The file, as the editor saves it.
final load = SceneDocument.decode(text);
for (final problem in load.problems) {
print(problem); // what could not be read, and why
}
// Staged for the renderer: entities become objects, lights, sprite layers.
final view = OrblitDocumentView(load.document, projectRoot: root);
// Every frame. The lists are assembled; the objects in them are kept.
OrblitScene scene(OrblitCamera camera, double seconds) => view.scene;
// An edit. Diffed against what was there, so only the entity that moved is
// rebuilt -- the other twenty are the objects the renderer already has.
final next = document.withEntity(id, moved);
view.apply(SceneDiff.between(document, next));

From scene_files.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='scene files' flutter run -d macos

Third person, first person and flat, blended between, framing drawn.

// One real camera, and as many shots as the scene has situations. Cutting to
// a different angle is raising a number — nothing outside the library moves a
// transform, which is what stops two systems fighting over the camera.
final subject = FixedTarget(Vector3.zero());
final chase = VirtualCamera(
name: 'Chase',
priority: 20,
follow: subject,
lookAt: subject,
// Behind and above in the subject's own frame, so it stays behind when the
// subject turns rather than staying north of it.
// Per axis, because the axes want different answers: a camera may
// lag a long way behind and must not float up and down.
body: FollowBody(
offset: Vector3(0, 2.4, 7),
damping: Vector3(0.35, 0.18, 0.5),
),
aim: ComposerAim(
screenY: 0.45,
// Inside this, the camera holds still. A camera that corrects for every
// twitch reads as a nervous operator rather than a steady one.
deadZoneWidth: 0.08,
deadZoneHeight: 0.10,
// Between the two it eases after the subject; past the soft edge it is
// dragged, because by then keeping them in frame matters more.
softZoneWidth: 0.35,
softZoneHeight: 0.30,
damping: 0.4,
),
);
final brain = CameraBrain(
blends: BlendTable(defaultBlend: Blend(BlendStyle.easeInOut, 0.9)),
)..add(chase)..add(watchtower)..add(orbit)..snap();
// Every frame: move the world, then ask what the camera should be doing.
brain.update(delta);
OrblitScene(
camera: OrblitCamera(
position: brain.state.position,
target: brain.state.position + brain.state.forward,
fieldOfView: brain.state.lens.fieldOfView,
),
// ...
);
// And to show the rules rather than guess at them:
final guides = brain.live?.aim.guides; // dead and soft, in fractions

From cameras.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='virtual cameras' flutter run -d macos