Unravel Engine C++ Reference
Loading...
Searching...
No Matches
gizmos_renderer.cpp
Go to the documentation of this file.
1#include "gizmos_renderer.h"
2#include <editor/events.h>
3
4#include <array>
5
7
8#include <engine/events.h>
11
13
14#include "gizmos/gizmos.h"
15
17
28
29namespace unravel
30{
31
32void gizmos_renderer::draw_grid(uint32_t pass_id, const camera& cam, const editing_manager::grid& grid)
33{
34 grid_program_->begin();
35
36 float grid_height = 0.0f;
37 math::vec4 u_params(grid_height, cam.get_near_clip(), cam.get_far_clip(), grid.opacity);
38 grid_program_->set_uniform("u_params", u_params);
39
40 auto topology = gfx::clip_quad(1.0f);
41 auto state = topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_ALPHA;
42
43 if(grid.depth_aware)
44 {
45 state |= BGFX_STATE_DEPTH_TEST_LEQUAL | BGFX_STATE_WRITE_Z;
46 }
47
48 gfx::set_state(state);
49 gfx::submit(pass_id, grid_program_->native_handle());
50 gfx::set_state(BGFX_STATE_DEFAULT);
51
52 grid_program_->end();
53}
54
55void gizmos_renderer::on_frame_render(rtti::context& ctx, scene& scn, entt::handle camera_entity, dd_2d_raii& dd_2d)
56{
57 if(!camera_entity)
58 return;
59
60 auto& em = ctx.get_cached<editing_manager>();
61 auto& camera_comp = camera_entity.get<camera_component>();
62 const auto& rview = camera_comp.get_render_view();
63 const auto& camera = camera_comp.get_camera();
64 const auto& view = camera.get_view();
65 const auto& proj = camera.get_projection();
66 const auto& obuffer = rview.fbo_get("OBUFFER_DEPTH");
67 auto size = obuffer->get_size();
68
69 bool selection_mask_drawn = false;
70
71 if(em.gizmos.show_selection_outline)
72 {
73 // Pass 1: Selection mask
74 resize_selection_mask_rt(size.width, size.height);
75 selection_mask_drawn = draw_selection_mask_pass(ctx, camera, selection_mask_);
76 }
77
78 {
79 // Pass 2: Gizmos
80 gfx::render_pass pass("Gizmos/Pass");
81 pass.bind(obuffer.get());
82 pass.set_view_proj(view, proj);
83
84 gfx::dd_raii dd(pass.id);
85
87
88 draw_selection_gizmos(ctx, camera, dd, dd_2d);
89
90 if(selection_mask_drawn)
91 {
92 draw_outline_pass(selection_mask_, obuffer, dd);
93 }
94
95 draw_icon_gizmos(ctx, scn, camera, dd);
96
97 if(em.show_grid)
98 {
99 draw_grid(pass.id, camera, em.grid_data);
100 }
101 }
102
103 if(em.gizmos.show_selection_wireframe)
104 {
105 // Pass 3: Vertex-pulling wireframe overlay for selected entities.
106 // Runs after gizmos so it renders on top of them.
107 draw_selection_wireframe_pass(ctx, camera, obuffer);
108 }
109
110}
111
113{
114 auto& am = ctx.get_cached<asset_manager>();
115
116 {
117 auto vs = am.get_asset<gfx::shader>("editor:/data/shaders/vs_wf_wireframe.sc");
118 auto fs = am.get_asset<gfx::shader>("editor:/data/shaders/fs_wf_wireframe.sc");
119 wireframe_program_.program = std::make_unique<gpu_program>(vs, fs);
120 wireframe_program_.cache_uniforms();
121 }
122
123 {
124 auto vs = am.get_asset<gfx::shader>("editor:/data/shaders/vs_wf_wireframe_skinned.sc");
125 auto fs = am.get_asset<gfx::shader>("editor:/data/shaders/fs_wf_wireframe.sc");
126 wireframe_program_skinned_.program = std::make_unique<gpu_program>(vs, fs);
127 wireframe_program_skinned_.cache_uniforms();
128 }
129
130 {
131 auto vs = am.get_asset<gfx::shader>("editor:/data/shaders/vs_grid.sc");
132 auto fs = am.get_asset<gfx::shader>("editor:/data/shaders/fs_grid.sc");
133 grid_program_ = std::make_unique<gpu_program>(vs, fs);
134 }
135
136 {
137 auto vs = am.get_asset<gfx::shader>("editor:/data/shaders/vs_outline_mask.sc");
138 auto fs = am.get_asset<gfx::shader>("editor:/data/shaders/fs_outline_mask.sc");
139 outline_mask_program_.program = std::make_unique<gpu_program>(vs, fs);
140 outline_mask_program_.cache_uniforms();
141 }
142
143 {
144 auto vs = am.get_asset<gfx::shader>("editor:/data/shaders/vs_outline_mask_skinned.sc");
145 auto fs = am.get_asset<gfx::shader>("editor:/data/shaders/fs_outline_mask.sc");
146 outline_mask_program_skinned_.program = std::make_unique<gpu_program>(vs, fs);
147 outline_mask_program_skinned_.cache_uniforms();
148 }
149
150 {
151 auto vs = am.get_asset<gfx::shader>("editor:/data/shaders/vs_clip_quad.sc");
152 auto fs = am.get_asset<gfx::shader>("editor:/data/shaders/fs_outline_detect.sc");
153 outline_program_.program = std::make_unique<gpu_program>(vs, fs);
154 outline_program_.cache_uniforms();
155 }
156
157 return true;
158}
159
160void gizmos_renderer::draw_selection_gizmos(rtti::context& ctx, const camera& camera, gfx::dd_raii& dd, dd_2d_raii& dd_2d)
161{
162 auto& em = ctx.get_cached<editing_manager>();
163
164 for(auto& s : em.get_selections())
165 {
166 draw_gizmo_var(ctx, s, camera, dd, dd_2d);
167 }
168}
169
170auto gizmos_renderer::draw_selection_mask_pass(rtti::context& ctx,
171 const camera& camera,
172 const gfx::frame_buffer::ptr& selection_mask) -> bool
173{
174 auto& em = ctx.get_cached<editing_manager>();
175 const auto& view = camera.get_view();
176 const auto& proj = camera.get_projection();
177
178 gfx::render_pass pass("Gizmos/Selection Mask Pass");
179 pass.bind(selection_mask.get());
180 pass.set_view_proj(view, proj);
181
182 gfx::set_view_clear(pass.id,
183 BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH,
184 0x00000000, // clear R8 to zero
185 1.0f,
186 0);
187
188 bool any_drawn = false;
189 for(auto& obj : em.get_selections())
190 {
191 if(obj.type() == entt::resolve<entt::handle>())
192 {
193 auto e = obj.cast<entt::handle>();
194 if(!e.valid())
195 {
196 continue;
197 }
198 auto transform_comp = e.try_get<transform_component>();
199
200 if(!transform_comp)
201 {
202 continue;
203 }
204 const auto& world_transform = transform_comp->get_transform_global();
205
206 if(auto model_comp = e.try_get<model_component>())
207 {
208 auto& model = model_comp->get_model();
209 if(!model.is_valid())
210 {
211 continue;
212 }
213
214 auto& current_lod_data = model_comp->get_lod_data_for_camera(&camera, gfx::get_render_frame());
215
216 auto lod = model.get_lod(current_lod_data.current_lod_index);
217 if(!lod)
218 {
219 continue;
220 }
221
222 // Test against the pose-aware world AABB (tracks node/bone animation),
223 // never the bind-pose mesh bounds.
224 if(!camera.get_frustum().test_aabb(model_comp->get_world_bounds()))
225 {
226 continue;
227 }
228
229 const auto& submesh_transforms = model_comp->get_submesh_transforms();
230 const auto& bone_transforms = model_comp->get_bone_transforms();
231 const auto& skinning_transforms = model_comp->get_skinning_transforms();
232
233 model::submit_callbacks callbacks;
234 callbacks.setup_begin = [&](const model::submit_callbacks::params& submit_params)
235 {
236 auto& prog =
237 submit_params.skinned ? outline_mask_program_skinned_.program : outline_mask_program_.program;
238 prog->begin();
239 };
240 callbacks.setup_params_per_instance = [&](const model::submit_callbacks::params& submit_params)
241 {
242 auto& prog =
243 submit_params.skinned ? outline_mask_program_skinned_.program : outline_mask_program_.program;
244 };
245 callbacks.setup_params_per_submesh =
246 [&](const model::submit_callbacks::params& submit_params, const material& mat)
247 {
248 auto& prog =
249 submit_params.skinned ? outline_mask_program_skinned_.program : outline_mask_program_.program;
250 gfx::submit(pass.id, prog->native_handle(), 0, submit_params.preserve_state);
251 };
252 callbacks.setup_end = [&](const model::submit_callbacks::params& submit_params)
253 {
254 auto& prog =
255 submit_params.skinned ? outline_mask_program_skinned_.program : outline_mask_program_.program;
256 prog->end();
257 };
258
259 model.submit(world_transform, submesh_transforms, bone_transforms, skinning_transforms, current_lod_data.current_lod_index, callbacks);
260
261 any_drawn = true;
262 }
263 }
264 }
265
266 return any_drawn;
267}
268
269void gizmos_renderer::draw_selection_wireframe_pass(rtti::context& ctx,
270 const camera& camera,
271 const gfx::frame_buffer::ptr& obuffer)
272{
273 const bool non_skinned_ready = wireframe_program_.program && wireframe_program_.program->is_valid();
274 const bool skinned_ready = wireframe_program_skinned_.program && wireframe_program_skinned_.program->is_valid();
275 if(!non_skinned_ready && !skinned_ready)
276 {
277 return;
278 }
279
280 auto& em = ctx.get_cached<editing_manager>();
281 const auto& view = camera.get_view();
282 const auto& proj = camera.get_projection();
283
284 gfx::render_pass pass("Gizmos/Selection Wireframe Pass");
285 pass.bind(obuffer.get());
286 pass.set_view_proj(view, proj);
287
288 // Overlay renders on top of the color buffer using alpha blending. Depth
289 // test is enabled so parts of the mesh occluded by the scene are hidden.
290 const uint64_t state = 0
291 | BGFX_STATE_WRITE_RGB
292 | BGFX_STATE_WRITE_A
293 | BGFX_STATE_DEPTH_TEST_LEQUAL
294 | BGFX_STATE_CULL_CCW
295 | BGFX_STATE_MSAA
296 | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA);
297
298 for(const auto& obj : em.get_selections())
299 {
300 if(obj.type() != entt::resolve<entt::handle>())
301 {
302 continue;
303 }
304
305 auto e = obj.cast<entt::handle>();
306 if(!e.valid())
307 {
308 continue;
309 }
310
311 auto* transform_comp = e.try_get<transform_component>();
312 auto* model_comp = e.try_get<model_component>();
313 if(!transform_comp || !model_comp)
314 {
315 continue;
316 }
317
318 auto& mdl = model_comp->get_model();
319 if(!mdl.is_valid())
320 {
321 continue;
322 }
323
324 auto& lod_data = model_comp->get_lod_data_for_camera(&camera, gfx::get_render_frame());
325 auto lod = mdl.get_lod(lod_data.current_lod_index);
326 if(!lod)
327 {
328 continue;
329 }
330
331 const auto& mesh_ptr = lod.get();
332 if(!mesh_ptr)
333 {
334 continue;
335 }
336
337 const auto& world_transform = transform_comp->get_transform_global();
338 // Pose-aware world AABB - bind-pose mesh bounds don't track node/bone animation.
339 if(!camera.get_frustum().test_aabb(model_comp->get_world_bounds()))
340 {
341 continue;
342 }
343
344 const auto& submesh_transforms = model_comp->get_submesh_transforms();
345 const auto& skinning_transforms = model_comp->get_skinning_transforms();
346
347 // Pack shader parameters. Layout:
348 // u_wf_params[0].xyz = color rgb
349 // u_wf_params[0].w = opacity
350 // u_wf_params[1].x = thickness (pixels)
351 // u_wf_params[1].y = vertex stride in floats
352 // u_wf_params[1].z = position offset in floats
353 // u_wf_params[1].w = index buffer starting offset (in indices) - per submesh
354 // u_wf_params[2].x = bone weight offset in floats (skinned only)
355 // u_wf_params[2].y = bone indices offset in floats (skinned only)
356 std::array<math::vec4, 3> params;
357 params[0] = em.gizmos.selection_wireframe_color;
358 params[1] = math::vec4(em.gizmos.selection_wireframe_thickness, 0.0f, 0.0f, 0.0f);
359 params[2] = math::vec4(0.0f, 0.0f, 0.0f, 0.0f);
360
361 model::submit_vertex_pulling_callbacks callbacks;
362
363 callbacks.setup_begin = [&](const model::submit_vertex_pulling_callbacks::params& info)
364 {
365 auto& prog = info.skinned ? wireframe_program_skinned_.program : wireframe_program_.program;
366 if(prog)
367 {
368 prog->begin();
369 }
370 };
371
372 callbacks.setup_params_per_instance = [&](const model::submit_vertex_pulling_callbacks::params& info)
373 {
374 params[1].y = static_cast<float>(info.vertex_stride_floats);
375 params[1].z = static_cast<float>(info.position_offset_floats);
376 if(info.skinned)
377 {
378 params[2].x = static_cast<float>(info.weight_offset_floats);
379 params[2].y = static_cast<float>(info.indices_offset_floats);
380 }
381 };
382
383 callbacks.setup_params_per_submesh = [&](const model::submit_vertex_pulling_callbacks::params& info)
384 {
385 auto& wf_prog = info.skinned ? wireframe_program_skinned_ : wireframe_program_;
386 params[1].w = static_cast<float>(info.index_start);
387 gfx::set_uniform(wf_prog.u_wf_params, params.data(), 3);
388
389 // Six vertices per triangle edge; three edges per triangle.
390 gfx::set_vertex_count(info.index_count * 6u);
391 gfx::set_state(state);
392 gfx::submit(pass.id, wf_prog.program->native_handle());
393 };
394
395 callbacks.setup_end = [&](const model::submit_vertex_pulling_callbacks::params& info)
396 {
397 auto& prog = info.skinned ? wireframe_program_skinned_.program : wireframe_program_.program;
398 if(prog)
399 {
400 prog->end();
401 }
402 };
403
404 mdl.submit_for_vertex_pulling(world_transform.get_matrix(),
405 submesh_transforms,
406 skinning_transforms,
407 lod_data.current_lod_index,
408 callbacks);
409 }
410}
411
412void gizmos_renderer::draw_outline_pass(const gfx::frame_buffer::ptr& selection_mask,
413 const gfx::frame_buffer::ptr& obuffer,
414 gfx::dd_raii& dd)
415{
416 auto size = obuffer->get_size();
417
418 outline_program_.program->begin();
419
420 // Bind the selection mask (R8) to sampler slot 0
421 gfx::set_texture(outline_program_.s_tex, 0, selection_mask);
422
423 float thickness = 3.0f;
424 // Compute inverse‐pixel dims:
425 float data[4] = {1.0f / float(size.width), 1.0f / float(size.height), thickness, 0.0f};
426 gfx::set_uniform(outline_program_.u_data, data);
427
428 // Outline color uniform:
429 float outline_color[4] = {1.0f, 0.5f, 0.2f, 1.0f};
430 gfx::set_uniform(outline_program_.u_outline_color, outline_color);
431
432 // Draw a full‐screen quad
433 auto topology = gfx::clip_quad(0.0f);
434
435 // Alpha-blend the outline over existing scene
436 gfx::set_state(topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_ALPHA);
437
438 gfx::submit(dd.view, outline_program_.program->native_handle());
439
440 outline_program_.program->end();
441}
442
443void gizmos_renderer::draw_icon_gizmos(rtti::context& ctx, scene& scn, const camera& camera, gfx::dd_raii& dd)
444{
445 auto& em = ctx.get_cached<editing_manager>();
446
447 if(!em.show_icon_gizmos)
448 return;
449
450 hpp::for_each_type<camera_component, light_component, reflection_probe_component, audio_source_component, particle_emitter_component>(
451 [&](auto tag)
452 {
453 using type_t = typename std::decay_t<decltype(tag)>::type;
454
455 scn.registry->view<type_t>().each(
456 [&](auto e, auto&& comp)
457 {
458 auto entity = scn.create_handle(e);
459 entt::meta_any s = entity;
460 draw_gizmo_billboard_var(ctx, s, camera, dd);
461 });
462 });
463}
464
466{
467 outline_mask_program_ = {};
468 outline_mask_program_skinned_ = {};
469 outline_program_ = {};
470 wireframe_program_ = {};
471 wireframe_program_skinned_ = {};
472 grid_program_.reset();
473 return true;
474}
475} // namespace unravel
Manages assets, including loading, unloading, and storage.
Class that contains core camera data, used for rendering and other purposes.
Class representing a camera. Contains functionality for manipulating and updating a camera....
Definition camera.h:62
auto get_projection() const -> const math::transform &
Retrieves the current projection matrix.
Definition camera.cpp:205
auto get_view() const -> const math::transform &
Retrieves the current view matrix.
Definition camera.cpp:278
auto get_frustum() const -> const math::frustum &
Retrieves the current camera object frustum.
Definition camera.cpp:372
void on_frame_render(rtti::context &ctx, scene &scn, entt::handle camera_entity, dd_2d_raii &dd_2d)
auto init(rtti::context &ctx) -> bool
auto deinit(rtti::context &ctx) -> bool
uint16_t view
std::string tag
Definition hub.cpp:32
texture_job_type type
const aiScene * scene
Definition cache.hpp:11
void submit(view_id _id, program_handle _handle, int32_t _depth, bool _preserveState)
void set_view_clear(view_id _id, uint16_t _flags, uint32_t _rgba, float _depth, uint8_t _stencil)
Definition graphics.cpp:881
void set_state(uint64_t _state, uint32_t _rgba)
Definition graphics.cpp:937
bgfx::Topology topology
Definition graphics.h:37
void set_uniform(uniform_handle _handle, const void *_value, uint16_t _num)
Definition graphics.cpp:977
void set_vertex_count(uint32_t _numVertices)
uint32_t get_render_frame()
auto clip_quad(float depth, float width, float height) -> uint64_t
void set_texture(uint8_t _stage, uniform_handle _sampler, texture_handle _handle, uint32_t _flags)
void draw_gizmo_var(rtti::context &ctx, entt::meta_any &var, const camera &cam, gfx::dd_raii &dd, dd_2d_raii &dd_2d)
Definition gizmos.cpp:41
void draw_gizmo_billboard_var(rtti::context &ctx, entt::meta_any &var, const camera &cam, gfx::dd_raii &dd)
Definition gizmos.cpp:52
entt::handle entity
view_id view
Definition debugdraw.h:16
void set_view_proj(const float *v, const float *p)
gfx::view_id id
void bind(const frame_buffer *fb=nullptr) const
auto get_cached() -> T &
Definition context.hpp:49
T width
Definition basetypes.hpp:55
T height
Definition basetypes.hpp:56
static void draw_system_gizmos(rtti::context &ctx, const camera &cam, gfx::dd_raii &dd)
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:70