Lysa  0.0
Lysa 3D Engine
Application class

src/Main.cpp

Application creates and owns all engine objects and wires the event callbacks that drive the main loop.

Application()
: lysa(contextConfiguration),
camera(window),
scene(std::make_unique<RotatingAssetScene>(camera)) {
if (!lysa::ctx().fs.directoryExists("app://shaders")) {
lysa::Log::critical("'shaders' directory not found ...");
return;
}
}

The constructor validates the shader directory early. If app://shaders is missing the compiled shaders are absent and the renderer would fail silently later; a critical log message and early return make the problem immediately visible.

Note
The shaders/ directory is populated by the shaders_copy CMake target (see Project layout).

Render view

auto view = lysa::RenderView(camera.getCamera(), *scene);
window.getRenderTarget().addView(view);

A lysa::RenderView binds a lysa::Camera to a lysa::Scene and associates the pair with a render target. Every render() call on the target draws all views registered with it. A single application can have multiple views (split-screen, picture-in-picture, etc.).

lysa::RenderView also carries optional viewport and scissors fields. When left at their zero-value defaults the renderer fills the entire render target. After a window resize you must update these fields and call updateView:

view.viewport = {}; // reset to full-target size
view.scissors = {};
window.getRenderTarget().updateView(view);

Event order in the main loop

Within a single frame, events are delivered in the following order:

  1. PHYSICS_PROCESS : fired zero or more times per frame to consume accumulated physics time at the fixed timestep. Each call delivers the fixed delta (default 1/60 s).
  2. PROCESS : fired once with the variable frame delta after all physics steps for this frame have been taken.
  3. render() : called explicitly inside the PROCESS handler after all scene mutations are complete.

This ordering is critical: always call render() after onProcess so the renderer sees the final transforms for the current frame, not the transforms from the previous frame.

Event subscriptions

Four events are subscribed in Application::run:

INPUT : forwards lysa::InputEvent objects from the window to the scene. The camera also handles input internally through its own subscription, so both the camera and any scene-level input handling receive the same events.

[&](const lysa::Event& evt) {
const auto& inputEvent = std::any_cast<const lysa::InputEvent&>(evt.payload);
scene->onInput(inputEvent);
});

RESIZED : resets the view's viewport and scissors to the new extent. Passing empty viewport and scissors structs tells the renderer to fill the entire render target. updateView applies the change without re-registering the view.

lysa::RenderTargetEvent::RESIZED, window.getRenderTarget().id,
[&](const lysa::Event& evt) {
view.viewport = {};
view.scissors = {};
window.getRenderTarget().updateView(view);
const auto extent = std::any_cast<const vireo::Extent>(evt.payload);
scene->onResize(extent);
});

PHYSICS_PROCESS : a fixed-timestep tick for physics simulation. Unused in this sample but wired for completeness; any derived scene that needs physics overrides onPhysicsProcess. The delta here is always the configured fixed timestep, not the real frame time.

[&](const lysa::Event& evt) {
const auto delta = std::any_cast<double>(evt.payload);
scene->onPhysicsProcess(delta);
});

PROCESS : the main game-loop tick. The scene's onProcess is called first (rotating the asset and flushing transforms), then render() presents the frame.

[&](const lysa::Event& evt) {
const auto delta = std::any_cast<double>(evt.payload);
scene->onProcess(delta);
window.getRenderTarget().render();
});

Event payloads are delivered via std::any. Use std::any_cast<T> to extract the value.

Main loop

void Application::run() {
// ... view creation and event subscriptions ...
lysa.run();
}

lysa::Lysa::run() enters the engine loop and blocks until lysa::ctx().exit is set to true, which MainWindow does on CLOSING. After run() returns, the Application destructor tears down objects in reverse declaration order: scene, then camera, then window, then lysa. This reverse order ensures that GPU resources owned by the scene and camera are released before the device is destroyed.

Next : What's next?