Program Listing for File GUISceneSharedVars.ixx

Program Listing for File GUISceneSharedVars.ixx#

Return to documentation for file (Src/GraphicsEngineVulkan/scene/GUISceneSharedVars.ixx)

module;

#include <cstddef>
#include <cstdint>
#include <iterator>

export module kataglyphis.vulkan.gui_scene_shared_vars;

// The one table of shadow-map resolutions; the combo's labels
// (GUISceneSharedVars::available_shadow_map_resolutions) and every consumer
// of shadow_map_res_index must derive from this array instead of re-typing
// their own copy, which is what let the GUI's label and the renderer's
// startup allocation drift apart (GUI claimed 4096, renderer built 2048).
export constexpr uint32_t kShadowMapResolutions[] = { 512, 1024, 2048, 4096 };

export inline constexpr int kShadowMapResolutionCount = static_cast<int>(std::size(kShadowMapResolutions));

// Range for the "Shadow distance" slider (GUI.cpp). One owner for the bounds,
// the same way kShadowMapResolutions is the one owner of the resolution combo.
export inline constexpr float kMinShadowDistance = 5.0F;
export inline constexpr float kMaxShadowDistance = 500.0F;

// Clamps out-of-range indices into [0, kShadowMapResolutionCount - 1] rather
// than silently falling through to 512 the way the old if/else chain did.
//
// In `Kataglyphis`, where the rest of the engine's API lives, and NOT at global
// scope - this was the only exported free function in the codebase that sat
// outside a namespace, and that is not merely a style point. Doxygen 1.15
// indexes an exported entity under both its .ixx file compound and its C++20
// module compound; it deduplicates namespace members, but for a global-scope
// free function Breathe is left with two IDENTICAL candidates and refuses to
// resolve either ("Unable to resolve function shadowResolutionForIndex with
// arguments (int) - Potential matches:" listing the same signature twice).
// Under Sphinx's -W that failed the entire docs build. Measured 2026-08-06,
// same tree, one variable: global scope 1 warning, namespaced 0.
//
// Callers inside Kataglyphis:: (VulkanRenderer's members) need no change -
// unqualified lookup finds it in the enclosing namespace.
export namespace Kataglyphis {

constexpr uint32_t shadowResolutionForIndex(int index)
{
    if (index < 0) index = 0;
    if (index >= kShadowMapResolutionCount) index = kShadowMapResolutionCount - 1;
    return kShadowMapResolutions[index];
}

}// namespace Kataglyphis

export struct GUISceneSharedVars
{
    float directional_light_radiance = 10.f;
    float directional_light_color[3] = { 1.f, 1.f, 1.f };
    // Angled rather than near-vertical: a straight-down sun drops every shadow
    // directly underneath its caster, where it is barely visible. This throws
    // the dinosaurs' shadows sideways across their ground plane, which is what
    // makes the cascaded shadow maps read at a glance.
    float directional_light_direction[3] = { -0.55f, -1.f, -0.35f };

    // Shadows
    // Index 2 ("2048") matches the pixel count VulkanRenderer::init actually
    // allocates at startup (via shadowResolutionForIndex below) without
    // needing a measurement of the 4x memory jump index 3 would cost.
    int shadow_map_res_index = 2;
    bool shadow_resolution_changed = false;
    int num_shadow_cascades = 3; // must stay <= MAX_CASCADES (SceneUBO array size)
    int pcf_radius = 2;
    float cascaded_shadow_intensity = 0.65f;
    // How far shadows are fitted, independent of the camera far plane. The
    // camera sees 150 units in debug but the scene ends at ~36, and fitting
    // cascades to the far plane spread the shadow map over empty space
    // (measured 3.80 cm/texel over the subject, vs 3.04 at this distance).
    // Geometry past this is unshadowed, which is the intended trade.
    float shadow_distance = 60.f;
    // Blend between logarithmic (1) and uniform (0) cascade splits. Defaults
    // OFF because measurement did not support turning it on for the scenes we
    // ship: the win here came entirely from shadow_distance above. Raise it
    // only for a camera that sits close to its subject, where it does help
    // (1.52 -> 1.01 cm/texel at 0.35). See the table in CascadedShadowMap.cpp.
    float cascade_split_lambda = 0.f;
    // Labels of kShadowMapResolutions, in the same order; index i's label
    // must always read as the pixel count shadowResolutionForIndex(i)
    // returns (pinned by ShadowResolutionUnit.EveryComboLabelMatchesThePixelCount).
    const char* available_shadow_map_resolutions[kShadowMapResolutionCount] = { "512", "1024", "2048", "4096" };
    static_assert(sizeof(available_shadow_map_resolutions) / sizeof(const char*) ==
                    static_cast<std::size_t>(kShadowMapResolutionCount),
                  "available_shadow_map_resolutions must have one label per kShadowMapResolutions entry");

    // Clouds
    int cloud_num_march_steps = 8;
    int cloud_num_march_steps_to_light = 3;
    float cloud_density_multiplier = 0.63f;
    float cloud_coverage_threshold = 0.493f;
    float cloud_pillowness = 0.966f;
    float cloud_cirrus_effect = 0.034f;
    bool cloud_powder_effect = true;
    bool clouds_enabled = false;
    float cloud_mesh_scale[3] = { 1000.f, 5.f, 1000.f };
    float cloud_mesh_offset[3] = { -0.364f, 367.f, -18.351f };

    // Shadows
    bool shadows_enabled = true;

    // Skybox
    bool skybox_enabled = true;

    // Camera
    // 45 mirrors Camera's own default (Camera.cpp's NDEBUG and debug
    // initialisers both use 45.F) - the two must not drift apart. Scope:
    // FOV only. near_plane/far_plane stay unexposed here because their
    // Camera defaults are configuration-dependent (150 debug / 4000
    // release), so a single GUI default would contradict one of them;
    // that is a separate decision, not this one.
    float camera_fov = 45.0F;

    // Model selection
    int selected_model_index = -1;
    bool model_reload_requested = false;
    bool model_transform_changed = false;
    float model_position[3] = { 0.f, 0.f, 0.f };
    float model_rotation[3] = { 0.f, 0.f, 0.f };
};