Loading...
Searching...
No Matches
EnvironmentLoader.hpp
1#pragma once
2#include <halp/controls.hpp>
3#include <halp/meta.hpp>
4
5#include <ossia/dataflow/geometry_port.hpp>
6
7#include <Gfx/Graph/GpuResourceRegistry.hpp>
8
9#include <cstdint>
10#include <memory>
11
12class QRhiResourceUpdateBatch;
13
14namespace score::gfx
15{
16class RenderList;
17struct Edge;
18}
19
20namespace Threedim
21{
22
23// Scene-producing node that defines the environment of a scene:
24// ambient light, exposure, gamma, fog.
25//
26// Pairs with the project-wide scene_spec merge rule: environment is
27// merged field-by-field using the params_set bitmask. This node sets
28// only ambient / exposure-gamma / fog bits — skybox texture and IBL
29// handles are owned by CubemapLoader / CubemapComposer (and a future
30// EnvironmentPrecompute for real IBL) and overlay cleanly via
31// merge_scenes.
32//
33// Downstream pipeline:
34// - `ossia::merge_scenes` overlays this environment onto the merged
35// scene_state — field groups without matching bits pass through
36// from whichever producer set them.
37// - ScenePreprocessor packs scene_environment fields into an Environment
38// Params UBO (auto-bound as aux buffer on Geometry Out).
39// - classic_pbr_ibl shaders read the UBO for ambient / exposure / fog.
41{
42public:
43 halp_meta(name, "Environment")
44 halp_meta(c_name, "environment_loader")
45 halp_meta(category, "Visuals/3D")
46 halp_meta(description, "Set a scene's ambient light, exposure, gamma and fog parameters.")
47 halp_meta(authors, "ossia team")
48 halp_meta(uuid, "d3f5a8c1-8b47-4e91-9c2d-6f1a9b5e3c82")
49
50 enum FogMode
51 {
52 FogLinear,
53 FogExponential,
54 FogExponentialSquared
55 };
56
57 struct ins
58 {
59 // Port-driven rebuild: each control's update() callback fires only
60 // on real change, triggering EnvironmentLoader::rebuild().
61 struct : halp::xyz_spinboxes_f32<"Ambient Color", halp::range{0., 1., 0.03}>
62 { void update(EnvironmentLoader& n) { n.rebuild(); } } ambient_color;
63 struct : halp::hslider_f32<"Ambient Intensity", halp::range{0., 8., 1.}>
64 { void update(EnvironmentLoader& n) { n.rebuild(); } } ambient_intensity;
65
66 // Photographic exposure value at ISO 100. Describes the scene's
67 // expected brightness in photometric terms; downstream shaders
68 // compensate so brighter scenes (higher EV100) display darker
69 // without manual rebalancing. Reference values:
70 // EV100 ≈ -3 moonlit night
71 // EV100 ≈ 3 indoor lighting
72 // EV100 ≈ 12 midday outdoor
73 // EV100 ≈ 16 direct sunlight
74 // EV100 = 0 leaves the linear multiplier at 1× (combined with the
75 // default exposure_stops below it), so a scene that never touches
76 // this control renders unchanged.
77 struct : halp::hslider_f32<"Exposure EV100", halp::range{-6., 18., 0.}>
78 { void update(EnvironmentLoader& n) { n.rebuild(); } } ev100;
79
80 // Fine-tune compensation atop EV100, in stops (±EV). Same role as
81 // a photographer's "exposure compensation" dial: ev100 sets the
82 // photographic anchor, exposure_stops biases above/below.
83 struct : halp::hslider_f32<"Exposure (stops)", halp::range{-8., 8., 0.}>
84 { void update(EnvironmentLoader& n) { n.rebuild(); } } exposure_stops;
85 struct : halp::hslider_f32<"Gamma", halp::range{1., 3., 2.2}>
86 { void update(EnvironmentLoader& n) { n.rebuild(); } } gamma;
87
88 struct : halp::toggle<"Fog">
89 { void update(EnvironmentLoader& n) { n.rebuild(); } } fog_enabled;
90 struct : halp::xyz_spinboxes_f32<"Fog Color", halp::range{0., 1., 0.8}>
91 { void update(EnvironmentLoader& n) { n.rebuild(); } } fog_color;
92 struct : halp::hslider_f32<"Fog Start", halp::range{0., 1000., 10.}>
93 { void update(EnvironmentLoader& n) { n.rebuild(); } } fog_start;
94 struct : halp::hslider_f32<"Fog End", halp::range{0., 10000., 100.}>
95 { void update(EnvironmentLoader& n) { n.rebuild(); } } fog_end;
96
97 // Downstream render-target dimensions (width, height). Stamped on
98 // scene_environment::render_target_size + params_render_target_size
99 // bit when both values > 0. Overrides the preprocessor's default
100 // derivation from the RenderList swap chain.
101 struct : halp::xy_spinboxes_i32<"Render target size", halp::range{0, 16384, 0}>
102 { void update(EnvironmentLoader& n) { n.rebuild(); } } render_target_size;
103
104 // Linear fog ramps from Fog Start to Fog End. The exponential modes
105 // start at Fog Start and use Fog Density per unit of distance:
106 // 1 - exp(-d * density) and 1 - exp(-(d * density)^2).
107 struct : halp::combobox_t<"Fog Mode", FogMode>
108 {
109 struct range
110 {
111 std::string_view values[3]{"Linear", "Exponential", "Exponential squared"};
112 int init{0};
113 };
114 void update(EnvironmentLoader& n) { n.rebuild(); }
115 } fog_mode;
116 struct : halp::hslider_f32<"Fog Density", halp::range{0., 1., 0.01}>
117 { void update(EnvironmentLoader& n) { n.rebuild(); } } fog_density;
118 } inputs;
119
120 struct outs
121 {
122 struct
123 {
124 halp_meta(name, "Scene");
125 ossia::scene_spec scene;
126 uint8_t dirty{0};
127 } scene_out;
128 } outputs;
129
130 // Rebuild m_state from current inputs. Invoked by each port's
131 // update() callback on real control changes. operator()() just
132 // republishes m_state, so the emitted shared_ptr + version stay
133 // stable when nothing changed — keeps every downstream cache hot.
134 void rebuild();
135 void operator()();
136
137 // Render-thread GPU hooks, invoked by CpuFilterNode. init allocates a
138 // slot in the Env arena once; update rebuilds the EnvParamsUBO bytes
139 // and uploads them into the slot (ScenePreprocessor will later pick
140 // these up directly instead of repacking the CPU struct — producer
141 // half only for now); release returns the slot.
142 void init(score::gfx::RenderList& r, QRhiResourceUpdateBatch& res);
143 void update(
144 score::gfx::RenderList& r, QRhiResourceUpdateBatch& res,
146 void release(score::gfx::RenderList& r);
147
148 // Invariant identity for the shared scene_environment struct we emit —
149 // holding one stable scene_state across frames lets downstream
150 // `scene.state.get()` comparisons short-circuit the no-op case. We
151 // mutate the state's environment in place on parameter changes.
152 std::shared_ptr<ossia::scene_state> m_state;
153 int64_t m_version{0};
154 uint8_t m_pending_dirty{ossia::scene_port::dirty_environment};
155
156 // Slot in RenderList::registry().buffer(Env). Allocated in init(),
157 // written in update(), freed in release().
159
160 // Ossia-facing snapshot of env_slot, stamped on scene_state::
161 // environment.raw_slot in operator()() so the preprocessor can
162 // resolve our slot via isLive(). Written once in init() on the
163 // render thread, read every tick in operator()() on the execution
164 // thread (trivially-copyable POD, initialised to zero so pre-init
165 // reads look like an invalid ref).
166 ossia::gpu_slot_ref m_env_ref{};
167};
168
169}
Definition EnvironmentLoader.hpp:41
List of nodes to be rendered to an output.
Definition RenderList.hpp:33
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
Definition EnvironmentLoader.hpp:58
Definition EnvironmentLoader.hpp:121
Definition MIDISync.hpp:128
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:236
Definition GpuResourceRegistry.hpp:84