C++ objects
Registering C++ objects
Anything the manifest cannot express (updaters, computed geometry, quantities) is defined in C++ and registered under a name, then placed by the manifest with object: name.
auto spot = Mesh::Add("spot.obj");
spot->setUpdater([](TimeObject t){ /* ... */ }); // configure animation
deck.registerObject("wobbly_spot",spot);
registerObject accepts several forms:
registerObject(name, std::function<PrimitiveInSlide()>): factory, primitive + state
registerObject(name, std::function<PrimitivePtr()>): factory, default state
registerObject(name, PrimitivePtr) / registerObject(name, PrimitiveInSlide): already-built
registerObject(name, PrimitiveGroup) (or its factory): several primitives placed together
Factories are called lazily, the first time the manifest uses the name, and the result is cached: a hot reload re-places the same primitive instead of rebuilding it, so meshes, textures and compiled latex survive edits.
With tunable parameters
Registered objects combine naturally with tunable parameters: declare the parameter next to the updater that reads it, and tune the animation live while editing the deck.
deck.registerObject("wobbly_spot", []() {
auto amp = Params::Add("spot/amplitude", 0.2, 0., 0.5);
auto spot = Mesh::Add("spot.obj");
spot->setUpdater([=](TimeObject t){
deform(spot, (scalar)amp, t.inner_time);
});
return spot;
});
Synchronizing with the deck : keyframes
An updater branching on t.relative_frame_number assumes a step structure that the manifest can freely reorder. Instead, mark the relevant frame in the manifest with a keyframe and test it by name: the branch follows the label wherever it moves:
spot->setUpdater([=](TimeObject t){
if (t.afterKeyframe("noise_on"))
addNoise(spot, t.inner_time);
});
afterKeyframe is true from the marked frame on, atKeyframe exactly on it, beforeKeyframe strictly before. An unknown label warns once in the terminal and answers false.