#include "pch.h" #include "app.h" #include "log.h" #include "node_canvas.h" #include "node_image_texture.h" Node* NodeCanvas::clone_instantiate() const { return new NodeCanvas(); } void NodeCanvas::init() { 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(GL_LINEAR, GL_NEAREST); m_sampler_linear.create(GL_LINEAR); m_sampler_stencil.create(GL_LINEAR, GL_REPEAT); m_face_plane.create<1>(2, 2); m_line.create(); CanvasMode::node = this; CanvasMode::canvas = m_canvas.get(); for (int i = 0; i < (int)Canvas::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(GL_LINEAR, GL_NEAREST); m_face_plane.create<1>(2, 2); m_canvas->snapshot_restore(); CanvasMode::node = this; CanvasMode::canvas = m_canvas.get(); for (int i = 0; i < (int)Canvas::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() { GLint vp[4]; GLfloat cc[4]; glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); float zoom = root()->m_zoom; auto box = m_clip * zoom; glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w); glClearColor(1, 1, 1, 0); glClear(GL_COLOR_BUFFER_BIT); glViewport(c.x, c.y, c.z, c.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.01f, 1000.f); glm::mat4 camera = m_canvas->m_cam_rot * glm::translate(m_canvas->m_cam_pos); m_canvas->m_mv = camera; m_canvas->m_proj = proj; m_canvas->m_box = box; m_canvas->m_vp = c; // auto plane_mvp = proj * camera * transform * // glm::scale(glm::vec3(sz, 1)); m_sampler.bind(0); m_sampler.bind(1); m_sampler.bind(2); m_sampler_stencil.bind(3); auto blend = glIsEnabled(GL_BLEND); auto depth = glIsEnabled(GL_DEPTH_TEST); 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); } // check if any layer use blend, otherwise draw directly on main framebuffer bool use_blend = false; for (size_t i = 0; i < m_canvas->m_order.size(); i++) { auto layer_index = m_canvas->m_order[i]; use_blend |= m_canvas->m_layers[layer_index].m_blend_mode != 0; } if (use_blend) { 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_order.size() + 500)) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); ShaderManager::use(kShader::Checkerboard); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); } } // if not using shader blend, use gl rasterizer blend use_blend ? glDisable(GL_BLEND) : glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); for (size_t i = 0; i < m_canvas->m_order.size(); i++) { auto layer_index = m_canvas->m_order[i]; for (int plane_index = 0; plane_index < 6; plane_index++) { 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 || !m_canvas->m_layers[layer_index].m_dirty_face[plane_index])) continue; if (use_blend) { m_blender_rtt.bindFramebuffer(); m_blender_rtt.clear(); } int z = (int)(m_canvas->m_order.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_state == Canvas::kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index) { m_sampler.bind(0); ShaderManager::use(kShader::CompErase); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexStroke, 1); ShaderManager::u_int(kShaderUniform::TexMask, 2); ShaderManager::u_float(kShaderUniform::StrokeAlpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); //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); glActiveTexture(GL_TEXTURE0); m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); glActiveTexture(GL_TEXTURE1); m_canvas->m_tmp[plane_index].bindTexture(); glActiveTexture(GL_TEXTURE2); m_canvas->m_smask.m_rtt[plane_index].bindTexture(); m_face_plane.draw_fill(); m_canvas->m_smask.m_rtt[plane_index].unbindTexture(); glActiveTexture(GL_TEXTURE1); m_canvas->m_tmp[plane_index].unbindTexture(); glActiveTexture(GL_TEXTURE0); m_canvas->m_layers[layer_index].m_rtt[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); auto& paper = TextureManager::get(m_canvas->m_current_stroke->m_brush.m_tex_stencil_id); ShaderManager::use(kShader::CompDraw); 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::TexStencil, 3); ShaderManager::u_float(kShaderUniform::StrokeAlpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity); 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_int(kShaderUniform::UseFragCoordUV2, false); ShaderManager::u_int(kShaderUniform::BlendMode, m_canvas->m_current_stroke->m_brush.m_blend_mode); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); glActiveTexture(GL_TEXTURE0); m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); glActiveTexture(GL_TEXTURE1); m_canvas->m_tmp[plane_index].bindTexture(); glActiveTexture(GL_TEXTURE2); m_canvas->m_smask.m_rtt[plane_index].bindTexture(); glActiveTexture(GL_TEXTURE3); paper.bind(); m_face_plane.draw_fill(); paper.unbind(); glActiveTexture(GL_TEXTURE2); m_canvas->m_smask.m_rtt[plane_index].unbindTexture(); glActiveTexture(GL_TEXTURE1); m_canvas->m_tmp[plane_index].unbindTexture(); glActiveTexture(GL_TEXTURE0); m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture(); } else { m_sampler.bind(0); m_sampler_linear.bind(1); ShaderManager::use(kShader::TextureAlphaSep); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexA, 1); ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity); ShaderManager::u_int(kShaderUniform::Highlight, m_canvas->m_layers[layer_index].m_hightlight); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); glActiveTexture(GL_TEXTURE0); m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); glActiveTexture(GL_TEXTURE1); m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); m_face_plane.draw_fill(); glActiveTexture(GL_TEXTURE1); m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture(); glActiveTexture(GL_TEXTURE0); m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture(); } if (use_blend) { m_blender_rtt.unbindFramebuffer(); } // draw the blended if (use_blend) { m_sampler.bind(0); m_sampler_linear.bind(1); m_sampler.bind(2); ShaderManager::use(kShader::TextureBlend); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexA, 1); #ifndef __IOS__ ShaderManager::u_int(kShaderUniform::TexBG, 2); #endif 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)); glActiveTexture(GL_TEXTURE0); m_blender_rtt.bindTexture(); glActiveTexture(GL_TEXTURE1); m_blender_rtt.bindTexture(); #ifndef __IOS__ glActiveTexture(GL_TEXTURE2); m_blender_bg.bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_blender_bg.size().x, m_blender_bg.size().y); #endif m_face_plane.draw_fill(); #ifndef __IOS__ glActiveTexture(GL_TEXTURE2); m_blender_bg.unbind(); #endif glActiveTexture(GL_TEXTURE1); m_blender_rtt.unbindTexture(); glActiveTexture(GL_TEXTURE0); m_blender_rtt.unbindTexture(); } } } if (use_blend) { m_cache_rtt.unbindFramebuffer(); } // draw the grid behind the layers using a temporary copy if (use_blend) { glEnable(GL_BLEND); //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_order.size() + 500)) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); ShaderManager::use(kShader::Checkerboard); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); } // draw the layers m_sampler.bind(0); glActiveTexture(GL_TEXTURE0); 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(); } for (auto& mode : *m_canvas->m_mode) mode->on_Draw(ortho_proj, proj, camera); glDisable(GL_DEPTH_TEST); auto transform_mode = static_cast(Canvas::modes[(int)Canvas::kCanvasMode::Transform][0]); bool importing = transform_mode->m_action == CanvasModeTransform::ActionType::Import; if (m_canvas->m_smask_active || m_canvas->m_state == Canvas::kCanvasMode::Transform && !importing) { m_canvas->modes[(int)Canvas::kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera); m_canvas->modes[(int)Canvas::kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera); } // keep drawing the grids if (m_canvas->m_state != Canvas::kCanvasMode::Grid) for (auto& mode : Canvas::modes[(int)Canvas::kCanvasMode::Grid]) mode->on_Draw(ortho_proj, proj, camera); App::I.grid->draw_heightmap(proj, camera); blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST); m_sampler.unbind(); 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) { if (new_size.x != m_canvas->m_width || new_size.y != m_canvas->m_height) { // actual screen size new_size = new_size * root()->m_zoom; #if defined(__IOS__) || defined(__ANDROID__) m_canvas->m_mixer.create((int)new_size.x * m_canvas->m_mixer_scale, (int)new_size.y * m_canvas->m_mixer_scale, -1, GL_RGBA16F); #else m_canvas->m_mixer.create((int)new_size.x * m_canvas->m_mixer_scale, (int)new_size.y * m_canvas->m_mixer_scale, -1, GL_RGBA32F); #endif m_blender_rtt.create((int)new_size.x, (int)new_size.y, -1, GL_RGBA8); m_cache_rtt.create((int)new_size.x, (int)new_size.y, -1, GL_RGBA8); m_blender_bg.create((int)new_size.x, (int)new_size.y, GL_RGBA8); if (auto img = root()->find("tex-debug")) img->tex.assign(m_canvas->m_mixer.getTextureID()); // m_canvas->resize((int)new_size.x, (int)new_size.y); // m_canvas->clear(); } } 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); auto loc = (me->m_pos - m_pos) * root()->m_zoom; switch (e->m_type) { case kEventType::MouseMove: if (stylus_eraser != me->m_eraser) { Canvas::set_mode(me->m_eraser ? Canvas::kCanvasMode::Erase : Canvas::kCanvasMode::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; for (auto& mode : *m_canvas->m_mode) mode->on_MouseEvent(me, loc); break; case kEventType::KeyDown: if (ke->m_key == kKey::KeyE) Canvas::set_mode(Canvas::kCanvasMode::Erase); if (ke->m_key == kKey::AndroidBack) if (!ActionManager::empty()) ActionManager::undo(); break; case kEventType::KeyUp: if (ke->m_key == kKey::KeyE) Canvas::set_mode(Canvas::kCanvasMode::Draw); if (ke->m_key == kKey::KeyTab) App::I.toggle_ui(); if (ke->m_key == kKey::KeyZ && App::I.keys[(int)kKey::KeyCtrl]) App::I.keys[(int)kKey::KeyShift] ? ActionManager::redo() : ActionManager::undo(); if (ke->m_key == kKey::KeyS && App::I.keys[(int)kKey::KeyCtrl] && !App::I.keys[(int)kKey::KeyShift]) { if (Canvas::I->m_newdoc) { App::I.dialog_save(); } else if (Canvas::I->m_unsaved) { Canvas::I->project_save(); } } if (ke->m_key == kKey::KeyS && App::I.keys[(int)kKey::KeyCtrl] && App::I.keys[(int)kKey::KeyShift]) { if (Canvas::I->m_newdoc) { App::I.dialog_save(); } else if (Canvas::I->m_unsaved) { App::I.dialog_save_ver(); } } 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; 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}; }