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 aWorldholds, each addable withadd_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.
- Environment
MapHandle - Index into the runtime environment-map table.
- Material
Handle - Index into the runtime material table.
- Mesh
Handle - 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§
- Baked
Mesh - A mesh value a world takes a baked geometry payload for, through
World::add_mesh.