Files
panopainter/src/node_canvas.cpp

762 lines
26 KiB
C++

#include "pch.h"
#include <algorithm>
#include <array>
#include <cstdint>
#include <memory>
#include <vector>
#include "app_core/canvas_hotkey.h"
#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_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<float, 4> query_node_canvas_clear_color()
{
return pp::legacy::ui_gl::query_clear_color("NodeCanvas");
}
void apply_node_canvas_clear_color(std::array<float, 4> color)
{
pp::legacy::ui_gl::set_clear_color(color, "NodeCanvas");
}
void clear_node_canvas_color_buffer(std::array<float, 4> 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<std::shared_ptr<Layer>>& layers,
const Brush* brush) noexcept
{
std::vector<int> 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::uint32_t>(std::max(width, 0)),
.height = static_cast<std::uint32_t>(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);
}
[[nodiscard]] kEventResult execute_node_canvas_handle_event(NodeCanvas& node_canvas, Event* e)
{
static bool stylus_eraser = false;
MouseEvent* me = static_cast<MouseEvent*>(e);
KeyEvent* ke = static_cast<KeyEvent*>(e);
GestureEvent* ge = static_cast<GestureEvent*>(e);
TouchEvent* te = static_cast<TouchEvent*>(e);
auto loc = (me->m_pos - node_canvas.m_pos) * node_canvas.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:
node_canvas.m_canvas->m_cur_pos = loc;
node_canvas.update_cursor();
for (auto& mode : *node_canvas.m_canvas->m_mode)
mode->on_MouseEvent(me, loc);
break;
case kEventType::MouseUnfocus:
(*node_canvas.m_canvas->m_mode)[0]->m_draw_tip = false;
App::I->show_cursor();
break;
case kEventType::MouseFocus:
node_canvas.update_cursor();
break;
case kEventType::KeyDown:
run_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_down,
ke->m_key,
node_canvas.m_mouse_focus);
for (auto& mode : *node_canvas.m_canvas->m_mode)
mode->on_KeyEvent(ke);
break;
case kEventType::KeyUp:
node_canvas.update_cursor();
run_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
ke->m_key,
node_canvas.m_mouse_focus);
for (auto& mode : *node_canvas.m_canvas->m_mode)
mode->on_KeyEvent(ke);
break;
case kEventType::GestureStart:
node_canvas.mouse_capture();
for (auto& mode : *node_canvas.m_canvas->m_mode)
mode->on_GestureEvent(ge);
break;
case kEventType::GestureMove:
for (auto& mode : *node_canvas.m_canvas->m_mode)
mode->on_GestureEvent(ge);
break;
case kEventType::GestureEnd:
pp::panopainter::release_legacy_mouse_capture(node_canvas);
for (auto& mode : *node_canvas.m_canvas->m_mode)
mode->on_GestureEvent(ge);
break;
case kEventType::TouchTap:
run_canvas_hotkey(
pp::app::CanvasHotkeyEvent::touch_tap,
kKey::Unknown,
node_canvas.m_mouse_focus,
te->m_finger_count);
break;
default:
return kEventResult::Available;
break;
}
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<float>(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<float>(-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
{
return new NodeCanvas();
}
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<Canvas>();
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);
}
void NodeCanvas::restore_context()
{
pp::panopainter::restore_legacy_node_canvas_context(*this);
}
void NodeCanvas::clear_context()
{
pp::panopainter::clear_legacy_node_canvas_context(*this);
}
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<NodeSliderH>("pitch-slider"))
pitch = (slider->get_value() - 0.5) * glm::half_pi<float>();
float yaw = 0;
if (auto slider = root()->find<NodeSliderH>("yaw-slider"))
yaw = (slider->get_value() - 0.5) * glm::half_pi<float>();
float roll = 0;
if (auto slider = root()->find<NodeSliderH>("roll-slider"))
roll = (slider->get_value() - 0.5) * glm::half_pi<float>();
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);
}
void NodeCanvas::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
{
pp::panopainter::handle_legacy_node_canvas_resize(*this, old_size, new_size, zoom);
}
kEventResult NodeCanvas::handle_event(Event* e)
{
Node::handle_event(e);
return execute_node_canvas_handle_event(*this, e);
}
void NodeCanvas::reset_camera()
{
pp::panopainter::reset_legacy_node_canvas_camera(*this);
}
void NodeCanvas::create_buffers()
{
pp::panopainter::create_legacy_node_canvas_buffers(*this);
}
void NodeCanvas::set_density(float d)
{
pp::panopainter::set_legacy_node_canvas_density(*this, d);
}
void NodeCanvas::set_cursor_visibility(kCursorVisibility mode)
{
pp::panopainter::set_legacy_node_canvas_cursor_visibility(*this, mode);
}
void NodeCanvas::update_cursor()
{
pp::panopainter::update_legacy_node_canvas_cursor(*this);
}
void NodeCanvas::on_tick(float dt)
{
pp::panopainter::tick_legacy_node_canvas_state(*this, dt);
}
void NodeCanvas::destroy()
{
pp::panopainter::destroy_legacy_node_canvas_state(*this);
}