Skip to main content

Crate concinnity

Crate concinnity 

Source
Expand description

Concinnity is a graphics application framework. A World holds the components describing what exists – a camera, lights, geometry, text – and an App runs that world on the engine’s loop. Behaviour is declared as data rather than assembled from calls, so an application’s job is to hand over a world and let the runtime drive it.

§Running a compiled world

A shipped application usually plays a world that was compiled ahead of time. That needs nothing but the runtime, which is the crate’s default build:

use concinnity::App;

fn main() {
    App::from_blob("my_game.cnb")
        .expect("my_game.cnb holds a compiled world")
        .run()
        .expect("the app runs");
}

Worlds are authored and compiled by the cook module, which is where to go next: it declares assets as typed values, resolves the references between them, and either compiles a World in memory or writes one out for a build like the above to play. It sits behind the cook feature, off by default. Start at cook.

§Assembling a world in code

Compiling is not always needed. Every type in the components vocabulary can be handed straight to World::add_component, so an application built only from those runs on the default feature set alone – no authoring step and no file on disk.

What is not a component but can be computed – generated geometry, image-based lighting, the built-in font – is baked by the bake module’s functions and handed to the world’s data-entry methods (World::add_mesh, World::add_material, World::add_environment_map), which return the handle a component references the result by. Every build of the crate carries bake; only an asset that has to be read from a file needs the cook importers.

Adding a GraphicsConfig is what opens a window. A world without one still runs, with everything but the rendering, which is how a test or a simulation-only tool drives one:

App::into_headless is the other way to that: it runs any world on the headless loop, including one authored to be seen.

use concinnity::components::{PhysicsConfig, TriggerVolume};
use concinnity::{App, World};

fn main() {
    let mut world = World::new();
    world.add_component(PhysicsConfig::default());
    world.add_component(TriggerVolume {
        position: [0.0, 1.0, 0.0],
        ..Default::default()
    });

    App::from_world(world).run().expect("the app runs");
}

§Composing vs Cooking

Add components directly when a world is small, or when it is decided at runtime and there is nothing to prepare in advance. Reach for the cook module when an asset needs work before it can run: an image or model to read from disk, a room to generate geometry for, a prefab to expand into the components it stands for. Both end at the same place, an App holding a World, and one application can use both.

§Features

The default build is the runtime alone: the world loop, the renderer, and the components vocabulary, which is all the examples above need.

--features cook adds the cook module described above. It carries the authoring half of the vocabulary (textures, meshes, prefabs, menus) and pulls in the importers that read them (glTF, FBX, images, fonts), so an application that only plays an already-compiled world should leave it off.

--features metal, --features directx and --features vulkan each name one rendering backend. A backend the target does not have is inert, so a build can name more than one and get the one that applies; where two do, Vulkan wins. native names all three and is what the default build carries: Metal on macOS, DirectX on Windows, Vulkan elsewhere.

--no-default-features --features std keeps the operating system and drops the renderer: no GPU code is in the graph at all, and every world runs on the headless loop, which is what a simulation-only tool or a build host with no GPU wants.

--features player builds concinnity-run, the standalone binary that plays a compiled world, and --features editor builds both it and the concinnity command-line tool. Both are for working on an application rather than for linking one: nothing in the library’s own API depends on either, and with neither enabled this crate builds no build dependency at all.

--no-default-features leaves the core no_std runtime: the component vocabulary, a World to hold it, and the headless loop that steps it, with no operating system underneath. It is the same loop App::into_headless selects on any tier, so what a test runs is what a no_std build runs. App::run reports the same Error everywhere, so what runs a world ports between tiers unchanged.

Modules§

bake
Bake payloads from typed values, entirely in memory.
components
The runtime component vocabulary (Camera3D, Room, DirectionalLight, Transform, …): every type a World holds, each addable with add_component.
cook
Compile authored worlds into runnable Worlds, entirely in memory.

Structs§

App
A runnable application.
AssetId
A dense integer handle for one asset, assigned at build time in world declaration order. Equality and hashing are integer ops.
EnvironmentMapHandle
Index into the runtime environment-map table.
MaterialHandle
Index into the runtime material table.
MeshHandle
Index into the runtime mesh table.
World
A world: the components an application is built from.

Enums§

CnResult
The engine’s flat result code, returned across the API and FFI seams.
Error
Why an application could not load its world, or could not run it.

Traits§

BakedMesh
A mesh value a world takes a baked geometry payload for, through World::add_mesh.