/* * Copyright (C) 2023 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace render { namespace { /** * Sorts render operations for the material pass. */ bool operation_compare(const render::operation* a, const render::operation* b) { // Render operations with materials first if (!a->material) { return false; } else if (!b->material) { return true; } const bool translucent_a = a->material->get_blend_mode() == material_blend_mode::translucent; const bool translucent_b = b->material->get_blend_mode() == material_blend_mode::translucent; if (translucent_a) { if (translucent_b) { // A and B are both translucent, render back to front return (a->depth < b->depth); } else { // A is translucent, B is opaque. Render B first return false; } } else { if (translucent_b) { // A is opaque, B is translucent. Render A first return true; } else { // A and B are both opaque const std::size_t hash_a = a->material->hash(); const std::size_t hash_b = b->material->hash(); if (hash_a == hash_b) { // A and B have same material hash, sort by VAO return (a->vertex_array < b->vertex_array); } else { // A and B have different material hashes, sort by hash return (hash_a < hash_b); } } } } } // namespace material_pass::material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): pass(rasterizer, framebuffer) { // Load LTC LUT textures ltc_lut_1 = resource_manager->load("ltc-lut-1.tex"); ltc_lut_2 = resource_manager->load("ltc-lut-2.tex"); // Load IBL BRDF LUT texture brdf_lut = resource_manager->load("brdf-lut.tex"); } void material_pass::render(render::context& ctx) { rasterizer->use_framebuffer(*framebuffer); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glDepthFunc(GL_GREATER); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glDisable(GL_STENCIL_TEST); // For half-z buffer glDepthRange(-1.0f, 1.0f); auto viewport = framebuffer->get_dimensions(); rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); //const gl::shader_program* active_shader_program = nullptr; const render::material* active_material = nullptr; std::size_t active_material_hash = 0; bool active_two_sided = false; material_blend_mode active_blend_mode = material_blend_mode::opaque; std::size_t active_cache_key = 0; shader_cache_entry* active_cache_entry = nullptr; // Gather information evaluate_camera(ctx); evaluate_lighting(ctx); evaluate_misc(ctx); // Sort render operations std::sort(std::execution::par_unseq, ctx.operations.begin(), ctx.operations.end(), operation_compare); for (const render::operation* operation: ctx.operations) { // Get operation material const render::material* material = operation->material.get(); if (!material) { if (!fallback_material) { // No material specified and no fallback material, skip operation continue; } // Use fallback material material = fallback_material.get(); } // Switch materials if necessary if (active_material != material) { if (!material->get_shader_template()) { continue; } if (active_material_hash != material->hash()) { // Set culling mode if (active_two_sided != material->is_two_sided()) { if (material->is_two_sided()) { glDisable(GL_CULL_FACE); } else { glEnable(GL_CULL_FACE); } active_two_sided = material->is_two_sided(); } // Set blend mode if (active_blend_mode != material->get_blend_mode()) { if (material->get_blend_mode() == material_blend_mode::translucent) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } active_blend_mode = material->get_blend_mode(); } active_material_hash = material->hash(); } // Calculate shader cache key std::size_t cache_key = hash::combine(lighting_state_hash, material->get_shader_template()->hash()); if (active_cache_key != cache_key) { // Lookup shader cache entry if (auto i = shader_cache.find(cache_key); i != shader_cache.end()) { active_cache_entry = &i->second; } else { // Construct cache entry active_cache_entry = &shader_cache[cache_key]; active_cache_entry->shader_program = generate_shader_program(*material->get_shader_template()); build_shader_command_buffer(active_cache_entry->shader_command_buffer, *active_cache_entry->shader_program); build_geometry_command_buffer(active_cache_entry->geometry_command_buffer, *active_cache_entry->shader_program); debug::log::trace("Generated material cache entry {:x}", cache_key); } // Bind shader and update shader-specific variables for (const auto& command: active_cache_entry->shader_command_buffer) { command(); } active_cache_key = cache_key; } // Find or build material command buffer std::vector>* material_command_buffer; if (auto i = active_cache_entry->material_command_buffers.find(material); i != active_cache_entry->material_command_buffers.end()) { material_command_buffer = &i->second; } else { material_command_buffer = &active_cache_entry->material_command_buffers[material]; build_material_command_buffer(*material_command_buffer, *active_cache_entry->shader_program, *material); debug::log::trace("Generated material command buffer"); } // Update material-dependent shader variables for (const auto& command: *material_command_buffer) { command(); } active_material = material; } // Update geometry-dependent shader variables model = &operation->transform; matrix_palette = operation->matrix_palette; for (const auto& command: active_cache_entry->geometry_command_buffer) { command(); } // Draw geometry if (operation->instance_count) { rasterizer->draw_arrays_instanced(*operation->vertex_array, operation->drawing_mode, operation->start_index, operation->index_count, operation->instance_count); } else { rasterizer->draw_arrays(*operation->vertex_array, operation->drawing_mode, operation->start_index, operation->index_count); } } ++frame; } void material_pass::set_fallback_material(std::shared_ptr fallback) { this->fallback_material = fallback; } void material_pass::evaluate_camera(const render::context& ctx) { view = &ctx.camera->get_view(); projection = &ctx.camera->get_projection(); view_projection = &ctx.camera->get_view_projection(); camera_position = &ctx.camera->get_translation(); camera_exposure = ctx.camera->get_exposure_normalization(); clip_depth = { ctx.camera->get_clip_near(), ctx.camera->get_clip_far() }; log_depth_coef = 2.0f / std::log2(clip_depth[1] + 1.0f); } void material_pass::evaluate_lighting(const render::context& ctx) { // Reset light and shadow counts light_probe_count = 0; directional_light_count = 0; directional_shadow_count = 0; spot_light_count = 0; point_light_count = 0; rectangle_light_count = 0; const auto& light_probes = ctx.collection->get_objects(scene::light_probe::object_type_id); for (const scene::object_base* object: light_probes) { if (!light_probe_count) { const scene::light_probe& light_probe = static_cast(*object); ++light_probe_count; light_probe_luminance_texture = light_probe.get_luminance_texture().get(); light_probe_illuminance_texture = light_probe.get_illuminance_texture().get(); } } const auto& lights = ctx.collection->get_objects(scene::light::object_type_id); for (const scene::object_base* object: lights) { const scene::light& light = static_cast(*object); switch (light.get_light_type()) { // Add directional light case scene::light_type::directional: { const scene::directional_light& directional_light = static_cast(light); const std::size_t index = directional_light_count; ++directional_light_count; if (directional_light_count > directional_light_colors.size()) { directional_light_colors.resize(directional_light_count); directional_light_directions.resize(directional_light_count); } directional_light_colors[index] = directional_light.get_colored_illuminance() * ctx.camera->get_exposure_normalization(); directional_light_directions[index] = directional_light.get_direction(); // Add directional shadow if (directional_light.is_shadow_caster() && directional_light.get_shadow_framebuffer()) { const std::size_t index = directional_shadow_count; ++directional_shadow_count; if (directional_shadow_count > directional_shadow_maps.size()) { directional_shadow_maps.resize(directional_shadow_count); directional_shadow_biases.resize(directional_shadow_count); directional_shadow_splits.resize(directional_shadow_count); directional_shadow_matrices.resize(directional_shadow_count); } directional_shadow_maps[index] = static_cast(directional_light.get_shadow_framebuffer()->get_depth_attachment()); directional_shadow_biases[index] = directional_light.get_shadow_bias(); directional_shadow_splits[index] = &directional_light.get_shadow_cascade_distances(); directional_shadow_matrices[index] = &directional_light.get_shadow_cascade_matrices(); } break; } // Add spot_light case scene::light_type::spot: { const scene::spot_light& spot_light = static_cast(light); const std::size_t index = spot_light_count; ++spot_light_count; if (spot_light_count > spot_light_colors.size()) { spot_light_colors.resize(spot_light_count); spot_light_positions.resize(spot_light_count); spot_light_directions.resize(spot_light_count); spot_light_cutoffs.resize(spot_light_count); } spot_light_colors[index] = spot_light.get_luminous_flux() * ctx.camera->get_exposure_normalization(); spot_light_positions[index] = spot_light.get_translation(); spot_light_directions[index] = spot_light.get_direction(); spot_light_cutoffs[index] = spot_light.get_cosine_cutoff(); break; } // Add point light case scene::light_type::point: { const scene::point_light& point_light = static_cast(light); const std::size_t index = point_light_count; ++point_light_count; if (point_light_count > point_light_colors.size()) { point_light_colors.resize(point_light_count); point_light_positions.resize(point_light_count); } point_light_colors[index] = point_light.get_colored_luminous_flux() * ctx.camera->get_exposure_normalization(); point_light_positions[index] = point_light.get_translation(); break; } // Add rectangle light case scene::light_type::rectangle: { const scene::rectangle_light& rectangle_light = static_cast(light); const std::size_t index = rectangle_light_count; ++rectangle_light_count; if (rectangle_light_count > rectangle_light_colors.size()) { rectangle_light_colors.resize(rectangle_light_count); rectangle_light_corners.resize(rectangle_light_count * 4); } rectangle_light_colors[index] = rectangle_light.get_colored_luminance() * ctx.camera->get_exposure_normalization(); const auto corners = rectangle_light.get_corners(); for (std::size_t i = 0; i < 4; ++i) { rectangle_light_corners[index * 4 + i] = corners[i]; } break; } default: break; } } // Generate lighting state hash lighting_state_hash = std::hash{}(light_probe_count); lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(directional_light_count)); lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(directional_shadow_count)); lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(point_light_count)); lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(spot_light_count)); lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(rectangle_light_count)); } void material_pass::evaluate_misc(const render::context& ctx) { time = ctx.t; timestep = ctx.dt; subframe = ctx.alpha; const auto viewport_size = framebuffer->get_dimensions(); resolution = { static_cast(std::get<0>(viewport_size)), static_cast(std::get<1>(viewport_size)) }; ///mouse_position = ... } std::unique_ptr material_pass::generate_shader_program(const gl::shader_template& shader_template) const { std::unordered_map definitions; definitions["VERTEX_POSITION"] = std::to_string(vertex_attribute::position); definitions["VERTEX_UV"] = std::to_string(vertex_attribute::uv); definitions["VERTEX_NORMAL"] = std::to_string(vertex_attribute::normal); definitions["VERTEX_TANGENT"] = std::to_string(vertex_attribute::tangent); definitions["VERTEX_COLOR"] = std::to_string(vertex_attribute::color); definitions["VERTEX_BONE_INDEX"] = std::to_string(vertex_attribute::bone_index); definitions["VERTEX_BONE_WEIGHT"] = std::to_string(vertex_attribute::bone_weight); definitions["VERTEX_BARYCENTRIC"] = std::to_string(vertex_attribute::barycentric); definitions["VERTEX_TARGET"] = std::to_string(vertex_attribute::target); definitions["FRAGMENT_OUTPUT_COLOR"] = std::to_string(0); definitions["LIGHT_PROBE_COUNT"] = std::to_string(light_probe_count); definitions["DIRECTIONAL_LIGHT_COUNT"] = std::to_string(directional_light_count); definitions["DIRECTIONAL_SHADOW_COUNT"] = std::to_string(directional_shadow_count); definitions["POINT_LIGHT_COUNT"] = std::to_string(point_light_count); definitions["SPOT_LIGHT_COUNT"] = std::to_string(spot_light_count); definitions["RECTANGLE_LIGHT_COUNT"] = std::to_string(rectangle_light_count); auto shader_program = shader_template.build(definitions); if (!shader_program->linked()) { debug::log::error("Failed to link material shader program: {}", shader_program->info()); debug::log::warning("{}", shader_template.configure(gl::shader_stage::fragment, definitions)); } return shader_program; } void material_pass::build_shader_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program) const { // Bind shader program command_buffer.emplace_back([&](){rasterizer->use_program(shader_program);}); // Update camera variables if (auto view_var = shader_program.variable("view")) { command_buffer.emplace_back([&, view_var](){view_var->update(*view);}); } if (auto projection_var = shader_program.variable("projection")) { command_buffer.emplace_back([&, projection_var](){projection_var->update(*projection);}); } if (auto view_projection_var = shader_program.variable("view_projection")) { command_buffer.emplace_back([&, view_projection_var](){view_projection_var->update(*view_projection);}); } if (auto camera_position_var = shader_program.variable("camera_position")) { command_buffer.emplace_back([&, camera_position_var](){camera_position_var->update(*camera_position);}); } if (auto camera_exposure_var = shader_program.variable("camera_exposure")) { command_buffer.emplace_back([&, camera_exposure_var](){camera_exposure_var->update(camera_exposure);}); } if (auto clip_depth_var = shader_program.variable("clip_depth")) { command_buffer.emplace_back([&, clip_depth_var](){clip_depth_var->update(clip_depth);}); } // Update IBL variables if (auto brdf_lut_var = shader_program.variable("brdf_lut")) { command_buffer.emplace_back ( [&, brdf_lut_var]() { brdf_lut_var->update(*brdf_lut); } ); } // Update light probe variables if (light_probe_count) { if (auto light_probe_luminance_texture_var = shader_program.variable("light_probe_luminance_texture")) { command_buffer.emplace_back ( [&, light_probe_luminance_texture_var]() { light_probe_luminance_texture_var->update(*light_probe_luminance_texture); } ); } if (auto light_probe_luminance_mip_scale_var = shader_program.variable("light_probe_luminance_mip_scale")) { command_buffer.emplace_back ( [&, light_probe_luminance_mip_scale_var]() { light_probe_luminance_mip_scale_var->update(std::max(static_cast(light_probe_luminance_texture->get_mip_count()) - 4.0f, 0.0f)); } ); } if (auto light_probe_illuminance_texture_var = shader_program.variable("light_probe_illuminance_texture")) { command_buffer.emplace_back ( [&, light_probe_illuminance_texture_var]() { light_probe_illuminance_texture_var->update(*light_probe_illuminance_texture); } ); } } // Update LTC variables if (auto ltc_lut_1_var = shader_program.variable("ltc_lut_1")) { if (auto ltc_lut_2_var = shader_program.variable("ltc_lut_2")) { command_buffer.emplace_back ( [&, ltc_lut_1_var, ltc_lut_2_var]() { ltc_lut_1_var->update(*ltc_lut_1); ltc_lut_2_var->update(*ltc_lut_2); } ); } } if (rectangle_light_count) { if (auto rectangle_light_colors_var = shader_program.variable("rectangle_light_colors")) { auto rectangle_light_corners_var = shader_program.variable("rectangle_light_corners"); if (rectangle_light_corners_var) { command_buffer.emplace_back ( [&, rectangle_light_colors_var, rectangle_light_corners_var]() { rectangle_light_colors_var->update(std::span{rectangle_light_colors.data(), rectangle_light_count}); rectangle_light_corners_var->update(std::span{rectangle_light_corners.data(), rectangle_light_count * 4}); } ); } } } // Update directional light variables if (directional_light_count) { if (auto directional_light_colors_var = shader_program.variable("directional_light_colors")) { if (auto directional_light_directions_var = shader_program.variable("directional_light_directions")) { command_buffer.emplace_back ( [&, directional_light_colors_var, directional_light_directions_var]() { directional_light_colors_var->update(std::span{directional_light_colors.data(), directional_light_count}); directional_light_directions_var->update(std::span{directional_light_directions.data(), directional_light_count}); } ); } } } // Update directional shadow variables if (directional_shadow_count) { if (auto directional_shadow_maps_var = shader_program.variable("directional_shadow_maps")) { auto directional_shadow_biases_var = shader_program.variable("directional_shadow_biases"); auto directional_shadow_splits_var = shader_program.variable("directional_shadow_splits"); auto directional_shadow_matrices_var = shader_program.variable("directional_shadow_matrices"); if (directional_shadow_maps_var && directional_shadow_biases_var && directional_shadow_splits_var && directional_shadow_matrices_var) { command_buffer.emplace_back ( [&, directional_shadow_maps_var, directional_shadow_biases_var, directional_shadow_splits_var, directional_shadow_matrices_var]() { directional_shadow_maps_var->update(std::span{directional_shadow_maps.data(), directional_shadow_count}); directional_shadow_biases_var->update(std::span{directional_shadow_biases.data(), directional_shadow_count}); std::size_t offset = 0; for (std::size_t i = 0; i < directional_shadow_count; ++i) { directional_shadow_splits_var->update(*directional_shadow_splits[i], offset * 4); directional_shadow_matrices_var->update(*directional_shadow_matrices[i], offset); offset += directional_shadow_splits[i]->size(); } } ); } } } // Update point light variables if (point_light_count) { if (auto point_light_colors_var = shader_program.variable("point_light_colors")) { auto point_light_positions_var = shader_program.variable("point_light_positions"); if (point_light_positions_var) { command_buffer.emplace_back ( [&, point_light_colors_var, point_light_positions_var]() { point_light_colors_var->update(std::span{point_light_colors.data(), point_light_count}); point_light_positions_var->update(std::span{point_light_positions.data(), point_light_count}); } ); } } } // Update spot light variables if (spot_light_count) { if (auto spot_light_colors_var = shader_program.variable("spot_light_colors")) { auto spot_light_positions_var = shader_program.variable("spot_light_positions"); auto spot_light_directions_var = shader_program.variable("spot_light_directions"); auto spot_light_cutoffs_var = shader_program.variable("spot_light_cutoffs"); if (spot_light_positions_var && spot_light_directions_var && spot_light_cutoffs_var) { command_buffer.emplace_back ( [&, spot_light_colors_var, spot_light_positions_var, spot_light_directions_var, spot_light_cutoffs_var]() { spot_light_colors_var->update(std::span{spot_light_colors.data(), spot_light_count}); spot_light_positions_var->update(std::span{spot_light_positions.data(), spot_light_count}); spot_light_directions_var->update(std::span{spot_light_directions.data(), spot_light_count}); spot_light_cutoffs_var->update(std::span{spot_light_cutoffs.data(), spot_light_count}); } ); } } } // Update time variable if (auto time_var = shader_program.variable("time")) { command_buffer.emplace_back([&, time_var](){time_var->update(time);}); } // Update timestep variable if (auto timestep_var = shader_program.variable("timestep")) { command_buffer.emplace_back([&, timestep_var](){timestep_var->update(timestep);}); } // Update frame variable if (auto frame_var = shader_program.variable("frame")) { command_buffer.emplace_back([&, frame_var](){frame_var->update(frame);}); } // Update subframe variable if (auto subframe_var = shader_program.variable("subframe")) { command_buffer.emplace_back([&, subframe_var](){subframe_var->update(subframe);}); } // Update resolution variable if (auto resolution_var = shader_program.variable("resolution")) { command_buffer.emplace_back([&, resolution_var](){resolution_var->update(resolution);}); } // Update mouse position variable if (auto mouse_position_var = shader_program.variable("mouse_position")) { command_buffer.emplace_back([&, mouse_position_var](){mouse_position_var->update(mouse_position);}); } } void material_pass::build_geometry_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program) const { // Update model matrix variable if (auto model_var = shader_program.variable("model")) { command_buffer.emplace_back([&, model_var](){model_var->update(*model);}); } // Update normal-model matrix variable if (auto normal_model_var = shader_program.variable("normal_model")) { command_buffer.emplace_back ( [&, normal_model_var]() { normal_model_var->update(math::transpose(math::inverse(math::matrix(*model)))); } ); } // Update model-view matrix and normal-model-view matrix variables auto model_view_var = shader_program.variable("model_view"); auto normal_model_view_var = shader_program.variable("normal_model_view"); if (model_view_var && normal_model_view_var) { command_buffer.emplace_back ( [&, model_view_var, normal_model_view_var]() { const auto model_view = (*view) * (*model); model_view_var->update(model_view); normal_model_view_var->update(math::transpose(math::inverse(math::matrix(model_view)))); } ); } else { if (model_view_var) { command_buffer.emplace_back([&, model_view_var](){model_view_var->update((*view) * (*model));}); } else if (normal_model_view_var) { command_buffer.emplace_back ( [&, normal_model_view_var]() { const auto model_view = (*view) * (*model); normal_model_view_var->update(math::transpose(math::inverse(math::matrix(model_view)))); } ); } } // Update model-view-projection matrix variable if (auto model_view_projection_var = shader_program.variable("model_view_projection")) { command_buffer.emplace_back([&, model_view_projection_var](){model_view_projection_var->update((*view_projection) * (*model));}); } // Update matrix palette variable if (auto matrix_palette_var = shader_program.variable("matrix_palette")) { command_buffer.emplace_back([&, matrix_palette_var](){matrix_palette_var->update(matrix_palette);}); } } void material_pass::build_material_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program, const material& material) const { for (const auto& [key, material_var]: material.get_variables()) { if (!material_var) { continue; } const auto shader_var = shader_program.variable(key); if (!shader_var) { continue; } const std::size_t size = std::min(material_var->size(), shader_var->size()); switch (shader_var->type()) { /// @TODO render::material_bool is broken due to the std::vector specialization. // case gl::shader_variable_type::bool1: // if (material_var->type() == material_variable_type::bool1) // { // command_buffer.emplace_back // ( // [size, shader_var, material_var = std::static_pointer_cast(material_var)]() // { // shader_var->update(std::span{material_var->data(), size}); // } // ); // } // break; case gl::shader_variable_type::bool2: if (material_var->type() == material_variable_type::bool2) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::bool3: if (material_var->type() == material_variable_type::bool3) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::bool4: if (material_var->type() == material_variable_type::bool4) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::int1: if (material_var->type() == material_variable_type::int1) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::int2: if (material_var->type() == material_variable_type::int2) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::int3: if (material_var->type() == material_variable_type::int3) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::int4: if (material_var->type() == material_variable_type::int4) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::uint1: if (material_var->type() == material_variable_type::uint1) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::uint2: if (material_var->type() == material_variable_type::uint2) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::uint3: if (material_var->type() == material_variable_type::uint3) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::uint4: if (material_var->type() == material_variable_type::uint4) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::float1: if (material_var->type() == material_variable_type::float1) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::float2: if (material_var->type() == material_variable_type::float2) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::float3: if (material_var->type() == material_variable_type::float3) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::float4: if (material_var->type() == material_variable_type::float4) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::float2x2: if (material_var->type() == material_variable_type::float2x2) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::float3x3: if (material_var->type() == material_variable_type::float3x3) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::float4x4: if (material_var->type() == material_variable_type::float4x4) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span{material_var->data(), size}); } ); } break; case gl::shader_variable_type::texture_1d: if (material_var->type() == material_variable_type::texture_1d) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span>{material_var->data(), size}); } ); } break; case gl::shader_variable_type::texture_2d: if (material_var->type() == material_variable_type::texture_2d) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span>{material_var->data(), size}); } ); } break; case gl::shader_variable_type::texture_3d: if (material_var->type() == material_variable_type::texture_3d) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span>{material_var->data(), size}); } ); } break; case gl::shader_variable_type::texture_cube: if (material_var->type() == material_variable_type::texture_cube) { command_buffer.emplace_back ( [size, shader_var, material_var = std::static_pointer_cast(material_var)]() { shader_var->update(std::span>{material_var->data(), size}); } ); } break; default: break; } } } } // namespace render