#pragma once #include "app.h" #include "app_core/document_animation.h" #include "legacy_canvas_stroke_composite_services.h" #include "legacy_canvas_stroke_erase_services.h" #include "shader.h" #include #include #include namespace pp::panopainter { struct LegacyCanvasDrawMergeCheckerboardUniforms { glm::mat4 mvp { 1.0f }; bool colorize = false; }; struct LegacyCanvasDrawMergeTextureUniforms { glm::mat4 mvp { 1.0f }; int texture_slot = 0; }; struct LegacyCanvasDrawMergeAtlasUniforms { glm::mat4 mvp { 1.0f }; int texture_slot = 0; glm::vec2 offset {}; glm::vec2 size { 1.0f }; }; struct LegacyCanvasDrawMergeEquirectUniforms { glm::mat4 mvp { 1.0f }; int texture_slot = 0; }; struct LegacyCanvasDrawMergeTextureMaskUniforms { int texture_slot = 0; glm::vec2 pattern_offset {}; }; struct LegacyCanvasDrawMergeTextureAlphaUniforms { glm::mat4 mvp { 1.0f }; int texture_slot = 0; float alpha = 1.0f; int highlight = 0; }; struct LegacyCanvasDrawMergeTextureBlendUniforms { glm::mat4 mvp { 1.0f }; int texture_slot = 0; int destination_texture_slot = 2; bool use_destination_texture = false; int blend_mode = 0; float alpha = 1.0f; }; struct LegacyCanvasDrawMergeTextureColorizeUniforms { glm::mat4 mvp { 1.0f }; int texture_slot = 0; glm::vec4 color { 1.0f }; }; struct LegacyCanvasDrawMergeShaderExecution { std::function use_shader; std::function set_int; std::function set_float; std::function set_vec2; std::function set_vec4; std::function set_mat4; }; inline void setup_legacy_canvas_draw_merge_checkerboard_shader( const LegacyCanvasDrawMergeCheckerboardUniforms& uniforms); struct LegacyCanvasDrawMergeLayerBlendUniforms { LegacyCanvasDrawMergeTextureBlendUniforms shader; bool copy_destination = false; }; struct LegacyCanvasDrawMergeLayerBlendExecution { std::function unbind_merge_framebuffer; std::function bind_samplers; std::function bind_merge_texture; std::function bind_destination_texture; std::function copy_destination_framebuffer; std::function draw; std::function unbind_destination_texture; std::function unbind_merge_texture; }; struct LegacyCanvasDrawMergeLayerTextureExecution { std::function bind_sampler; std::function bind_layer_texture; std::function draw; std::function unbind_layer_texture; }; struct LegacyCanvasDrawMergeLayerCompositeExecution { std::function execute_temporary_erase; std::function execute_temporary_paint; std::function execute_layer_texture; std::function execute_layer_blend; }; struct LegacyCanvasDrawMergeLayerPlaneExecution { std::function bind_blender_framebuffer; std::function clear_blender_framebuffer; std::function unbind_blender_framebuffer; std::function prepare_temporary_erase; std::function draw_temporary_erase_frame; std::function cleanup_temporary_erase; std::function prepare_temporary_paint; std::function draw_temporary_paint_frame; std::function cleanup_temporary_paint; std::function prepare_layer_texture; std::function draw_layer_texture_frame; std::function cleanup_layer_texture; std::function draw_blend; }; struct LegacyCanvasDrawMergeLayerPathExecution { std::function bind_blender_framebuffer; std::function clear_blender_framebuffer; std::function unbind_blender_framebuffer; std::function prepare_temporary_erase; std::function cleanup_temporary_erase; std::function prepare_temporary_paint; std::function cleanup_temporary_paint; std::function prepare_layer_texture; std::function cleanup_layer_texture; std::function draw_blend; std::function draw_debug_outline; std::function draw_frame; }; struct LegacyCanvasDrawMergeLayerPathGlUniforms { LegacyStrokeEraseUniforms temporary_erase; LegacyStrokeCompositeUniforms temporary_paint; LegacyCanvasDrawMergeTextureAlphaUniforms layer_texture; LegacyCanvasDrawMergeLayerBlendUniforms blend; bool use_nearest_sampler = false; bool use_dual_texture = false; }; struct LegacyCanvasDrawMergeLayerPathGlExecution { std::function bind_blender_framebuffer; std::function clear_blender_framebuffer; std::function unbind_blender_framebuffer; std::function bind_sampler; std::function bind_nearest_sampler; std::function bind_stencil_sampler; std::function set_active_texture_unit; std::function bind_temporary_texture; std::function unbind_temporary_texture; std::function bind_smask_texture; std::function unbind_smask_texture; std::function bind_temporary_dual_texture; std::function unbind_temporary_dual_texture; std::function bind_pattern_texture; std::function draw_face; std::function bind_blender_texture; std::function unbind_blender_texture; std::function bind_destination_texture; std::function unbind_destination_texture; std::function copy_destination_framebuffer; std::function draw_debug_outline; std::function draw_frame; }; struct LegacyCanvasDrawLayerVisit { size_t layer_index = 0; int plane_index = 0; int z = 0; int first_frame = 0; int last_frame = 0; }; struct LegacyCanvasDrawMergeTemporaryCompositeExecution { std::function setup; std::function bind_samplers; std::function bind_textures; std::function draw; std::function unbind_textures; }; struct LegacyCanvasDrawMergeBackgroundSetupUniforms { bool draw_merged = false; bool use_blend = false; }; struct LegacyCanvasDrawMergeBackgroundSetupExecution { std::function disable_blend; std::function draw_checkerboard_plane; }; template < typename Setup, typename BindSamplers, typename BindTextures, typename Draw, typename UnbindTextures> [[nodiscard]] inline LegacyCanvasDrawMergeTemporaryCompositeExecution make_legacy_canvas_draw_merge_temporary_erase_composite( Setup&& setup, BindSamplers&& bind_samplers, BindTextures&& bind_textures, Draw&& draw, UnbindTextures&& unbind_textures) { return LegacyCanvasDrawMergeTemporaryCompositeExecution { .setup = std::forward(setup), .bind_samplers = std::forward(bind_samplers), .bind_textures = std::forward(bind_textures), .draw = std::forward(draw), .unbind_textures = std::forward(unbind_textures), }; } template < typename Setup, typename BindSamplers, typename BindTextures, typename Draw, typename UnbindTextures> [[nodiscard]] inline LegacyCanvasDrawMergeTemporaryCompositeExecution make_legacy_canvas_draw_merge_temporary_paint_composite( Setup&& setup, BindSamplers&& bind_samplers, BindTextures&& bind_textures, Draw&& draw, UnbindTextures&& unbind_textures) { return LegacyCanvasDrawMergeTemporaryCompositeExecution { .setup = std::forward(setup), .bind_samplers = std::forward(bind_samplers), .bind_textures = std::forward(bind_textures), .draw = std::forward(draw), .unbind_textures = std::forward(unbind_textures), }; } struct LegacyCanvasDrawMergePlaneSetupUniforms { LegacyCanvasDrawMergeCheckerboardUniforms checkerboard; bool use_blend = false; bool draw_checkerboard = false; }; struct LegacyCanvasDrawMergePlaneSetupExecution { std::function clear_plane; std::function disable_blend; std::function enable_blend; std::function draw; }; struct LegacyCanvasDrawMergeFinalPlaneCompositeUniforms { LegacyCanvasDrawMergeCheckerboardUniforms checkerboard; LegacyCanvasDrawMergeTextureUniforms texture; bool draw_checkerboard = false; }; struct LegacyCanvasDrawMergeFinalPlaneCompositeExecution { std::function bind_merged_texture_copy_target; std::function copy_merged_framebuffer; std::function enable_blend; std::function draw; std::function bind_sampler; std::function bind_merged_texture; std::function unbind_merged_texture; }; struct LegacyCanvasDrawMergeCacheToScreenCompositeUniforms { LegacyCanvasDrawMergeCheckerboardUniforms checkerboard; LegacyCanvasDrawMergeTextureUniforms texture; }; struct LegacyCanvasDrawMergeCacheToScreenCompositeExecution { std::function enable_blend; std::function draw_checkerboard_plane; std::function bind_sampler; std::function bind_cache_texture; std::function draw_cache_texture; std::function unbind_cache_texture; }; struct LegacyCanvasDrawMergeDisplayResolveUniforms { LegacyCanvasDrawMergeTextureUniforms texture; }; struct LegacyCanvasDrawMergeDisplayResolveExecution { std::function unbind_resolve_framebuffer; std::function clear_color_buffer; std::function apply_viewport; std::function bind_sampler; std::function bind_resolve_texture; std::function draw; std::function unbind_resolve_texture; }; struct LegacyCanvasDrawMergePostDrawExecution { std::function draw_mask_free; std::function draw_mask_line; std::function draw_smask_faces; std::function draw_grid_modes; std::function draw_heightmap; std::function draw_current_modes; }; template [[nodiscard]] inline auto make_legacy_canvas_draw_merge_heightmap_draw( GridT* grid, const glm::mat4& proj, const glm::mat4& camera) { return [grid, proj, camera] { grid->draw_heightmap(proj, camera, false); }; } template [[nodiscard]] inline auto make_legacy_canvas_draw_merge_current_modes_draw( ModesT* modes, const glm::mat4& ortho_proj, const glm::mat4& proj, const glm::mat4& camera) { return [modes, ortho_proj, proj, camera] { for (auto& mode : *modes) mode->on_Draw(ortho_proj, proj, camera); }; } template [[nodiscard]] inline auto make_legacy_canvas_draw_merge_grid_modes_draw( ModesT* modes, const glm::mat4& ortho_proj, const glm::mat4& proj, const glm::mat4& camera) { return [modes, ortho_proj, proj, camera] { for (auto& mode : *modes) mode->on_Draw(ortho_proj, proj, camera); }; } template [[nodiscard]] inline auto make_legacy_canvas_draw_merge_background_checkerboard_plane( glm::mat4 proj, glm::mat4 camera, float layer_scale, PlaneTransform plane_transform, DrawPlane draw_plane) { return [proj, camera, layer_scale, plane_transform, draw_plane](int plane_index) { auto checkerboard_uniforms = LegacyCanvasDrawMergeCheckerboardUniforms { .mvp = proj * camera * glm::scale(glm::vec3(layer_scale)) * plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1.f)), .colorize = false, }; setup_legacy_canvas_draw_merge_checkerboard_shader(checkerboard_uniforms); draw_plane(); }; } struct LegacyCanvasDrawMergeSmaskFacesExecution { std::function set_active_texture_unit; std::function enable_blend; std::function bind_face_texture; std::function draw_face; std::function unbind_face_texture; }; [[nodiscard]] inline LegacyCanvasDrawMergeShaderExecution legacy_shader_manager_draw_merge_execution() noexcept { return { .use_shader = [](kShader shader) { ShaderManager::use(shader); }, .set_int = [](kShaderUniform uniform, int value) { ShaderManager::u_int(uniform, value); }, .set_float = [](kShaderUniform uniform, float value) { ShaderManager::u_float(uniform, value); }, .set_vec2 = [](kShaderUniform uniform, const glm::vec2& value) { ShaderManager::u_vec2(uniform, value); }, .set_vec4 = [](kShaderUniform uniform, const glm::vec4& value) { ShaderManager::u_vec4(uniform, value); }, .set_mat4 = [](kShaderUniform uniform, const glm::mat4& value) { ShaderManager::u_mat4(uniform, value); }, }; } inline void apply_legacy_canvas_draw_merge_mvp( const glm::mat4& mvp, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.set_mat4(kShaderUniform::MVP, mvp); } inline void apply_legacy_canvas_draw_merge_mvp(const glm::mat4& mvp) { apply_legacy_canvas_draw_merge_mvp(mvp, legacy_shader_manager_draw_merge_execution()); } inline void setup_legacy_canvas_draw_merge_checkerboard_shader( const LegacyCanvasDrawMergeCheckerboardUniforms& uniforms, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.use_shader(kShader::Checkerboard); execution.set_int(kShaderUniform::Colorize, uniforms.colorize); execution.set_mat4(kShaderUniform::MVP, uniforms.mvp); } inline void setup_legacy_canvas_draw_merge_checkerboard_shader( const LegacyCanvasDrawMergeCheckerboardUniforms& uniforms) { setup_legacy_canvas_draw_merge_checkerboard_shader(uniforms, legacy_shader_manager_draw_merge_execution()); } inline void setup_legacy_canvas_draw_merge_texture_shader( const LegacyCanvasDrawMergeTextureUniforms& uniforms, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.use_shader(kShader::Texture); execution.set_int(kShaderUniform::Tex, uniforms.texture_slot); execution.set_mat4(kShaderUniform::MVP, uniforms.mvp); } inline void setup_legacy_canvas_draw_merge_texture_shader( const LegacyCanvasDrawMergeTextureUniforms& uniforms) { setup_legacy_canvas_draw_merge_texture_shader(uniforms, legacy_shader_manager_draw_merge_execution()); } inline void setup_legacy_canvas_draw_merge_atlas_shader( const LegacyCanvasDrawMergeAtlasUniforms& uniforms, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.use_shader(kShader::Atlas); execution.set_vec2(kShaderUniform::Tof, uniforms.offset); execution.set_vec2(kShaderUniform::Tsz, uniforms.size); execution.set_int(kShaderUniform::Tex, uniforms.texture_slot); execution.set_mat4(kShaderUniform::MVP, uniforms.mvp); } inline void setup_legacy_canvas_draw_merge_atlas_shader( const LegacyCanvasDrawMergeAtlasUniforms& uniforms) { setup_legacy_canvas_draw_merge_atlas_shader(uniforms, legacy_shader_manager_draw_merge_execution()); } inline void setup_legacy_canvas_draw_merge_equirect_shader( const LegacyCanvasDrawMergeEquirectUniforms& uniforms, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.use_shader(kShader::Equirect); execution.set_mat4(kShaderUniform::MVP, uniforms.mvp); execution.set_int(kShaderUniform::Tex, uniforms.texture_slot); } inline void setup_legacy_canvas_draw_merge_equirect_shader( const LegacyCanvasDrawMergeEquirectUniforms& uniforms) { setup_legacy_canvas_draw_merge_equirect_shader(uniforms, legacy_shader_manager_draw_merge_execution()); } inline void setup_legacy_canvas_draw_merge_texture_mask_shader( const LegacyCanvasDrawMergeTextureMaskUniforms& uniforms, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.use_shader(kShader::TextureMask); execution.set_int(kShaderUniform::Tex, uniforms.texture_slot); execution.set_vec2(kShaderUniform::PatternOffset, uniforms.pattern_offset); } inline void setup_legacy_canvas_draw_merge_texture_mask_shader( const LegacyCanvasDrawMergeTextureMaskUniforms& uniforms) { setup_legacy_canvas_draw_merge_texture_mask_shader(uniforms, legacy_shader_manager_draw_merge_execution()); } inline void setup_legacy_canvas_draw_merge_texture_alpha_shader( const LegacyCanvasDrawMergeTextureAlphaUniforms& uniforms, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.use_shader(kShader::TextureAlpha); execution.set_int(kShaderUniform::Tex, uniforms.texture_slot); execution.set_float(kShaderUniform::Alpha, uniforms.alpha); execution.set_int(kShaderUniform::Highlight, uniforms.highlight); execution.set_mat4(kShaderUniform::MVP, uniforms.mvp); } inline void setup_legacy_canvas_draw_merge_texture_alpha_shader( const LegacyCanvasDrawMergeTextureAlphaUniforms& uniforms) { setup_legacy_canvas_draw_merge_texture_alpha_shader(uniforms, legacy_shader_manager_draw_merge_execution()); } inline void setup_legacy_canvas_draw_merge_texture_blend_shader( const LegacyCanvasDrawMergeTextureBlendUniforms& uniforms, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.use_shader(kShader::TextureBlend); execution.set_int(kShaderUniform::Tex, uniforms.texture_slot); if (uniforms.use_destination_texture) { execution.set_int(kShaderUniform::TexBG, uniforms.destination_texture_slot); } execution.set_int(kShaderUniform::BlendMode, uniforms.blend_mode); execution.set_float(kShaderUniform::Alpha, uniforms.alpha); execution.set_mat4(kShaderUniform::MVP, uniforms.mvp); } inline void setup_legacy_canvas_draw_merge_texture_blend_shader( const LegacyCanvasDrawMergeTextureBlendUniforms& uniforms) { setup_legacy_canvas_draw_merge_texture_blend_shader(uniforms, legacy_shader_manager_draw_merge_execution()); } inline void setup_legacy_canvas_draw_merge_texture_colorize_shader( const LegacyCanvasDrawMergeTextureColorizeUniforms& uniforms, const LegacyCanvasDrawMergeShaderExecution& execution) noexcept { execution.use_shader(kShader::TextureColorize); execution.set_int(kShaderUniform::Tex, uniforms.texture_slot); execution.set_vec4(kShaderUniform::Col, uniforms.color); execution.set_mat4(kShaderUniform::MVP, uniforms.mvp); } inline void setup_legacy_canvas_draw_merge_texture_colorize_shader( const LegacyCanvasDrawMergeTextureColorizeUniforms& uniforms) { setup_legacy_canvas_draw_merge_texture_colorize_shader(uniforms, legacy_shader_manager_draw_merge_execution()); } inline void execute_legacy_canvas_draw_merge_layer_blend( const LegacyCanvasDrawMergeLayerBlendUniforms& uniforms, const LegacyCanvasDrawMergeLayerBlendExecution& execution) { execution.unbind_merge_framebuffer(); execution.bind_samplers(); setup_legacy_canvas_draw_merge_texture_blend_shader(uniforms.shader); execution.bind_merge_texture(); if (uniforms.copy_destination) { execution.bind_destination_texture(); execution.copy_destination_framebuffer(); } execution.draw(); if (uniforms.copy_destination) { execution.unbind_destination_texture(); } execution.unbind_merge_texture(); } inline void execute_legacy_canvas_draw_merge_layer_texture( const LegacyCanvasDrawMergeTextureAlphaUniforms& uniforms, const LegacyCanvasDrawMergeLayerTextureExecution& execution) { execution.bind_sampler(); setup_legacy_canvas_draw_merge_texture_alpha_shader(uniforms); execution.bind_layer_texture(); execution.draw(); execution.unbind_layer_texture(); } template [[nodiscard]] inline auto make_legacy_canvas_draw_merge_layer_texture_draw( LayerMergeT* layer_merge, SamplerT* sampler, FacePlaneT* face_plane, SetActiveTextureUnit set_active_texture_unit, glm::mat4 proj, glm::mat4 camera, PlaneTransform plane_transform) { return [layer_merge, sampler, face_plane, set_active_texture_unit, proj, camera, plane_transform](int plane_index) { const auto mvp = proj * camera * plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); execute_legacy_canvas_draw_merge_layer_texture( { .mvp = mvp, .texture_slot = 0, .alpha = 1.f, .highlight = false, }, { .bind_sampler = [sampler, set_active_texture_unit] { sampler->bind(0); set_active_texture_unit(0); }, .bind_layer_texture = [layer_merge, plane_index] { layer_merge->rtt(plane_index).bindTexture(); }, .draw = [face_plane] { face_plane->draw_fill(); }, .unbind_layer_texture = [layer_merge, plane_index] { layer_merge->rtt(plane_index).unbindTexture(); }, }); }; } template [[nodiscard]] inline auto make_legacy_canvas_draw_merge_layer_frame_draw( LayerT* layer, FacePlaneT* face_plane, SetActiveTextureUnit set_active_texture_unit, int plane_index, float layer_opacity) { return [layer, face_plane, set_active_texture_unit, plane_index, layer_opacity](int frame, float onion_alpha) { ShaderManager::u_float(kShaderUniform::Alpha, layer_opacity * onion_alpha); set_active_texture_unit(0); layer->rtt(plane_index, frame).bindTexture(); face_plane->draw_fill(); set_active_texture_unit(0); layer->rtt(plane_index, frame).unbindTexture(); }; } inline void execute_legacy_canvas_draw_merge_layer_composite( bool is_temporary_erase, bool is_temporary_paint, bool use_blend, const LegacyCanvasDrawMergeLayerCompositeExecution& execution) { if (is_temporary_erase) { execution.execute_temporary_erase(); } else if (is_temporary_paint) { execution.execute_temporary_paint(); } else { execution.execute_layer_texture(); } if (use_blend) { execution.execute_layer_blend(); } } inline void execute_legacy_canvas_draw_merge_layer_plane( bool is_temporary_erase, bool is_temporary_paint, bool use_blend, int first_frame, int last_frame, const std::function& frame_alpha, const LegacyCanvasDrawMergeLayerPlaneExecution& execution) { if (use_blend) { execution.bind_blender_framebuffer(); execution.clear_blender_framebuffer(); } if (is_temporary_erase) { execution.prepare_temporary_erase(); for (int frame = first_frame; frame <= last_frame; frame++) { execution.draw_temporary_erase_frame(frame, frame_alpha(frame)); } execution.cleanup_temporary_erase(); } else if (is_temporary_paint) { execution.prepare_temporary_paint(); for (int frame = first_frame; frame <= last_frame; frame++) { execution.draw_temporary_paint_frame(frame, frame_alpha(frame)); } execution.cleanup_temporary_paint(); } else { execution.prepare_layer_texture(); for (int frame = first_frame; frame <= last_frame; frame++) { execution.draw_layer_texture_frame(frame, frame_alpha(frame)); } execution.cleanup_layer_texture(); } if (use_blend) { execution.unbind_blender_framebuffer(); execution.draw_blend(); } } inline void execute_legacy_canvas_draw_merge_layer_path( bool is_temporary_erase, bool is_temporary_paint, bool use_blend, int first_frame, int last_frame, const std::function& frame_alpha, const LegacyCanvasDrawMergeLayerPathExecution& execution) { if (use_blend) { execution.bind_blender_framebuffer(); execution.clear_blender_framebuffer(); } if (is_temporary_erase) { execution.prepare_temporary_erase(); for (int frame = first_frame; frame <= last_frame; frame++) { execution.draw_frame(frame, frame_alpha(frame)); } execution.cleanup_temporary_erase(); } else if (is_temporary_paint) { execution.prepare_temporary_paint(); for (int frame = first_frame; frame <= last_frame; frame++) { execution.draw_frame(frame, frame_alpha(frame)); } execution.cleanup_temporary_paint(); } else { execution.prepare_layer_texture(); for (int frame = first_frame; frame <= last_frame; frame++) { execution.draw_frame(frame, frame_alpha(frame)); } execution.cleanup_layer_texture(); } if (use_blend) { execution.unbind_blender_framebuffer(); execution.draw_blend(); } execution.draw_debug_outline(); } [[nodiscard]] inline LegacyCanvasDrawMergeLayerPathExecution make_legacy_canvas_draw_merge_layer_path_gl_execution( const LegacyCanvasDrawMergeLayerPathGlUniforms& uniforms, const LegacyCanvasDrawMergeLayerPathGlExecution& execution) { return { .bind_blender_framebuffer = execution.bind_blender_framebuffer, .clear_blender_framebuffer = execution.clear_blender_framebuffer, .unbind_blender_framebuffer = execution.unbind_blender_framebuffer, .prepare_temporary_erase = [uniforms, execution] { execution.bind_sampler(0); execution.bind_sampler(1); execution.bind_sampler(2); setup_legacy_stroke_erase_shader(uniforms.temporary_erase); execution.set_active_texture_unit(1); execution.bind_temporary_texture(); execution.set_active_texture_unit(2); execution.bind_smask_texture(); }, .cleanup_temporary_erase = [execution] { execution.set_active_texture_unit(2); execution.unbind_smask_texture(); execution.set_active_texture_unit(1); execution.unbind_temporary_texture(); }, .prepare_temporary_paint = [uniforms, execution] { execution.bind_sampler(0); execution.bind_sampler(1); execution.bind_sampler(2); execution.bind_sampler(3); execution.bind_stencil_sampler(4); setup_legacy_stroke_composite_shader(uniforms.temporary_paint); execution.set_active_texture_unit(1); execution.bind_temporary_texture(); execution.set_active_texture_unit(2); execution.bind_smask_texture(); execution.set_active_texture_unit(3); if (uniforms.use_dual_texture) { execution.bind_temporary_dual_texture(); } execution.set_active_texture_unit(4); execution.bind_pattern_texture(); }, .cleanup_temporary_paint = [uniforms, execution] { execution.set_active_texture_unit(3); if (uniforms.use_dual_texture) { execution.unbind_temporary_dual_texture(); } execution.set_active_texture_unit(2); execution.unbind_smask_texture(); execution.set_active_texture_unit(1); execution.unbind_temporary_texture(); }, .prepare_layer_texture = [uniforms, execution] { if (uniforms.use_nearest_sampler) { execution.bind_nearest_sampler(0); } else { execution.bind_sampler(0); } setup_legacy_canvas_draw_merge_texture_alpha_shader(uniforms.layer_texture); }, .cleanup_layer_texture = [] { }, .draw_blend = [uniforms, execution] { execute_legacy_canvas_draw_merge_layer_blend( uniforms.blend, { .unbind_merge_framebuffer = execution.unbind_blender_framebuffer, .bind_samplers = [execution] { execution.bind_sampler(0); execution.bind_sampler(2); }, .bind_merge_texture = [execution] { execution.set_active_texture_unit(0); execution.bind_blender_texture(); }, .bind_destination_texture = [execution] { execution.set_active_texture_unit(2); execution.bind_destination_texture(); }, .copy_destination_framebuffer = execution.copy_destination_framebuffer, .draw = execution.draw_face, .unbind_destination_texture = [execution] { execution.set_active_texture_unit(2); execution.unbind_destination_texture(); }, .unbind_merge_texture = [execution] { execution.set_active_texture_unit(0); execution.unbind_blender_texture(); }, }); }, .draw_debug_outline = execution.draw_debug_outline, .draw_frame = execution.draw_frame, }; } template < typename BindBlenderFramebuffer, typename ClearBlenderFramebuffer, typename UnbindBlenderFramebuffer, typename BindSampler, typename BindNearestSampler, typename BindStencilSampler, typename SetActiveTextureUnit, typename BindTemporaryTexture, typename UnbindTemporaryTexture, typename BindSmaskTexture, typename UnbindSmaskTexture, typename BindTemporaryDualTexture, typename UnbindTemporaryDualTexture, typename BindPatternTexture, typename DrawFace, typename BindBlenderTexture, typename UnbindBlenderTexture, typename BindDestinationTexture, typename UnbindDestinationTexture, typename CopyDestinationFramebuffer, typename DrawDebugOutline, typename DrawFrame> [[nodiscard]] inline LegacyCanvasDrawMergeLayerPathExecution make_legacy_canvas_draw_merge_layer_path_gl_execution( const LegacyCanvasDrawMergeLayerPathGlUniforms& uniforms, BindBlenderFramebuffer&& bind_blender_framebuffer, ClearBlenderFramebuffer&& clear_blender_framebuffer, UnbindBlenderFramebuffer&& unbind_blender_framebuffer, BindSampler&& bind_sampler, BindNearestSampler&& bind_nearest_sampler, BindStencilSampler&& bind_stencil_sampler, SetActiveTextureUnit&& set_active_texture_unit, BindTemporaryTexture&& bind_temporary_texture, UnbindTemporaryTexture&& unbind_temporary_texture, BindSmaskTexture&& bind_smask_texture, UnbindSmaskTexture&& unbind_smask_texture, BindTemporaryDualTexture&& bind_temporary_dual_texture, UnbindTemporaryDualTexture&& unbind_temporary_dual_texture, BindPatternTexture&& bind_pattern_texture, DrawFace&& draw_face, BindBlenderTexture&& bind_blender_texture, UnbindBlenderTexture&& unbind_blender_texture, BindDestinationTexture&& bind_destination_texture, UnbindDestinationTexture&& unbind_destination_texture, CopyDestinationFramebuffer&& copy_destination_framebuffer, DrawDebugOutline&& draw_debug_outline, DrawFrame&& draw_frame) { return make_legacy_canvas_draw_merge_layer_path_gl_execution( uniforms, LegacyCanvasDrawMergeLayerPathGlExecution { .bind_blender_framebuffer = std::forward(bind_blender_framebuffer), .clear_blender_framebuffer = std::forward(clear_blender_framebuffer), .unbind_blender_framebuffer = std::forward(unbind_blender_framebuffer), .bind_sampler = std::forward(bind_sampler), .bind_nearest_sampler = std::forward(bind_nearest_sampler), .bind_stencil_sampler = std::forward(bind_stencil_sampler), .set_active_texture_unit = std::forward(set_active_texture_unit), .bind_temporary_texture = std::forward(bind_temporary_texture), .unbind_temporary_texture = std::forward(unbind_temporary_texture), .bind_smask_texture = std::forward(bind_smask_texture), .unbind_smask_texture = std::forward(unbind_smask_texture), .bind_temporary_dual_texture = std::forward(bind_temporary_dual_texture), .unbind_temporary_dual_texture = std::forward(unbind_temporary_dual_texture), .bind_pattern_texture = std::forward(bind_pattern_texture), .draw_face = std::forward(draw_face), .bind_blender_texture = std::forward(bind_blender_texture), .unbind_blender_texture = std::forward(unbind_blender_texture), .bind_destination_texture = std::forward(bind_destination_texture), .unbind_destination_texture = std::forward(unbind_destination_texture), .copy_destination_framebuffer = std::forward(copy_destination_framebuffer), .draw_debug_outline = std::forward(draw_debug_outline), .draw_frame = std::forward(draw_frame), }); } template inline void execute_legacy_canvas_draw_layer_traversal( size_t layer_count, PlanOnionRange&& plan_onion_range, ShouldDrawPlane&& should_draw_plane, VisitLayerPlane&& visit_layer_plane, LogOnionRangeFailure&& log_onion_range_failure) { for (size_t i = 0; i < layer_count; ++i) { const auto layer_index = i; const auto onion_range_result = plan_onion_range(layer_index); if (!onion_range_result) { log_onion_range_failure(onion_range_result.status().message); continue; } const auto onion_range = onion_range_result.value(); for (int plane_index = 0; plane_index < 6; ++plane_index) { if (!should_draw_plane(layer_index, plane_index, onion_range.first_frame, onion_range.last_frame)) { continue; } visit_layer_plane(LegacyCanvasDrawLayerVisit { .layer_index = layer_index, .plane_index = plane_index, .z = static_cast(layer_count - i), .first_frame = onion_range.first_frame, .last_frame = onion_range.last_frame, }, onion_range); } } } inline void execute_legacy_canvas_draw_merge_temporary_composite( const LegacyCanvasDrawMergeTemporaryCompositeExecution& execution) { execution.setup(); execution.bind_samplers(); execution.bind_textures(); execution.draw(); execution.unbind_textures(); } inline void execute_legacy_canvas_draw_merge_background_setup( const LegacyCanvasDrawMergeBackgroundSetupUniforms& uniforms, const LegacyCanvasDrawMergeBackgroundSetupExecution& execution) { if (uniforms.draw_merged) { execution.disable_blend(); } if (uniforms.draw_merged || !uniforms.use_blend) { for (int plane_index = 0; plane_index < 6; ++plane_index) { execution.draw_checkerboard_plane(plane_index); } } } template < typename PrepareBlendCache, typename DrawBackground, typename ConfigureBlendState, typename DisableDepthTest, typename PlanOnionRange, typename ShouldDrawPlane, typename VisitLayerPlane, typename LogOnionRangeFailure, typename CompositeBlendCache> inline void execute_legacy_canvas_draw_unmerged_shell( bool use_blend, size_t layer_count, PrepareBlendCache&& prepare_blend_cache, DrawBackground&& draw_background, ConfigureBlendState&& configure_blend_state, DisableDepthTest&& disable_depth_test, PlanOnionRange&& plan_onion_range, ShouldDrawPlane&& should_draw_plane, VisitLayerPlane&& visit_layer_plane, LogOnionRangeFailure&& log_onion_range_failure, CompositeBlendCache&& composite_blend_cache) { if (use_blend) { prepare_blend_cache(); } draw_background(); configure_blend_state(use_blend); disable_depth_test(); execute_legacy_canvas_draw_layer_traversal( layer_count, std::forward(plan_onion_range), std::forward(should_draw_plane), std::forward(visit_layer_plane), std::forward(log_onion_range_failure)); if (use_blend) { composite_blend_cache(); } } template < typename MakeLayerPathExecution, typename ShouldDrawTemporaryErase, typename ShouldDrawTemporaryPaint, typename FrameAlpha> inline void execute_legacy_canvas_draw_unmerged_layer_path( const LegacyCanvasDrawLayerVisit& visit, const auto& onion_range, bool use_blend, const glm::mat4& proj, const glm::mat4& camera, const glm::mat4& orientation, const auto& plane_transform, MakeLayerPathExecution&& make_layer_path_execution, ShouldDrawTemporaryErase&& should_draw_temporary_erase, ShouldDrawTemporaryPaint&& should_draw_temporary_paint, FrameAlpha&& frame_alpha) { const auto plane_index = visit.plane_index; const auto plane_mvp_z = proj * camera * glm::scale(glm::vec3(visit.z + 1)) * orientation * plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); const auto layer_path_execution = make_layer_path_execution(visit.layer_index, plane_index, plane_mvp_z); execute_legacy_canvas_draw_merge_layer_path( should_draw_temporary_erase(visit), should_draw_temporary_paint(visit), use_blend, visit.first_frame, visit.last_frame, [&](int frame) { return frame_alpha(onion_range, frame); }, layer_path_execution); } template < typename NodeCanvasT, typename PrepareBlendCache, typename DrawBackground, typename ConfigureBlendState, typename DisableDepthTest, typename MakeLayerPathExecution, typename LogOnionRangeFailure, typename CompositeBlendCache> inline void execute_legacy_canvas_draw_unmerged_node_canvas_shell( NodeCanvasT& node_canvas, bool use_blend, const glm::mat4& proj, const glm::mat4& camera, const glm::mat4& orientation, PrepareBlendCache&& prepare_blend_cache, DrawBackground&& draw_background, ConfigureBlendState&& configure_blend_state, DisableDepthTest&& disable_depth_test, MakeLayerPathExecution&& make_layer_path_execution, LogOnionRangeFailure&& log_onion_range_failure, CompositeBlendCache&& composite_blend_cache) { if (use_blend) { prepare_blend_cache(); } draw_background(); configure_blend_state(use_blend); disable_depth_test(); const auto plan_onion_range = [&](size_t layer_index) { return pp::app::plan_animation_onion_frame_range( node_canvas.m_canvas->m_layers[layer_index]->frames_count(), node_canvas.m_canvas->m_layers[layer_index]->m_frame_index, App::I->animation->get_onion_size()); }; const auto should_draw_plane = [&](size_t layer_index, int plane_index, int first_frame, int last_frame) { bool faces = false; for (int frame = first_frame; frame <= last_frame; ++frame) { faces |= node_canvas.m_canvas->m_layers[layer_index]->face(plane_index, frame); } if (node_canvas.m_canvas->m_show_tmp && node_canvas.m_canvas->m_current_layer_idx == layer_index) { return true; } return node_canvas.m_canvas->m_layers[layer_index]->m_visible && node_canvas.m_canvas->m_layers[layer_index]->m_opacity != .0f && faces; }; execute_legacy_canvas_draw_layer_traversal( node_canvas.m_canvas->m_layers.size(), plan_onion_range, should_draw_plane, [&](const LegacyCanvasDrawLayerVisit& visit, const auto& onion_range) { execute_legacy_canvas_draw_unmerged_layer_path( visit, onion_range, use_blend, proj, camera, orientation, node_canvas.m_canvas->m_plane_transform, make_layer_path_execution, [&](const LegacyCanvasDrawLayerVisit& visit) { return node_canvas.m_canvas->m_current_stroke && node_canvas.m_canvas->m_current_mode == kCanvasMode::Erase && node_canvas.m_canvas->m_show_tmp && node_canvas.m_canvas->m_current_layer_idx == visit.layer_index; }, [&](const LegacyCanvasDrawLayerVisit& visit) { return node_canvas.m_canvas->m_current_stroke && node_canvas.m_canvas->m_show_tmp && node_canvas.m_canvas->m_current_layer_idx == visit.layer_index; }, [&](const auto& onion_range, int frame) { return pp::app::animation_onion_frame_alpha(onion_range, frame); }); }, std::forward(log_onion_range_failure)); if (use_blend) { composite_blend_cache(); } } template < typename PrepareDensityRenderTarget, typename PrepareStandardRenderTarget, typename DrawMergedPass, typename DrawUnmergedPass> inline void execute_legacy_canvas_draw_node_canvas_shell( bool use_density_render_target, bool draw_merged, PrepareDensityRenderTarget&& prepare_density_render_target, PrepareStandardRenderTarget&& prepare_standard_render_target, DrawMergedPass&& draw_merged_pass, DrawUnmergedPass&& draw_unmerged_pass) { if (use_density_render_target) { prepare_density_render_target(); } else { prepare_standard_render_target(); } if (draw_merged) { draw_merged_pass(); return; } draw_unmerged_pass(); } template inline void prepare_legacy_node_canvas_draw_setup( NodeCanvasT& node_canvas, const glm::vec4& box, const glm::ivec4& c, const glm::mat4& proj, const glm::mat4& camera) { node_canvas.m_canvas->m_mv = camera; node_canvas.m_canvas->m_proj = proj; node_canvas.m_canvas->m_box = box; node_canvas.m_canvas->m_vp = c; for (int plane_index = 0; plane_index < 6; plane_index++) { node_canvas.m_canvas->m_plane_unproject[plane_index] = glm::inverse(node_canvas.m_canvas->m_proj * node_canvas.m_canvas->m_mv * node_canvas.m_canvas->m_plane_transform[plane_index]); node_canvas.m_canvas->m_plane_dir[plane_index] = -(node_canvas.m_canvas->m_plane_transform[plane_index] * glm::vec4(node_canvas.m_canvas->m_plane_origin[plane_index], 1)); node_canvas.m_canvas->m_plane_shape[plane_index] = node_canvas.m_canvas->face_to_shape2D(plane_index); } } template < typename NodeCanvasT, typename ApplyCapability, typename SetActiveTextureUnit, typename DrawFacePlane> inline void execute_legacy_canvas_draw_merged_pass( NodeCanvasT& node_canvas, const glm::mat4& proj, const glm::mat4& camera, ApplyCapability&& apply_node_canvas_capability, SetActiveTextureUnit&& set_active_texture_unit, DrawFacePlane&& draw_face_plane) { execute_legacy_canvas_draw_merge_background_setup( { .draw_merged = true, }, { .disable_blend = [&] { apply_node_canvas_capability(pp::renderer::gl::blend_state(), false); }, .draw_checkerboard_plane = make_legacy_canvas_draw_merge_background_checkerboard_plane( proj, camera, node_canvas.m_canvas->m_layers.size() + 500.f, node_canvas.m_canvas->m_plane_transform, std::forward(draw_face_plane)), }); const auto draw_merged_texture_plane = make_legacy_canvas_draw_merge_layer_texture_draw( &node_canvas.m_canvas->m_layers_merge, &node_canvas.m_sampler, &node_canvas.m_face_plane, std::forward(set_active_texture_unit), proj, camera, node_canvas.m_canvas->m_plane_transform); for (int plane_index = 0; plane_index < 6; plane_index++) { draw_merged_texture_plane(plane_index); } } template < typename NodeCanvasT, typename ApplyViewport, typename ApplyCapability, typename DrawFacePlane, typename BindSampler, typename MakeLayerPathExecution, typename MakeCacheToScreenCheckerboardPlane, typename LogOnionRangeFailure> inline void execute_legacy_canvas_draw_unmerged_node_canvas_pass( NodeCanvasT& node_canvas, bool use_blend, const glm::mat4& proj, const glm::mat4& camera, const glm::mat4& orientation, const glm::ivec4& c, ApplyViewport&& apply_node_canvas_viewport, ApplyCapability&& apply_node_canvas_capability, DrawFacePlane&& draw_face_plane, BindSampler&& bind_sampler, MakeLayerPathExecution&& make_layer_path_execution, MakeCacheToScreenCheckerboardPlane&& make_cache_to_screen_checkerboard_plane, LogOnionRangeFailure&& log_onion_range_failure) { const auto prepare_blend_cache = [&] { apply_node_canvas_viewport(0, 0, node_canvas.m_cache_rtt.getWidth(), node_canvas.m_cache_rtt.getHeight()); node_canvas.m_cache_rtt.bindFramebuffer(); node_canvas.m_cache_rtt.clear({ 1, 1, 1, 0 }); }; const auto draw_background = [&] { execute_legacy_canvas_draw_merge_background_setup( { .use_blend = use_blend, }, { .disable_blend = [&] { apply_node_canvas_capability(pp::renderer::gl::blend_state(), false); }, .draw_checkerboard_plane = make_legacy_canvas_draw_merge_background_checkerboard_plane( proj, camera, node_canvas.m_canvas->m_layers.size() + 500.f, node_canvas.m_canvas->m_plane_transform, draw_face_plane), }); }; const auto configure_blend_state = [&](bool enable_shader_blend) { apply_node_canvas_capability(pp::renderer::gl::blend_state(), !enable_shader_blend); }; const auto disable_depth_test = [&] { apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); }; execute_legacy_canvas_draw_unmerged_node_canvas_shell( node_canvas, use_blend, proj, camera, orientation, prepare_blend_cache, draw_background, configure_blend_state, disable_depth_test, make_layer_path_execution, log_onion_range_failure, [&] { node_canvas.m_cache_rtt.unbindFramebuffer(); if (node_canvas.m_density != 1.f) { apply_node_canvas_viewport(0, 0, node_canvas.m_rtt.getWidth(), node_canvas.m_rtt.getHeight()); } else { apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); } execute_legacy_canvas_draw_merge_cache_to_screen_composite( LegacyCanvasDrawMergeCacheToScreenCompositeUniforms { .checkerboard = { .colorize = false, }, .texture = { .mvp = glm::ortho(-1, 1, -1, 1), .texture_slot = 0, }, }, { .enable_blend = [&] { apply_node_canvas_capability(pp::renderer::gl::blend_state(), true); }, .draw_checkerboard_plane = make_cache_to_screen_checkerboard_plane(), .bind_sampler = [&] { bind_sampler(); }, .bind_cache_texture = [&] { node_canvas.m_cache_rtt.bindTexture(); }, .draw_cache_texture = [&] { draw_face_plane(); }, .unbind_cache_texture = [&] { node_canvas.m_cache_rtt.unbindTexture(); }, }); }); } template < typename NodeCanvasT, typename ApplyViewport, typename ApplyCapability, typename SetSampler, typename MakeLayerPathExecution, typename LogOnionRangeFailure> inline void execute_legacy_canvas_draw_node_canvas_unmerged_pass( NodeCanvasT& node_canvas, bool use_blend, bool copy_blend_destination, const glm::mat4& proj, const glm::mat4& camera, const glm::mat4& layer_orientation, const glm::ivec4& c, ApplyViewport&& apply_node_canvas_viewport, ApplyCapability&& apply_node_canvas_capability, SetSampler&& set_sampler, MakeLayerPathExecution&& make_layer_path_execution, LogOnionRangeFailure&& log_onion_range_failure) { const auto& brush = node_canvas.m_canvas->m_current_stroke->m_brush; execute_legacy_canvas_draw_unmerged_node_canvas_pass( node_canvas, use_blend, proj, camera, layer_orientation, c, std::forward(apply_node_canvas_viewport), std::forward(apply_node_canvas_capability), [&] { node_canvas.m_face_plane.draw_fill(); }, std::forward(set_sampler), [&](size_t layer_index, int plane_index, const glm::mat4& plane_mvp_z) { return make_layer_path_execution( layer_index, plane_index, plane_mvp_z, brush.get(), copy_blend_destination, node_canvas.m_canvas->m_cam_fov < 20.f); }, [&] { return make_legacy_canvas_draw_merge_cache_to_screen_checkerboard_plane( proj, camera, node_canvas.m_canvas->m_layers.size() + 500.f, node_canvas.m_canvas->m_plane_transform, [&] { node_canvas.m_face_plane.draw_fill(); }); }, std::forward(log_onion_range_failure)); } inline void execute_legacy_canvas_draw_merge_plane_setup( const LegacyCanvasDrawMergePlaneSetupUniforms& uniforms, const LegacyCanvasDrawMergePlaneSetupExecution& execution) { execution.clear_plane(); if (uniforms.use_blend) { execution.disable_blend(); execution.clear_plane(); return; } if (uniforms.draw_checkerboard) { setup_legacy_canvas_draw_merge_checkerboard_shader(uniforms.checkerboard); execution.draw(); } execution.enable_blend(); } inline void execute_legacy_canvas_draw_merge_final_plane_composite( const LegacyCanvasDrawMergeFinalPlaneCompositeUniforms& uniforms, const LegacyCanvasDrawMergeFinalPlaneCompositeExecution& execution) { execution.bind_merged_texture_copy_target(); execution.copy_merged_framebuffer(); execution.enable_blend(); if (uniforms.draw_checkerboard) { setup_legacy_canvas_draw_merge_checkerboard_shader(uniforms.checkerboard); execution.draw(); } execution.bind_sampler(); execution.bind_merged_texture(); setup_legacy_canvas_draw_merge_texture_shader(uniforms.texture); execution.draw(); execution.unbind_merged_texture(); } inline void execute_legacy_canvas_draw_merge_cache_to_screen_composite( const LegacyCanvasDrawMergeCacheToScreenCompositeUniforms& uniforms, const LegacyCanvasDrawMergeCacheToScreenCompositeExecution& execution) { execution.enable_blend(); for (int plane_index = 0; plane_index < 6; ++plane_index) { execution.draw_checkerboard_plane(uniforms.checkerboard, plane_index); } execution.bind_sampler(); execution.bind_cache_texture(); setup_legacy_canvas_draw_merge_texture_shader(uniforms.texture); execution.draw_cache_texture(); execution.unbind_cache_texture(); } template [[nodiscard]] inline auto make_legacy_canvas_draw_merge_cache_to_screen_checkerboard_plane( glm::mat4 proj, glm::mat4 camera, float layer_scale, PlaneTransform plane_transform, DrawPlane draw_plane) { return [proj, camera, layer_scale, plane_transform, draw_plane]( const LegacyCanvasDrawMergeCheckerboardUniforms& uniforms, int plane_index) { auto checkerboard_uniforms = uniforms; checkerboard_uniforms.mvp = proj * camera * glm::scale(glm::vec3(layer_scale)) * plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1.f)); setup_legacy_canvas_draw_merge_checkerboard_shader(checkerboard_uniforms); draw_plane(); }; } inline void execute_legacy_canvas_draw_merge_display_resolve( const LegacyCanvasDrawMergeDisplayResolveUniforms& uniforms, const LegacyCanvasDrawMergeDisplayResolveExecution& execution) { execution.unbind_resolve_framebuffer(); execution.clear_color_buffer(); execution.apply_viewport(); execution.bind_sampler(); execution.bind_resolve_texture(); setup_legacy_canvas_draw_merge_texture_shader(uniforms.texture); execution.draw(); execution.unbind_resolve_texture(); } inline void execute_legacy_canvas_draw_merge_post_draw( bool smask_active, bool draw_mask_overlay, int smask_mode, bool draw_grid_modes, const LegacyCanvasDrawMergePostDrawExecution& execution) { if (smask_active || draw_mask_overlay) { if (smask_mode == 1) { execution.draw_mask_free(); } else if (smask_mode == 2) { execution.draw_mask_line(); } } if (smask_active) { execution.draw_smask_faces(); } if (draw_grid_modes) { execution.draw_grid_modes(); } execution.draw_heightmap(); execution.draw_current_modes(); } template < typename DrawMaskFree, typename DrawMaskLine, typename DrawSmaskFaces, typename DrawGridModes, typename DrawHeightmap, typename DrawCurrentModes> inline void execute_legacy_canvas_draw_merge_post_draw_callbacks( bool smask_active, bool draw_mask_overlay, int smask_mode, bool draw_grid_modes_enabled, DrawMaskFree&& draw_mask_free, DrawMaskLine&& draw_mask_line, DrawSmaskFaces&& draw_smask_faces, DrawGridModes&& draw_grid_modes_callback, DrawHeightmap&& draw_heightmap, DrawCurrentModes&& draw_current_modes) { execute_legacy_canvas_draw_merge_post_draw( smask_active, draw_mask_overlay, smask_mode, draw_grid_modes_enabled, LegacyCanvasDrawMergePostDrawExecution { .draw_mask_free = std::forward(draw_mask_free), .draw_mask_line = std::forward(draw_mask_line), .draw_smask_faces = std::forward(draw_smask_faces), .draw_grid_modes = std::forward(draw_grid_modes_callback), .draw_heightmap = std::forward(draw_heightmap), .draw_current_modes = std::forward(draw_current_modes), }); } inline void execute_legacy_canvas_draw_merge_smask_faces( const LegacyCanvasDrawMergeTextureMaskUniforms& uniforms, const glm::mat4& proj, const glm::mat4& camera, float layer_scale, const std::array& plane_transform, const LegacyCanvasDrawMergeSmaskFacesExecution& execution) { setup_legacy_canvas_draw_merge_texture_mask_shader(uniforms); execution.set_active_texture_unit(); execution.enable_blend(); for (int plane_index = 0; plane_index < 6; ++plane_index) { auto plane_mvp = proj * camera * glm::scale(glm::vec3(layer_scale)) * plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1.f)); apply_legacy_canvas_draw_merge_mvp(plane_mvp); execution.bind_face_texture(plane_index); execution.draw_face(); execution.unbind_face_texture(plane_index); } } } // namespace pp::panopainter