Antkeeper  0.0.1
material-pass.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
21 #include <engine/config.hpp>
28 #include <engine/gl/texture.hpp>
32 #include <engine/render/model.hpp>
34 #include <engine/scene/camera.hpp>
41 #include <engine/config.hpp>
45 #include <cmath>
46 #include <execution>
47 
48 namespace render {
49 
50 namespace {
51 
55 bool operation_compare(const render::operation* a, const render::operation* b)
56 {
57  // Render operations with materials first
58  if (!a->material)
59  {
60  return false;
61  }
62  else if (!b->material)
63  {
64  return true;
65  }
66 
67  const bool translucent_a = a->material->get_blend_mode() == material_blend_mode::translucent;
68  const bool translucent_b = b->material->get_blend_mode() == material_blend_mode::translucent;
69 
70  if (translucent_a)
71  {
72  if (translucent_b)
73  {
74  // A and B are both translucent, render back to front
75  return (a->depth < b->depth);
76  }
77  else
78  {
79  // A is translucent, B is opaque. Render B first
80  return false;
81  }
82  }
83  else
84  {
85  if (translucent_b)
86  {
87  // A is opaque, B is translucent. Render A first
88  return true;
89  }
90  else
91  {
92  // A and B are both opaque, sort by material hash
93  const std::size_t hash_a = a->material->hash();
94  const std::size_t hash_b = b->material->hash();
95 
96  if (hash_a != hash_b)
97  {
98  // A and B have different material hashes, sort by hash
99  return (hash_a < hash_b);
100  }
101  else
102  {
103  // A and B have the same material hash, sort by layer mask
104  if (a->layer_mask != b->layer_mask)
105  {
106  return (a->layer_mask < b->layer_mask);
107  }
108  else
109  {
110  // A and B have the same layer mask, sort by VAO
111  return (a->vertex_array < b->vertex_array);
112  }
113  }
114  }
115  }
116 }
117 
118 } // namespace
119 
121  pass(pipeline, framebuffer)
122 {
123  // Load LTC LUT textures
124  ltc_lut_1 = resource_manager->load<gl::texture_2d>("ltc-lut-1.tex");
125  ltc_lut_2 = resource_manager->load<gl::texture_2d>("ltc-lut-2.tex");
126 
127  // Load IBL BRDF LUT texture
128  brdf_lut = resource_manager->load<gl::texture_2d>("brdf-lut.tex");
129 }
130 
132 {
134 
140 
141  const auto& viewport_dimensions = (m_framebuffer) ? m_framebuffer->dimensions() : m_pipeline->get_default_framebuffer_dimensions();
142  const gl::viewport viewport[1] =
143  {{
144  0,
145  0,
146  static_cast<float>(viewport_dimensions[0]),
147  static_cast<float>(viewport_dimensions[1])
148  }};
149  m_pipeline->set_viewport(0, viewport);
150 
151  //const gl::shader_program* active_shader_program = nullptr;
152  const render::material* active_material = nullptr;
153  std::size_t active_material_hash = 0;
154  bool active_two_sided = false;
156  std::size_t active_cache_key = 0;
157  shader_cache_entry* active_cache_entry = nullptr;
158  std::uint32_t active_layer_mask = 0;
159  std::size_t active_lighting_state_hash = 0;
160 
161  // Gather information
162  evaluate_camera(ctx);
163  evaluate_misc(ctx);
164 
165  // Sort render operations
166  std::sort(std::execution::par_unseq, ctx.operations.begin(), ctx.operations.end(), operation_compare);
167 
168  for (const render::operation* operation: ctx.operations)
169  {
170  // Get operation material
172  if (!material)
173  {
174  if (!fallback_material)
175  {
176  // No material specified and no fallback material, skip operation
177  continue;
178  }
179 
180  // Use fallback material
181  material = fallback_material.get();
182  }
183 
184  // Evaluate visible lights
185  if (active_layer_mask != operation->layer_mask)
186  {
187  evaluate_lighting(ctx, operation->layer_mask & ctx.camera->get_layer_mask());
188  active_layer_mask = operation->layer_mask;
189  }
190 
191  // Switch materials if necessary
192  if (active_material != material || active_lighting_state_hash != lighting_state_hash)
193  {
194  // if (!material->get_shader_template())
195  // {
196  // continue;
197  // }
198 
199  if (active_material_hash != material->hash())
200  {
201  // Set culling mode
202  if (active_two_sided != material->is_two_sided())
203  {
204  if (material->is_two_sided())
205  {
207  }
208  else
209  {
211  }
212 
213  active_two_sided = material->is_two_sided();
214  }
215 
216  // Set blend mode
217  if (active_blend_mode != material->get_blend_mode())
218  {
220  {
223  ({
230  });
231  }
232  else
233  {
235  }
236 
237  active_blend_mode = material->get_blend_mode();
238  }
239 
240  active_material_hash = material->hash();
241  }
242 
243  // Calculate shader cache key
244  std::size_t cache_key = hash_combine(lighting_state_hash, material->get_shader_template()->hash());
245  if (active_cache_key != cache_key)
246  {
247  // Lookup shader cache entry
248  if (auto i = shader_cache.find(cache_key); i != shader_cache.end())
249  {
250  active_cache_entry = &i->second;
251  }
252  else
253  {
254  // Construct cache entry
255  active_cache_entry = &shader_cache[cache_key];
256  active_cache_entry->shader_program = generate_shader_program(*material->get_shader_template(), material->get_blend_mode());
257  build_shader_command_buffer(active_cache_entry->shader_command_buffer, *active_cache_entry->shader_program);
258  build_geometry_command_buffer(active_cache_entry->geometry_command_buffer, *active_cache_entry->shader_program);
259 
260  debug::log_trace("Generated material cache entry {:x}", cache_key);
261  }
262 
263  // Bind shader and update shader-specific variables
264  for (const auto& command: active_cache_entry->shader_command_buffer)
265  {
266  command();
267  }
268 
269  active_cache_key = cache_key;
270  }
271 
272  // Find or build material command buffer
273  std::vector<std::function<void()>>* material_command_buffer;
274  if (auto i = active_cache_entry->material_command_buffers.find(material); i != active_cache_entry->material_command_buffers.end())
275  {
276  material_command_buffer = &i->second;
277  }
278  else
279  {
280  material_command_buffer = &active_cache_entry->material_command_buffers[material];
281  build_material_command_buffer(*material_command_buffer, *active_cache_entry->shader_program, *material);
282 
283  debug::log_trace("Generated material command buffer");
284  }
285 
286  // Update material-dependent shader variables
287  for (const auto& command: *material_command_buffer)
288  {
289  command();
290  }
291 
292  active_material = material;
293  active_lighting_state_hash = lighting_state_hash;
294  }
295 
296 
298 
299  // @see Persson, E., & Studios, A. (2012). Creating vast game worlds: Experiences from avalanche studios. In ACM SIGGRAPH 2012 Talks (pp. 1-1).
300  model_view = *model;
301  model_view[3] -= view_translation;
302  model_view = view_rotation * model_view;
303  // model_view = (*view) * (*model);
304 
305  matrix_palette = operation->matrix_palette;
306 
307  // Update geometry-dependent shader variables
308  for (const auto& command: active_cache_entry->geometry_command_buffer)
309  {
310  command();
311  }
312 
317  }
318 
319  ++frame;
320 }
321 
322 void material_pass::set_fallback_material(std::shared_ptr<render::material> fallback)
323 {
324  this->fallback_material = fallback;
325 }
326 
327 void material_pass::evaluate_camera(const render::context& ctx)
328 {
329  view = &ctx.camera->get_view();
330  inv_view = &ctx.camera->get_inv_view();
331  view_translation = math::fvec4(ctx.camera->get_translation());
332  view_rotation = math::fmat4(math::fmat3(*view));
333  projection = &ctx.camera->get_projection();
334  view_projection = &ctx.camera->get_view_projection();
335  camera_position = &ctx.camera->get_translation();
336  camera_exposure = ctx.camera->get_exposure_normalization();
337 }
338 
339 void material_pass::evaluate_lighting(const render::context& ctx, std::uint32_t layer_mask)
340 {
341  // Reset light and shadow counts
342  light_probe_count = 0;
343  directional_light_count = 0;
344  directional_shadow_count = 0;
345  spot_light_count = 0;
346  point_light_count = 0;
347  rectangle_light_count = 0;
348 
349  const auto& light_probes = ctx.collection->get_objects(scene::light_probe::object_type_id);
350  for (const scene::object_base* object: light_probes)
351  {
352  if (!(object->get_layer_mask() & layer_mask))
353  {
354  continue;
355  }
356 
357  if (!light_probe_count)
358  {
359  const scene::light_probe& light_probe = static_cast<const scene::light_probe&>(*object);
360  ++light_probe_count;
361  light_probe_luminance_texture = light_probe.get_luminance_texture().get();
362  light_probe_illuminance_texture = light_probe.get_illuminance_texture().get();
363  }
364  }
365 
366  const auto& lights = ctx.collection->get_objects(scene::light::object_type_id);
367  for (const scene::object_base* object: lights)
368  {
369  if (!(object->get_layer_mask() & layer_mask))
370  {
371  continue;
372  }
373 
374  const scene::light& light = static_cast<const scene::light&>(*object);
375 
376  switch (light.get_light_type())
377  {
378  // Add directional light
380  {
381  const scene::directional_light& directional_light = static_cast<const scene::directional_light&>(light);
382 
383  const std::size_t index = directional_light_count;
384 
385  ++directional_light_count;
386  if (directional_light_count > directional_light_colors.size())
387  {
388  directional_light_colors.resize(directional_light_count);
389  directional_light_directions.resize(directional_light_count);
390  }
391 
392  directional_light_colors[index] = directional_light.get_colored_illuminance() * ctx.camera->get_exposure_normalization();
393  directional_light_directions[index] = directional_light.get_direction() * ctx.camera->get_rotation();
394 
395  // Add directional shadow
396  if (directional_light.is_shadow_caster() && directional_light.get_shadow_framebuffer())
397  {
398  const std::size_t index = directional_shadow_count;
399 
400  ++directional_shadow_count;
401  if (directional_shadow_count > directional_shadow_maps.size())
402  {
403  directional_shadow_maps.resize(directional_shadow_count);
404  directional_shadow_splits.resize(directional_shadow_count);
405  directional_shadow_fade_ranges.resize(directional_shadow_count);
406  directional_shadow_matrices.resize(directional_shadow_count);
407  }
408 
409  directional_shadow_maps[index] = directional_light.get_shadow_texture().get();
410  directional_shadow_splits[index] = directional_light.get_shadow_cascade_distances();
411  directional_shadow_fade_ranges[index] = directional_light.get_shadow_fade_range();
412  directional_shadow_matrices[index] = directional_light.get_shadow_cascade_matrices();
413  }
414  break;
415  }
416 
417  // Add spot_light
419  {
420  const scene::spot_light& spot_light = static_cast<const scene::spot_light&>(light);
421 
422  const std::size_t index = spot_light_count;
423 
424  ++spot_light_count;
425  if (spot_light_count > spot_light_colors.size())
426  {
427  spot_light_colors.resize(spot_light_count);
428  spot_light_positions.resize(spot_light_count);
429  spot_light_directions.resize(spot_light_count);
430  spot_light_cutoffs.resize(spot_light_count);
431  }
432 
433  spot_light_colors[index] = spot_light.get_luminous_flux() * ctx.camera->get_exposure_normalization();
434  spot_light_positions[index] = spot_light.get_translation() - ctx.camera->get_translation();
435  spot_light_directions[index] = spot_light.get_direction() * ctx.camera->get_rotation();
436  spot_light_cutoffs[index] = spot_light.get_cosine_cutoff();
437  break;
438  }
439 
440  // Add point light
442  {
443  const scene::point_light& point_light = static_cast<const scene::point_light&>(light);
444 
445  const std::size_t index = point_light_count;
446 
447  ++point_light_count;
448  if (point_light_count > point_light_colors.size())
449  {
450  point_light_colors.resize(point_light_count);
451  point_light_positions.resize(point_light_count);
452  }
453 
454  point_light_colors[index] = point_light.get_colored_luminous_flux() * ctx.camera->get_exposure_normalization();
455  point_light_positions[index] = point_light.get_translation() - ctx.camera->get_translation();
456 
457  break;
458  }
459 
460  // Add rectangle light
462  {
463  const scene::rectangle_light& rectangle_light = static_cast<const scene::rectangle_light&>(light);
464 
465  const std::size_t index = rectangle_light_count;
466 
467  ++rectangle_light_count;
468  if (rectangle_light_count > rectangle_light_colors.size())
469  {
470  rectangle_light_colors.resize(rectangle_light_count);
471  rectangle_light_corners.resize(rectangle_light_count * 4);
472  }
473 
474  rectangle_light_colors[index] = rectangle_light.get_colored_luminance() * ctx.camera->get_exposure_normalization();
475 
476  const auto corners = rectangle_light.get_corners();
477  for (std::size_t i = 0; i < 4; ++i)
478  {
479  rectangle_light_corners[index * 4 + i] = (corners[i] - ctx.camera->get_translation()) * ctx.camera->get_rotation();
480  }
481 
482  break;
483  }
484 
485  default:
486  break;
487  }
488  }
489 
490  // Generate lighting state hash
491  lighting_state_hash = std::hash<std::size_t>{}(light_probe_count);
492  lighting_state_hash = hash_combine(lighting_state_hash, std::hash<std::size_t>{}(directional_light_count));
493  lighting_state_hash = hash_combine(lighting_state_hash, std::hash<std::size_t>{}(directional_shadow_count));
494  lighting_state_hash = hash_combine(lighting_state_hash, std::hash<std::size_t>{}(point_light_count));
495  lighting_state_hash = hash_combine(lighting_state_hash, std::hash<std::size_t>{}(spot_light_count));
496  lighting_state_hash = hash_combine(lighting_state_hash, std::hash<std::size_t>{}(rectangle_light_count));
497 }
498 
499 void material_pass::evaluate_misc(const render::context& ctx)
500 {
501  time = ctx.t;
502  timestep = ctx.dt;
503  subframe = ctx.alpha;
504 
505  const auto& viewport_dimensions = (m_framebuffer) ? m_framebuffer->dimensions() : m_pipeline->get_default_framebuffer_dimensions();
506  resolution =
507  {
508  static_cast<float>(viewport_dimensions[0]),
509  static_cast<float>(viewport_dimensions[1])
510  };
511 
513 }
514 
515 std::unique_ptr<gl::shader_program> material_pass::generate_shader_program(const gl::shader_template& shader_template, material_blend_mode blend_mode) const
516 {
517  std::unordered_map<std::string, std::string> definitions;
518 
519  definitions["VERTEX_POSITION"] = std::to_string(vertex_attribute_location::position);
520  definitions["VERTEX_UV"] = std::to_string(vertex_attribute_location::uv);
521  definitions["VERTEX_NORMAL"] = std::to_string(vertex_attribute_location::normal);
522  definitions["VERTEX_TANGENT"] = std::to_string(vertex_attribute_location::tangent);
523  definitions["VERTEX_COLOR"] = std::to_string(vertex_attribute_location::color);
524  definitions["VERTEX_BONE_INDEX"] = std::to_string(vertex_attribute_location::bone_index);
525  definitions["VERTEX_BONE_WEIGHT"] = std::to_string(vertex_attribute_location::bone_weight);
526  definitions["VERTEX_BARYCENTRIC"] = std::to_string(vertex_attribute_location::barycentric);
527  definitions["VERTEX_TARGET"] = std::to_string(vertex_attribute_location::target);
528 
529  definitions["FRAGMENT_OUTPUT_COLOR"] = "0";
530 
531  definitions["LIGHT_PROBE_COUNT"] = std::to_string(light_probe_count);
532  definitions["DIRECTIONAL_LIGHT_COUNT"] = std::to_string(directional_light_count);
533  definitions["DIRECTIONAL_SHADOW_COUNT"] = std::to_string(directional_shadow_count);
534  definitions["POINT_LIGHT_COUNT"] = std::to_string(point_light_count);
535  definitions["SPOT_LIGHT_COUNT"] = std::to_string(spot_light_count);
536  definitions["RECTANGLE_LIGHT_COUNT"] = std::to_string(rectangle_light_count);
537 
538  if (blend_mode == material_blend_mode::masked)
539  {
540  definitions["MASKED_OPACITY"] = "1";
541  }
542 
543  auto shader_program = shader_template.build(definitions);
544 
545  if (!shader_program->linked())
546  {
547  debug::log_error("Failed to link material shader program: {}", shader_program->info());
548  debug::log_warning("{}", shader_template.configure(gl::shader_stage::fragment, definitions));
549  }
550 
551  return shader_program;
552 }
553 
554 void material_pass::build_shader_command_buffer(std::vector<std::function<void()>>& command_buffer, const gl::shader_program& shader_program) const
555 {
556  // Bind shader program
557  command_buffer.emplace_back([&](){m_pipeline->bind_shader_program(&shader_program);});
558 
559  // Update camera variables
560  if (auto view_var = shader_program.variable("view"))
561  {
562  command_buffer.emplace_back([&, view_var](){view_var->update(*view);});
563  }
564  if (auto inv_view_var = shader_program.variable("inv_view"))
565  {
566  command_buffer.emplace_back([&, inv_view_var](){inv_view_var->update(*inv_view);});
567  }
568  if (auto projection_var = shader_program.variable("projection"))
569  {
570  command_buffer.emplace_back([&, projection_var](){projection_var->update(*projection);});
571  }
572  if (auto view_projection_var = shader_program.variable("view_projection"))
573  {
574  command_buffer.emplace_back([&, view_projection_var](){view_projection_var->update(*view_projection);});
575  }
576  if (auto camera_position_var = shader_program.variable("camera_position"))
577  {
578  command_buffer.emplace_back([&, camera_position_var](){camera_position_var->update(*camera_position);});
579  }
580  if (auto camera_exposure_var = shader_program.variable("camera_exposure"))
581  {
582  command_buffer.emplace_back([&, camera_exposure_var](){camera_exposure_var->update(camera_exposure);});
583  }
584 
585  // Update IBL variables
586  if (auto brdf_lut_var = shader_program.variable("brdf_lut"))
587  {
588  command_buffer.emplace_back
589  (
590  [&, brdf_lut_var]()
591  {
592  brdf_lut_var->update(*brdf_lut);
593  }
594  );
595  }
596 
597  // Update light probe variables
598  if (light_probe_count)
599  {
600  if (auto light_probe_luminance_texture_var = shader_program.variable("light_probe_luminance_texture"))
601  {
602  command_buffer.emplace_back
603  (
604  [&, light_probe_luminance_texture_var]()
605  {
606  light_probe_luminance_texture_var->update(*light_probe_luminance_texture);
607  }
608  );
609  }
610 
611  if (auto light_probe_luminance_mip_scale_var = shader_program.variable("light_probe_luminance_mip_scale"))
612  {
613  command_buffer.emplace_back
614  (
615  [&, light_probe_luminance_mip_scale_var]()
616  {
617  light_probe_luminance_mip_scale_var->update(std::max<float>(static_cast<float>(light_probe_luminance_texture->get_image_view()->get_mip_level_count()) - 4.0f, 0.0f));
618  }
619  );
620  }
621 
622  if (auto light_probe_illuminance_texture_var = shader_program.variable("light_probe_illuminance_texture"))
623  {
624  command_buffer.emplace_back
625  (
626  [&, light_probe_illuminance_texture_var]()
627  {
628  light_probe_illuminance_texture_var->update(*light_probe_illuminance_texture);
629  }
630  );
631  }
632  }
633 
634  // Update LTC variables
635  if (auto ltc_lut_1_var = shader_program.variable("ltc_lut_1"))
636  {
637  if (auto ltc_lut_2_var = shader_program.variable("ltc_lut_2"))
638  {
639  command_buffer.emplace_back
640  (
641  [&, ltc_lut_1_var, ltc_lut_2_var]()
642  {
643  ltc_lut_1_var->update(*ltc_lut_1);
644  ltc_lut_2_var->update(*ltc_lut_2);
645  }
646  );
647  }
648  }
649  if (rectangle_light_count)
650  {
651  if (auto rectangle_light_colors_var = shader_program.variable("rectangle_light_colors"))
652  {
653  auto rectangle_light_corners_var = shader_program.variable("rectangle_light_corners");
654 
655  if (rectangle_light_corners_var)
656  {
657  command_buffer.emplace_back
658  (
659  [&, rectangle_light_colors_var, rectangle_light_corners_var]()
660  {
661  rectangle_light_colors_var->update(std::span<const math::fvec3>{rectangle_light_colors.data(), rectangle_light_count});
662  rectangle_light_corners_var->update(std::span<const math::fvec3>{rectangle_light_corners.data(), rectangle_light_count * 4});
663  }
664  );
665  }
666  }
667  }
668 
669  // Update directional light variables
670  if (directional_light_count)
671  {
672  if (auto directional_light_colors_var = shader_program.variable("directional_light_colors"))
673  {
674  if (auto directional_light_directions_var = shader_program.variable("directional_light_directions"))
675  {
676  command_buffer.emplace_back
677  (
678  [&, directional_light_colors_var, directional_light_directions_var]()
679  {
680  directional_light_colors_var->update(std::span<const math::fvec3>{directional_light_colors.data(), directional_light_count});
681  directional_light_directions_var->update(std::span<const math::fvec3>{directional_light_directions.data(), directional_light_count});
682  }
683  );
684  }
685  }
686  }
687 
688  // Update directional shadow variables
689  if (directional_shadow_count)
690  {
691  if (auto directional_shadow_maps_var = shader_program.variable("directional_shadow_maps"))
692  {
693  auto directional_shadow_splits_var = shader_program.variable("directional_shadow_splits");
694  auto directional_shadow_fade_ranges_var = shader_program.variable("directional_shadow_fade_ranges");
695  auto directional_shadow_matrices_var = shader_program.variable("directional_shadow_matrices");
696 
697  if (directional_shadow_maps_var && directional_shadow_splits_var && directional_shadow_fade_ranges_var && directional_shadow_matrices_var)
698  {
699  command_buffer.emplace_back
700  (
701  [&, directional_shadow_maps_var, directional_shadow_splits_var, directional_shadow_fade_ranges_var, directional_shadow_matrices_var]()
702  {
703  directional_shadow_maps_var->update(std::span<const gl::texture_2d* const>{directional_shadow_maps.data(), directional_shadow_count});
704 
705  std::size_t offset = 0;
706  for (std::size_t i = 0; i < directional_shadow_count; ++i)
707  {
708  directional_shadow_splits_var->update(directional_shadow_splits[i], i);
709  directional_shadow_fade_ranges_var->update(directional_shadow_fade_ranges[i], i);
710  directional_shadow_matrices_var->update(directional_shadow_matrices[i], offset);
711  offset += directional_shadow_matrices[i].size();
712  }
713  }
714  );
715  }
716  }
717  }
718 
719  // Update point light variables
720  if (point_light_count)
721  {
722  if (auto point_light_colors_var = shader_program.variable("point_light_colors"))
723  {
724  auto point_light_positions_var = shader_program.variable("point_light_positions");
725 
726  if (point_light_positions_var)
727  {
728  command_buffer.emplace_back
729  (
730  [&, point_light_colors_var, point_light_positions_var]()
731  {
732  point_light_colors_var->update(std::span<const math::fvec3>{point_light_colors.data(), point_light_count});
733  point_light_positions_var->update(std::span<const math::fvec3>{point_light_positions.data(), point_light_count});
734  }
735  );
736  }
737  }
738  }
739 
740  // Update spot light variables
741  if (spot_light_count)
742  {
743  if (auto spot_light_colors_var = shader_program.variable("spot_light_colors"))
744  {
745  auto spot_light_positions_var = shader_program.variable("spot_light_positions");
746  auto spot_light_directions_var = shader_program.variable("spot_light_directions");
747  auto spot_light_cutoffs_var = shader_program.variable("spot_light_cutoffs");
748 
749  if (spot_light_positions_var && spot_light_directions_var && spot_light_cutoffs_var)
750  {
751  command_buffer.emplace_back
752  (
753  [&, spot_light_colors_var, spot_light_positions_var, spot_light_directions_var, spot_light_cutoffs_var]()
754  {
755  spot_light_colors_var->update(std::span<const math::fvec3>{spot_light_colors.data(), spot_light_count});
756  spot_light_positions_var->update(std::span<const math::fvec3>{spot_light_positions.data(), spot_light_count});
757  spot_light_directions_var->update(std::span<const math::fvec3>{spot_light_directions.data(), spot_light_count});
758  spot_light_cutoffs_var->update(std::span<const math::fvec2>{spot_light_cutoffs.data(), spot_light_count});
759  }
760  );
761  }
762  }
763  }
764 
765  // Update time variable
766  if (auto time_var = shader_program.variable("time"))
767  {
768  command_buffer.emplace_back([&, time_var](){time_var->update(time);});
769  }
770 
771  // Update timestep variable
772  if (auto timestep_var = shader_program.variable("timestep"))
773  {
774  command_buffer.emplace_back([&, timestep_var](){timestep_var->update(timestep);});
775  }
776 
777  // Update frame variable
778  if (auto frame_var = shader_program.variable("frame"))
779  {
780  command_buffer.emplace_back([&, frame_var](){frame_var->update(frame);});
781  }
782 
783  // Update subframe variable
784  if (auto subframe_var = shader_program.variable("subframe"))
785  {
786  command_buffer.emplace_back([&, subframe_var](){subframe_var->update(subframe);});
787  }
788 
789  // Update resolution variable
790  if (auto resolution_var = shader_program.variable("resolution"))
791  {
792  command_buffer.emplace_back([&, resolution_var](){resolution_var->update(resolution);});
793  }
794 
795  // Update mouse position variable
796  if (auto mouse_position_var = shader_program.variable("mouse_position"))
797  {
798  command_buffer.emplace_back([&, mouse_position_var](){mouse_position_var->update(mouse_position);});
799  }
800 }
801 
802 void material_pass::build_geometry_command_buffer(std::vector<std::function<void()>>& command_buffer, const gl::shader_program& shader_program) const
803 {
804  // Update model matrix variable
805  if (auto model_var = shader_program.variable("model"))
806  {
807  command_buffer.emplace_back([&, model_var](){model_var->update(*model);});
808  }
809 
810  // Update normal-model matrix variable
811  if (auto normal_model_var = shader_program.variable("normal_model"))
812  {
813  command_buffer.emplace_back
814  (
815  [&, normal_model_var]()
816  {
817  normal_model_var->update(math::transpose(math::inverse(math::fmat3(*model))));
818  }
819  );
820  }
821 
822  // Update model-view matrix and normal-model-view matrix variables
823  auto model_view_var = shader_program.variable("model_view");
824  auto normal_model_view_var = shader_program.variable("normal_model_view");
825  if (model_view_var && normal_model_view_var)
826  {
827  command_buffer.emplace_back
828  (
829  [&, model_view_var, normal_model_view_var]()
830  {
831  model_view_var->update(model_view);
832  normal_model_view_var->update(math::transpose(math::inverse(math::fmat3(model_view))));
833  }
834  );
835  }
836  else
837  {
838  if (model_view_var)
839  {
840  command_buffer.emplace_back([&, model_view_var](){model_view_var->update(model_view);});
841  }
842  else if (normal_model_view_var)
843  {
844  command_buffer.emplace_back
845  (
846  [&, normal_model_view_var]()
847  {
848  normal_model_view_var->update(math::transpose(math::inverse(math::fmat3(model_view))));
849  }
850  );
851  }
852  }
853 
854  // Update model-view-projection matrix variable
855  if (auto model_view_projection_var = shader_program.variable("model_view_projection"))
856  {
857  command_buffer.emplace_back([&, model_view_projection_var](){model_view_projection_var->update((*projection) * model_view);});
858  }
859 
860  // Update matrix palette variable
861  if (auto matrix_palette_var = shader_program.variable("matrix_palette"))
862  {
863  command_buffer.emplace_back([&, matrix_palette_var](){matrix_palette_var->update(matrix_palette);});
864  }
865 }
866 
867 void material_pass::build_material_command_buffer(std::vector<std::function<void()>>& command_buffer, const gl::shader_program& shader_program, const material& material) const
868 {
869  for (const auto& [key, material_var]: material.get_variables())
870  {
871  if (!material_var)
872  {
873  continue;
874  }
875 
876  const auto shader_var = shader_program.variable(key);
877  if (!shader_var)
878  {
879  continue;
880  }
881 
882  const std::size_t size = std::min<std::size_t>(material_var->size(), shader_var->size());
883 
884  switch (shader_var->type())
885  {
887  // case gl::shader_variable_type::bvec1:
888  // if (material_var->type() == material_variable_type::bvec1)
889  // {
890  // command_buffer.emplace_back
891  // (
892  // [size, shader_var, material_var = std::static_pointer_cast<matvar_bool>(material_var)]()
893  // {
894  // shader_var->update(std::span<const bool>{material_var->data(), size});
895  // }
896  // );
897  // }
898  // break;
900  if (material_var->type() == material_variable_type::bvec2)
901  {
902  command_buffer.emplace_back
903  (
904  [size, shader_var, material_var = std::static_pointer_cast<matvar_bvec2>(material_var)]()
905  {
906  shader_var->update(std::span<const math::bvec2>{material_var->data(), size});
907  }
908  );
909  }
910  break;
912  if (material_var->type() == material_variable_type::bvec3)
913  {
914  command_buffer.emplace_back
915  (
916  [size, shader_var, material_var = std::static_pointer_cast<matvar_bvec3>(material_var)]()
917  {
918  shader_var->update(std::span<const math::bvec3>{material_var->data(), size});
919  }
920  );
921  }
922  break;
924  if (material_var->type() == material_variable_type::bvec4)
925  {
926  command_buffer.emplace_back
927  (
928  [size, shader_var, material_var = std::static_pointer_cast<matvar_bvec4>(material_var)]()
929  {
930  shader_var->update(std::span<const math::bvec4>{material_var->data(), size});
931  }
932  );
933  }
934  break;
936  if (material_var->type() == material_variable_type::ivec1)
937  {
938  command_buffer.emplace_back
939  (
940  [size, shader_var, material_var = std::static_pointer_cast<matvar_int>(material_var)]()
941  {
942  shader_var->update(std::span<const int>{material_var->data(), size});
943  }
944  );
945  }
946  break;
948  if (material_var->type() == material_variable_type::ivec2)
949  {
950  command_buffer.emplace_back
951  (
952  [size, shader_var, material_var = std::static_pointer_cast<matvar_ivec2>(material_var)]()
953  {
954  shader_var->update(std::span<const math::ivec2>{material_var->data(), size});
955  }
956  );
957  }
958  break;
960  if (material_var->type() == material_variable_type::ivec3)
961  {
962  command_buffer.emplace_back
963  (
964  [size, shader_var, material_var = std::static_pointer_cast<matvar_ivec3>(material_var)]()
965  {
966  shader_var->update(std::span<const math::ivec3>{material_var->data(), size});
967  }
968  );
969  }
970  break;
972  if (material_var->type() == material_variable_type::ivec4)
973  {
974  command_buffer.emplace_back
975  (
976  [size, shader_var, material_var = std::static_pointer_cast<matvar_ivec4>(material_var)]()
977  {
978  shader_var->update(std::span<const math::ivec4>{material_var->data(), size});
979  }
980  );
981  }
982  break;
984  if (material_var->type() == material_variable_type::uvec1)
985  {
986  command_buffer.emplace_back
987  (
988  [size, shader_var, material_var = std::static_pointer_cast<matvar_uint>(material_var)]()
989  {
990  shader_var->update(std::span<const unsigned int>{material_var->data(), size});
991  }
992  );
993  }
994  break;
996  if (material_var->type() == material_variable_type::uvec2)
997  {
998  command_buffer.emplace_back
999  (
1000  [size, shader_var, material_var = std::static_pointer_cast<matvar_uvec2>(material_var)]()
1001  {
1002  shader_var->update(std::span<const math::uvec2>{material_var->data(), size});
1003  }
1004  );
1005  }
1006  break;
1008  if (material_var->type() == material_variable_type::uvec3)
1009  {
1010  command_buffer.emplace_back
1011  (
1012  [size, shader_var, material_var = std::static_pointer_cast<matvar_uvec3>(material_var)]()
1013  {
1014  shader_var->update(std::span<const math::uvec3>{material_var->data(), size});
1015  }
1016  );
1017  }
1018  break;
1020  if (material_var->type() == material_variable_type::uvec4)
1021  {
1022  command_buffer.emplace_back
1023  (
1024  [size, shader_var, material_var = std::static_pointer_cast<matvar_uvec4>(material_var)]()
1025  {
1026  shader_var->update(std::span<const math::uvec4>{material_var->data(), size});
1027  }
1028  );
1029  }
1030  break;
1032  if (material_var->type() == material_variable_type::fvec1)
1033  {
1034  command_buffer.emplace_back
1035  (
1036  [size, shader_var, material_var = std::static_pointer_cast<matvar_float>(material_var)]()
1037  {
1038  shader_var->update(std::span<const float>{material_var->data(), size});
1039  }
1040  );
1041  }
1042  break;
1044  if (material_var->type() == material_variable_type::fvec2)
1045  {
1046  command_buffer.emplace_back
1047  (
1048  [size, shader_var, material_var = std::static_pointer_cast<matvar_fvec2>(material_var)]()
1049  {
1050  shader_var->update(std::span<const math::fvec2>{material_var->data(), size});
1051  }
1052  );
1053  }
1054  break;
1056  if (material_var->type() == material_variable_type::fvec3)
1057  {
1058  command_buffer.emplace_back
1059  (
1060  [size, shader_var, material_var = std::static_pointer_cast<matvar_fvec3>(material_var)]()
1061  {
1062  shader_var->update(std::span<const math::fvec3>{material_var->data(), size});
1063  }
1064  );
1065  }
1066  break;
1068  if (material_var->type() == material_variable_type::fvec4)
1069  {
1070  command_buffer.emplace_back
1071  (
1072  [size, shader_var, material_var = std::static_pointer_cast<matvar_fvec4>(material_var)]()
1073  {
1074  shader_var->update(std::span<const math::fvec4>{material_var->data(), size});
1075  }
1076  );
1077  }
1078  break;
1080  if (material_var->type() == material_variable_type::fmat2)
1081  {
1082  command_buffer.emplace_back
1083  (
1084  [size, shader_var, material_var = std::static_pointer_cast<matvar_fmat2>(material_var)]()
1085  {
1086  shader_var->update(std::span<const math::fmat2>{material_var->data(), size});
1087  }
1088  );
1089  }
1090  break;
1092  if (material_var->type() == material_variable_type::fmat3)
1093  {
1094  command_buffer.emplace_back
1095  (
1096  [size, shader_var, material_var = std::static_pointer_cast<matvar_fmat3>(material_var)]()
1097  {
1098  shader_var->update(std::span<const math::fmat3>{material_var->data(), size});
1099  }
1100  );
1101  }
1102  break;
1104  if (material_var->type() == material_variable_type::fmat4)
1105  {
1106  command_buffer.emplace_back
1107  (
1108  [size, shader_var, material_var = std::static_pointer_cast<matvar_fmat4>(material_var)]()
1109  {
1110  shader_var->update(std::span<const math::fmat4>{material_var->data(), size});
1111  }
1112  );
1113  }
1114  break;
1116  if (material_var->type() == material_variable_type::texture_1d)
1117  {
1118  command_buffer.emplace_back
1119  (
1120  [size, shader_var, material_var = std::static_pointer_cast<matvar_texture_1d>(material_var)]()
1121  {
1122  shader_var->update(std::span<const std::shared_ptr<gl::texture_1d>>{material_var->data(), size});
1123  }
1124  );
1125  }
1126  break;
1128  if (material_var->type() == material_variable_type::texture_2d)
1129  {
1130  command_buffer.emplace_back
1131  (
1132  [size, shader_var, material_var = std::static_pointer_cast<matvar_texture_2d>(material_var)]()
1133  {
1134  shader_var->update(std::span<const std::shared_ptr<gl::texture_2d>>{material_var->data(), size});
1135  }
1136  );
1137  }
1138  break;
1140  if (material_var->type() == material_variable_type::texture_3d)
1141  {
1142  command_buffer.emplace_back
1143  (
1144  [size, shader_var, material_var = std::static_pointer_cast<matvar_texture_3d>(material_var)]()
1145  {
1146  shader_var->update(std::span<const std::shared_ptr<gl::texture_3d>>{material_var->data(), size});
1147  }
1148  );
1149  }
1150  break;
1152  if (material_var->type() == material_variable_type::texture_cube)
1153  {
1154  command_buffer.emplace_back
1155  (
1156  [size, shader_var, material_var = std::static_pointer_cast<matvar_texture_cube>(material_var)]()
1157  {
1158  shader_var->update(std::span<const std::shared_ptr<gl::texture_cube>>{material_var->data(), size});
1159  }
1160  );
1161  }
1162  break;
1163  default:
1164  break;
1165  }
1166  }
1167 }
1168 
1169 } // namespace render
constexpr const std::array< std::uint32_t, 2 > & dimensions() const noexcept
Returns the dimensions of the framebuffer.
Definition: framebuffer.hpp:67
Graphics pipeline interface.
Definition: pipeline.hpp:48
void set_primitive_topology(primitive_topology topology)
Sets the primitive topology to use for drawing.
Definition: pipeline.cpp:356
void bind_shader_program(const gl::shader_program *shader_program)
Sets the vertex input.
Definition: pipeline.cpp:292
void bind_framebuffer(const gl::framebuffer *framebuffer)
Sets the vertex input.
Definition: pipeline.cpp:275
void set_color_blend_enabled(bool enabled)
Controls whether blending is enabled for the corresponding color attachment.
Definition: pipeline.cpp:953
void set_cull_mode(cull_mode mode)
Sets the triangle culling mode.
Definition: pipeline.cpp:503
void set_stencil_test_enabled(bool enabled)
Controls whether stencil testing is enabled.
Definition: pipeline.cpp:685
void set_viewport(std::uint32_t first_viewport, std::span< const viewport > viewports)
Sets one or more viewports.
Definition: pipeline.cpp:381
void bind_vertex_buffers(std::uint32_t first_binding, std::span< const vertex_buffer *const > buffers, std::span< const std::size_t > offsets, std::span< const std::size_t > strides)
Binds vertex buffers.
Definition: pipeline.cpp:326
void set_color_blend_equation(const color_blend_equation &equation)
Sets the color blend factors and operations.
Definition: pipeline.cpp:970
void set_depth_compare_op(gl::compare_op compare_op)
Sets the depth comparison operator.
Definition: pipeline.cpp:675
void draw(std::uint32_t vertex_count, std::uint32_t instance_count, std::uint32_t first_vertex, std::uint32_t first_instance)
Draws primitives.
Definition: pipeline.cpp:1028
constexpr const std::array< std::uint32_t, 2 > & get_default_framebuffer_dimensions() const noexcept
Returns the dimensions of the default framebuffer.
Definition: pipeline.hpp:383
void set_depth_test_enabled(bool enabled)
Controls whether depth testing is enabled.
Definition: pipeline.cpp:649
void bind_vertex_array(const vertex_array *array)
Binds a vertex array.
Definition: pipeline.cpp:309
Shader program which can be linked to shader objects and executed.
const shader_variable * variable(hash::fnv1a32_t key) const
Returns a pointer to an active shader variable with the given name, or nullptr if not found.
Template used to for generating one or more shader variants from a single source.
std::string configure(gl::shader_stage stage, const dictionary_type &definitions={}) const
Configures shader object source code for a given shader stage and template dictionary.
std::unique_ptr< gl::shader_program > build(const dictionary_type &definitions={}) const
Configures and compiles shader objects, then links them into a shader program.
2D texture.
Definition: texture.hpp:141
constexpr const std::shared_ptr< image_view_cube > & get_image_view() const noexcept
Returns the image view.
Definition: texture.hpp:264
void set_fallback_material(std::shared_ptr< render::material > fallback)
Sets the material to be used when a render operation is missing a material. If no fallback material i...
void render(render::context &ctx) override
material_pass(gl::pipeline *pipeline, const gl::framebuffer *framebuffer, resource_manager *resource_manager)
A material is associated with exactly one shader program and contains a set of material properties wh...
Definition: material.hpp:37
const std::shared_ptr< gl::shader_template > & get_shader_template() const noexcept
Returns the shader template with which this material is associated.
Definition: material.hpp:134
std::size_t hash() const noexcept
Returns a hash of the material state.
Definition: material.hpp:179
bool is_two_sided() const noexcept
Returns true if the material surface is two-sided, and false otherwise.
Definition: material.hpp:96
material_blend_mode get_blend_mode() const noexcept
Returns the material blend mode.
Definition: material.hpp:102
Render pass.
Definition: pass.hpp:34
gl::pipeline * m_pipeline
Definition: pass.hpp:63
const gl::framebuffer * m_framebuffer
Definition: pass.hpp:64
Manages the loading, caching, and saving of resources.
std::shared_ptr< T > load(const std::filesystem::path &path)
Loads and caches a resource.
constexpr const math::fmat4 & get_projection() const noexcept
Returns the camera's projection matrix.
Definition: camera.hpp:235
constexpr const math::fmat4 & get_view() const noexcept
Returns the camera's view matrix.
Definition: camera.hpp:223
constexpr const math::fmat4 & get_view_projection() const noexcept
Returns the camera's view-projection matrix.
Definition: camera.hpp:247
constexpr float get_exposure_normalization() const noexcept
Returns the camera's exposure normalization factor.
Definition: camera.hpp:217
constexpr const math::fmat4 & get_inv_view() const noexcept
Returns the inverse of the camera's view matrix.
Definition: camera.hpp:229
const std::vector< object_base * > & get_objects() const noexcept
Returns all objects in the collection.
Definition: collection.hpp:56
Light source with parallel rays and constant intensity.
constexpr float get_shadow_fade_range() const noexcept
Returns the distance from the maximum shadow distance at which shadows will begin to fade out.
constexpr bool is_shadow_caster() const noexcept
Returns true if the light casts shadows, false otherwise.
constexpr const std::shared_ptr< gl::framebuffer > & get_shadow_framebuffer() const noexcept
Returns the shadow map framebuffer, of nullptr if no shadow map framebuffer is set.
constexpr const math::fvec3 & get_colored_illuminance() const noexcept
Returns the color-modulated illuminance of the light on a surface perpendicular to the light directio...
constexpr const math::fvec4 & get_shadow_cascade_distances() const noexcept
Returns the array of shadow cascade far clipping plane distances.
constexpr const std::shared_ptr< gl::texture_2d > & get_shadow_texture() const noexcept
Returns the shadow map texture.
constexpr std::span< const math::fmat4 > get_shadow_cascade_matrices() const noexcept
Returns the array of world-space to cascade texture-space transformation matrices.
constexpr const math::fvec3 & get_direction() const noexcept
Returns a unit vector pointing in the light direction.
const std::shared_ptr< gl::texture_cube > & get_luminance_texture() const noexcept
Returns the light probe's luminance texture.
Definition: light-probe.hpp:74
const std::shared_ptr< gl::texture_1d > & get_illuminance_texture() const noexcept
Returns the light probe's illuminance texture.
Definition: light-probe.hpp:90
Abstract base class for light objects.
Definition: scene/light.hpp:32
virtual light_type get_light_type() const noexcept=0
Returns an enumeration denoting the light object type.
Abstract base class for scene objects.
Definition: object.hpp:37
constexpr std::uint32_t get_layer_mask() const noexcept
Returns the layer mask of the object.
Definition: object.hpp:121
constexpr const vector_type & get_translation() const noexcept
Returns the translation of the object.
Definition: object.hpp:133
constexpr const quaternion_type & get_rotation() const noexcept
Returns the rotation of the object.
Definition: object.hpp:139
static const std::atomic< std::size_t > object_type_id
Unique type ID for this scene object type.
Definition: object.hpp:175
Light source that radiates outward from a point.
Definition: point-light.hpp:32
constexpr const math::fvec3 & get_colored_luminous_flux() const noexcept
Returns the color-modulated luminous flux of the light.
Definition: point-light.hpp:58
Rectangular area light.
constexpr const math::fvec3 & get_colored_luminance() const noexcept
Returns the color-modulated luminance of the light.
constexpr std::span< const math::fvec3, 4 > get_corners() const noexcept
Returns the world-space positions of the light corners.
Directional cone light source.
Definition: spot-light.hpp:33
const math::fvec2 & get_cosine_cutoff() const noexcept
Returns the cosine of the spot light cutoff angles.
Definition: spot-light.hpp:77
const math::fvec3 & get_luminous_flux() const noexcept
Returns the luminous flux of the spot light, in lm.
Definition: spot-light.hpp:52
const math::fvec3 & get_direction() const noexcept
Returns the direction vector.
Definition: spot-light.hpp:65
constexpr std::uint32_t hash_combine(std::uint32_t x, std::uint32_t y) noexcept
Combines two hash values.
Commands which operate on entity::id components.
Definition: commands.cpp:28
log_message< log_message_severity::warning, Args... > log_warning
Formats and logs a warning message.
Definition: log.hpp:130
log_message< log_message_severity::trace, Args... > log_trace
Formats and logs a trace message.
Definition: log.hpp:88
log_message< log_message_severity::error, Args... > log_error
Formats and logs an error message.
Definition: log.hpp:144
@ a
Vertex A region.
@ b
Vertex B region.
@ none
No triangles are discarded.
@ back
Back-facing triangles are discarded.
@ fragment
Fragment shader stage.
@ greater_or_equal
Comparison evaluates reference >= test.
constexpr matrix< T, M, N > transpose(const matrix< T, N, M > &m) noexcept
Calculates the transpose of a matrix.
constexpr matrix< T, N, N > inverse(const matrix< T, N, N > &m) noexcept
Calculates the inverse of a square matrix.
T offset(T longitude)
Calculates the UTC offset at a given longitude.
Definition: utc.hpp:38
@ barycentric
Vertex barycentric coordinates (vec3)
@ uv
Vertex UV texture coordinates (vec2)
High-level rendering.
material_blend_mode
Material blend modes.
@ opaque
Material is fully opaque.
@ masked
Material has binary masked opacity.
@ translucent
Material is translucent.
@ point
Point light.
@ spot
Spot light.
@ rectangle
Rectangle light.
@ directional
Directional light.
Viewport position, dimensions, and depth range.
Definition: viewport.hpp:31
n by m column-major matrix.
Definition: math/matrix.hpp:44
n-dimensional vector.
Definition: vector.hpp:44
Context of a renderer.
Definition: context.hpp:40
const scene::camera * camera
Pointer to the camera.
Definition: context.hpp:42
float alpha
Subframe interpolation factor.
Definition: context.hpp:54
scene::collection * collection
Collection of scene objects being rendered.
Definition: context.hpp:45
float dt
Timestep, in seconds.
Definition: context.hpp:51
std::vector< const operation * > operations
Render operations generated by visible objects.
Definition: context.hpp:60
float t
Current time, in seconds.
Definition: context.hpp:48
Atomic render operation.
Definition: operation.hpp:38
std::uint32_t vertex_count
Definition: operation.hpp:45
std::uint32_t instance_count
Definition: operation.hpp:47
std::span< const math::fmat4 > matrix_palette
Definition: operation.hpp:54
std::size_t vertex_offset
Definition: operation.hpp:42
std::size_t vertex_stride
Definition: operation.hpp:43
std::uint32_t layer_mask
Definition: operation.hpp:56
std::uint32_t first_instance
Definition: operation.hpp:46
math::fmat4 transform
Definition: operation.hpp:51
gl::primitive_topology primitive_topology
Definition: operation.hpp:39
std::uint32_t first_vertex
Definition: operation.hpp:44
const gl::vertex_buffer * vertex_buffer
Definition: operation.hpp:41
std::shared_ptr< render::material > material
Definition: operation.hpp:49
const gl::vertex_array * vertex_array
Definition: operation.hpp:40