#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 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) { 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 { return pp::panopainter::legacy_canvas_stroke_draw_project(*this, B, project_3d, mv); } 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); }