Unravel Engine C++ Reference
Loading...
Searching...
No Matches
reflection_probe_component.cpp
Go to the documentation of this file.
3
4namespace unravel
5{
6
8{
9 if(probe_.type == probe_type::sphere)
10 {
11 auto sphere = math::bsphere(math::vec3(0.0f, 0.0f, 0.0f), probe_.sphere_data.range);
12 math::bbox result;
13 result.from_sphere(sphere.position, sphere.radius);
14 return result;
15 }
16 else if(probe_.type == probe_type::box)
17 {
18 math::bbox result;
19 result.min = -probe_.box_data.extents;
20 result.max = probe_.box_data.extents;
21 return result;
22 }
23
24 return {};
25}
26
28 const math::vec3& position,
29 const math::vec3& scale,
30 const math::vec3& view_origin,
31 const math::transform& view,
32 const math::transform& proj) const -> int
33{
34 if(probe_.type == probe_type::sphere)
35 {
37 rect.right,
38 rect.top,
41 probe_.sphere_data.range * math::max(scale.x, math::max(scale.y, scale.z)),
42 view_origin,
43 view,
44 proj);
45 }
46 else if(probe_.type == probe_type::box)
47 {
48 float w2 = math::pow(scale.x * probe_.box_data.extents.x * 2.0f, 2.0f);
49 float h2 = math::pow(scale.y * probe_.box_data.extents.y * 2.0f, 2.0f);
50 float l2 = math::pow(scale.z * probe_.box_data.extents.z * 2.0f, 2.0f);
51 float d2 = w2 + h2 + l2;
52 float d = math::sqrt(d2);
53
55 rect.right,
56 rect.top,
59 d,
60 view_origin,
61 view,
62 proj);
63 }
64 else
65 {
66 return 1;
67 }
68}
69
71{
72 return face_rviews_[idx];
73}
74
76{
77 auto& tex = rview_.tex_get_or_emplace("CUBEMAP");
78 const uint16_t size = probe_resolution_to_size(resolution_);
79 constexpr gfx::texture_format format = gfx::texture_format::RGBA16F;
81 {
82 tex.reset();
83 tex = std::make_shared<gfx::texture>(size,
84 true,
85 1,
86 format,
87 BGFX_TEXTURE_COMPUTE_WRITE |BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_RT);
88 }
89
90 return tex;
91}
92
94{
95 auto& tex = rview_.tex_get_or_emplace("CUBEMAP_PREFILTERED");
96 const uint16_t size = probe_resolution_to_size(resolution_);
97 constexpr gfx::texture_format format = gfx::texture_format::RGBA16F;
99 {
100 tex.reset();
101 tex = std::make_shared<gfx::texture>(size,
102 true,
103 1,
104 format,
105 BGFX_TEXTURE_COMPUTE_WRITE | BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_RT);
106 }
107
108 return tex;
109}
110
112{
113 auto& fbo = face_rviews_[face].fbo_get_or_emplace("CUBEMAP");
114 auto& tex = face_rviews_[face].tex_get_or_emplace("CUBEMAP_FACE");
115 const uint16_t size = probe_resolution_to_size(resolution_);
116 constexpr gfx::texture_format format = gfx::texture_format::RGBA16F;
117 const bool recreate_texture = gfx::needs_recreate(tex, {size, size}, format);
118 if(recreate_texture)
119 {
120 tex.reset();
121 tex = std::make_shared<gfx::texture>(size,
122 size,
123 true,
124 1,
125 format,
126 BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_RT | BGFX_TEXTURE_COMPUTE_WRITE);
127
129 att.layer = 0;
130 att.texture = tex;
131 att.generate_mips = true;
132
133 fbo.reset();
134 fbo = std::make_shared<gfx::frame_buffer>();
135 fbo->populate({att});
136 }
137
138
139 return fbo;
140}
141
143{
144 // Dormant path: no active bake. Drive the realtime refresh timer and release transient resources.
145 if(!has_pending_bake_)
146 {
147 if(update_mode_ == probe_update_mode::realtime)
148 {
149 time_since_last_refresh_ += dt;
150 if(time_since_last_refresh_ >= update_interval_)
151 {
152 // Realtime refreshes start a new bake; keep time-slicing (first_generation_ stays false).
153 has_pending_bake_ = true;
154 time_since_last_refresh_ = 0.0f;
155 }
156
157 }
158 return;
159 }
160
161 // Active bake path: check whether all six faces have been emitted this cycle.
162 bool fully_generated = true;
163 for(auto& frame : generated_frame_)
164 {
165 fully_generated &= frame != uint64_t(-1);
166 }
167
168 if(fully_generated)
169 {
170 // Bake cycle finished. Reset per-frame tracking and go dormant; on_demand/once probes stay
171 // quiet until someone calls mark_dirty() again, realtime probes will reschedule themselves.
172 for(auto& frame : generated_frame_)
173 {
174 frame = uint64_t(-1);
175 }
176 first_generation_ = false;
177 has_pending_bake_ = false;
178 time_since_last_refresh_ = 0.0f;
179 }
180
181 generated_faces_count_ = 0;
182}
183
185{
186 face_rviews_ = {};
187 rview_ = {};
188}
189
191{
192 return probe_;
193}
194
196{
197 if(probe == probe_)
198 {
199 return;
200 }
201
202 touch();
203
204 probe_ = probe;
205
206 // Edits to the probe data trigger an automatic rebuild, except for "on_demand" probes which
207 // are fully manual: the owner must call mark_dirty() explicitly (e.g. from script or from the
208 // editor Bake button). Switching INTO on_demand leaves the existing bake alone.
209 if(update_mode_ != probe_update_mode::on_demand)
210 {
211 mark_dirty();
212 }
213}
214
216{
217 if(update_mode_ == mode)
218 {
219 return;
220 }
221
222 touch();
223
224 const auto previous = update_mode_;
225 update_mode_ = mode;
226 time_since_last_refresh_ = 0.0f;
227
228 // Transitioning out of on_demand means the probe should start honoring its schedule immediately,
229 // so kick off a bake. Transitioning into on_demand stops future automatic bakes but leaves the
230 // current cubemap intact.
232 {
233 mark_dirty();
234 }
235}
236
238{
239 // Short-circuit: probes with no outstanding bake are entirely "already generated" as far as
240 // the pipeline is concerned - they do not need any per-face rendering this frame.
241 if(!has_pending_bake_)
242 {
243 return true;
244 }
245
246 bool generated = true;
247 for(size_t i = 0; i < generated_frame_.size(); ++i)
248 {
249 generated &= already_generated(i);
250 }
251 return generated;
252}
253
255{
256 if(!has_pending_bake_)
257 {
258 return true;
259 }
260
261 // Time-slice: once we've emitted the per-frame budget, treat remaining faces as done-for-now.
262 // first_generation_ waives the budget so the initial bake produces a usable result in one frame.
263 if(!first_generation_)
264 {
265 if(faces_per_frame_ > 0 && generated_faces_count_ >= faces_per_frame_)
266 {
267 return true;
268 }
269 }
270
271 return generated_frame_[face] != uint64_t(-1);
272}
274{
275 generated_frame_[face] = frame;
276 generated_faces_count_++;
277}
278
279void reflection_probe_component::mark_dirty(bool force_full_first_frame)
280{
281 // Reset per-face tracking so the pipeline starts a new bake cycle.
282 for(auto& frame : generated_frame_)
283 {
284 frame = uint64_t(-1);
285 }
286 generated_faces_count_ = 0;
287 has_pending_bake_ = true;
288 time_since_last_refresh_ = 0.0f;
289
290 if(force_full_first_frame)
291 {
292 // first_generation_ semantics: when true, no per-frame face budget is enforced,
293 // so the pipeline pushes all six faces in a single frame. Good for manual "Bake now".
294 first_generation_ = true;
295 }
296}
297
299{
300 return has_pending_bake_;
301}
302
304{
305 if(!has_pending_bake_)
306 {
307 return false;
308 }
309
310 for(const auto frame : generated_frame_)
311 {
312 if(frame != uint64_t(-1))
313 {
314 return false;
315 }
316 }
317
318 return true;
319}
320
322{
323 if(!has_pending_bake_)
324 {
325 return true;
326 }
327
328 for(const auto frame : generated_frame_)
329 {
330 if(frame == uint64_t(-1))
331 {
332 return false;
333 }
334 }
335
336 return true;
337}
338
340{
341 if(resolution_ == resolution)
342 {
343 return;
344 }
345
346 touch();
347 resolution_ = resolution;
349
350 if(update_mode_ != probe_update_mode::on_demand)
351 {
352 mark_dirty();
353 }
354}
355
357{
358 if(capture_sky_ == capture)
359 {
360 return;
361 }
362
363 touch();
364 capture_sky_ = capture;
365
366 if(update_mode_ != probe_update_mode::on_demand)
367 {
368 mark_dirty();
369 }
370}
371
373{
374 if(capture_shadows_ == capture)
375 {
376 return;
377 }
378
379 touch();
380 capture_shadows_ = capture;
381
382 if(update_mode_ != probe_update_mode::on_demand)
383 {
384 mark_dirty();
385 }
386}
387} // namespace unravel
gfx::texture_format format
Provides storage for common representation of spherical bounding volume, and wraps up common function...
Definition bsphere.h:18
General purpose transformation class designed to maintain each component of the transformation separa...
Definition transform.hpp:27
auto is_dirty() const -> bool
Returns true if the probe has pending or in-flight cubemap generation work.
auto compute_projected_sphere_rect(irect32_t &rect, const math::vec3 &position, const math::vec3 &scale, const math::vec3 &view_origin, const math::transform &view, const math::transform &proj) const -> int
Computes the projected sphere rectangle.
void mark_dirty(bool force_full_first_frame=false)
Requests the probe to rebuild its cubemap.
void set_capture_sky(bool capture)
Sets whether the atmospheric sky pass runs during cubemap capture. Disable for interior/local probes ...
auto get_cubemap_fbo(size_t face) -> const gfx::frame_buffer::ptr &
Gets the cubemap frame buffer object (FBO).
auto get_bounds() const -> math::bbox
Gets the bounding box of the probe object.
auto is_bake_complete() const -> bool
Returns true when all six cubemap faces have been rendered in the current bake cycle.
auto get_probe() const -> const reflection_probe &
Gets the reflection probe object.
void set_resolution(probe_resolution resolution)
Sets the cubemap face resolution. Triggers a rebuild when changed.
auto get_cubemap_prefiltered() -> const gfx::texture::ptr &
Gets the cubemap texture.
void set_capture_shadows(bool capture)
Sets whether shadow maps are built and sampled during cubemap capture.
void update(float dt)
Advances the probe's update bookkeeping. Called once per frame by reflection_probe_system.
auto is_bake_cycle_unstarted() const -> bool
Returns true when a bake has been requested but no cubemap face has been emitted yet.
void set_generation_frame(size_t face, uint64_t frame)
marks the genrerated face this frame
void set_update_mode(probe_update_mode mode)
Sets the probe's update policy. Switching INTO on_demand leaves the current bake state as-is; switchi...
auto get_render_view(size_t idx) -> gfx::render_view &
Gets the render view.
auto already_generated() const -> bool
Check if the cubemap was generated this frame.
auto get_cubemap() -> const gfx::texture::ptr &
void set_probe(const reflection_probe &probe)
Sets the reflection probe object.
math::vec3 position
Definition defaults.cpp:52
uint16_t view
uint32_t frame
Definition graphics.cpp:23
bgfx::TextureFormat::Enum texture_format
Definition format.h:10
auto needs_recreate(const gfx::frame_buffer::ptr &fbo, const usize32_t &size) -> bool
Definition bbox.cpp:5
std::uint32_t compute_projected_sphere_rect(std::int32_t &left, std::int32_t &right, std::int32_t &top, std::int32_t &bottom, const glm::vec3 &sphere_center, float radius, const glm::vec3 &view_origin, const glm::mat4 &view, const glm::mat4 &proj)
Definition math.h:227
probe_update_mode
Enum class describing when a reflection probe refreshes its cubemap.
probe_resolution
Fixed cubemap face resolutions supported by reflection probes.
auto probe_resolution_to_size(probe_resolution resolution) -> std::uint16_t
Converts a probe_resolution enum value to its pixel size.
@ sphere
Sphere type reflection probe.
@ box
Box type reflection probe.
std::vector< float > scale
std::shared_ptr< gfx::texture > texture
Texture handle.
std::uint16_t layer
Cubemap side or depth layer/slice.
Storage for box vector values and wraps up common functionality.
Definition bbox.h:21
vec3 max
The maximum vector value of the bounding box.
Definition bbox.h:311
bbox & from_sphere(const vec3 &center, float radius)
Calculates the bounding box based on the sphere specified.
Definition bbox.cpp:213
vec3 min
The minimum vector value of the bounding box.
Definition bbox.h:306
void touch()
Marks the component as 'touched'.
math::vec3 extents
Extents of the box projection.
float range
Range of the sphere projection.
Structure representing a reflection probe.
probe_type type
Type of the reflection probe.
box box_data
Data describing box projection.
sphere sphere_data
Data describing sphere projection.
float size