#include "pch.h" #include #include #include #include #include "app_core/canvas_hotkey.h" #include "app_core/canvas_tool_ui.h" #include "app.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 "settings.h" #include "renderer_gl/opengl_capabilities.h" namespace { void set_active_texture_unit(std::uint32_t unit_index) { glActiveTexture(pp::renderer::gl::active_texture_unit(unit_index)); } void unbind_texture_2d() { glBindTexture(pp::renderer::gl::texture_2d_target(), 0); } 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) { case kKey::AndroidBack: return pp::app::CanvasHotkeyKey::android_back; case kKey::KeyAlt: return pp::app::CanvasHotkeyKey::alt; case kKey::KeyE: return pp::app::CanvasHotkeyKey::e; case kKey::KeyS: return pp::app::CanvasHotkeyKey::s; case kKey::KeyTab: return pp::app::CanvasHotkeyKey::tab; case kKey::KeyZ: return pp::app::CanvasHotkeyKey::z; case kKey::KeyBracketLeft: return pp::app::CanvasHotkeyKey::bracket_left; case kKey::KeyBracketRight: return pp::app::CanvasHotkeyKey::bracket_right; default: return pp::app::CanvasHotkeyKey::other; } } pp::app::CanvasHotkeyState canvas_hotkey_state(bool mouse_focused, int touch_finger_count = 0) noexcept { pp::app::CanvasHotkeyState state; state.ctrl_down = App::I && App::I->keys[(int)kKey::KeyCtrl]; state.shift_down = App::I && App::I->keys[(int)kKey::KeyShift]; state.mouse_focused = mouse_focused; const auto history = pp::panopainter::legacy_history_snapshot(); state.undo_count = history.undo_count; state.redo_count = history.redo_count; state.touch_finger_count = touch_finger_count; return state; } void execute_canvas_hotkey_plan(const pp::app::CanvasHotkeyPlan& plan) { const auto status = pp::panopainter::execute_legacy_canvas_hotkey_plan(plan); if (!status.ok()) LOG("Canvas hotkey action failed: %s", status.message); } void run_canvas_hotkey( pp::app::CanvasHotkeyEvent event, kKey key, bool mouse_focused, int touch_finger_count = 0) { const auto plan = pp::app::plan_canvas_hotkey( event, canvas_hotkey_key(key), canvas_hotkey_state(mouse_focused, touch_finger_count)); if (plan) execute_canvas_hotkey_plan(plan.value()); else LOG("Canvas hotkey planning failed: %s", plan.status().message); } void run_canvas_tool_mode(pp::app::CanvasToolMode mode) { const auto plan = pp::app::plan_canvas_tool_select(mode); const auto status = pp::panopainter::execute_legacy_canvas_input_tool_plan(plan); if (!status.ok()) LOG("Canvas input tool action failed: %s", status.message); } } Node* NodeCanvas::clone_instantiate() const { return new NodeCanvas(); } void NodeCanvas::init() { m_density = Settings::value_or("vp-scale", 1.f); m_cursor_visibility = (kCursorVisibility)Settings::value_or("show-cursor", 0); m_mouse_ignore = false; m_canvas = std::make_unique(); m_canvas->create(CANVAS_RES, CANVAS_RES); 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); } void NodeCanvas::restore_context() { Node::restore_context(); m_canvas->create(CANVAS_RES, CANVAS_RES); m_sampler.create(); m_sampler.set_filter( pp::renderer::gl::linear_texture_filter(), pp::renderer::gl::nearest_texture_filter()); m_face_plane.create<1>(2, 2); m_canvas->snapshot_restore(); CanvasMode::node = this; for (int i = 0; i < (int)kCanvasMode::COUNT; i++) for (auto m : Canvas::modes[i]) m->init(); } void NodeCanvas::clear_context() { Node::clear_context(); m_canvas->snapshot_save(); m_canvas->clear_context(); // TODO: clear CanvasMode objects } 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; GLint vp[4]; GLfloat cc[4]; glGetIntegerv(pp::renderer::gl::viewport_query(), vp); glGetFloatv(pp::renderer::gl::color_clear_value_query(), cc); auto blend = glIsEnabled(pp::renderer::gl::blend_state()); auto depth = glIsEnabled(pp::renderer::gl::depth_test_state()); auto scissor = glIsEnabled(pp::renderer::gl::scissor_test_state()); glDisable(pp::renderer::gl::scissor_test_state()); glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - 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; m_canvas->m_mv = camera; m_canvas->m_proj = proj; m_canvas->m_box = box; m_canvas->m_vp = c; 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(); // pre computed helpers for (int plane_index = 0; plane_index < 6; plane_index++) { //glm::mat4 plane_camera = glm::lookAt(m_canvas->m_plane_origin[plane_index], m_canvas->m_plane_normal[plane_index], m_canvas->m_plane_tangent[plane_index]); m_canvas->m_plane_unproject[plane_index] = glm::inverse(m_canvas->m_proj * m_canvas->m_mv * m_canvas->m_plane_transform[plane_index]); m_canvas->m_plane_dir[plane_index] = -(m_canvas->m_plane_transform[plane_index] * glm::vec4(m_canvas->m_plane_origin[plane_index], 1)); // face is the 2d shape of the cube plane i projected onto the window space m_canvas->m_plane_shape[plane_index] = m_canvas->face_to_shape2D(plane_index); } if (m_density != 1.f) { m_rtt.bindFramebuffer(); glClearColor(1, 1, 0, 0); glClear(pp::renderer::gl::framebuffer_color_buffer_mask()); glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); } else { glClearColor(1, 1, 1, 0); glClear(pp::renderer::gl::framebuffer_color_buffer_mask()); glViewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); } // NOTE: draw_merge has been disabled for worst performance bool draw_merged = !(m_canvas->m_current_mode == kCanvasMode::Camera); draw_merged = false; if (draw_merged) { glDisable(pp::renderer::gl::blend_state()); // draw the grid for (int plane_index = 0; plane_index < 6; plane_index++) { auto plane_mvp = proj * camera * glm::scale(glm::vec3(m_canvas->m_layers.size() + 500)) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); ShaderManager::use(kShader::Checkerboard); ShaderManager::u_int(kShaderUniform::Colorize, false); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); int z = 1; auto plane_mvp_z = proj * camera * //glm::scale(glm::vec3(z + 1)) * //glm::eulerAngleYXZ(yaw, pitch, roll) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); m_sampler.bind(0); ShaderManager::use(kShader::TextureAlpha); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_float(kShaderUniform::Alpha, 1.f); ShaderManager::u_int(kShaderUniform::Highlight, false); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); set_active_texture_unit(0); m_canvas->m_layers_merge.rtt(plane_index).bindTexture(); m_face_plane.draw_fill(); m_canvas->m_layers_merge.rtt(plane_index).unbindTexture(); } } else { const auto blend_gate = node_canvas_blend_gate_plan( m_cache_rtt.getWidth(), m_cache_rtt.getHeight(), m_canvas->m_layers, m_canvas->m_current_stroke ? 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; if (use_blend) { glViewport(0, 0, m_cache_rtt.getWidth(), m_cache_rtt.getHeight()); m_cache_rtt.bindFramebuffer(); m_cache_rtt.clear({ 1, 1, 1, 0 }); } else { // draw the grid for (int plane_index = 0; plane_index < 6; plane_index++) { auto plane_mvp = proj * camera * glm::scale(glm::vec3(m_canvas->m_layers.size() + 500)) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); ShaderManager::use(kShader::Checkerboard); ShaderManager::u_int(kShaderUniform::Colorize, false); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); } } // if not using shader blend, use gl rasterizer blend use_blend ? glDisable(pp::renderer::gl::blend_state()) : glEnable(pp::renderer::gl::blend_state()); glDisable(pp::renderer::gl::depth_test_state()); const auto& b = m_canvas->m_current_stroke->m_brush; for (size_t i = 0; i < m_canvas->m_layers.size(); i++) { auto layer_index = i; for (int plane_index = 0; plane_index < 6; plane_index++) { int onion_size = App::I->animation->get_onion_size(); int frame_current = m_canvas->m_layers[layer_index]->m_frame_index; int frame_start = glm::max(frame_current - onion_size, 0); int frame_end = glm::min(frame_current + onion_size, m_canvas->m_layers[layer_index]->frames_count() - 1); bool faces = false; for (int frame = frame_start; frame <= frame_end; frame++) faces |= m_canvas->m_layers[layer_index]->face(plane_index, frame); if (!(m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index) && (!m_canvas->m_layers[layer_index]->m_visible || m_canvas->m_layers[layer_index]->m_opacity == .0f || !faces)) continue; if (use_blend) { m_blender_rtt.bindFramebuffer(); m_blender_rtt.clear(); } int z = (int)(m_canvas->m_layers.size() - i); auto plane_mvp_z = proj * camera * glm::scale(glm::vec3(z + 1)) * glm::eulerAngleYXZ(yaw, pitch, roll) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); if (m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index) { m_sampler.bind(0); m_sampler.bind(1); m_sampler.bind(2); ShaderManager::use(kShader::CompErase); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexStroke, 1); ShaderManager::u_int(kShaderUniform::TexMask, 2); //ShaderManager::u_vec2(kShaderUniform::Resolution, zw(m_canvas->m_box) / zoom); //ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index]->m_alpha_locked); ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); set_active_texture_unit(1); m_canvas->m_tmp[plane_index].bindTexture(); set_active_texture_unit(2); m_canvas->m_smask.rtt(plane_index).bindTexture(); for (int frame = frame_start; frame <= frame_end; frame++) { float onion_alpha = 1.f - (float)glm::abs(frame - frame_current) / (float)(onion_size + 1); ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index]->m_opacity* onion_alpha); set_active_texture_unit(0); m_canvas->m_layers[layer_index]->rtt(plane_index, frame).bindTexture(); m_face_plane.draw_fill(); set_active_texture_unit(0); m_canvas->m_layers[layer_index]->rtt(plane_index, frame).unbindTexture(); } set_active_texture_unit(2); m_canvas->m_smask.rtt(plane_index).unbindTexture(); set_active_texture_unit(1); m_canvas->m_tmp[plane_index].unbindTexture(); } else if(m_canvas->m_current_stroke && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index) { m_sampler.bind(0); m_sampler.bind(1); m_sampler.bind(2); m_sampler.bind(3); m_sampler_stencil.bind(4); glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale); if (b->m_pattern_flipx) patt_scale.x *= -1.f; if (b->m_pattern_flipy) patt_scale.y *= -1.f; ShaderManager::use(kShader::CompDraw); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexStroke, 1); ShaderManager::u_int(kShaderUniform::TexMask, 2); ShaderManager::u_int(kShaderUniform::TexDual, 3); ShaderManager::u_int(kShaderUniform::TexPattern, 4); ShaderManager::u_vec2(kShaderUniform::Resolution, Canvas::I->m_size); ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active); ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index]->m_alpha_locked); ShaderManager::u_int(kShaderUniform::UseFragcoord, false); ShaderManager::u_int(kShaderUniform::BlendMode, b->m_blend_mode); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); ShaderManager::u_int(kShaderUniform::UseDual, b->m_dual_enabled); ShaderManager::u_int(kShaderUniform::DualBlendMode, b->m_dual_blend_mode); ShaderManager::u_float(kShaderUniform::DualAlpha, b->m_dual_opacity); ShaderManager::u_int(kShaderUniform::UsePattern, b->m_pattern_enabled && !b->m_pattern_eachsample); ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale); ShaderManager::u_float(kShaderUniform::PatternInvert, b->m_pattern_invert); ShaderManager::u_float(kShaderUniform::PatternBright, b->m_pattern_brightness); ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast); ShaderManager::u_float(kShaderUniform::PatternDepth, b->m_pattern_depth); ShaderManager::u_int(kShaderUniform::PatternBlendMode, b->m_pattern_blend_mode); ShaderManager::u_vec2(kShaderUniform::PatternOffset, Canvas::I->m_pattern_offset); set_active_texture_unit(1); m_canvas->m_tmp[plane_index].bindTexture(); set_active_texture_unit(2); m_canvas->m_smask.rtt(plane_index).bindTexture(); set_active_texture_unit(3); if (b->m_dual_enabled) m_canvas->m_tmp_dual[plane_index].bindTexture(); set_active_texture_unit(4); b->m_pattern_texture ? b->m_pattern_texture->bind() : unbind_texture_2d(); for (int frame = frame_start; frame <= frame_end; frame++) { float onion_alpha = 1.f - (float)glm::abs(frame - frame_current) / (float)(onion_size + 1); ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index]->m_opacity * onion_alpha); set_active_texture_unit(0); m_canvas->m_layers[layer_index]->rtt(plane_index, frame).bindTexture(); m_face_plane.draw_fill(); set_active_texture_unit(0); m_canvas->m_layers[layer_index]->rtt(plane_index, frame).unbindTexture(); } set_active_texture_unit(3); if (b->m_dual_enabled) m_canvas->m_tmp_dual[plane_index].unbindTexture(); set_active_texture_unit(2); m_canvas->m_smask.rtt(plane_index).unbindTexture(); set_active_texture_unit(1); m_canvas->m_tmp[plane_index].unbindTexture(); } else { m_canvas->m_cam_fov < 20.f ? m_sampler_nearest.bind(0) : m_sampler.bind(0); ShaderManager::use(kShader::TextureAlpha); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::Highlight, m_canvas->m_layers[layer_index]->m_hightlight); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); for (int frame = frame_start; frame <= frame_end; frame++) { float onion_alpha = 1.f - (float)glm::abs(frame - frame_current) / (float)(onion_size + 1); ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index]->m_opacity * onion_alpha); set_active_texture_unit(0); m_canvas->m_layers[layer_index]->rtt(plane_index, frame).bindTexture(); m_face_plane.draw_fill(); m_canvas->m_layers[layer_index]->rtt(plane_index, frame).unbindTexture(); } } if (use_blend) { m_blender_rtt.unbindFramebuffer(); } // draw the blended if (use_blend) { m_sampler.bind(0); m_sampler.bind(2); ShaderManager::use(kShader::TextureBlend); ShaderManager::u_int(kShaderUniform::Tex, 0); if (copy_blend_destination) ShaderManager::u_int(kShaderUniform::TexBG, 2); ShaderManager::u_int(kShaderUniform::BlendMode, m_canvas->m_layers[layer_index]->m_blend_mode); ShaderManager::u_float(kShaderUniform::Alpha, 1.f); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-1, 1, -1, 1)); set_active_texture_unit(0); m_blender_rtt.bindTexture(); if (copy_blend_destination) { set_active_texture_unit(2); m_blender_bg.bind(); glCopyTexSubImage2D(pp::renderer::gl::texture_2d_target(), 0, 0, 0, 0, 0, m_blender_bg.size().x, m_blender_bg.size().y); } m_face_plane.draw_fill(); if (copy_blend_destination) { set_active_texture_unit(2); m_blender_bg.unbind(); } set_active_texture_unit(0); m_blender_rtt.unbindTexture(); } #ifdef _DEBUG // draw dirty area { auto bb = m_canvas->m_layers[layer_index]->box(plane_index) / (float)m_canvas->m_layers[layer_index]->w; glm::vec2 bbmin = xy(bb); glm::vec2 bbsz = zw(bb) - xy(bb); ShaderManager::use(kShader::Color); ShaderManager::u_vec4(kShaderUniform::Col, { 1, 0, 0, 1 }); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z * 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)) ); m_face_plane.draw_stroke(); } #endif } } if (use_blend) { m_cache_rtt.unbindFramebuffer(); if (m_density != 1.f) glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); else glViewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); } // draw the grid behind the layers using a temporary copy if (use_blend) { glEnable(pp::renderer::gl::blend_state()); //draw the grid for (int plane_index = 0; plane_index < 6; plane_index++) { auto plane_mvp = proj * camera * glm::scale(glm::vec3(m_canvas->m_layers.size() + 500.f)) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1.f)); ShaderManager::use(kShader::Checkerboard); ShaderManager::u_int(kShaderUniform::Colorize, false); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); } // draw the layers m_sampler.bind(0); set_active_texture_unit(0); m_cache_rtt.bindTexture(); ShaderManager::use(kShader::Texture); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-1, 1, -1, 1)); m_face_plane.draw_fill(); m_cache_rtt.unbindTexture(); } } glDisable(pp::renderer::gl::depth_test_state()); if (m_canvas->m_smask_active || m_canvas->m_current_mode == kCanvasMode::Copy || m_canvas->m_current_mode == kCanvasMode::Cut) { if (m_canvas->m_smask_mode == 1) m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera); else if (m_canvas->m_smask_mode == 2) m_canvas->modes[(int)kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera); } if (m_canvas->m_smask_active) { ShaderManager::use(kShader::TextureMask); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_vec2(kShaderUniform::PatternOffset, m_outline_pan); set_active_texture_unit(0); glEnable(pp::renderer::gl::blend_state()); //draw the cube faces for (int plane_index = 0; plane_index < 6; plane_index++) { auto plane_mvp = proj * camera * glm::scale(glm::vec3(m_canvas->m_layers.size() + 500.f)) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1.f)); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_canvas->m_smask.rtt(plane_index).bindTexture(); m_face_plane.draw_fill(); m_canvas->m_smask.rtt(plane_index).unbindTexture(); } } // keep drawing the grids if (m_canvas->m_current_mode != kCanvasMode::Grid) for (auto& mode : Canvas::modes[(int)kCanvasMode::Grid]) mode->on_Draw(ortho_proj, proj, camera); App::I->grid->draw_heightmap(proj, camera, false); for (auto& mode : *m_canvas->m_mode) mode->on_Draw(ortho_proj, proj, camera); if (m_density != 1.f) { m_rtt.unbindFramebuffer(); glClearColor(1, 1, 1, 0); glClear(pp::renderer::gl::framebuffer_color_buffer_mask()); glViewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); // draw the canvas m_sampler_nearest.bind(0); set_active_texture_unit(0); m_rtt.bindTexture(); ShaderManager::use(kShader::Texture); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-1, 1, -1, 1)); m_face_plane.draw_fill(); m_rtt.unbindTexture(); } scissor ? glEnable(pp::renderer::gl::scissor_test_state()) : glDisable(pp::renderer::gl::scissor_test_state()); blend ? glEnable(pp::renderer::gl::blend_state()) : glDisable(pp::renderer::gl::blend_state()); depth ? glEnable(pp::renderer::gl::depth_test_state()) : glDisable(pp::renderer::gl::depth_test_state()); glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); } void NodeCanvas::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) { if (new_size.x != m_canvas->m_width || new_size.y != m_canvas->m_height) { new_size = new_size * m_density; create_buffers(); } } kEventResult NodeCanvas::handle_event(Event* e) { static bool stylus_eraser = false; Node::handle_event(e); MouseEvent* me = static_cast(e); KeyEvent* ke = static_cast(e); GestureEvent* ge = static_cast(e); TouchEvent* te = static_cast(e); auto loc = (me->m_pos - m_pos) * root()->m_zoom; switch (e->m_type) { case kEventType::MouseMove: if (stylus_eraser != me->m_eraser) { run_canvas_tool_mode(me->m_eraser ? pp::app::CanvasToolMode::erase : pp::app::CanvasToolMode::draw); stylus_eraser = me->m_eraser; } case kEventType::MouseScroll: case kEventType::MouseDownL: case kEventType::MouseUpL: case kEventType::MouseDownR: case kEventType::MouseUpR: case kEventType::MouseCancel: m_canvas->m_cur_pos = loc; update_cursor(); for (auto& mode : *m_canvas->m_mode) mode->on_MouseEvent(me, loc); break; case kEventType::MouseUnfocus: (*m_canvas->m_mode)[0]->m_draw_tip = false; App::I->show_cursor(); break; case kEventType::MouseFocus: update_cursor(); break; case kEventType::KeyDown: run_canvas_hotkey( pp::app::CanvasHotkeyEvent::key_down, ke->m_key, m_mouse_focus); for (auto& mode : *m_canvas->m_mode) mode->on_KeyEvent(ke); break; case kEventType::KeyUp: update_cursor(); run_canvas_hotkey( pp::app::CanvasHotkeyEvent::key_up, ke->m_key, m_mouse_focus); for (auto& mode : *m_canvas->m_mode) mode->on_KeyEvent(ke); break; case kEventType::GestureStart: mouse_capture(); for (auto& mode : *m_canvas->m_mode) mode->on_GestureEvent(ge); break; case kEventType::GestureMove: for (auto& mode : *m_canvas->m_mode) mode->on_GestureEvent(ge); break; case kEventType::GestureEnd: mouse_release(); for (auto& mode : *m_canvas->m_mode) mode->on_GestureEvent(ge); break; case kEventType::TouchTap: run_canvas_hotkey( pp::app::CanvasHotkeyEvent::touch_tap, kKey::Unknown, m_mouse_focus, te->m_finger_count); break; default: return kEventResult::Available; break; } return kEventResult::Consumed; } void NodeCanvas::reset_camera() { m_canvas->m_cam_rot = glm::mat4(1); m_canvas->m_cam_pos = {0, 0, 0}; m_canvas->m_cam_fov = 85; m_canvas->m_pan = {0, 0}; } void NodeCanvas::create_buffers() { auto new_size = GetSize() * m_density; LOG("NodeCanvas::create_buffers size: %d x %d density %f", (int)new_size.x, (int)new_size.y, m_density); m_canvas->m_mixer.create((int)new_size.x * m_canvas->m_mixer_scale, (int)new_size.y * m_canvas->m_mixer_scale, -1, pp::renderer::gl::rgba8_internal_format()); m_blender_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format()); m_cache_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format()); m_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format(), true); m_blender_bg.create((int)new_size.x, (int)new_size.y, pp::renderer::gl::rgba8_internal_format()); } void NodeCanvas::set_density(float d) { m_density = d; create_buffers(); } void NodeCanvas::set_cursor_visibility(kCursorVisibility mode) { m_cursor_visibility = mode; } void NodeCanvas::update_cursor() { bool visible = true; if (m_canvas->m_current_mode == kCanvasMode::Draw || m_canvas->m_current_mode == kCanvasMode::Erase) { if (m_cursor_visibility == kCursorVisibility::Always) visible = true; if (m_cursor_visibility == kCursorVisibility::Never) visible = false; if (m_cursor_visibility == kCursorVisibility::SmallBrush) visible = m_canvas->m_current_brush->m_tip_size < 10; if (m_cursor_visibility == kCursorVisibility::NotPainting) visible = !m_canvas->get_mode()->m_drawing; if (App::I->keys[(int)kKey::KeyAlt] || m_canvas->get_mode()->m_resizing || m_canvas->get_mode()->m_picking) visible = true; } visible ? App::I->show_cursor() : App::I->hide_cursor(); } void NodeCanvas::on_tick(float dt) { m_outline_pan = glm::fract(m_outline_pan + dt * 0.01f); } void NodeCanvas::destroy() { m_blender_rtt.destroy(); m_cache_rtt.destroy(); m_rtt.destroy(); m_blender_bg.destroy(); m_face_plane.destroy(); m_line.destroy(); m_grid.destroy(); Node::destroy(); }