diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 3f8decbf..a2554831 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -25,6 +25,8 @@ set(PP_LEGACY_PAINT_DOCUMENT_SOURCES src/legacy_canvas_layer_services.cpp src/legacy_canvas_stroke_commit_services.cpp src/legacy_canvas_stroke_live_services.cpp + src/legacy_canvas_stroke_runtime_services.cpp + src/legacy_canvas_stroke_runtime_services.h src/legacy_canvas_document_io_services.cpp src/legacy_canvas_object_draw_services.cpp src/legacy_canvas_object_draw_services.h @@ -160,6 +162,8 @@ set(PP_PANOPAINTER_UI_SOURCES src/legacy_brush_panel_services.h src/legacy_document_animation_services.cpp src/legacy_document_animation_services.h + src/legacy_node_canvas_draw_services.cpp + src/legacy_node_canvas_draw_services.h src/legacy_node_canvas_state_services.cpp src/legacy_node_canvas_state_services.h src/legacy_brush_preset_panel_ui.cpp @@ -190,6 +194,7 @@ set(PP_PANOPAINTER_UI_SOURCES src/node_panel_layer.cpp src/node_panel_quick.cpp src/node_panel_stroke.cpp + src/legacy_node_stroke_preview_runtime_services.cpp src/node_stroke_preview.cpp src/node_tool_bucket.cpp src/node_usermanual.cpp diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 144f4f79..d257bc3b 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -79,15 +79,15 @@ What is still carrying too much live ownership: Current hotspot files: -- `src/canvas.cpp`: 1271 lines +- `src/canvas.cpp`: 1010 lines - `src/app_layout.cpp`: 125 lines - `src/canvas_modes.cpp`: 720 lines - `src/node.cpp`: 803 lines - `src/main.cpp`: 271 lines - `src/node_panel_brush.cpp`: 435 lines -- `src/node_stroke_preview.cpp`: 751 lines -- `src/node_canvas.cpp`: 702 lines -- `src/app.cpp`: 575 lines +- `src/node_stroke_preview.cpp`: 607 lines +- `src/node_canvas.cpp`: 219 lines +- `src/app.cpp`: 502 lines - `src/app_dialogs.cpp`: 168 lines Current architecture mismatches that must be treated as real blockers: @@ -268,6 +268,11 @@ Current architecture mismatches that must be treated as real blockers: `Canvas::flood_fill(...)`, and `Canvas::FloodData::apply()` now also route through `src/legacy_canvas_layer_services.cpp` instead of staying inline in `src/canvas.cpp`, which trims another coherent retained layer/fill workflow + pocket, while `Canvas::stroke_end(...)`, `Canvas::stroke_cancel(...)`, + `Canvas::stroke_draw_mix(...)`, `Canvas::stroke_draw_project(...)`, + `Canvas::stroke_update(...)`, and `Canvas::stroke_start(...)` now also route + through `src/legacy_canvas_stroke_runtime_services.*` instead of staying + inline in `src/canvas.cpp`, which trims another large retained stroke/runtime pocket, while the `CanvasModeTransform` interaction family now also routes through `src/legacy_canvas_mode_transform.cpp` instead of staying inline in @@ -302,6 +307,14 @@ Current architecture mismatches that must be treated as real blockers: `src/canvas_modes.cpp`, while preview stroke preparation, dual-brush setup, and live pass-orchestration request assembly now also route through retained preview execution helpers, + while `NodeStrokePreview` retained lifecycle, worker-thread shell, + render-to-image path, on-screen handling, and preview texture ownership now + also route through `src/legacy_node_stroke_preview_runtime_services.cpp` + instead of staying inline in `src/node_stroke_preview.cpp`, while + `NodeCanvas::init()` plus the remaining `NodeCanvas::draw()` outer shell now + also route through `src/legacy_node_canvas_draw_services.*` instead of + staying inline in `src/node_canvas.cpp`, which materially reduces the live + node to a thinner controller surface around event routing and state wrappers, while `Node::on_event(...)` plus mouse/key capture and release ownership now also route through `src/legacy_ui_node_event.*` instead of staying inline in `src/node.cpp`, which materially thins the base scene-graph event shell diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index df1921b6..b6be3b59 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -91,7 +91,8 @@ Status: In Progress Why now: `src/canvas.cpp` is still the biggest single architectural blocker at about -1271 lines, with `src/canvas_modes.cpp` still large at about 720 lines. +1010 lines, with `src/canvas_modes.cpp` now materially thinner and the next +remaining render-shell pressure shifting toward preview/canvas nodes. Current slice: - Canvas state-management helpers for picking, clear/clear-all, layer @@ -135,6 +136,12 @@ Current slice: `src/legacy_canvas_layer_services.cpp` instead of staying inline in `src/canvas.cpp`, which trims another coherent retained layer/fill workflow pocket from the live canvas shell. +- `Canvas::stroke_end(...)`, `Canvas::stroke_cancel(...)`, + `Canvas::stroke_draw_mix(...)`, `Canvas::stroke_draw_project(...)`, + `Canvas::stroke_update(...)`, and `Canvas::stroke_start(...)` now also live + in `src/legacy_canvas_stroke_runtime_services.*` instead of staying inline in + `src/canvas.cpp`, which trims another large retained live stroke/runtime + pocket from the canvas shell. - The `CanvasModePen` and `CanvasModeLine` interaction families now also live in `src/legacy_canvas_mode_pen_line.cpp` instead of staying inline in `src/canvas_modes.cpp`, which materially thins another retained pen/line @@ -307,6 +314,16 @@ Current slice: live in `src/legacy_node_canvas_state_services.*` instead of staying inline in `src/node_canvas.cpp`, which materially thins another retained state/control pocket without reopening the draw path. +- `NodeStrokePreview` retained lifecycle, worker-thread shell, render-to-image, + on-screen handling, and preview texture ownership now also live in + `src/legacy_node_stroke_preview_runtime_services.cpp` instead of staying + inline in `src/node_stroke_preview.cpp`, which materially thins the preview + node around its runtime-facing shell even though live pass execution still + remains. +- `NodeCanvas::init()` plus the remaining `NodeCanvas::draw()` outer shell now + also live in `src/legacy_node_canvas_draw_services.*` instead of staying + inline in `src/node_canvas.cpp`, which materially reduces the live node to a + thinner controller surface around event routing and state wrappers. Write scope: - `src/node_stroke_preview.cpp` diff --git a/src/canvas.cpp b/src/canvas.cpp index 4e5fb16e..a13accc3 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -9,6 +9,7 @@ #include "legacy_canvas_stroke_composite_services.h" #include "legacy_canvas_stroke_edge_services.h" #include "legacy_canvas_stroke_execution_services.h" +#include "legacy_canvas_stroke_runtime_services.h" #include "legacy_canvas_stroke_shader_services.h" #include "legacy_canvas_object_draw_services.h" #include "legacy_canvas_projection_services.h" @@ -252,12 +253,6 @@ void delete_canvas_renderbuffer(GLuint renderbuffer) } - -pp::panopainter::LegacyCanvasStrokeMixPassShell make_canvas_stroke_mix_pass_shell( - Canvas& canvas, - const glm::vec2& bb_min, - const glm::vec2& bb_sz); - Canvas* Canvas::I; std::vector Canvas::modes[] = { { new CanvasModePen, new CanvasModeBasicCamera }, // brush @@ -307,114 +302,16 @@ glm::mat4 Canvas::m_plane_transform[6] = { glm::lookAt(glm::vec3(), { 0,-1, 0}, {0, 0, 1}), // bottom }; -void Canvas::stroke_end() -{ - if (!m_current_stroke) - return; - if (m_current_stroke->has_sample()) - { - m_commit_delayed = true; - } - else - { - m_show_tmp = false; - stroke_commit(); - m_current_stroke = nullptr; - } -} -void Canvas::stroke_cancel() -{ - if (!m_current_stroke) - return; - m_current_stroke = nullptr; - m_show_tmp = false; -} +void Canvas::stroke_end() { pp::panopainter::legacy_canvas_stroke_end(*this); } +void Canvas::stroke_cancel() { pp::panopainter::legacy_canvas_stroke_cancel(*this); } void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) { - gl_state gl; - gl.save(); - const auto mix_shell = make_canvas_stroke_mix_pass_shell(*this, bb_min, bb_sz); - [[maybe_unused]] const auto mix_result = pp::panopainter::execute_legacy_canvas_stroke_mix_pass_shell( - mix_shell.setup.begin, - mix_shell.setup.end, - mix_shell.request); - - gl.restore(); + pp::panopainter::legacy_canvas_stroke_draw_mix(*this, bb_min, bb_sz); } std::array, 6> Canvas::stroke_draw_project(std::array& B, bool project_3d /*= false*/, glm::mat4 mv /*= glm::mat4(1)*/) const { - // intersect P with the current face to clip diverging points from the plane - const auto unp_vp = zw(m_box); - const auto unp_inv = glm::inverse(m_proj * m_mv); - std::array, 6> ret; - for (int i = 0; i < 6; i++) - { - struct ray_t { - glm::vec3 o; - glm::vec3 d; - vertex_t v; - ray_t(glm::vec3 o, glm::vec3 d, vertex_t v) : o(o), d(d), v(v) { } - }; - std::vector rays; - if (project_3d) - { - rays.reserve(B.size()); - for (auto const& b : B) - rays.emplace_back(glm::vec3(0), b.pos, b); - } - else - { - auto P = poly_intersect(B.data(), B.data() + 4, m_plane_shape[i]); - rays.reserve(P.size()); - for (auto const& p : P) - { - glm::vec3 ray_origin, ray_dir; - auto clip_space = glm::vec2(p.pos.x, unp_vp.y - p.pos.y - 1.f) / unp_vp * 2.f - 1.f; - auto wp0 = unp_inv * glm::vec4(clip_space, 0, 1); - auto wp1 = unp_inv * glm::vec4(clip_space, .5, 1); - ray_origin = xyz(wp0 / wp0.w); - ray_dir = glm::normalize(xyz(wp1 / wp1.w) - ray_origin); - rays.emplace_back(ray_origin, ray_dir, p); - } - } - glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]); - std::vector face_ret; - face_ret.reserve(rays.size()); - for (auto const& r : rays) - { - glm::vec3 hit; - float hit_t; - if (ray_intersect(r.o, r.d, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit, hit_t)) - { - glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); - - //P[j].uvs2 = xy(P[j].pos) / glm::vec2(App::I->width, App::I->height); - vertex_t v; - v.pos.x = -(plane_local.x * 0.5f - 0.5f) * m_width; - v.pos.y = (plane_local.y * 0.5f + 0.5f) * m_height; - - // Black magic - BEWARE! - // interpolation perspective correction, use the current camera projection to correct the interpolation - // because the new shape will have z fixed with an ortho projection when drawn to the face - // we need to imitate the same perspective as the once in the camera - // see: https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes - auto hit_cam = mv * glm::vec4(hit, 1); - v.pos.z = 0; - v.pos.w = hit_cam.z; - v.uvs = r.v.uvs * hit_cam.z; - v.uvs2 = r.v.uvs2 * hit_cam.z; - face_ret.emplace_back(v); - } - else - { - break; - } - } - if (face_ret.size() >= 3) - ret[i] = std::move(face_ret); - } - return ret; + return pp::panopainter::legacy_canvas_stroke_draw_project(*this, B, project_3d, mv); } static void execute_canvas_draw_merge_layer_composite( @@ -842,96 +739,6 @@ static void execute_canvas_draw_merge_plane_final_composite( } } -pp::panopainter::LegacyCanvasStrokeMixPassShell make_canvas_stroke_mix_pass_shell( - Canvas& canvas, - const glm::vec2& bb_min, - const glm::vec2& bb_sz) -{ - const auto layer_index = canvas.m_current_layer_idx; - auto& current_layer = *canvas.m_layers[layer_index]; - std::array plane_transform {}; - std::copy(std::begin(Canvas::m_plane_transform), std::end(Canvas::m_plane_transform), plane_transform.begin()); - const auto mix_planes = pp::panopainter::plan_legacy_canvas_stroke_mix_pass_planes( - current_layer.m_visible, - current_layer.m_opacity, - glm::scale(glm::vec3(1, -1, 1)) * canvas.m_proj * canvas.m_mv, - plane_transform, - [&](int plane_index) { - return current_layer.face(plane_index); - }); - const auto& b = canvas.m_current_stroke->m_brush; - return pp::panopainter::make_legacy_canvas_stroke_mix_pass_shell( - [&] { - canvas.m_mixer.bindFramebuffer(); - apply_canvas_viewport(0, 0, canvas.m_mixer.getWidth(), canvas.m_mixer.getHeight()); - apply_canvas_capability(depth_test_state(), false); - apply_canvas_capability(scissor_test_state(), true); - apply_canvas_capability(blend_state(), false); - apply_canvas_scissor( - static_cast(bb_min.x), - static_cast(bb_min.y), - static_cast(bb_sz.x), - static_cast(bb_sz.y)); - }, - [&] { - canvas.m_mixer.unbindFramebuffer(); - }, - "Canvas::stroke_draw_mix", - canvas.m_size, - mix_planes, - [&] { - canvas.m_sampler.bind(0); - canvas.m_sampler.bind(1); - canvas.m_sampler.bind(2); - }, - [&] { - canvas.m_sampler.unbind(); - }, - [&](int plane_index, const glm::mat4& plane_mvp_z) { - (void)plane_index; - pp::panopainter::setup_legacy_stroke_composite_shader( - pp::panopainter::LegacyStrokeCompositeUniforms { - .resolution = canvas.m_size, - .mvp = plane_mvp_z, - .pattern_texture_slot = 3, - .layer_alpha = 1.0f, - .alpha_lock = false, - .mask_enabled = false, - .use_fragcoord = false, - .blend_mode = b->m_blend_mode, - .use_dual = false, - .use_pattern = false, - }); - }, - [&](int plane_index) { - set_active_texture_unit(0); - current_layer.rtt(plane_index).bindTexture(); - }, - [&](int plane_index) { - set_active_texture_unit(1); - canvas.m_tmp[plane_index].bindTexture(); - }, - [&](int plane_index) { - set_active_texture_unit(2); - canvas.m_smask.rtt(plane_index).bindTexture(); - }, - [&] { - canvas.m_node->m_face_plane.draw_fill(); - }, - [&](int plane_index) { - set_active_texture_unit(2); - canvas.m_smask.rtt(plane_index).unbindTexture(); - }, - [&](int plane_index) { - set_active_texture_unit(1); - canvas.m_tmp[plane_index].unbindTexture(); - }, - [&](int plane_index) { - set_active_texture_unit(0); - current_layer.rtt(plane_index).unbindTexture(); - }); -} - static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution make_canvas_draw_merge_temporary_paint_request( Canvas& canvas, @@ -1157,87 +964,8 @@ void Canvas::draw_merge(bool draw_checkerboard, std::array faces /*= SI draw_checkerboard); } -void Canvas::stroke_update(glm::vec3 point, float pressure) -{ - m_current_stroke->add_point(point, pressure); - if (m_dual_stroke) - m_dual_stroke->add_point(point, pressure); -} -void Canvas::stroke_start(glm::vec3 point, float pressure) -{ - assert(App::I->is_render_thread()); - - // need to commit this now before starting a new stroke - if (m_current_stroke && m_commit_delayed) - { - m_show_tmp = false; - m_commit_delayed = false; - stroke_commit(); - m_current_stroke = nullptr; - m_dual_stroke = nullptr; - } - - m_pattern_offset = m_current_brush->m_pattern_rand_offset ? - glm::vec2((rand()%1000)*0.001f, (rand()%1000)*0.001f) : - glm::vec2(0); - - m_current_stroke = std::make_unique(); - m_current_stroke->m_camera.rot = m_cam_rot; - m_current_stroke->m_camera.fov = m_cam_fov; - if (m_current_mode == kCanvasMode::Line) - m_current_stroke->m_filter_points = false; - m_current_stroke->randomize_prng(); - m_current_stroke->start(m_current_brush); - m_current_stroke->add_point(point, pressure); - - // Generate a brush for the dual-brush - if (m_current_brush->m_dual_enabled) - { - auto dual_brush = std::make_shared(); - dual_brush->m_tip_flow = m_current_brush->m_dual_flow; - dual_brush->m_tip_opacity = m_current_brush->m_dual_opacity; - dual_brush->m_tip_flipx = m_current_brush->m_dual_flipx; - dual_brush->m_tip_flipy = m_current_brush->m_dual_flipy; - dual_brush->m_tip_invert = m_current_brush->m_dual_invert; - dual_brush->m_blend_mode = m_current_brush->m_dual_blend_mode; - dual_brush->m_tip_randflipx = m_current_brush->m_dual_randflip; - dual_brush->m_tip_randflipy = m_current_brush->m_dual_randflip; - dual_brush->m_tip_size = m_current_brush->m_dual_size * m_current_brush->m_tip_size; - dual_brush->m_tip_spacing = m_current_brush->m_dual_spacing; - dual_brush->m_jitter_scatter = m_current_brush->m_dual_scatter; - dual_brush->m_jitter_scatter_bothaxis = m_current_brush->m_dual_scatter_bothaxis; - dual_brush->m_jitter_angle = m_current_brush->m_dual_rotate; - dual_brush->m_tip_texture = m_current_brush->m_dual_texture; - dual_brush->m_tip_aspect = m_current_brush->m_dual_aspect; - m_dual_stroke = std::make_unique(); - m_dual_stroke->m_camera.rot = m_cam_rot; - m_dual_stroke->m_camera.fov = m_cam_fov; - m_dual_stroke->start(dual_brush); - m_dual_stroke->add_point(point, pressure); - } - - auto const& l = m_layers[m_current_layer_idx]; - for (int i = 0; i < 6; i++) - { - m_dirty_box[i] = glm::vec4(m_width, m_height, 0, 0); // reset bounding box - m_dirty_face[i] = false; - - m_tmp[i].bindFramebuffer(); - m_tmp[i].clear({ 0, 0, 0, 0 }); - m_tmp[i].unbindFramebuffer(); - - if (m_current_brush->m_dual_enabled) - { - m_tmp_dual[i].bindFramebuffer(); - m_tmp_dual[i].clear({ 0, 0, 0, 0 }); - m_tmp_dual[i].unbindFramebuffer(); - } - } - m_mixer.bindFramebuffer(); - m_mixer.clear(); - m_mixer.unbindFramebuffer(); - m_show_tmp = true; -} +void Canvas::stroke_update(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_update(*this, point, pressure); } +void Canvas::stroke_start(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_start(*this, point, pressure); } void Canvas::destroy() { for (int i = 0; i < 6; i++) diff --git a/src/legacy_canvas_stroke_runtime_services.cpp b/src/legacy_canvas_stroke_runtime_services.cpp new file mode 100644 index 00000000..926dc7f8 --- /dev/null +++ b/src/legacy_canvas_stroke_runtime_services.cpp @@ -0,0 +1,336 @@ +#include "pch.h" + +#include "canvas.h" +#include "app.h" +#include "legacy_canvas_stroke_runtime_services.h" + +#include "legacy_canvas_stroke_composite_services.h" +#include "legacy_canvas_stroke_services.h" +#include "legacy_ui_gl_dispatch.h" +#include "renderer_gl/opengl_capabilities.h" + +#include +#include +#include + +namespace { + +GLenum depth_test_state() +{ + return static_cast(pp::renderer::gl::depth_test_state()); +} + +GLenum scissor_test_state() +{ + return static_cast(pp::renderer::gl::scissor_test_state()); +} + +GLenum blend_state() +{ + return static_cast(pp::renderer::gl::blend_state()); +} + +void set_active_texture_unit(std::uint32_t unit_index) +{ + pp::legacy::ui_gl::activate_texture_unit(unit_index, "Canvas"); +} + +void apply_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) +{ + pp::legacy::ui_gl::apply_viewport(x, y, width, height, "Canvas"); +} + +void apply_canvas_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) +{ + pp::legacy::ui_gl::apply_scissor_rect(x, y, width, height, "Canvas"); +} + +void apply_canvas_capability(std::uint32_t capability, bool enabled) +{ + pp::legacy::ui_gl::set_capability(capability, enabled, "Canvas"); +} + +} // namespace + +namespace pp::panopainter { + +LegacyCanvasStrokeMixPassShell make_legacy_canvas_stroke_mix_pass_shell( + Canvas& canvas, + const glm::vec2& bb_min, + const glm::vec2& bb_sz) +{ + const auto layer_index = canvas.m_current_layer_idx; + auto& current_layer = *canvas.m_layers[layer_index]; + std::array plane_transform {}; + std::copy(std::begin(Canvas::m_plane_transform), std::end(Canvas::m_plane_transform), plane_transform.begin()); + const auto mix_planes = pp::panopainter::plan_legacy_canvas_stroke_mix_pass_planes( + current_layer.m_visible, + current_layer.m_opacity, + glm::scale(glm::vec3(1, -1, 1)) * canvas.m_proj * canvas.m_mv, + plane_transform, + [&](int plane_index) { + return current_layer.face(plane_index); + }); + const auto& b = canvas.m_current_stroke->m_brush; + return pp::panopainter::make_legacy_canvas_stroke_mix_pass_shell( + [&] { + canvas.m_mixer.bindFramebuffer(); + apply_canvas_viewport(0, 0, canvas.m_mixer.getWidth(), canvas.m_mixer.getHeight()); + apply_canvas_capability(depth_test_state(), false); + apply_canvas_capability(scissor_test_state(), true); + apply_canvas_capability(blend_state(), false); + apply_canvas_scissor( + static_cast(bb_min.x), + static_cast(bb_min.y), + static_cast(bb_sz.x), + static_cast(bb_sz.y)); + }, + [&] { + canvas.m_mixer.unbindFramebuffer(); + }, + "Canvas::stroke_draw_mix", + canvas.m_size, + mix_planes, + [&] { + canvas.m_sampler.bind(0); + canvas.m_sampler.bind(1); + canvas.m_sampler.bind(2); + }, + [&] { + canvas.m_sampler.unbind(); + }, + [&](int plane_index, const glm::mat4& plane_mvp_z) { + (void)plane_index; + pp::panopainter::setup_legacy_stroke_composite_shader( + pp::panopainter::LegacyStrokeCompositeUniforms { + .resolution = canvas.m_size, + .mvp = plane_mvp_z, + .pattern_texture_slot = 3, + .layer_alpha = 1.0f, + .alpha_lock = false, + .mask_enabled = false, + .use_fragcoord = false, + .blend_mode = b->m_blend_mode, + .use_dual = false, + .use_pattern = false, + }); + }, + [&](int plane_index) { + set_active_texture_unit(0); + current_layer.rtt(plane_index).bindTexture(); + }, + [&](int plane_index) { + set_active_texture_unit(1); + canvas.m_tmp[plane_index].bindTexture(); + }, + [&](int plane_index) { + set_active_texture_unit(2); + canvas.m_smask.rtt(plane_index).bindTexture(); + }, + [&] { + canvas.m_node->m_face_plane.draw_fill(); + }, + [&](int plane_index) { + set_active_texture_unit(2); + canvas.m_smask.rtt(plane_index).unbindTexture(); + }, + [&](int plane_index) { + set_active_texture_unit(1); + canvas.m_tmp[plane_index].unbindTexture(); + }, + [&](int plane_index) { + set_active_texture_unit(0); + current_layer.rtt(plane_index).unbindTexture(); + }); +} + +void legacy_canvas_stroke_end(Canvas& canvas) +{ + if (!canvas.m_current_stroke) + return; + if (canvas.m_current_stroke->has_sample()) + { + canvas.m_commit_delayed = true; + } + else + { + canvas.m_show_tmp = false; + canvas.stroke_commit(); + canvas.m_current_stroke = nullptr; + } +} + +void legacy_canvas_stroke_cancel(Canvas& canvas) +{ + if (!canvas.m_current_stroke) + return; + canvas.m_current_stroke = nullptr; + canvas.m_show_tmp = false; +} + +void legacy_canvas_stroke_draw_mix(Canvas& canvas, const glm::vec2& bb_min, const glm::vec2& bb_sz) +{ + gl_state gl; + gl.save(); + const auto mix_shell = make_legacy_canvas_stroke_mix_pass_shell(canvas, bb_min, bb_sz); + [[maybe_unused]] const auto mix_result = pp::panopainter::execute_legacy_canvas_stroke_mix_pass_shell( + mix_shell.setup.begin, + mix_shell.setup.end, + mix_shell.request); + + gl.restore(); +} + +std::array, 6> legacy_canvas_stroke_draw_project( + const Canvas& canvas, + std::array& B, + bool project_3d /*= false*/, + glm::mat4 mv /*= glm::mat4(1)*/) +{ + const auto unp_vp = zw(canvas.m_box); + const auto unp_inv = glm::inverse(canvas.m_proj * canvas.m_mv); + std::array, 6> ret; + for (int i = 0; i < 6; i++) + { + struct ray_t { + glm::vec3 o; + glm::vec3 d; + vertex_t v; + ray_t(glm::vec3 o, glm::vec3 d, vertex_t v) : o(o), d(d), v(v) { } + }; + std::vector rays; + if (project_3d) + { + rays.reserve(B.size()); + for (auto const& b : B) + rays.emplace_back(glm::vec3(0), b.pos, b); + } + else + { + auto P = poly_intersect(B.data(), B.data() + 4, canvas.m_plane_shape[i]); + rays.reserve(P.size()); + for (auto const& p : P) + { + glm::vec3 ray_origin, ray_dir; + auto clip_space = glm::vec2(p.pos.x, unp_vp.y - p.pos.y - 1.f) / unp_vp * 2.f - 1.f; + auto wp0 = unp_inv * glm::vec4(clip_space, 0, 1); + auto wp1 = unp_inv * glm::vec4(clip_space, .5, 1); + ray_origin = xyz(wp0 / wp0.w); + ray_dir = glm::normalize(xyz(wp1 / wp1.w) - ray_origin); + rays.emplace_back(ray_origin, ray_dir, p); + } + } + glm::mat4 plane_camera = glm::lookAt(canvas.m_plane_origin[i], canvas.m_plane_normal[i], canvas.m_plane_tangent[i]); + std::vector face_ret; + face_ret.reserve(rays.size()); + for (auto const& r : rays) + { + glm::vec3 hit; + float hit_t; + if (ray_intersect(r.o, r.d, canvas.m_plane_origin[i], canvas.m_plane_normal[i], canvas.m_plane_tangent[i], hit, hit_t)) + { + glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); + + vertex_t v; + v.pos.x = -(plane_local.x * 0.5f - 0.5f) * canvas.m_width; + v.pos.y = (plane_local.y * 0.5f + 0.5f) * canvas.m_height; + + auto hit_cam = mv * glm::vec4(hit, 1); + v.pos.z = 0; + v.pos.w = hit_cam.z; + v.uvs = r.v.uvs * hit_cam.z; + v.uvs2 = r.v.uvs2 * hit_cam.z; + face_ret.emplace_back(v); + } + else + { + break; + } + } + if (face_ret.size() >= 3) + ret[i] = std::move(face_ret); + } + return ret; +} + +void legacy_canvas_stroke_update(Canvas& canvas, glm::vec3 point, float pressure) +{ + canvas.m_current_stroke->add_point(point, pressure); + if (canvas.m_dual_stroke) + canvas.m_dual_stroke->add_point(point, pressure); +} + +void legacy_canvas_stroke_start(Canvas& canvas, glm::vec3 point, float pressure) +{ + assert(App::I->is_render_thread()); + + if (canvas.m_current_stroke && canvas.m_commit_delayed) + { + canvas.m_show_tmp = false; + canvas.m_commit_delayed = false; + canvas.stroke_commit(); + canvas.m_current_stroke = nullptr; + canvas.m_dual_stroke = nullptr; + } + + canvas.m_pattern_offset = canvas.m_current_brush->m_pattern_rand_offset ? + glm::vec2((rand() % 1000) * 0.001f, (rand() % 1000) * 0.001f) : + glm::vec2(0); + + canvas.m_current_stroke = std::make_unique(); + canvas.m_current_stroke->m_camera.rot = canvas.m_cam_rot; + canvas.m_current_stroke->m_camera.fov = canvas.m_cam_fov; + if (canvas.m_current_mode == kCanvasMode::Line) + canvas.m_current_stroke->m_filter_points = false; + canvas.m_current_stroke->randomize_prng(); + canvas.m_current_stroke->start(canvas.m_current_brush); + canvas.m_current_stroke->add_point(point, pressure); + + if (canvas.m_current_brush->m_dual_enabled) + { + auto dual_brush = std::make_shared(); + dual_brush->m_tip_flow = canvas.m_current_brush->m_dual_flow; + dual_brush->m_tip_opacity = canvas.m_current_brush->m_dual_opacity; + dual_brush->m_tip_flipx = canvas.m_current_brush->m_dual_flipx; + dual_brush->m_tip_flipy = canvas.m_current_brush->m_dual_flipy; + dual_brush->m_tip_invert = canvas.m_current_brush->m_dual_invert; + dual_brush->m_blend_mode = canvas.m_current_brush->m_dual_blend_mode; + dual_brush->m_tip_randflipx = canvas.m_current_brush->m_dual_randflip; + dual_brush->m_tip_randflipy = canvas.m_current_brush->m_dual_randflip; + dual_brush->m_tip_size = canvas.m_current_brush->m_dual_size * canvas.m_current_brush->m_tip_size; + dual_brush->m_tip_spacing = canvas.m_current_brush->m_dual_spacing; + dual_brush->m_jitter_scatter = canvas.m_current_brush->m_dual_scatter; + dual_brush->m_jitter_scatter_bothaxis = canvas.m_current_brush->m_dual_scatter_bothaxis; + dual_brush->m_jitter_angle = canvas.m_current_brush->m_dual_rotate; + dual_brush->m_tip_texture = canvas.m_current_brush->m_dual_texture; + dual_brush->m_tip_aspect = canvas.m_current_brush->m_dual_aspect; + canvas.m_dual_stroke = std::make_unique(); + canvas.m_dual_stroke->m_camera.rot = canvas.m_cam_rot; + canvas.m_dual_stroke->m_camera.fov = canvas.m_cam_fov; + canvas.m_dual_stroke->start(dual_brush); + canvas.m_dual_stroke->add_point(point, pressure); + } + + for (int i = 0; i < 6; i++) + { + canvas.m_dirty_box[i] = glm::vec4(canvas.m_width, canvas.m_height, 0, 0); + canvas.m_dirty_face[i] = false; + + canvas.m_tmp[i].bindFramebuffer(); + canvas.m_tmp[i].clear({ 0, 0, 0, 0 }); + canvas.m_tmp[i].unbindFramebuffer(); + + if (canvas.m_current_brush->m_dual_enabled) + { + canvas.m_tmp_dual[i].bindFramebuffer(); + canvas.m_tmp_dual[i].clear({ 0, 0, 0, 0 }); + canvas.m_tmp_dual[i].unbindFramebuffer(); + } + } + canvas.m_mixer.bindFramebuffer(); + canvas.m_mixer.clear(); + canvas.m_mixer.unbindFramebuffer(); + canvas.m_show_tmp = true; +} + +} // namespace pp::panopainter diff --git a/src/legacy_canvas_stroke_runtime_services.h b/src/legacy_canvas_stroke_runtime_services.h new file mode 100644 index 00000000..67f53c76 --- /dev/null +++ b/src/legacy_canvas_stroke_runtime_services.h @@ -0,0 +1,28 @@ +#pragma once + +#include "legacy_canvas_stroke_execution_services.h" + +#include +#include + +class Canvas; + +namespace pp::panopainter { + +[[nodiscard]] LegacyCanvasStrokeMixPassShell make_legacy_canvas_stroke_mix_pass_shell( + Canvas& canvas, + const glm::vec2& bb_min, + const glm::vec2& bb_sz); + +void legacy_canvas_stroke_end(Canvas& canvas); +void legacy_canvas_stroke_cancel(Canvas& canvas); +void legacy_canvas_stroke_draw_mix(Canvas& canvas, const glm::vec2& bb_min, const glm::vec2& bb_sz); +[[nodiscard]] std::array, 6> legacy_canvas_stroke_draw_project( + const Canvas& canvas, + std::array& B, + bool project_3d = false, + glm::mat4 mv = glm::mat4(1)); +void legacy_canvas_stroke_update(Canvas& canvas, glm::vec3 point, float pressure); +void legacy_canvas_stroke_start(Canvas& canvas, glm::vec3 point, float pressure); + +} // namespace pp::panopainter diff --git a/src/legacy_node_canvas_draw_services.cpp b/src/legacy_node_canvas_draw_services.cpp new file mode 100644 index 00000000..d425bb50 --- /dev/null +++ b/src/legacy_node_canvas_draw_services.cpp @@ -0,0 +1,548 @@ +#include "pch.h" + +#include "legacy_node_canvas_draw_services.h" + +#include +#include +#include +#include +#include + +#include "app.h" +#include "legacy_canvas_draw_merge_services.h" +#include "legacy_preference_storage.h" +#include "legacy_ui_gl_dispatch.h" +#include "legacy_ui_overlay_services.h" +#include "log.h" +#include "node_panel_grid.h" +#include "node_image_texture.h" +#include "paint_renderer/compositor.h" +#include "renderer_gl/opengl_capabilities.h" +#include "util.h" + +namespace { + +void set_active_texture_unit(std::uint32_t unit_index) +{ + pp::legacy::ui_gl::activate_texture_unit(unit_index, "NodeCanvas"); +} + +void unbind_texture_2d() +{ + pp::legacy::ui_gl::unbind_texture_2d("NodeCanvas"); +} + +void apply_node_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) +{ + pp::legacy::ui_gl::apply_viewport(x, y, width, height, "NodeCanvas"); +} + +pp::renderer::gl::OpenGlViewportRect query_node_canvas_viewport() +{ + return pp::legacy::ui_gl::query_viewport_rect("NodeCanvas"); +} + +std::array query_node_canvas_clear_color() +{ + return pp::legacy::ui_gl::query_clear_color("NodeCanvas"); +} + +void apply_node_canvas_clear_color(std::array color) +{ + pp::legacy::ui_gl::set_clear_color(color, "NodeCanvas"); +} + +void clear_node_canvas_color_buffer(std::array color) +{ + pp::legacy::ui_gl::clear_color_buffer(color, "NodeCanvas"); +} + +void apply_node_canvas_capability(std::uint32_t state, bool enabled) +{ + pp::legacy::ui_gl::set_capability(state, enabled, "NodeCanvas"); +} + +bool query_node_canvas_capability(std::uint32_t state) +{ + return pp::legacy::ui_gl::query_capability(state, "NodeCanvas"); +} + +pp::renderer::RenderDeviceFeatures node_canvas_stroke_composite_features() noexcept +{ + return ShaderManager::render_device_features(); +} + +pp::paint_renderer::CanvasBlendGatePlan node_canvas_blend_gate_plan( + int width, + int height, + const std::vector>& layers, + const Brush* brush) noexcept +{ + std::vector layer_blend_modes; + layer_blend_modes.reserve(layers.size()); + for (const auto& layer : layers) { + if (!layer) { + continue; + } + layer_blend_modes.push_back(layer->m_blend_mode); + } + + const auto plan = pp::paint_renderer::plan_canvas_blend_gate( + node_canvas_stroke_composite_features(), + pp::paint_renderer::CanvasBlendGateRequest { + .extent = pp::renderer::Extent2D { + .width = static_cast(std::max(width, 0)), + .height = static_cast(std::max(height, 0)), + }, + .layer_blend_modes = layer_blend_modes, + .has_stroke_blend_mode = brush != nullptr, + .stroke_blend_mode = brush ? brush->m_blend_mode : 0, + }); + if (plan) { + return plan.value(); + } + + pp::paint_renderer::CanvasBlendGatePlan fallback; + fallback.shader_blend = true; + fallback.complex_blend = true; + fallback.compatibility_fallback = true; + return fallback; +} + +pp::panopainter::LegacyCanvasDrawMergeLayerPathExecution make_node_canvas_layer_path_execution( + NodeCanvas& node_canvas, + size_t layer_index, + int plane_index, + const glm::mat4& plane_mvp_z, + const Brush* brush, + bool copy_blend_destination, + bool use_nearest_sampler) +{ + auto* layer = node_canvas.m_canvas->m_layers[layer_index].get(); + const auto plane_mvp = plane_mvp_z; + const auto brush_ptr = brush; + const auto smask_active = node_canvas.m_canvas->m_smask_active; + auto draw_layer_frame = pp::panopainter::make_legacy_canvas_draw_merge_layer_frame_draw( + layer, + &node_canvas.m_face_plane, + set_active_texture_unit, + plane_index, + layer->m_opacity); + + glm::vec2 patt_scale = glm::vec2(brush_ptr->m_pattern_scale); + if (brush_ptr->m_pattern_flipx) + patt_scale.x *= -1.f; + if (brush_ptr->m_pattern_flipy) + patt_scale.y *= -1.f; + + return pp::panopainter::make_legacy_canvas_draw_merge_layer_path_gl_execution( + { + .temporary_erase = { + .mvp = plane_mvp, + .texture_slot = 0, + .stroke_texture_slot = 1, + .mask_texture_slot = 2, + .mask_enabled = smask_active, + }, + .temporary_paint = { + .resolution = Canvas::I->m_size, + .pattern = { + .scale = patt_scale, + .invert = static_cast(brush_ptr->m_pattern_invert), + .brightness = brush_ptr->m_pattern_brightness, + .contrast = brush_ptr->m_pattern_contrast, + .depth = brush_ptr->m_pattern_depth, + .blend_mode = brush_ptr->m_pattern_blend_mode, + .offset = Canvas::I->m_pattern_offset, + }, + .mvp = plane_mvp, + .layer_alpha = 1.0f, + .alpha_lock = layer->m_alpha_locked, + .mask_enabled = smask_active, + .use_fragcoord = false, + .blend_mode = brush_ptr->m_blend_mode, + .use_dual = brush_ptr->m_dual_enabled, + .dual_blend_mode = brush_ptr->m_dual_blend_mode, + .dual_alpha = brush_ptr->m_dual_opacity, + .use_pattern = brush_ptr->m_pattern_enabled && !brush_ptr->m_pattern_eachsample, + }, + .layer_texture = { + .mvp = plane_mvp, + .texture_slot = 0, + .alpha = 1.f, + .highlight = layer->m_hightlight, + }, + .blend = { + .shader = { + .mvp = glm::ortho(-1, 1, -1, 1), + .texture_slot = 0, + .destination_texture_slot = 2, + .use_destination_texture = copy_blend_destination, + .blend_mode = layer->m_blend_mode, + .alpha = 1.f, + }, + .copy_destination = copy_blend_destination, + }, + .use_nearest_sampler = use_nearest_sampler, + .use_dual_texture = brush_ptr->m_dual_enabled, + }, + [&node_canvas] { + node_canvas.m_blender_rtt.bindFramebuffer(); + }, + [&node_canvas] { + node_canvas.m_blender_rtt.clear(); + }, + [&node_canvas] { + node_canvas.m_blender_rtt.unbindFramebuffer(); + }, + [&node_canvas](int unit) { + node_canvas.m_sampler.bind(unit); + }, + [&node_canvas](int unit) { + node_canvas.m_sampler_nearest.bind(unit); + }, + [&node_canvas](int unit) { + node_canvas.m_sampler_stencil.bind(unit); + }, + [](int unit) { + set_active_texture_unit(unit); + }, + [&node_canvas, plane_index] { + node_canvas.m_canvas->m_tmp[plane_index].bindTexture(); + }, + [&node_canvas, plane_index] { + node_canvas.m_canvas->m_tmp[plane_index].unbindTexture(); + }, + [&node_canvas, plane_index] { + node_canvas.m_canvas->m_smask.rtt(plane_index).bindTexture(); + }, + [&node_canvas, plane_index] { + node_canvas.m_canvas->m_smask.rtt(plane_index).unbindTexture(); + }, + [&node_canvas, plane_index] { + node_canvas.m_canvas->m_tmp_dual[plane_index].bindTexture(); + }, + [&node_canvas, plane_index] { + node_canvas.m_canvas->m_tmp_dual[plane_index].unbindTexture(); + }, + [brush_ptr] { + brush_ptr->m_pattern_texture ? brush_ptr->m_pattern_texture->bind() : unbind_texture_2d(); + }, + [&node_canvas] { + node_canvas.m_face_plane.draw_fill(); + }, + [&node_canvas] { + node_canvas.m_blender_rtt.bindTexture(); + }, + [&node_canvas] { + node_canvas.m_blender_rtt.unbindTexture(); + }, + [&node_canvas] { + node_canvas.m_blender_bg.bind(); + }, + [&node_canvas] { + node_canvas.m_blender_bg.unbind(); + }, + [&node_canvas] { + copy_framebuffer_to_texture_2d( + 0, + 0, + 0, + 0, + node_canvas.m_blender_bg.size().x, + node_canvas.m_blender_bg.size().y); + }, +#ifdef _DEBUG + [&node_canvas, layer_index, plane_index, plane_mvp] { + auto bb = node_canvas.m_canvas->m_layers[layer_index]->box(plane_index) / (float)node_canvas.m_canvas->m_layers[layer_index]->w; + glm::vec2 bbmin = xy(bb); + glm::vec2 bbsz = zw(bb) - xy(bb); + pp::panopainter::configure_legacy_ui_color_shader( + plane_mvp + * glm::translate(glm::vec3(bbmin * 2.f, 0)) + * glm::translate(glm::vec3(-1, -1, 0)) + * glm::scale(glm::vec3(bbsz, 1)) + * glm::translate(glm::vec3(1, 1, 0)), + { 1, 0, 0, 1 }); + node_canvas.m_face_plane.draw_stroke(); + }, +#else + [] { + }, +#endif + draw_layer_frame); +} + +void execute_node_canvas_draw_unmerged_pass( + NodeCanvas& node_canvas, + const glm::mat4& proj, + const glm::mat4& camera, + const glm::ivec4& c, + float yaw, + float pitch, + float roll) +{ + const auto blend_gate = node_canvas_blend_gate_plan( + node_canvas.m_cache_rtt.getWidth(), + node_canvas.m_cache_rtt.getHeight(), + node_canvas.m_canvas->m_layers, + node_canvas.m_canvas->m_current_stroke ? node_canvas.m_canvas->m_current_stroke->m_brush.get() : nullptr); + const bool use_blend = blend_gate.shader_blend; + const bool copy_blend_destination = use_blend && !blend_gate.reads_destination_color; + const auto layer_orientation = glm::eulerAngleYXZ(yaw, pitch, roll); + + pp::panopainter::execute_legacy_canvas_draw_node_canvas_unmerged_pass( + node_canvas, + use_blend, + copy_blend_destination, + proj, + camera, + layer_orientation, + c, + [&](int x, int y, int width, int height) { + apply_node_canvas_viewport(x, y, width, height); + }, + [&](auto state, bool enabled) { + apply_node_canvas_capability(state, enabled); + }, + [&] { + node_canvas.m_sampler.bind(0); + set_active_texture_unit(0); + }, + [&](size_t layer_index, int plane_index, const glm::mat4& plane_mvp_z, const Brush* brush, bool copy_blend_destination, bool use_nearest_sampler) { + return make_node_canvas_layer_path_execution( + node_canvas, + layer_index, + plane_index, + plane_mvp_z, + brush, + copy_blend_destination, + use_nearest_sampler); + }, + [&](const char* message) { + LOG("NodeCanvas onion frame range failed: %s", message); + }); +} + +void execute_node_canvas_draw_merged_pass( + NodeCanvas& node_canvas, + const glm::mat4& proj, + const glm::mat4& camera) +{ + pp::panopainter::execute_legacy_canvas_draw_merged_pass( + node_canvas, + proj, + camera, + [&](auto state, bool enabled) { + apply_node_canvas_capability(state, enabled); + }, + [](int unit) { + set_active_texture_unit(unit); + }, + [&] { + node_canvas.m_face_plane.draw_fill(); + }); +} + +void execute_node_canvas_draw_merge_tail( + NodeCanvas& node_canvas, + const glm::mat4& ortho_proj, + const glm::mat4& proj, + const glm::mat4& camera, + const glm::ivec4& c) +{ + apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); + + pp::panopainter::execute_legacy_canvas_draw_merge_post_draw_callbacks( + node_canvas.m_canvas->m_smask_active, + node_canvas.m_canvas->m_current_mode == kCanvasMode::Copy || node_canvas.m_canvas->m_current_mode == kCanvasMode::Cut, + node_canvas.m_canvas->m_smask_mode, + node_canvas.m_canvas->m_current_mode != kCanvasMode::Grid, + [&] { + node_canvas.m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera); + }, + [&] { + node_canvas.m_canvas->modes[(int)kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera); + }, + [&] { + pp::panopainter::execute_legacy_canvas_draw_merge_smask_faces( + pp::panopainter::LegacyCanvasDrawMergeTextureMaskUniforms { + .texture_slot = 0, + .pattern_offset = node_canvas.m_outline_pan, + }, + proj, + camera, + node_canvas.m_canvas->m_layers.size() + 500.f, + std::to_array(node_canvas.m_canvas->m_plane_transform), + { + .set_active_texture_unit = [&] { + set_active_texture_unit(0); + }, + .enable_blend = [&] { + apply_node_canvas_capability(pp::renderer::gl::blend_state(), true); + }, + .bind_face_texture = [&](int plane_index) { + node_canvas.m_canvas->m_smask.rtt(plane_index).bindTexture(); + }, + .draw_face = [&] { + node_canvas.m_face_plane.draw_fill(); + }, + .unbind_face_texture = [&](int plane_index) { + node_canvas.m_canvas->m_smask.rtt(plane_index).unbindTexture(); + }, + }); + }, + pp::panopainter::make_legacy_canvas_draw_merge_grid_modes_draw( + &Canvas::modes[(int)kCanvasMode::Grid], + ortho_proj, + proj, + camera), + pp::panopainter::make_legacy_canvas_draw_merge_heightmap_draw(App::I->grid.get(), proj, camera), + pp::panopainter::make_legacy_canvas_draw_merge_current_modes_draw( + node_canvas.m_canvas->m_mode, + ortho_proj, + proj, + camera)); + + if (node_canvas.m_density != 1.f) { + pp::panopainter::execute_legacy_canvas_draw_merge_display_resolve( + pp::panopainter::LegacyCanvasDrawMergeDisplayResolveUniforms { + .texture = { + .mvp = glm::ortho(-1, 1, -1, 1), + .texture_slot = 0, + }, + }, + { + .unbind_resolve_framebuffer = [&] { + node_canvas.m_rtt.unbindFramebuffer(); + }, + .clear_color_buffer = [&] { + clear_node_canvas_color_buffer({ 1.f, 1.f, 1.f, 0.f }); + }, + .apply_viewport = [&] { + apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); + }, + .bind_sampler = [&] { + node_canvas.m_sampler_nearest.bind(0); + }, + .bind_resolve_texture = [&] { + set_active_texture_unit(0); + node_canvas.m_rtt.bindTexture(); + }, + .draw = [&] { + node_canvas.m_face_plane.draw_fill(); + }, + .unbind_resolve_texture = [&] { + node_canvas.m_rtt.unbindTexture(); + }, + }); + } +} + +} // namespace + +namespace pp::panopainter { + +void init_legacy_node_canvas_shell(NodeCanvas& node_canvas) +{ + const auto preferences = read_legacy_canvas_preferences(); + node_canvas.m_density = preferences.viewport_density; + node_canvas.m_cursor_visibility = (NodeCanvas::kCursorVisibility)preferences.cursor_mode; + + node_canvas.m_mouse_ignore = false; + node_canvas.m_canvas = std::make_unique(); + const int canvas_resolution = App::I->default_canvas_resolution(); + node_canvas.m_canvas->create(canvas_resolution, canvas_resolution); + node_canvas.m_canvas->m_unsaved = false; + node_canvas.m_canvas->m_node = &node_canvas; + + node_canvas.m_sampler.create(); + //node_canvas.m_sampler.set_filter(pp::renderer::gl::linear_texture_filter(), pp::renderer::gl::nearest_texture_filter()); + + node_canvas.m_sampler_nearest.create(pp::renderer::gl::nearest_texture_filter()); + + node_canvas.m_sampler_linear.create(pp::renderer::gl::linear_texture_filter()); + node_canvas.m_sampler_stencil.create( + pp::renderer::gl::linear_texture_filter(), + pp::renderer::gl::repeat_texture_wrap()); + node_canvas.m_face_plane.create<1>(2, 2); + node_canvas.m_line.create(); + CanvasMode::node = &node_canvas; + for (int i = 0; i < (int)kCanvasMode::COUNT; i++) + for (auto m : Canvas::modes[i]) + m->init(); + + node_canvas.m_grid.create(1, 1, node_canvas.m_grid_divs); +} + +void draw_legacy_node_canvas_shell(NodeCanvas& node_canvas) +{ + // sanity checks + float zoom = node_canvas.root()->m_zoom; + if (zoom == 0.f) + zoom = 1.f; + auto box = node_canvas.m_clip * zoom; + if (box.z == 0 || box.w == 0) + return; + + const auto vp = query_node_canvas_viewport(); + const auto cc = query_node_canvas_clear_color(); + const auto blend = query_node_canvas_capability(pp::renderer::gl::blend_state()); + const auto depth = query_node_canvas_capability(pp::renderer::gl::depth_test_state()); + const auto scissor = query_node_canvas_capability(pp::renderer::gl::scissor_test_state()); + + apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), false); + + glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp.height - box.y - box.w), box.z, box.w); + + //m_canvas->m_cam_rot = m_pan * 0.003f; + + glm::mat4 ortho_proj = glm::ortho(0.f, box.z, 0.f, box.w, -1000.f, 1000.f); + glm::mat4 proj = glm::perspective(glm::radians(node_canvas.m_canvas->m_cam_fov), box.z / box.w, 0.001f, 1000.f); + glm::mat4 camera = glm::translate(node_canvas.m_canvas->m_cam_pos) * node_canvas.m_canvas->m_cam_rot; + + float pitch = 0; + if (auto slider = node_canvas.root()->find("pitch-slider")) + pitch = (slider->get_value() - 0.5) * glm::half_pi(); + float yaw = 0; + if (auto slider = node_canvas.root()->find("yaw-slider")) + yaw = (slider->get_value() - 0.5) * glm::half_pi(); + float roll = 0; + if (auto slider = node_canvas.root()->find("roll-slider")) + roll = (slider->get_value() - 0.5) * glm::half_pi(); + + prepare_legacy_node_canvas_draw_setup(node_canvas, box, c, proj, camera); + + // NOTE: draw_merge has been disabled for worst performance + bool draw_merged = !(node_canvas.m_canvas->m_current_mode == kCanvasMode::Camera); + draw_merged = false; + + execute_legacy_canvas_draw_node_canvas_shell( + node_canvas.m_density != 1.f, + draw_merged, + [&] { + node_canvas.m_rtt.bindFramebuffer(); + clear_node_canvas_color_buffer({ 1.f, 1.f, 0.f, 0.f }); + apply_node_canvas_viewport(0, 0, node_canvas.m_rtt.getWidth(), node_canvas.m_rtt.getHeight()); + }, + [&] { + clear_node_canvas_color_buffer({ 1.f, 1.f, 1.f, 0.f }); + apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); + }, + [&] { + execute_node_canvas_draw_merged_pass(node_canvas, proj, camera); + }, + [&] { + execute_node_canvas_draw_unmerged_pass(node_canvas, proj, camera, c, yaw, pitch, roll); + }); + + execute_node_canvas_draw_merge_tail(node_canvas, ortho_proj, proj, camera, c); + + scissor ? apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), true) : apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), false); + blend ? apply_node_canvas_capability(pp::renderer::gl::blend_state(), true) : apply_node_canvas_capability(pp::renderer::gl::blend_state(), false); + depth ? apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), true) : apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); + apply_node_canvas_viewport(vp.x, vp.y, vp.width, vp.height); + apply_node_canvas_clear_color(cc); +} + +} // namespace pp::panopainter diff --git a/src/legacy_node_canvas_draw_services.h b/src/legacy_node_canvas_draw_services.h new file mode 100644 index 00000000..13e4d14d --- /dev/null +++ b/src/legacy_node_canvas_draw_services.h @@ -0,0 +1,10 @@ +#pragma once + +#include "node_canvas.h" + +namespace pp::panopainter { + +void init_legacy_node_canvas_shell(NodeCanvas& node_canvas); +void draw_legacy_node_canvas_shell(NodeCanvas& node_canvas); + +} // namespace pp::panopainter diff --git a/src/legacy_node_stroke_preview_runtime_services.cpp b/src/legacy_node_stroke_preview_runtime_services.cpp new file mode 100644 index 00000000..fa6475cf --- /dev/null +++ b/src/legacy_node_stroke_preview_runtime_services.cpp @@ -0,0 +1,187 @@ +#include "pch.h" +#include "log.h" +#include "node_stroke_preview.h" +#include "texture.h" +#include "shader.h" +#include "bezier.h" +#include "canvas.h" +#include "app.h" +#include "legacy_canvas_draw_merge_services.h" +#include "legacy_canvas_stroke_composite_services.h" +#include "legacy_canvas_stroke_execution_services.h" +#include "legacy_canvas_stroke_preview_services.h" +#include "legacy_canvas_stroke_shader_services.h" +#include "legacy_canvas_stroke_services.h" +#include "legacy_node_stroke_preview_execution_services.h" +#include "legacy_ui_gl_dispatch.h" +#include "paint_renderer/compositor.h" +#include "renderer_gl/opengl_capabilities.h" +#include "util.h" +#include +#include +#include + +std::atomic_int NodeStrokePreview::s_instances{ 0 }; +std::atomic_bool NodeStrokePreview::s_running{ false }; +std::mutex NodeStrokePreview::s_render_mutex; +BlockingQueue> NodeStrokePreview::s_queue; + +RTT NodeStrokePreview::m_rtt; +RTT NodeStrokePreview::m_rtt_mixer; +Texture2D NodeStrokePreview::m_tex; // blending tmp texture +Texture2D NodeStrokePreview::m_tex_dual; +Texture2D NodeStrokePreview::m_tex_background; +Sampler NodeStrokePreview::m_sampler_linear; +Sampler NodeStrokePreview::m_sampler_linear_repeat; +Sampler NodeStrokePreview::m_sampler_mipmap; +DynamicShape NodeStrokePreview::m_brush_shape; +std::jthread NodeStrokePreview::s_renderer; + +void NodeStrokePreview::terminate_renderer() +{ + if (!s_renderer.joinable()) + return; + + s_running = false; + s_renderer.request_stop(); + s_queue.UnlockGetters(); + s_renderer.join(); +} + +void NodeStrokePreview::empty_queue() +{ + s_queue.q.clear(); +} + +void NodeStrokePreview::restore_context() +{ + NodeBorder::restore_context(); + init_controls(); + if (m_size.x > 0 && m_size.y > 0) + m_tex_preview.create(static_cast(m_size.x), static_cast(m_size.y)); + draw_stroke(); +} + +void NodeStrokePreview::clear_context() +{ + NodeBorder::clear_context(); + m_tex_preview.destroy(); +} + +Image NodeStrokePreview::render_to_image() +{ + std::lock_guard _lock(s_render_mutex); + + App::I->render_task([this] { + draw_stroke_immediate(); + }); + return m_tex_preview.get_image(); +} + +void NodeStrokePreview::draw_stroke() +{ + if (m_size.x == 0 || m_size.y == 0) + return; + std::unique_lock queue_lock(s_queue.mutex); + if (!s_renderer.joinable()) + { + s_running = true; + s_renderer = std::jthread([](std::stop_token stop_token) { + BT_SetTerminate(); + + m_sampler_linear.create(); + m_sampler_linear_repeat.create( + pp::renderer::gl::linear_texture_filter(), + pp::renderer::gl::repeat_texture_wrap()); + m_sampler_mipmap.create(); + m_sampler_mipmap.set_filter( + pp::renderer::gl::linear_mipmap_linear_texture_filter(), + pp::renderer::gl::linear_texture_filter()); + m_brush_shape.create(); + while (s_running && !stop_token.stop_requested()) + { + auto node = s_queue.Get(); + if (node) + { + std::lock_guard _lock(s_render_mutex); + + // if the brush is not already loaded, load it and then destroy it + bool to_unload = (node->m_brush->m_tip_texture == nullptr); + node->m_brush->preload(); + + App::I->render_task([node, to_unload] + { + gl_state gl; + gl.save(); + + node->m_brush->load(); + node->draw_stroke_immediate(); + if (to_unload) + node->m_brush->unload(); + + gl.restore(); + }); + + node->app_redraw(); + + //std::this_thread::sleep_for(std::chrono::milliseconds(30)); + std::this_thread::yield(); + } + } + m_rtt.destroy(); + m_rtt_mixer.destroy(); + m_tex.destroy(); + m_tex_dual.destroy(); + m_tex_background.destroy(); + m_brush_shape.destroy(); + s_running = false; + }); + } + queue_lock.unlock(); + s_queue.PostUnique(std::static_pointer_cast(shared_from_this()), m_draw_first); +} + +void NodeStrokePreview::draw() +{ + pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader( + pp::panopainter::LegacyCanvasDrawMergeTextureUniforms { + .mvp = m_mvp, + .texture_slot = 0, + }); + m_tex_preview.bind(); + m_sampler_linear.bind(0); + m_plane.draw_fill(); + m_sampler_linear.unbind(); + m_tex_preview.unbind(); +} + +void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) +{ + if (m_preview_size == (new_size * root()->m_zoom) || !m_brush) + return; + + m_preview_size = new_size * root()->m_zoom; + + if (m_on_screen) + draw_stroke(); +} + +void NodeStrokePreview::destroy() +{ + m_tex_preview.destroy(); + Node::destroy(); +} + +void NodeStrokePreview::handle_on_screen(bool old_visibility, bool new_visibility) +{ + parent::handle_on_screen(old_visibility, new_visibility); + if (new_visibility) + { + draw_stroke(); + } + else + { + s_queue.Remove(std::static_pointer_cast(shared_from_this())); + m_tex_preview.destroy(); + } +} diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index 1c569f17..5009a3f1 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -10,111 +10,16 @@ #include "app_core/canvas_tool_ui.h" #include "app_core/document_animation.h" #include "app.h" -#include "node_panel_grid.h" -#include "legacy_canvas_draw_merge_services.h" -#include "legacy_canvas_stroke_composite_services.h" -#include "legacy_canvas_stroke_erase_services.h" +#include "legacy_node_canvas_draw_services.h" #include "legacy_node_canvas_state_services.h" -#include "legacy_preference_storage.h" -#include "legacy_ui_gl_dispatch.h" #include "legacy_ui_overlay_services.h" #include "legacy_canvas_tool_services.h" #include "legacy_history_services.h" #include "log.h" #include "node_canvas.h" -#include "node_image_texture.h" -#include "paint_renderer/compositor.h" -#include "renderer_gl/opengl_capabilities.h" -#include "util.h" namespace { -void set_active_texture_unit(std::uint32_t unit_index) -{ - pp::legacy::ui_gl::activate_texture_unit(unit_index, "NodeCanvas"); -} -void unbind_texture_2d() -{ - pp::legacy::ui_gl::unbind_texture_2d("NodeCanvas"); -} - -void apply_node_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) -{ - pp::legacy::ui_gl::apply_viewport(x, y, width, height, "NodeCanvas"); -} - -pp::renderer::gl::OpenGlViewportRect query_node_canvas_viewport() -{ - return pp::legacy::ui_gl::query_viewport_rect("NodeCanvas"); -} - -std::array query_node_canvas_clear_color() -{ - return pp::legacy::ui_gl::query_clear_color("NodeCanvas"); -} - -void apply_node_canvas_clear_color(std::array color) -{ - pp::legacy::ui_gl::set_clear_color(color, "NodeCanvas"); -} - -void clear_node_canvas_color_buffer(std::array color) -{ - pp::legacy::ui_gl::clear_color_buffer(color, "NodeCanvas"); -} - -void apply_node_canvas_capability(std::uint32_t state, bool enabled) -{ - pp::legacy::ui_gl::set_capability(state, enabled, "NodeCanvas"); -} - -bool query_node_canvas_capability(std::uint32_t state) -{ - return pp::legacy::ui_gl::query_capability(state, "NodeCanvas"); -} - -pp::renderer::RenderDeviceFeatures node_canvas_stroke_composite_features() noexcept -{ - return ShaderManager::render_device_features(); -} - -pp::paint_renderer::CanvasBlendGatePlan node_canvas_blend_gate_plan( - int width, - int height, - const std::vector>& layers, - const Brush* brush) noexcept -{ - std::vector layer_blend_modes; - layer_blend_modes.reserve(layers.size()); - for (const auto& layer : layers) { - if (!layer) { - continue; - } - layer_blend_modes.push_back(layer->m_blend_mode); - } - - const auto plan = pp::paint_renderer::plan_canvas_blend_gate( - node_canvas_stroke_composite_features(), - pp::paint_renderer::CanvasBlendGateRequest { - .extent = pp::renderer::Extent2D { - .width = static_cast(std::max(width, 0)), - .height = static_cast(std::max(height, 0)), - }, - .layer_blend_modes = layer_blend_modes, - .has_stroke_blend_mode = brush != nullptr, - .stroke_blend_mode = brush ? brush->m_blend_mode : 0, - }); - if (plan) { - return plan.value(); - } - - pp::paint_renderer::CanvasBlendGatePlan fallback; - fallback.shader_blend = true; - fallback.complex_blend = true; - fallback.compatibility_fallback = true; - return fallback; -} - pp::app::CanvasHotkeyKey canvas_hotkey_key(kKey key) noexcept { switch (key) { @@ -265,336 +170,6 @@ void run_canvas_tool_mode(pp::app::CanvasToolMode mode) return kEventResult::Consumed; } -pp::panopainter::LegacyCanvasDrawMergeLayerPathExecution make_node_canvas_layer_path_execution( - NodeCanvas& node_canvas, - size_t layer_index, - int plane_index, - const glm::mat4& plane_mvp_z, - const Brush* brush, - bool copy_blend_destination, - bool use_nearest_sampler) -{ - auto* layer = node_canvas.m_canvas->m_layers[layer_index].get(); - const auto plane_mvp = plane_mvp_z; - const auto brush_ptr = brush; - const auto smask_active = node_canvas.m_canvas->m_smask_active; - auto draw_layer_frame = pp::panopainter::make_legacy_canvas_draw_merge_layer_frame_draw( - layer, - &node_canvas.m_face_plane, - set_active_texture_unit, - plane_index, - layer->m_opacity); - - glm::vec2 patt_scale = glm::vec2(brush_ptr->m_pattern_scale); - if (brush_ptr->m_pattern_flipx) - patt_scale.x *= -1.f; - if (brush_ptr->m_pattern_flipy) - patt_scale.y *= -1.f; - - return pp::panopainter::make_legacy_canvas_draw_merge_layer_path_gl_execution( - { - .temporary_erase = { - .mvp = plane_mvp, - .texture_slot = 0, - .stroke_texture_slot = 1, - .mask_texture_slot = 2, - .mask_enabled = smask_active, - }, - .temporary_paint = { - .resolution = Canvas::I->m_size, - .pattern = { - .scale = patt_scale, - .invert = static_cast(brush_ptr->m_pattern_invert), - .brightness = brush_ptr->m_pattern_brightness, - .contrast = brush_ptr->m_pattern_contrast, - .depth = brush_ptr->m_pattern_depth, - .blend_mode = brush_ptr->m_pattern_blend_mode, - .offset = Canvas::I->m_pattern_offset, - }, - .mvp = plane_mvp, - .layer_alpha = 1.0f, - .alpha_lock = layer->m_alpha_locked, - .mask_enabled = smask_active, - .use_fragcoord = false, - .blend_mode = brush_ptr->m_blend_mode, - .use_dual = brush_ptr->m_dual_enabled, - .dual_blend_mode = brush_ptr->m_dual_blend_mode, - .dual_alpha = brush_ptr->m_dual_opacity, - .use_pattern = brush_ptr->m_pattern_enabled && !brush_ptr->m_pattern_eachsample, - }, - .layer_texture = { - .mvp = plane_mvp, - .texture_slot = 0, - .alpha = 1.f, - .highlight = layer->m_hightlight, - }, - .blend = { - .shader = { - .mvp = glm::ortho(-1, 1, -1, 1), - .texture_slot = 0, - .destination_texture_slot = 2, - .use_destination_texture = copy_blend_destination, - .blend_mode = layer->m_blend_mode, - .alpha = 1.f, - }, - .copy_destination = copy_blend_destination, - }, - .use_nearest_sampler = use_nearest_sampler, - .use_dual_texture = brush_ptr->m_dual_enabled, - }, - [&node_canvas] { - node_canvas.m_blender_rtt.bindFramebuffer(); - }, - [&node_canvas] { - node_canvas.m_blender_rtt.clear(); - }, - [&node_canvas] { - node_canvas.m_blender_rtt.unbindFramebuffer(); - }, - [&node_canvas](int unit) { - node_canvas.m_sampler.bind(unit); - }, - [&node_canvas](int unit) { - node_canvas.m_sampler_nearest.bind(unit); - }, - [&node_canvas](int unit) { - node_canvas.m_sampler_stencil.bind(unit); - }, - [](int unit) { - set_active_texture_unit(unit); - }, - [&node_canvas, plane_index] { - node_canvas.m_canvas->m_tmp[plane_index].bindTexture(); - }, - [&node_canvas, plane_index] { - node_canvas.m_canvas->m_tmp[plane_index].unbindTexture(); - }, - [&node_canvas, plane_index] { - node_canvas.m_canvas->m_smask.rtt(plane_index).bindTexture(); - }, - [&node_canvas, plane_index] { - node_canvas.m_canvas->m_smask.rtt(plane_index).unbindTexture(); - }, - [&node_canvas, plane_index] { - node_canvas.m_canvas->m_tmp_dual[plane_index].bindTexture(); - }, - [&node_canvas, plane_index] { - node_canvas.m_canvas->m_tmp_dual[plane_index].unbindTexture(); - }, - [brush_ptr] { - brush_ptr->m_pattern_texture ? brush_ptr->m_pattern_texture->bind() : unbind_texture_2d(); - }, - [&node_canvas] { - node_canvas.m_face_plane.draw_fill(); - }, - [&node_canvas] { - node_canvas.m_blender_rtt.bindTexture(); - }, - [&node_canvas] { - node_canvas.m_blender_rtt.unbindTexture(); - }, - [&node_canvas] { - node_canvas.m_blender_bg.bind(); - }, - [&node_canvas] { - node_canvas.m_blender_bg.unbind(); - }, - [&node_canvas] { - copy_framebuffer_to_texture_2d( - 0, - 0, - 0, - 0, - node_canvas.m_blender_bg.size().x, - node_canvas.m_blender_bg.size().y); - }, -#ifdef _DEBUG - [&node_canvas, layer_index, plane_index, plane_mvp] { - auto bb = node_canvas.m_canvas->m_layers[layer_index]->box(plane_index) / (float)node_canvas.m_canvas->m_layers[layer_index]->w; - glm::vec2 bbmin = xy(bb); - glm::vec2 bbsz = zw(bb) - xy(bb); - pp::panopainter::configure_legacy_ui_color_shader( - plane_mvp - * glm::translate(glm::vec3(bbmin * 2.f, 0)) - * glm::translate(glm::vec3(-1, -1, 0)) - * glm::scale(glm::vec3(bbsz, 1)) - * glm::translate(glm::vec3(1, 1, 0)), - { 1, 0, 0, 1 }); - node_canvas.m_face_plane.draw_stroke(); - }, -#else - [] { - }, -#endif - draw_layer_frame); -} - -void execute_node_canvas_draw_unmerged_pass( - NodeCanvas& node_canvas, - const glm::mat4& proj, - const glm::mat4& camera, - const glm::ivec4& c, - float yaw, - float pitch, - float roll) -{ - const auto blend_gate = node_canvas_blend_gate_plan( - node_canvas.m_cache_rtt.getWidth(), - node_canvas.m_cache_rtt.getHeight(), - node_canvas.m_canvas->m_layers, - node_canvas.m_canvas->m_current_stroke ? node_canvas.m_canvas->m_current_stroke->m_brush.get() : nullptr); - const bool use_blend = blend_gate.shader_blend; - const bool copy_blend_destination = use_blend && !blend_gate.reads_destination_color; - const auto layer_orientation = glm::eulerAngleYXZ(yaw, pitch, roll); - - pp::panopainter::execute_legacy_canvas_draw_node_canvas_unmerged_pass( - node_canvas, - use_blend, - copy_blend_destination, - proj, - camera, - layer_orientation, - c, - [&](int x, int y, int width, int height) { - apply_node_canvas_viewport(x, y, width, height); - }, - [&](auto state, bool enabled) { - apply_node_canvas_capability(state, enabled); - }, - [&] { - node_canvas.m_sampler.bind(0); - set_active_texture_unit(0); - }, - [&](size_t layer_index, int plane_index, const glm::mat4& plane_mvp_z, const Brush* brush, bool copy_blend_destination, bool use_nearest_sampler) { - return make_node_canvas_layer_path_execution( - node_canvas, - layer_index, - plane_index, - plane_mvp_z, - brush, - copy_blend_destination, - use_nearest_sampler); - }, - [&](const char* message) { - LOG("NodeCanvas onion frame range failed: %s", message); - }); -} - -void execute_node_canvas_draw_merged_pass( - NodeCanvas& node_canvas, - const glm::mat4& proj, - const glm::mat4& camera) -{ - pp::panopainter::execute_legacy_canvas_draw_merged_pass( - node_canvas, - proj, - camera, - [&](auto state, bool enabled) { - apply_node_canvas_capability(state, enabled); - }, - [](int unit) { - set_active_texture_unit(unit); - }, - [&] { - node_canvas.m_face_plane.draw_fill(); - }); -} - -void execute_node_canvas_draw_merge_tail( - NodeCanvas& node_canvas, - const glm::mat4& ortho_proj, - const glm::mat4& proj, - const glm::mat4& camera, - const glm::ivec4& c) -{ - apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); - - pp::panopainter::execute_legacy_canvas_draw_merge_post_draw_callbacks( - node_canvas.m_canvas->m_smask_active, - node_canvas.m_canvas->m_current_mode == kCanvasMode::Copy || node_canvas.m_canvas->m_current_mode == kCanvasMode::Cut, - node_canvas.m_canvas->m_smask_mode, - node_canvas.m_canvas->m_current_mode != kCanvasMode::Grid, - [&] { - node_canvas.m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera); - }, - [&] { - node_canvas.m_canvas->modes[(int)kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera); - }, - [&] { - pp::panopainter::execute_legacy_canvas_draw_merge_smask_faces( - pp::panopainter::LegacyCanvasDrawMergeTextureMaskUniforms { - .texture_slot = 0, - .pattern_offset = node_canvas.m_outline_pan, - }, - proj, - camera, - node_canvas.m_canvas->m_layers.size() + 500.f, - std::to_array(node_canvas.m_canvas->m_plane_transform), - { - .set_active_texture_unit = [&] { - set_active_texture_unit(0); - }, - .enable_blend = [&] { - apply_node_canvas_capability(pp::renderer::gl::blend_state(), true); - }, - .bind_face_texture = [&](int plane_index) { - node_canvas.m_canvas->m_smask.rtt(plane_index).bindTexture(); - }, - .draw_face = [&] { - node_canvas.m_face_plane.draw_fill(); - }, - .unbind_face_texture = [&](int plane_index) { - node_canvas.m_canvas->m_smask.rtt(plane_index).unbindTexture(); - }, - }); - }, - pp::panopainter::make_legacy_canvas_draw_merge_grid_modes_draw( - &Canvas::modes[(int)kCanvasMode::Grid], - ortho_proj, - proj, - camera), - pp::panopainter::make_legacy_canvas_draw_merge_heightmap_draw(App::I->grid.get(), proj, camera), - pp::panopainter::make_legacy_canvas_draw_merge_current_modes_draw( - node_canvas.m_canvas->m_mode, - ortho_proj, - proj, - camera)); - - if (node_canvas.m_density != 1.f) { - pp::panopainter::execute_legacy_canvas_draw_merge_display_resolve( - pp::panopainter::LegacyCanvasDrawMergeDisplayResolveUniforms { - .texture = { - .mvp = glm::ortho(-1, 1, -1, 1), - .texture_slot = 0, - }, - }, - { - .unbind_resolve_framebuffer = [&] { - node_canvas.m_rtt.unbindFramebuffer(); - }, - .clear_color_buffer = [&] { - clear_node_canvas_color_buffer({ 1.f, 1.f, 1.f, 0.f }); - }, - .apply_viewport = [&] { - apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); - }, - .bind_sampler = [&] { - node_canvas.m_sampler_nearest.bind(0); - }, - .bind_resolve_texture = [&] { - set_active_texture_unit(0); - node_canvas.m_rtt.bindTexture(); - }, - .draw = [&] { - node_canvas.m_face_plane.draw_fill(); - }, - .unbind_resolve_texture = [&] { - node_canvas.m_rtt.unbindTexture(); - }, - }); - } -} - } Node* NodeCanvas::clone_instantiate() const @@ -604,34 +179,7 @@ Node* NodeCanvas::clone_instantiate() const void NodeCanvas::init() { - const auto preferences = pp::panopainter::read_legacy_canvas_preferences(); - m_density = preferences.viewport_density; - m_cursor_visibility = (kCursorVisibility)preferences.cursor_mode; - - m_mouse_ignore = false; - m_canvas = std::make_unique(); - const int canvas_resolution = App::I->default_canvas_resolution(); - m_canvas->create(canvas_resolution, canvas_resolution); - m_canvas->m_unsaved = false; - m_canvas->m_node = this; - - m_sampler.create(); - //m_sampler.set_filter(pp::renderer::gl::linear_texture_filter(), pp::renderer::gl::nearest_texture_filter()); - - m_sampler_nearest.create(pp::renderer::gl::nearest_texture_filter()); - - m_sampler_linear.create(pp::renderer::gl::linear_texture_filter()); - m_sampler_stencil.create( - pp::renderer::gl::linear_texture_filter(), - pp::renderer::gl::repeat_texture_wrap()); - m_face_plane.create<1>(2, 2); - m_line.create(); - CanvasMode::node = this; - for (int i = 0; i < (int)kCanvasMode::COUNT; i++) - for (auto m : Canvas::modes[i]) - m->init(); - - m_grid.create(1, 1, m_grid_divs); + pp::panopainter::init_legacy_node_canvas_shell(*this); } void NodeCanvas::restore_context() @@ -646,72 +194,7 @@ void NodeCanvas::clear_context() void NodeCanvas::draw() { - // sanity checks - float zoom = root()->m_zoom; - if (zoom == 0.f) - zoom = 1.f; - auto box = m_clip * zoom; - if (box.z == 0 || box.w == 0) - return; - - const auto vp = query_node_canvas_viewport(); - const auto cc = query_node_canvas_clear_color(); - const auto blend = query_node_canvas_capability(pp::renderer::gl::blend_state()); - const auto depth = query_node_canvas_capability(pp::renderer::gl::depth_test_state()); - const auto scissor = query_node_canvas_capability(pp::renderer::gl::scissor_test_state()); - - apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), false); - - glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp.height - box.y - box.w), box.z, box.w); - - //m_canvas->m_cam_rot = m_pan * 0.003f; - - glm::mat4 ortho_proj = glm::ortho(0.f, box.z, 0.f, box.w, -1000.f, 1000.f); - glm::mat4 proj = glm::perspective(glm::radians(m_canvas->m_cam_fov), box.z / box.w, 0.001f, 1000.f); - glm::mat4 camera = glm::translate(m_canvas->m_cam_pos) * m_canvas->m_cam_rot; - - float pitch = 0; - if (auto slider = root()->find("pitch-slider")) - pitch = (slider->get_value() - 0.5) * glm::half_pi(); - float yaw = 0; - if (auto slider = root()->find("yaw-slider")) - yaw = (slider->get_value() - 0.5) * glm::half_pi(); - float roll = 0; - if (auto slider = root()->find("roll-slider")) - roll = (slider->get_value() - 0.5) * glm::half_pi(); - - pp::panopainter::prepare_legacy_node_canvas_draw_setup(*this, box, c, proj, camera); - - // NOTE: draw_merge has been disabled for worst performance - bool draw_merged = !(m_canvas->m_current_mode == kCanvasMode::Camera); - draw_merged = false; - - pp::panopainter::execute_legacy_canvas_draw_node_canvas_shell( - m_density != 1.f, - draw_merged, - [&] { - m_rtt.bindFramebuffer(); - clear_node_canvas_color_buffer({ 1.f, 1.f, 0.f, 0.f }); - apply_node_canvas_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); - }, - [&] { - clear_node_canvas_color_buffer({ 1.f, 1.f, 1.f, 0.f }); - apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); - }, - [&] { - execute_node_canvas_draw_merged_pass(*this, proj, camera); - }, - [&] { - execute_node_canvas_draw_unmerged_pass(*this, proj, camera, c, yaw, pitch, roll); - }); - - execute_node_canvas_draw_merge_tail(*this, ortho_proj, proj, camera, c); - - scissor ? apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), true) : apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), false); - blend ? apply_node_canvas_capability(pp::renderer::gl::blend_state(), true) : apply_node_canvas_capability(pp::renderer::gl::blend_state(), false); - depth ? apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), true) : apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); - apply_node_canvas_viewport(vp.x, vp.y, vp.width, vp.height); - apply_node_canvas_clear_color(cc); + pp::panopainter::draw_legacy_node_canvas_shell(*this); } void NodeCanvas::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index 90429a4b..7fab11f4 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -19,7 +19,6 @@ #include "util.h" #include #include -#include namespace { @@ -360,39 +359,6 @@ void execute_stroke_preview_background_capture_pass( } -std::atomic_int NodeStrokePreview::s_instances{ 0 }; -std::atomic_bool NodeStrokePreview::s_running{ false }; -std::mutex NodeStrokePreview::s_render_mutex; -BlockingQueue> NodeStrokePreview::s_queue; - -RTT NodeStrokePreview::m_rtt; -RTT NodeStrokePreview::m_rtt_mixer; -Texture2D NodeStrokePreview::m_tex; // blending tmp texture -Texture2D NodeStrokePreview::m_tex_dual; -Texture2D NodeStrokePreview::m_tex_background; -Sampler NodeStrokePreview::m_sampler_linear; -Sampler NodeStrokePreview::m_sampler_linear_repeat; -Sampler NodeStrokePreview::m_sampler_mipmap; -DynamicShape NodeStrokePreview::m_brush_shape; -std::jthread NodeStrokePreview::s_renderer; - - -void NodeStrokePreview::terminate_renderer() -{ - if (!s_renderer.joinable()) - return; - - s_running = false; - s_renderer.request_stop(); - s_queue.UnlockGetters(); - s_renderer.join(); -} - -void NodeStrokePreview::empty_queue() -{ - s_queue.q.clear(); -} - Node* NodeStrokePreview::clone_instantiate() const { return new NodeStrokePreview(); @@ -420,21 +386,6 @@ void NodeStrokePreview::init_controls() // Canvas::I->m_current_brush.m_tex_id = const_hash("data/thumbs/Round-Hard.png"); } -void NodeStrokePreview::restore_context() -{ - NodeBorder::restore_context(); - init_controls(); - if (m_size.x > 0 && m_size.y > 0) - m_tex_preview.create(static_cast(m_size.x), static_cast(m_size.y)); - draw_stroke(); -} - -void NodeStrokePreview::clear_context() -{ - NodeBorder::clear_context(); - m_tex_preview.destroy(); -} - void NodeStrokePreview::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) { const auto& b = m_brush; @@ -701,121 +652,3 @@ void NodeStrokePreview::draw_stroke_immediate() apply_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height); apply_stroke_preview_clear_color(cc); } - -Image NodeStrokePreview::render_to_image() -{ - std::lock_guard _lock(s_render_mutex); - - App::I->render_task([this] { - draw_stroke_immediate(); - }); - return m_tex_preview.get_image(); -} - -void NodeStrokePreview::draw_stroke() -{ - if (m_size.x == 0 || m_size.y == 0) - return; - std::unique_lock queue_lock(s_queue.mutex); - if (!s_renderer.joinable()) - { - s_running = true; - s_renderer = std::jthread([](std::stop_token stop_token) { - BT_SetTerminate(); - - m_sampler_linear.create(); - m_sampler_linear_repeat.create( - pp::renderer::gl::linear_texture_filter(), - pp::renderer::gl::repeat_texture_wrap()); - m_sampler_mipmap.create(); - m_sampler_mipmap.set_filter( - pp::renderer::gl::linear_mipmap_linear_texture_filter(), - pp::renderer::gl::linear_texture_filter()); - m_brush_shape.create(); - while (s_running && !stop_token.stop_requested()) - { - auto node = s_queue.Get(); - if (node) - { - std::lock_guard _lock(s_render_mutex); - - // if the brush is not already loaded, load it and then destroy it - bool to_unload = (node->m_brush->m_tip_texture == nullptr); - node->m_brush->preload(); - - App::I->render_task([node, to_unload] - { - gl_state gl; - gl.save(); - - node->m_brush->load(); - node->draw_stroke_immediate(); - if (to_unload) - node->m_brush->unload(); - - gl.restore(); - }); - - node->app_redraw(); - - //std::this_thread::sleep_for(std::chrono::milliseconds(30)); - std::this_thread::yield(); - } - } - m_rtt.destroy(); - m_rtt_mixer.destroy(); - m_tex.destroy(); - m_tex_dual.destroy(); - m_tex_background.destroy(); - m_brush_shape.destroy(); - s_running = false; - }); - } - queue_lock.unlock(); - s_queue.PostUnique(std::static_pointer_cast(shared_from_this()), m_draw_first); -} - -void NodeStrokePreview::draw() -{ - pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader( - pp::panopainter::LegacyCanvasDrawMergeTextureUniforms { - .mvp = m_mvp, - .texture_slot = 0, - }); - m_tex_preview.bind(); - m_sampler_linear.bind(0); - m_plane.draw_fill(); - m_sampler_linear.unbind(); - m_tex_preview.unbind(); -} - -void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) -{ - if (m_preview_size == (new_size * root()->m_zoom) || !m_brush) - return; - - m_preview_size = new_size * root()->m_zoom; - - if (m_on_screen) - draw_stroke(); -} - -void NodeStrokePreview::destroy() -{ - m_tex_preview.destroy(); - Node::destroy(); -} - -void NodeStrokePreview::handle_on_screen(bool old_visibility, bool new_visibility) -{ - parent::handle_on_screen(old_visibility, new_visibility); - if (new_visibility) - { - draw_stroke(); - } - else - { - s_queue.Remove(std::static_pointer_cast(shared_from_this())); - m_tex_preview.destroy(); - } -}