Extract node events, transform mode, and preview pass shells

This commit is contained in:
2026-06-16 19:30:55 +02:00
parent 200265e11d
commit f78f72b607
11 changed files with 1086 additions and 999 deletions

View File

@@ -43,6 +43,8 @@ set(PP_LEGACY_RENDERER_GL_SOURCES
set(PP_LEGACY_UI_CORE_SOURCES set(PP_LEGACY_UI_CORE_SOURCES
src/legacy_ui_node_loader.cpp src/legacy_ui_node_loader.cpp
src/legacy_ui_node_loader.h src/legacy_ui_node_loader.h
src/legacy_ui_node_event.cpp
src/legacy_ui_node_event.h
src/legacy_ui_node_execution.cpp src/legacy_ui_node_execution.cpp
src/layout.cpp src/layout.cpp
src/node.cpp src/node.cpp
@@ -70,6 +72,7 @@ set(PP_LEGACY_APP_SOURCES
src/canvas_modes.cpp src/canvas_modes.cpp
src/legacy_canvas_mode_helpers.cpp src/legacy_canvas_mode_helpers.cpp
src/legacy_canvas_mode_helpers.h src/legacy_canvas_mode_helpers.h
src/legacy_canvas_mode_transform.cpp
src/legacy_app_shell_services.cpp src/legacy_app_shell_services.cpp
src/legacy_app_shell_services.h src/legacy_app_shell_services.h
src/legacy_canvas_tool_services.cpp src/legacy_canvas_tool_services.cpp

View File

@@ -81,11 +81,11 @@ Current hotspot files:
- `src/canvas.cpp`: 2122 lines - `src/canvas.cpp`: 2122 lines
- `src/app_layout.cpp`: 125 lines - `src/app_layout.cpp`: 125 lines
- `src/canvas_modes.cpp`: 1626 lines - `src/canvas_modes.cpp`: 1014 lines
- `src/node.cpp`: 1368 lines - `src/node.cpp`: 995 lines
- `src/main.cpp`: 271 lines - `src/main.cpp`: 271 lines
- `src/node_panel_brush.cpp`: 652 lines - `src/node_panel_brush.cpp`: 652 lines
- `src/node_stroke_preview.cpp`: 910 lines - `src/node_stroke_preview.cpp`: 751 lines
- `src/node_canvas.cpp`: 877 lines - `src/node_canvas.cpp`: 877 lines
- `src/app.cpp`: 575 lines - `src/app.cpp`: 575 lines
- `src/app_dialogs.cpp`: 168 lines - `src/app_dialogs.cpp`: 168 lines
@@ -240,6 +240,10 @@ Current architecture mismatches that must be treated as real blockers:
`src/node_stroke_preview.cpp`, while the immediate preview pass-sequencing `src/node_stroke_preview.cpp`, while the immediate preview pass-sequencing
family inside `draw_stroke_immediate()` now also routes through family inside `draw_stroke_immediate()` now also routes through
`NodeStrokePreview::execute_stroke_draw_immediate_pass_sequence(...)`, while `NodeStrokePreview::execute_stroke_draw_immediate_pass_sequence(...)`, while
the remaining immediate preview pass shell now also routes through
`execute_legacy_node_stroke_preview_draw_immediate_shell(...)`, which
materially reduces the live preview-pass body even though broader
worker/readback flow still remains inline, while
`NodeCanvas::draw()` unmerged-pass blend-gate, layer-orientation, and `NodeCanvas::draw()` unmerged-pass blend-gate, layer-orientation, and
callback-assembly setup now also route through callback-assembly setup now also route through
`execute_node_canvas_draw_unmerged_pass(...)`, which trims another coherent `execute_node_canvas_draw_unmerged_pass(...)`, which trims another coherent
@@ -256,7 +260,11 @@ Current architecture mismatches that must be treated as real blockers:
larger stroke commit/sample execution family now also route through larger stroke commit/sample execution family now also route through
`src/legacy_canvas_stroke_commit_services.*` instead of staying inline in `src/legacy_canvas_stroke_commit_services.*` instead of staying inline in
`src/canvas.cpp`, which trims another large retained stroke-render and `src/canvas.cpp`, which trims another large retained stroke-render and
viewport-state execution family from the live canvas shell, viewport-state execution family from the live canvas shell, while the
`CanvasModeTransform` interaction family now also routes through
`src/legacy_canvas_mode_transform.cpp` instead of staying inline in
`src/canvas_modes.cpp`, which materially thins another retained canvas-view
and transform-mode execution pocket,
while `NodePanelBrush` save/restore/scan/reload/find/get-path ownership now while `NodePanelBrush` save/restore/scan/reload/find/get-path ownership now
routes through `src/legacy_brush_panel_services.*` instead of staying inline routes through `src/legacy_brush_panel_services.*` instead of staying inline
in `src/node_panel_brush.cpp`, which trims another retained brush-workflow in `src/node_panel_brush.cpp`, which trims another retained brush-workflow
@@ -274,7 +282,11 @@ Current architecture mismatches that must be treated as real blockers:
`src/canvas_modes.cpp`, while `src/canvas_modes.cpp`, while
preview stroke preparation, dual-brush setup, and live pass-orchestration preview stroke preparation, dual-brush setup, and live pass-orchestration
request assembly now also route through retained preview execution helpers, request assembly now also route through retained preview execution helpers,
while `Node` child attach/detach/reorder operations now route through named 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
without changing its public surface, while `Node` child attach/detach/reorder
operations now route through named
local helpers in `src/node.cpp`, and `Node::load_internal(...)` child XML local helpers in `src/node.cpp`, and `Node::load_internal(...)` child XML
loading now also routes through `src/legacy_ui_node_loader.*`, which makes loading now also routes through `src/legacy_ui_node_loader.*`, which makes
the scene-graph mutation and child-instantiation paths easier to reason the scene-graph mutation and child-instantiation paths easier to reason

View File

@@ -91,7 +91,7 @@ Status: In Progress
Why now: Why now:
`src/canvas.cpp` is still the biggest single architectural blocker at about `src/canvas.cpp` is still the biggest single architectural blocker at about
2122 lines. 2122 lines, with `src/canvas_modes.cpp` still large at about 1014 lines.
Current slice: Current slice:
- Canvas state-management helpers for picking, clear/clear-all, layer - Canvas state-management helpers for picking, clear/clear-all, layer
@@ -122,6 +122,10 @@ Current slice:
`src/legacy_canvas_mode_helpers.*` instead of staying inline in `src/legacy_canvas_mode_helpers.*` instead of staying inline in
`src/canvas_modes.cpp`, which trims another coherent retained canvas-view `src/canvas_modes.cpp`, which trims another coherent retained canvas-view
interaction pocket from the broader canvas/render hotspot family. interaction pocket from the broader canvas/render hotspot family.
- The `CanvasModeTransform` interaction family now also lives in
`src/legacy_canvas_mode_transform.cpp` instead of staying inline in
`src/canvas_modes.cpp`, which materially thins another retained
transform-mode pocket from the broader canvas/render hotspot family.
Write scope: Write scope:
- `src/canvas.cpp` - `src/canvas.cpp`
@@ -182,6 +186,11 @@ Current slice:
which trims another coherent setup pocket from which trims another coherent setup pocket from
`src/node_stroke_preview.cpp` even though worker/readback ownership and `src/node_stroke_preview.cpp` even though worker/readback ownership and
broader preview flow still remain inline. broader preview flow still remain inline.
- The remaining immediate preview pass shell in
`NodeStrokePreview::draw_stroke_immediate()` now also routes through
`execute_legacy_node_stroke_preview_draw_immediate_shell(...)`, which
materially reduces the live preview-pass body even though worker/readback
ownership still remains inline.
- `NodeCanvas` merged-path per-plane merged-texture draw execution now also - `NodeCanvas` merged-path per-plane merged-texture draw execution now also
routes through `execute_legacy_canvas_draw_merge_layer_texture(...)`. routes through `execute_legacy_canvas_draw_merge_layer_texture(...)`.
- `NodeCanvas` merged-path and non-blend checkerboard background setup now also - `NodeCanvas` merged-path and non-blend checkerboard background setup now also
@@ -706,6 +715,10 @@ Current slice:
now also lives in `src/legacy_ui_node_execution.cpp` instead of staying now also lives in `src/legacy_ui_node_execution.cpp` instead of staying
inline in `src/node.cpp`, which materially thins the base scene-graph file inline in `src/node.cpp`, which materially thins the base scene-graph file
without changing its public surface. without changing its public surface.
- `Node::on_event(...)` plus mouse/key capture and release now also live in
`src/legacy_ui_node_event.*` instead of staying inline in `src/node.cpp`,
which materially thins the base scene-graph event shell without changing its
public surface.
Write scope: Write scope:
- `src/node.cpp` - `src/node.cpp`

View File

@@ -947,557 +947,6 @@ void CanvasModeFill::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, cons
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CanvasModeTransform::init()
{
m_sphere.create(1.f, glm::radians(-10.f), glm::radians(10.f), glm::radians(-10.f), glm::radians(10.f), 1.f);
m_circle.create<16>(1.f);
for (int i = 0; i < 6; i++)
{
m_shape[i].create();
m_points_face[i].clear();
}
m_xform = glm::mat4(1);
m_xform_local = glm::mat4(1);
}
void CanvasModeTransform::enter(kCanvasMode prev)
{
m_commit_on_leave = false;
for (int i = 0; i < 6; i++)
{
m_shape[i].clear();
m_points_face[i].clear();
}
if (m_action == ActionType::Import)
{
float aspect = 1.f;
if (m_source_image.data())
{
m_tex[0].create(m_source_image);
aspect = (float)m_source_image.width / (float)m_source_image.height;
}
auto center = zw(Canvas::I->m_box) * 0.5f;
glm::vec2 bb_sz = glm::vec2(aspect, 1.f) * 100.f * App::I->zoom;
glm::vec2 bb_min = center - bb_sz * 0.5f;
glm::vec2 bb_max = center + bb_sz * 0.5f;
glm::vec2 midpoint = (bb_min + bb_max) * 0.5f;
auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1);
auto center_mat = glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up));
m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up)));
m_xform_local = glm::mat4(1);
corners.clear();
corners.emplace_back(bb_min, 0); // A
corners.emplace_back(bb_max, 0); // C
corners.emplace_back(bb_max.x, bb_min.y, 0); // B
corners.emplace_back(bb_min.x, bb_max.y, 0); // D
corners.emplace_back(midpoint, 0);
corners.emplace_back(midpoint + (bb_max - bb_min) * glm::vec2(0.75f, 0), 0);
for (auto& c : corners)
c = center_mat * glm::vec4(Canvas::I->point_trace(c), 1);
m_points_face[0] = std::vector<vertex_t>({
vertex_t(corners[0], { 0, 0 }),
vertex_t(corners[2], { 1, 0 }),
vertex_t(corners[1], { 1, 1 }),
vertex_t(corners[3], { 0, 1 }),
});
auto shape3d = triangulate(m_points_face[0]);
m_shape[0].update_vertices(shape3d.data(), (int)shape3d.size());
m_commit_on_leave = true;
return;
}
// avoid recursive loop, store the last different mode not using Transform
static kCanvasMode last_prev = kCanvasMode::Draw;
if (prev != kCanvasMode::Copy && prev != kCanvasMode::Cut && prev != kCanvasMode::Import)
last_prev = prev;
if (prev != kCanvasMode::MaskFree && prev != kCanvasMode::MaskLine)
{
Canvas::set_mode(last_prev);
return;
}
auto m = static_cast<CanvasModeMaskBase*>(Canvas::I->modes[(int)prev][0]);
if (m->m_points2d.size() < 3)
{
Canvas::set_mode(last_prev);
return;
}
Canvas::I->m_smask_active = false;
auto points = m->m_points2d;
Canvas::I->push_camera();
Canvas::I->set_camera(m->m_selection_cam);
glm::vec2 bb_min(FLT_MAX);
glm::vec2 bb_max(-FLT_MAX);
for (auto p2d : points)
{
bb_min = glm::min(bb_min, p2d);
bb_max = glm::max(bb_max, p2d);
}
glm::vec2 midpoint = (bb_min + bb_max) * 0.5f;
auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1);
auto center_mat = glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up));
m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up)));
m_xform_local = glm::mat4(1);
corners.clear();
corners.emplace_back(bb_min, 0);
corners.emplace_back(bb_max, 0);
corners.emplace_back(bb_max.x, bb_min.y, 0);
corners.emplace_back(bb_min.x, bb_max.y, 0);
corners.emplace_back(midpoint, 0);
corners.emplace_back(midpoint + (bb_max-bb_min) * glm::vec2(0.75f, 0), 0);
for (auto& c : corners)
c = center_mat * glm::vec4(Canvas::I->point_trace(c), 1);
for (int plane = 0; plane < 6; plane++)
{
auto face = Canvas::I->face_to_shape2D(plane);
auto shape2d = poly_intersect(points, face);
if (shape2d.size() < 3 || face.empty())
{
m_shape[plane].clear();
m_points_face[plane].clear();
continue;
}
m_points_face[plane].reserve(shape2d.size());
glm::vec2 bb_min(Canvas::I->m_size);
glm::vec2 bb_max(0, 0);
for (auto p2d : shape2d)
{
p2d.y = Canvas::I->m_box.w - p2d.y - 1;
auto p2d_clip = ((p2d / zw(Canvas::I->m_box)) * 2.f - 1.f);
auto p3d_plane = Canvas::I->m_plane_unproject[plane] * glm::vec4(p2d_clip, 0, 1);
if (p3d_plane.w < 0)
continue;
auto p3d_norm = -p3d_plane / p3d_plane.z;
auto p2d_plane = xy(p3d_norm);
auto p2d_plane_raster = (p2d_plane * 0.5f + 0.5f) * Canvas::I->m_size;
auto p3d_world = Canvas::I->m_plane_transform[plane] * glm::vec4(p2d_plane, -1, 1);
bb_min = glm::min(bb_min, p2d_plane_raster);
bb_max = glm::max(bb_max, p2d_plane_raster);
//glm::vec3 pt_o, pt_d;
//Canvas::I->point_unproject(p2d, pt_o, pt_d);
vertex_t v;
v.pos = glm::vec4(xyz(p3d_world), 1);
v.uvs = p2d_plane_raster;
m_points_face[plane].push_back(v);
}
if (m_points_face[plane].size() < 3)
{
m_shape[plane].clear();
m_points_face[plane].clear();
continue;
}
auto bb_sz = bb_max - bb_min;
for (auto& v : m_points_face[plane])
{
v.uvs2 = v.uvs / Canvas::I->m_size;
v.uvs = (v.uvs - bb_min) / bb_sz;
v.pos = center_mat * v.pos;
}
auto shape3d = triangulate(m_points_face[plane]);
App::I->render_task([&]
{
m_shape[plane].update_vertices(shape3d.data(), (int)shape3d.size());
Canvas::I->m_layers[Canvas::I->m_current_layer_idx]->rtt(plane).bindFramebuffer();
m_tex[plane].create(bb_sz.x, bb_sz.y);
m_tex[plane].bind();
copy_framebuffer_to_texture_2d(
0,
0,
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x),
static_cast<int>(bb_sz.y));
m_tex[plane].unbind();
Canvas::I->m_layers[Canvas::I->m_current_layer_idx]->rtt(plane).unbindFramebuffer();
});
m_commit_on_leave = true;
}
if (m_action == ActionType::Cut)
{
auto& layer = Canvas::I->m_layers[Canvas::I->m_current_layer_idx];
glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f);
auto action = new ActionStroke;
action->was_saved = !Canvas::I->m_unsaved;
for (int i = 0; i < 6; i++)
{
auto plane_camera = glm::lookAt(glm::vec3(0), Canvas::I->m_plane_origin[i], Canvas::I->m_plane_tangent[i]);
auto mvp = proj * plane_camera * m_xform * m_xform_local;
glm::vec2 bb_min(Canvas::I->m_size);
glm::vec2 bb_max(0, 0);
for (int j = 0; j < 6; j++)
{
for (auto p : m_points_face[j])
{
auto p_clip = mvp * p.pos;
auto p_norm = p_clip / p_clip.w;
if (p_clip.w < 0 || glm::any(glm::greaterThan(glm::abs(xy(p_norm)), { 1, 1 })))
continue;
auto p_raster = (xy(p_norm) * 0.5f + 0.5f) * Canvas::I->m_size;
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster));
bb_max = glm::min(Canvas::I->m_size, glm::max(bb_max, p_raster));
}
}
glm::vec2 pad(2);
bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad);
bb_max = glm::min(Canvas::I->m_size, glm::ceil(bb_max) + pad);
auto bb_sz = bb_max - bb_min;
if (bb_sz.x <= 0.f || bb_sz.y <= 0.f)
continue;
action->m_image[i] = std::make_unique<uint8_t[]>(bb_sz.x * bb_sz.y * 4);
action->m_box[i] = { bb_min, bb_max };
action->m_old_box[i] = layer->box(i);
action->m_old_dirty[i] = layer->face(i);
layer->face(i) = true;
layer->box(i) = {
glm::min(xy(layer->box(i)), bb_min),
glm::max(zw(layer->box(i)), bb_max),
};
App::I->render_task([&]
{
apply_canvas_mode_viewport(0, 0, layer->w, layer->h);
apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false);
apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false);
set_canvas_mode_active_texture_unit(0);
pp::panopainter::setup_legacy_vr_color_shader({
.color = { 0, 0, 0, 0 },
.mvp = mvp,
});
layer->rtt(i).bindFramebuffer();
// copy framebuffer to action data
layer->rtt(i).readPixelsRgba8(
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x),
static_cast<int>(bb_sz.y),
action->m_image[i].get());
for (int j = 0; j < 6; j++)
m_shape[j].draw_fill();
layer->rtt(i).unbindFramebuffer();
});
}
action->m_layer_idx = Canvas::I->m_current_layer_idx;
action->m_frame_idx = Canvas::I->layer().m_frame_index;
action->m_canvas = Canvas::I;
//action->m_stroke = std::move(m_current_stroke);
ActionManager::add(action);
m_source_image.destroy();
}
Canvas::I->pop_camera();
}
void CanvasModeTransform::leave(kCanvasMode next)
{
if (!m_commit_on_leave)
return;
auto& layer = Canvas::I->m_layers[Canvas::I->m_current_layer_idx];
glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f);
auto action = new ActionStroke;
action->was_saved = !Canvas::I->m_unsaved;
for (int i = 0; i < 6; i++)
{
auto plane_camera = glm::lookAt(glm::vec3(0), Canvas::I->m_plane_origin[i], Canvas::I->m_plane_tangent[i]);
auto mv = plane_camera * m_xform * m_xform_local;
auto mvp = proj * mv;
std::vector<glm::vec2> poly2d;
static std::vector<glm::vec2> face_corners{ {1,1}, {-1,1}, {-1,-1}, {1,-1} };
for (int j = 0; j < 6; j++)
{
std::vector<glm::vec3> poly_cam;
poly_cam.reserve(m_points_face[j].size());
for (auto p : m_points_face[j])
poly_cam.push_back(mv * p.pos);
auto poly_clipped = poly_clip_near(poly_cam, 0.01f);
for (auto p : poly_clipped)
{
auto p_clip = proj * glm::vec4(p, 1);
if (p_clip.w < 0)
continue;
auto p_norm = p_clip / p_clip.w;
poly2d.push_back(p_norm);
}
}
auto clipped = poly_intersect(poly2d, face_corners);
glm::vec2 bb_min(Canvas::I->m_size);
glm::vec2 bb_max(0, 0);
for (auto p_norm : clipped)
{
auto p_raster = (p_norm * 0.5f + 0.5f) * Canvas::I->m_size;
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster));
bb_max = glm::min(Canvas::I->m_size, glm::max(bb_max, p_raster));
}
glm::vec2 pad(2);
bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad);
bb_max = glm::min(Canvas::I->m_size, glm::ceil(bb_max) + pad);
auto bb_sz = bb_max - bb_min;
if (clipped.empty() || bb_sz.x <= 0.f || bb_sz.y <= 0.f)
continue;
action->m_image[i] = std::make_unique<uint8_t[]>(bb_sz.x * bb_sz.y * 4);
action->m_box[i] = { bb_min, bb_max };
action->m_old_box[i] = layer->box(i);
action->m_old_dirty[i] = layer->face(i);
layer->face(i) = true;
layer->box(i) = {
glm::min(xy(layer->box(i)), bb_min),
glm::max(zw(layer->box(i)), bb_max),
};
App::I->render_task([&]
{
layer->rtt(i).bindFramebuffer();
apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false);
apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false);
set_canvas_mode_active_texture_unit(0);
apply_canvas_mode_viewport(0, 0, layer->rtt(i).getWidth(), layer->rtt(i).getHeight());
// save fb content for history
layer->rtt(i).readPixelsRgba8(
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x),
static_cast<int>(bb_sz.y),
action->m_image[i].get());
// copy fb content to texture for blending
set_canvas_mode_active_texture_unit(0);
Canvas::I->m_tex2[i].bind();
copy_framebuffer_to_texture_2d(
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x),
static_cast<int>(bb_sz.y));
// slot for m_tex
set_canvas_mode_active_texture_unit(1);
for (int j = 0; j < 6; j++)
{
pp::panopainter::setup_legacy_stroke_composite_shader(
pp::panopainter::LegacyStrokeCompositeUniforms {
.resolution = Canvas::I->m_size,
.mvp = mvp,
.texture_slot = 0,
.stroke_texture_slot = 1,
.layer_alpha = 1.0f,
.alpha_lock = false,
.mask_enabled = false,
.use_fragcoord = true,
.blend_mode = 0,
.use_dual = false,
.use_pattern = false,
});
Canvas::I->m_sampler_linear.bind(1);
Canvas::I->m_sampler_linear.bind(0);
m_tex[j].bind();
m_shape[j].draw_fill();
m_tex[j].unbind();
}
layer->rtt(i).unbindFramebuffer();
});
}
action->m_layer_idx = Canvas::I->m_current_layer_idx;
action->m_canvas = Canvas::I;
action->m_frame_idx = Canvas::I->layer().m_frame_index;
//action->m_stroke = std::move(m_current_stroke);
ActionManager::add(action);
layer->optimize();
//auto m = static_cast<CanvasModeMaskFree*>(Canvas::I->modes[(int)kCanvasMode::MaskFree][0]);
//m->clear();
}
void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
{
const bool depth = query_canvas_mode_capability(pp::renderer::gl::depth_test_state());
apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false);
apply_canvas_mode_capability(pp::renderer::gl::blend_state(), true);
for (int i = 0; i < 6; i++)
{
pp::panopainter::setup_legacy_vr_color_shader({
.color = { 0, 1, 1, .1 },
.mvp = proj * camera * m_xform * m_xform_local,
});
m_shape[i].draw_fill();
pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader({
.mvp = proj * camera * m_xform * m_xform_local,
.texture_slot = 0,
});
set_canvas_mode_active_texture_unit(0);
m_tex[i].bind();
Canvas::I->m_sampler_linear.bind(0);
m_shape[i].draw_fill();
m_tex[i].unbind();
}
auto m2d = Canvas::I->m_proj * Canvas::I->m_mv * m_xform * m_xform_local;
for (int i = 0; i < corners.size(); i++)
{
auto c = m2d * glm::vec4(corners[i], 1);
if (c.w < 0)
continue;
auto c3d = c / c.w;
auto c2d = (xy(c3d) * 0.5f + 0.5f) * zw(Canvas::I->m_box);
pp::panopainter::setup_legacy_vr_color_shader({
.color = { 1, 1, 1, i == corner_hl ? 1.f : .1f },
.mvp = ortho * glm::translate(glm::vec3(c2d, 0)) * glm::scale(glm::vec3(20.f) * App::I->zoom),
});
m_circle.draw_fill();
// draw black border
pp::panopainter::setup_legacy_vr_color_shader({
.color = { 0, 0, 0, 1 },
.mvp = ortho * glm::translate(glm::vec3(c2d, 0)) * glm::scale(glm::vec3(20.f) * App::I->zoom),
});
m_circle.draw_stroke();
}
if (depth) apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), true);
}
void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{
auto m2d = glm::scale(glm::vec3(1, -1, 1)) * Canvas::I->m_proj *
Canvas::I->m_mv * m_xform * m_xform_local;
switch (me->m_type)
{
case kEventType::MouseDownR:
{
}
break;
case kEventType::MouseUpL:
m_dragging = false;
corner_hl = -1;
break;
case kEventType::MouseDownL:
corner_hl = -1;
corners2d.resize(corners.size());
for (int i = 0; i < corners.size(); i++)
{
auto c = m2d * glm::vec4(corners[i], 1);
corners2d[i] = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(Canvas::I->m_box);
float d = glm::distance(corners2d[i], loc);
if (d < 20.f * App::I->zoom)
corner_hl = i;
}
if (corner_hl != -1)
{
m_dragging = true;
m_drag_start = loc;
m_drag_xform = m_xform;
m_drag_xform_local = m_xform_local;
m_drag_corner = corner_hl;
m_drag_corners2d = corners2d;
if (m_drag_corner < 4)
{
m_drag_diag = glm::distance(corners2d[4], corners2d[m_drag_corner]);
}
}
break;
case kEventType::MouseMove:
{
corner_hl = -1;
corners2d.resize(corners.size());
for (int i = 0; i < corners.size(); i++)
{
auto c = m2d * glm::vec4(corners[i], 1);
corners2d[i] = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(Canvas::I->m_box);
float d = glm::distance(corners2d[i], loc);
if (d < 20.f * App::I->zoom)
corner_hl = i;
}
if (m_dragging)
{
auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1);
//auto diff = glm::radians(loc - m_drag_start) * 0.1f;
//auto m = glm::eulerAngleXY(-diff.y, -diff.x);
//m_xform = m * m_drag_xform;
if (m_drag_corner > -1 && m_drag_corner < 4)
{
auto diag = glm::distance(corners2d[4], loc);
auto scale = diag / m_drag_diag;
m_xform_local = m_drag_xform_local * glm::scale(glm::vec3(scale, scale, 1));
}
if (m_drag_corner == 4)
{
m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(loc), xyz(cam_up)));
}
if (m_drag_corner == 5)
{
auto a = glm::normalize(m_drag_corners2d[m_drag_corner] - m_drag_corners2d[4]);
auto b = glm::normalize(loc - m_drag_corners2d[4]);
auto angle = glm::orientedAngle(a, b);
m_xform_local = m_drag_xform_local * glm::eulerAngleZ(-angle);
}
}
/*
{
auto p2d = loc;
//p2d.y = Canvas::I->m_box.w - p2d.y - 1;
auto p2d_clip = ((p2d / zw(Canvas::I->m_box)) * 2.f - 1.f) * glm::vec2(1, -1);
auto p3d_plane = Canvas::I->m_plane_unproject[0] * glm::vec4(p2d_clip, 0, 1);
auto p2d_plane = -p3d_plane / p3d_plane.z;
// auto p3d_world = Canvas::I->m_plane_transform[0] * glm::vec4(p2d_plane, -1, 1);
int x = 0;
LOG("pt %f %f %f %f", p2d_plane.x, p2d_plane.y, p2d_plane.z, p2d_plane.w);
}
*/
}
break;
default:
break;
}
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc) void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)

View File

@@ -0,0 +1,567 @@
#include "pch.h"
#include "canvas_modes.h"
#include "app.h"
#include "canvas.h"
#include "legacy_canvas_mode_helpers.h"
#include "legacy_canvas_draw_merge_services.h"
#include "legacy_canvas_stroke_composite_services.h"
#include "legacy_ui_overlay_services.h"
#include "renderer_gl/opengl_capabilities.h"
using pp::legacy_canvas_mode::apply_canvas_mode_capability;
using pp::legacy_canvas_mode::apply_canvas_mode_viewport;
using pp::legacy_canvas_mode::query_canvas_mode_capability;
using pp::legacy_canvas_mode::set_canvas_mode_active_texture_unit;
void CanvasModeTransform::init()
{
m_sphere.create(1.f, glm::radians(-10.f), glm::radians(10.f), glm::radians(-10.f), glm::radians(10.f), 1.f);
m_circle.create<16>(1.f);
for (int i = 0; i < 6; i++)
{
m_shape[i].create();
m_points_face[i].clear();
}
m_xform = glm::mat4(1);
m_xform_local = glm::mat4(1);
}
void CanvasModeTransform::enter(kCanvasMode prev)
{
m_commit_on_leave = false;
for (int i = 0; i < 6; i++)
{
m_shape[i].clear();
m_points_face[i].clear();
}
if (m_action == ActionType::Import)
{
float aspect = 1.f;
if (m_source_image.data())
{
m_tex[0].create(m_source_image);
aspect = (float)m_source_image.width / (float)m_source_image.height;
}
auto center = zw(Canvas::I->m_box) * 0.5f;
glm::vec2 bb_sz = glm::vec2(aspect, 1.f) * 100.f * App::I->zoom;
glm::vec2 bb_min = center - bb_sz * 0.5f;
glm::vec2 bb_max = center + bb_sz * 0.5f;
glm::vec2 midpoint = (bb_min + bb_max) * 0.5f;
auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1);
auto center_mat = glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up));
m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up)));
m_xform_local = glm::mat4(1);
corners.clear();
corners.emplace_back(bb_min, 0); // A
corners.emplace_back(bb_max, 0); // C
corners.emplace_back(bb_max.x, bb_min.y, 0); // B
corners.emplace_back(bb_min.x, bb_max.y, 0); // D
corners.emplace_back(midpoint, 0);
corners.emplace_back(midpoint + (bb_max - bb_min) * glm::vec2(0.75f, 0), 0);
for (auto& c : corners)
c = center_mat * glm::vec4(Canvas::I->point_trace(c), 1);
m_points_face[0] = std::vector<vertex_t>({
vertex_t(corners[0], { 0, 0 }),
vertex_t(corners[2], { 1, 0 }),
vertex_t(corners[1], { 1, 1 }),
vertex_t(corners[3], { 0, 1 }),
});
auto shape3d = triangulate(m_points_face[0]);
m_shape[0].update_vertices(shape3d.data(), (int)shape3d.size());
m_commit_on_leave = true;
return;
}
// avoid recursive loop, store the last different mode not using Transform
static kCanvasMode last_prev = kCanvasMode::Draw;
if (prev != kCanvasMode::Copy && prev != kCanvasMode::Cut && prev != kCanvasMode::Import)
last_prev = prev;
if (prev != kCanvasMode::MaskFree && prev != kCanvasMode::MaskLine)
{
Canvas::set_mode(last_prev);
return;
}
auto m = static_cast<CanvasModeMaskBase*>(Canvas::I->modes[(int)prev][0]);
if (m->m_points2d.size() < 3)
{
Canvas::set_mode(last_prev);
return;
}
Canvas::I->m_smask_active = false;
auto points = m->m_points2d;
Canvas::I->push_camera();
Canvas::I->set_camera(m->m_selection_cam);
glm::vec2 bb_min(FLT_MAX);
glm::vec2 bb_max(-FLT_MAX);
for (auto p2d : points)
{
bb_min = glm::min(bb_min, p2d);
bb_max = glm::max(bb_max, p2d);
}
glm::vec2 midpoint = (bb_min + bb_max) * 0.5f;
auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1);
auto center_mat = glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up));
m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up)));
m_xform_local = glm::mat4(1);
corners.clear();
corners.emplace_back(bb_min, 0);
corners.emplace_back(bb_max, 0);
corners.emplace_back(bb_max.x, bb_min.y, 0);
corners.emplace_back(bb_min.x, bb_max.y, 0);
corners.emplace_back(midpoint, 0);
corners.emplace_back(midpoint + (bb_max-bb_min) * glm::vec2(0.75f, 0), 0);
for (auto& c : corners)
c = center_mat * glm::vec4(Canvas::I->point_trace(c), 1);
for (int plane = 0; plane < 6; plane++)
{
auto face = Canvas::I->face_to_shape2D(plane);
auto shape2d = poly_intersect(points, face);
if (shape2d.size() < 3 || face.empty())
{
m_shape[plane].clear();
m_points_face[plane].clear();
continue;
}
m_points_face[plane].reserve(shape2d.size());
glm::vec2 bb_min(Canvas::I->m_size);
glm::vec2 bb_max(0, 0);
for (auto p2d : shape2d)
{
p2d.y = Canvas::I->m_box.w - p2d.y - 1;
auto p2d_clip = ((p2d / zw(Canvas::I->m_box)) * 2.f - 1.f);
auto p3d_plane = Canvas::I->m_plane_unproject[plane] * glm::vec4(p2d_clip, 0, 1);
if (p3d_plane.w < 0)
continue;
auto p3d_norm = -p3d_plane / p3d_plane.z;
auto p2d_plane = xy(p3d_norm);
auto p2d_plane_raster = (p2d_plane * 0.5f + 0.5f) * Canvas::I->m_size;
auto p3d_world = Canvas::I->m_plane_transform[plane] * glm::vec4(p2d_plane, -1, 1);
bb_min = glm::min(bb_min, p2d_plane_raster);
bb_max = glm::max(bb_max, p2d_plane_raster);
//glm::vec3 pt_o, pt_d;
//Canvas::I->point_unproject(p2d, pt_o, pt_d);
vertex_t v;
v.pos = glm::vec4(xyz(p3d_world), 1);
v.uvs = p2d_plane_raster;
m_points_face[plane].push_back(v);
}
if (m_points_face[plane].size() < 3)
{
m_shape[plane].clear();
m_points_face[plane].clear();
continue;
}
auto bb_sz = bb_max - bb_min;
for (auto& v : m_points_face[plane])
{
v.uvs2 = v.uvs / Canvas::I->m_size;
v.uvs = (v.uvs - bb_min) / bb_sz;
v.pos = center_mat * v.pos;
}
auto shape3d = triangulate(m_points_face[plane]);
App::I->render_task([&]
{
m_shape[plane].update_vertices(shape3d.data(), (int)shape3d.size());
Canvas::I->m_layers[Canvas::I->m_current_layer_idx]->rtt(plane).bindFramebuffer();
m_tex[plane].create(bb_sz.x, bb_sz.y);
m_tex[plane].bind();
copy_framebuffer_to_texture_2d(
0,
0,
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x),
static_cast<int>(bb_sz.y));
m_tex[plane].unbind();
Canvas::I->m_layers[Canvas::I->m_current_layer_idx]->rtt(plane).unbindFramebuffer();
});
m_commit_on_leave = true;
}
if (m_action == ActionType::Cut)
{
auto& layer = Canvas::I->m_layers[Canvas::I->m_current_layer_idx];
glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f);
auto action = new ActionStroke;
action->was_saved = !Canvas::I->m_unsaved;
for (int i = 0; i < 6; i++)
{
auto plane_camera = glm::lookAt(glm::vec3(0), Canvas::I->m_plane_origin[i], Canvas::I->m_plane_tangent[i]);
auto mvp = proj * plane_camera * m_xform * m_xform_local;
glm::vec2 bb_min(Canvas::I->m_size);
glm::vec2 bb_max(0, 0);
for (int j = 0; j < 6; j++)
{
for (auto p : m_points_face[j])
{
auto p_clip = mvp * p.pos;
auto p_norm = p_clip / p_clip.w;
if (p_clip.w < 0 || glm::any(glm::greaterThan(glm::abs(xy(p_norm)), { 1, 1 })))
continue;
auto p_raster = (xy(p_norm) * 0.5f + 0.5f) * Canvas::I->m_size;
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster));
bb_max = glm::min(Canvas::I->m_size, glm::max(bb_max, p_raster));
}
}
glm::vec2 pad(2);
bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad);
bb_max = glm::min(Canvas::I->m_size, glm::ceil(bb_max) + pad);
auto bb_sz = bb_max - bb_min;
if (bb_sz.x <= 0.f || bb_sz.y <= 0.f)
continue;
action->m_image[i] = std::make_unique<uint8_t[]>(bb_sz.x * bb_sz.y * 4);
action->m_box[i] = { bb_min, bb_max };
action->m_old_box[i] = layer->box(i);
action->m_old_dirty[i] = layer->face(i);
layer->face(i) = true;
layer->box(i) = {
glm::min(xy(layer->box(i)), bb_min),
glm::max(zw(layer->box(i)), bb_max),
};
App::I->render_task([&]
{
apply_canvas_mode_viewport(0, 0, layer->w, layer->h);
apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false);
apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false);
set_canvas_mode_active_texture_unit(0);
pp::panopainter::setup_legacy_vr_color_shader({
.color = { 0, 0, 0, 0 },
.mvp = mvp,
});
layer->rtt(i).bindFramebuffer();
// copy framebuffer to action data
layer->rtt(i).readPixelsRgba8(
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x),
static_cast<int>(bb_sz.y),
action->m_image[i].get());
for (int j = 0; j < 6; j++)
m_shape[j].draw_fill();
layer->rtt(i).unbindFramebuffer();
});
}
action->m_layer_idx = Canvas::I->m_current_layer_idx;
action->m_frame_idx = Canvas::I->layer().m_frame_index;
action->m_canvas = Canvas::I;
//action->m_stroke = std::move(m_current_stroke);
ActionManager::add(action);
m_source_image.destroy();
}
Canvas::I->pop_camera();
}
void CanvasModeTransform::leave(kCanvasMode next)
{
if (!m_commit_on_leave)
return;
auto& layer = Canvas::I->m_layers[Canvas::I->m_current_layer_idx];
glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f);
auto action = new ActionStroke;
action->was_saved = !Canvas::I->m_unsaved;
for (int i = 0; i < 6; i++)
{
auto plane_camera = glm::lookAt(glm::vec3(0), Canvas::I->m_plane_origin[i], Canvas::I->m_plane_tangent[i]);
auto mv = plane_camera * m_xform * m_xform_local;
auto mvp = proj * mv;
std::vector<glm::vec2> poly2d;
static std::vector<glm::vec2> face_corners{ {1,1}, {-1,1}, {-1,-1}, {1,-1} };
for (int j = 0; j < 6; j++)
{
std::vector<glm::vec3> poly_cam;
poly_cam.reserve(m_points_face[j].size());
for (auto p : m_points_face[j])
poly_cam.push_back(mv * p.pos);
auto poly_clipped = poly_clip_near(poly_cam, 0.01f);
for (auto p : poly_clipped)
{
auto p_clip = proj * glm::vec4(p, 1);
if (p_clip.w < 0)
continue;
auto p_norm = p_clip / p_clip.w;
poly2d.push_back(p_norm);
}
}
auto clipped = poly_intersect(poly2d, face_corners);
glm::vec2 bb_min(Canvas::I->m_size);
glm::vec2 bb_max(0, 0);
for (auto p_norm : clipped)
{
auto p_raster = (p_norm * 0.5f + 0.5f) * Canvas::I->m_size;
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster));
bb_max = glm::min(Canvas::I->m_size, glm::max(bb_max, p_raster));
}
glm::vec2 pad(2);
bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad);
bb_max = glm::min(Canvas::I->m_size, glm::ceil(bb_max) + pad);
auto bb_sz = bb_max - bb_min;
if (clipped.empty() || bb_sz.x <= 0.f || bb_sz.y <= 0.f)
continue;
action->m_image[i] = std::make_unique<uint8_t[]>(bb_sz.x * bb_sz.y * 4);
action->m_box[i] = { bb_min, bb_max };
action->m_old_box[i] = layer->box(i);
action->m_old_dirty[i] = layer->face(i);
layer->face(i) = true;
layer->box(i) = {
glm::min(xy(layer->box(i)), bb_min),
glm::max(zw(layer->box(i)), bb_max),
};
App::I->render_task([&]
{
layer->rtt(i).bindFramebuffer();
apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false);
apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false);
set_canvas_mode_active_texture_unit(0);
apply_canvas_mode_viewport(0, 0, layer->rtt(i).getWidth(), layer->rtt(i).getHeight());
// save fb content for history
layer->rtt(i).readPixelsRgba8(
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x),
static_cast<int>(bb_sz.y),
action->m_image[i].get());
// copy fb content to texture for blending
set_canvas_mode_active_texture_unit(0);
Canvas::I->m_tex2[i].bind();
copy_framebuffer_to_texture_2d(
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x),
static_cast<int>(bb_sz.y));
// slot for m_tex
set_canvas_mode_active_texture_unit(1);
for (int j = 0; j < 6; j++)
{
pp::panopainter::setup_legacy_stroke_composite_shader(
pp::panopainter::LegacyStrokeCompositeUniforms {
.resolution = Canvas::I->m_size,
.mvp = mvp,
.texture_slot = 0,
.stroke_texture_slot = 1,
.layer_alpha = 1.0f,
.alpha_lock = false,
.mask_enabled = false,
.use_fragcoord = true,
.blend_mode = 0,
.use_dual = false,
.use_pattern = false,
});
Canvas::I->m_sampler_linear.bind(1);
Canvas::I->m_sampler_linear.bind(0);
m_tex[j].bind();
m_shape[j].draw_fill();
m_tex[j].unbind();
}
layer->rtt(i).unbindFramebuffer();
});
}
action->m_layer_idx = Canvas::I->m_current_layer_idx;
action->m_canvas = Canvas::I;
action->m_frame_idx = Canvas::I->layer().m_frame_index;
//action->m_stroke = std::move(m_current_stroke);
ActionManager::add(action);
layer->optimize();
//auto m = static_cast<CanvasModeMaskFree*>(Canvas::I->modes[(int)kCanvasMode::MaskFree][0]);
//m->clear();
}
void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
{
const bool depth = query_canvas_mode_capability(pp::renderer::gl::depth_test_state());
apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false);
apply_canvas_mode_capability(pp::renderer::gl::blend_state(), true);
for (int i = 0; i < 6; i++)
{
pp::panopainter::setup_legacy_vr_color_shader({
.color = { 0, 1, 1, .1 },
.mvp = proj * camera * m_xform * m_xform_local,
});
m_shape[i].draw_fill();
pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader({
.mvp = proj * camera * m_xform * m_xform_local,
.texture_slot = 0,
});
set_canvas_mode_active_texture_unit(0);
m_tex[i].bind();
Canvas::I->m_sampler_linear.bind(0);
m_shape[i].draw_fill();
m_tex[i].unbind();
}
auto m2d = Canvas::I->m_proj * Canvas::I->m_mv * m_xform * m_xform_local;
for (int i = 0; i < corners.size(); i++)
{
auto c = m2d * glm::vec4(corners[i], 1);
if (c.w < 0)
continue;
auto c3d = c / c.w;
auto c2d = (xy(c3d) * 0.5f + 0.5f) * zw(Canvas::I->m_box);
pp::panopainter::setup_legacy_vr_color_shader({
.color = { 1, 1, 1, i == corner_hl ? 1.f : .1f },
.mvp = ortho * glm::translate(glm::vec3(c2d, 0)) * glm::scale(glm::vec3(20.f) * App::I->zoom),
});
m_circle.draw_fill();
// draw black border
pp::panopainter::setup_legacy_vr_color_shader({
.color = { 0, 0, 0, 1 },
.mvp = ortho * glm::translate(glm::vec3(c2d, 0)) * glm::scale(glm::vec3(20.f) * App::I->zoom),
});
m_circle.draw_stroke();
}
if (depth) apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), true);
}
void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{
auto m2d = glm::scale(glm::vec3(1, -1, 1)) * Canvas::I->m_proj *
Canvas::I->m_mv * m_xform * m_xform_local;
switch (me->m_type)
{
case kEventType::MouseDownR:
{
}
break;
case kEventType::MouseUpL:
m_dragging = false;
corner_hl = -1;
break;
case kEventType::MouseDownL:
corner_hl = -1;
corners2d.resize(corners.size());
for (int i = 0; i < corners.size(); i++)
{
auto c = m2d * glm::vec4(corners[i], 1);
corners2d[i] = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(Canvas::I->m_box);
float d = glm::distance(corners2d[i], loc);
if (d < 20.f * App::I->zoom)
corner_hl = i;
}
if (corner_hl != -1)
{
m_dragging = true;
m_drag_start = loc;
m_drag_xform = m_xform;
m_drag_xform_local = m_xform_local;
m_drag_corner = corner_hl;
m_drag_corners2d = corners2d;
if (m_drag_corner < 4)
{
m_drag_diag = glm::distance(corners2d[4], corners2d[m_drag_corner]);
}
}
break;
case kEventType::MouseMove:
{
corner_hl = -1;
corners2d.resize(corners.size());
for (int i = 0; i < corners.size(); i++)
{
auto c = m2d * glm::vec4(corners[i], 1);
corners2d[i] = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(Canvas::I->m_box);
float d = glm::distance(corners2d[i], loc);
if (d < 20.f * App::I->zoom)
corner_hl = i;
}
if (m_dragging)
{
auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1);
//auto diff = glm::radians(loc - m_drag_start) * 0.1f;
//auto m = glm::eulerAngleXY(-diff.y, -diff.x);
//m_xform = m * m_drag_xform;
if (m_drag_corner > -1 && m_drag_corner < 4)
{
auto diag = glm::distance(corners2d[4], loc);
auto scale = diag / m_drag_diag;
m_xform_local = m_drag_xform_local * glm::scale(glm::vec3(scale, scale, 1));
}
if (m_drag_corner == 4)
{
m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(loc), xyz(cam_up)));
}
if (m_drag_corner == 5)
{
auto a = glm::normalize(m_drag_corners2d[m_drag_corner] - m_drag_corners2d[4]);
auto b = glm::normalize(loc - m_drag_corners2d[4]);
auto angle = glm::orientedAngle(a, b);
m_xform_local = m_drag_xform_local * glm::eulerAngleZ(-angle);
}
}
/*
{
auto p2d = loc;
//p2d.y = Canvas::I->m_box.w - p2d.y - 1;
auto p2d_clip = ((p2d / zw(Canvas::I->m_box)) * 2.f - 1.f) * glm::vec2(1, -1);
auto p3d_plane = Canvas::I->m_plane_unproject[0] * glm::vec4(p2d_clip, 0, 1);
auto p2d_plane = -p3d_plane / p3d_plane.z;
// auto p3d_world = Canvas::I->m_plane_transform[0] * glm::vec4(p2d_plane, -1, 1);
int x = 0;
LOG("pt %f %f %f %f", p2d_plane.x, p2d_plane.y, p2d_plane.z, p2d_plane.w);
}
*/
}
break;
default:
break;
}
}

View File

@@ -400,6 +400,94 @@ struct LegacyNodeStrokePreviewImmediatePassSequenceRequest {
return request.execute_final_composite(); return request.execute_final_composite();
} }
[[nodiscard]] inline bool execute_legacy_node_stroke_preview_final_composite(
glm::vec2 size,
glm::vec2 pattern_scale,
const Brush& brush,
const pp::paint_renderer::CanvasStrokeCompositePassPlan& composite_pass,
Texture2D& background_texture,
Texture2D& stroke_texture,
Texture2D& dual_texture,
Texture2D& preview_texture,
Sampler& linear_sampler,
Sampler& repeat_sampler,
std::function<void()> bind_pattern_texture,
std::function<void()> draw_composite);
template <
typename Frame,
typename ExecuteDualPass,
typename CaptureBackground,
typename ComputeFrames,
typename BeforeFrame,
typename SetupSampleShader,
typename DrawSample,
typename FinishMainPass,
typename BindPatternTexture,
typename DrawComposite>
[[nodiscard]] inline bool execute_legacy_node_stroke_preview_draw_immediate_shell(
const Brush& brush,
const LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
Texture2D& stroke_texture,
RTT& mixer_rtt,
RTT& render_target,
Texture2D& background_texture,
Texture2D& dual_texture,
Texture2D& preview_texture,
Sampler& linear_sampler,
Sampler& repeat_sampler,
bool copy_stroke_destination,
glm::vec2 size,
std::uint32_t stroke_texture_slot,
ExecuteDualPass&& execute_dual_pass,
CaptureBackground&& capture_background,
ComputeFrames&& compute_frames,
BeforeFrame&& before_frame,
SetupSampleShader&& setup_sample_shader,
DrawSample&& draw_sample,
FinishMainPass&& finish_main_pass,
BindPatternTexture&& bind_pattern_texture,
DrawComposite&& draw_composite)
{
return execute_legacy_node_stroke_preview_immediate_pass_sequence(
LegacyNodeStrokePreviewImmediatePassSequenceRequest {
.execute_dual_pass = std::forward<ExecuteDualPass>(execute_dual_pass),
.capture_background = std::forward<CaptureBackground>(capture_background),
.execute_main_live_pass = [&]() -> bool {
return execute_legacy_node_stroke_preview_main_live_pass(
make_legacy_node_stroke_preview_main_live_pass_request<Frame>(
brush,
pass_orchestration,
stroke_texture,
mixer_rtt,
render_target,
copy_stroke_destination,
size,
stroke_texture_slot,
std::forward<ComputeFrames>(compute_frames),
std::forward<BeforeFrame>(before_frame),
std::forward<SetupSampleShader>(setup_sample_shader),
std::forward<DrawSample>(draw_sample),
std::forward<FinishMainPass>(finish_main_pass)));
},
.execute_final_composite = [&]() -> bool {
return execute_legacy_node_stroke_preview_final_composite(
size,
glm::vec2(brush.m_pattern_scale),
brush,
pass_orchestration.material.composite_pass,
background_texture,
stroke_texture,
dual_texture,
preview_texture,
linear_sampler,
repeat_sampler,
std::forward<BindPatternTexture>(bind_pattern_texture),
std::forward<DrawComposite>(draw_composite));
},
});
}
[[nodiscard]] inline bool execute_legacy_node_stroke_preview_final_composite( [[nodiscard]] inline bool execute_legacy_node_stroke_preview_final_composite(
glm::vec2 size, glm::vec2 size,
glm::vec2 pattern_scale, glm::vec2 pattern_scale,

View File

@@ -0,0 +1,309 @@
#include "pch.h"
#include "legacy_ui_node_event.h"
#include "node.h"
#include "layout.h"
namespace pp::panopainter {
namespace {
void transfer_mouse_focus(Node& node, Event* e)
{
if (e->m_cat != kEventCategory::MouseEvent)
return;
if (node.child_mouse_focus == node.current_mouse_capture)
return;
if (!node.is_child(node.current_mouse_capture.get()))
return;
MouseEvent* me = static_cast<MouseEvent*>(e);
if (node.child_mouse_focus)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseUnfocus;
node.child_mouse_focus->handle_event(&e2);
node.child_mouse_focus->m_mouse_focus = false;
}
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseFocus;
node.current_mouse_capture->handle_event(&e2);
node.child_mouse_focus = node.current_mouse_capture;
node.child_mouse_focus->m_mouse_focus = true;
}
bool should_skip_children(Node& node, Event* e)
{
bool skip_children = false;
if (e->m_cat == kEventCategory::MouseEvent)
{
MouseEvent* me = static_cast<MouseEvent*>(e);
skip_children |= !node.m_mouse_inside && !point_in_rect(me->m_pos, node.m_clip);
}
skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) &&
(node.m_mouse_captured) && (node.root()->current_mouse_capture.get() == &node) && node.m_capture_children; // <-- THIS IS WRONG "!m_capture_children" is correct, but it breaks everything if changed
return skip_children;
}
void handle_mouse_focus_transition(Node& node, Event* e, const std::shared_ptr<Node>& child)
{
if (e->m_cat != kEventCategory::MouseEvent || node.child_mouse_focus.get() == child.get())
return;
MouseEvent* me = static_cast<MouseEvent*>(e);
if (node.child_mouse_focus && !node.child_mouse_focus->m_destroyed)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseUnfocus;
node.child_mouse_focus->handle_event(&e2);
node.child_mouse_focus->m_mouse_focus = false;
}
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseFocus;
child->handle_event(&e2);
if (!child->m_destroyed)
{
node.child_mouse_focus = child;
node.child_mouse_focus->m_mouse_focus = true;
}
}
void update_mouse_inside(Node& node, MouseEvent* me)
{
bool old_inside = node.m_mouse_inside;
node.m_mouse_inside = point_in_rect(me->m_pos, node.m_clip);
if (old_inside == false && node.m_mouse_inside == true)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseEnter;
node.handle_event(&e2);
}
if (old_inside == true && node.m_mouse_inside == false)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseLeave;
node.handle_event(&e2);
}
}
} // namespace
kEventResult handle_legacy_ui_node_event(Node& node, Event* e)
{
kEventResult ret = kEventResult::Available;
if (e->m_cat == kEventCategory::KeyEvent && node.current_key_capture)
return node.current_key_capture->on_event(e);
if (node.current_mouse_capture && node.current_mouse_capture.get() != &node)
{
transfer_mouse_focus(node, e);
return node.current_mouse_capture->on_event(e);
}
// skip mouse events if outside
if (!node.m_display || glm::any(glm::lessThanEqual(zw(node.m_clip), { 0, 0 })))
return kEventResult::Available;
if (!should_skip_children(node, e))
{
// make a copy because any handler can change the children vector
auto children_copy = node.m_children;
for (auto it = children_copy.rbegin(); it != children_copy.rend(); ++it)
{
if ((*it)->on_event(e) == kEventResult::Consumed)
{
if (node.m_flood_events)
{
ret = kEventResult::Consumed;
}
else
{
handle_mouse_focus_transition(node, e, *it);
ret = kEventResult::Consumed;
break;
}
}
}
if (ret == kEventResult::Consumed)
{
if (e->m_cat == kEventCategory::MouseEvent)
update_mouse_inside(node, static_cast<MouseEvent*>(e));
return kEventResult::Consumed;
}
}
switch (e->m_cat)
{
case kEventCategory::MouseEvent:
{
if (node.m_mouse_ignore)
break;
MouseEvent* me = static_cast<MouseEvent*>(e);
bool inside = point_in_rect(me->m_pos, node.m_clip);
bool inside_old = node.m_mouse_inside;
node.m_mouse_inside = inside;
switch (e->m_type)
{
case kEventType::MouseScroll:
case kEventType::MouseDownL:
case kEventType::MouseDownR:
case kEventType::MouseUpL:
case kEventType::MouseUpR:
if ((inside || node.m_mouse_captured) && ((node.handle_event(e) == kEventResult::Consumed) || node.m_force_mouse_capture))
return kEventResult::Consumed;
break;
case kEventType::MouseMove:
if (inside_old == false && inside == true)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseEnter;
node.handle_event(&e2);
}
if (inside || node.m_mouse_captured)
{
ret = node.handle_event(e);
if (node.m_force_mouse_capture)
ret = kEventResult::Consumed;
}
if (inside_old == true && inside == false)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseLeave;
node.handle_event(&e2);
}
break;
default:
if (node.handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
break;
}
case kEventCategory::GestureEvent:
{
if (node.m_mouse_ignore)
break;
GestureEvent* ge = static_cast<GestureEvent*>(e);
bool inside = point_in_rect(ge->m_pos, node.m_clip);
node.m_mouse_inside = inside;
if ((inside || node.m_mouse_captured) && node.handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
default:
if (node.handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
return ret;
}
void legacy_ui_node_mouse_capture(Node& node)
{
if (!node.m_parent || !node.m_manager)
return;
auto root = node.m_manager->get_ref("main");
if (!root)
return;
auto& c = root->current_mouse_capture;
auto& s = root->m_capture_stack;
// already owner of capture
if (c.get() == &node || std::find_if(s.begin(), s.end(),
[&node](const auto& a) { return a.get() == &node; }) != s.end())
return;
if (c)
{
if (c->is_child_recursive(&node))
{
// save on stack
s.push_back(c);
}
else
{
// cancel previous owner
MouseEvent e;
e.m_type = kEventType::MouseCancel;
c->handle_event(&e);
// TODO: only delete nodes on a different tree,
// so preserve direct parents of this
// also clear the whole stack
//s.clear();
s.push_back(c);
}
}
// make current
c = node.shared_from_this();
node.m_mouse_captured = true;
}
void legacy_ui_node_mouse_release(Node& node)
{
if (!node.m_parent || !node.m_manager)
return;
auto root = node.m_manager->get_ref("main");
if (!root)
return;
auto& c = root->current_mouse_capture;
auto& s = root->m_capture_stack;
s.erase(std::remove_if(s.begin(), s.end(),
[&node](const auto& a) { return a.get() == &node; }), s.end());
if (c.get() == &node)
{
if (s.empty())
{
c = nullptr;
}
else
{
c = s.back();
s.pop_back();
}
}
node.m_mouse_captured = false;
}
void legacy_ui_node_key_capture(Node& node)
{
if (!node.m_parent || !node.m_manager)
return;
auto root = node.m_manager->get_ref("main");
if (!root)
return;
root->current_key_capture = node.shared_from_this();
node.m_key_captured = true;
}
void legacy_ui_node_key_release(Node& node)
{
if (!node.m_parent || !node.m_manager)
return;
auto root = node.m_manager->get_ref("main");
if (!root)
return;
if (root->current_key_capture.get() == &node)
root->current_key_capture = nullptr;
node.m_key_captured = false;
}
} // namespace pp::panopainter

View File

@@ -0,0 +1,15 @@
#pragma once
#include "event.h"
class Node;
namespace pp::panopainter {
kEventResult handle_legacy_ui_node_event(Node& node, Event* e);
void legacy_ui_node_mouse_capture(Node& node);
void legacy_ui_node_mouse_release(Node& node);
void legacy_ui_node_key_capture(Node& node);
void legacy_ui_node_key_release(Node& node);
} // namespace pp::panopainter

View File

@@ -1,6 +1,7 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "log.h" #include "log.h"
#include "legacy_ui_node_event.h"
#include "legacy_ui_node_loader.h" #include "legacy_ui_node_loader.h"
#include "node.h" #include "node.h"
#include "layout.h" #include "layout.h"
@@ -154,180 +155,7 @@ bool Node::added_to_root()
kEventResult Node::on_event(Event* e) kEventResult Node::on_event(Event* e)
{ {
kEventResult ret = kEventResult::Available; return pp::panopainter::handle_legacy_ui_node_event(*this, e);
if (e->m_cat == kEventCategory::KeyEvent && current_key_capture)
return current_key_capture->on_event(e);
if (current_mouse_capture && current_mouse_capture.get() != this)
{
if (e->m_cat == kEventCategory::MouseEvent &&
child_mouse_focus != current_mouse_capture &&
is_child(current_mouse_capture.get()))
{
MouseEvent* me = static_cast<MouseEvent*>(e);
if (child_mouse_focus)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseUnfocus;
child_mouse_focus->handle_event(&e2);
child_mouse_focus->m_mouse_focus = false;
}
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseFocus;
current_mouse_capture->handle_event(&e2);
child_mouse_focus = current_mouse_capture;
child_mouse_focus->m_mouse_focus = true;
}
return current_mouse_capture->on_event(e);
}
bool skip_children = false;
// skip mouse events if outside
if (e->m_cat == kEventCategory::MouseEvent)
{
MouseEvent* me = static_cast<MouseEvent*>(e);
skip_children |= !m_mouse_inside && !point_in_rect(me->m_pos, m_clip);
}
skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) &&
(m_mouse_captured) && (root()->current_mouse_capture.get() == this) && m_capture_children; // <-- THIS IS WRONG "!m_capture_children" is correct, but it breaks everything if changed
if (!m_display || glm::any(glm::lessThanEqual(zw(m_clip), { 0, 0 })))
return kEventResult::Available;
if (!skip_children)
{
// make a copy because any handler can change the children vector
auto children_copy = m_children;
for (auto it = children_copy.rbegin(); it != children_copy.rend(); ++it)
{
if ((*it)->on_event(e) == kEventResult::Consumed)
{
if (m_flood_events)
{
ret = kEventResult::Consumed;
}
else
{
if (e->m_cat == kEventCategory::MouseEvent && child_mouse_focus.get() != it->get())
{
MouseEvent* me = static_cast<MouseEvent*>(e);
if (child_mouse_focus && !child_mouse_focus->m_destroyed)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseUnfocus;
child_mouse_focus->handle_event(&e2);
child_mouse_focus->m_mouse_focus = false;
}
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseFocus;
(*it)->handle_event(&e2);
if (!(*it)->m_destroyed)
{
child_mouse_focus = *it;
child_mouse_focus->m_mouse_focus = true;
}
//else
//{
// child_mouse_focus = nullptr;
//}
}
ret = kEventResult::Consumed;
break;
}
}
}
if (ret == kEventResult::Consumed)
{
if (e->m_cat == kEventCategory::MouseEvent)
{
MouseEvent* me = static_cast<MouseEvent*>(e);
bool old_inside = m_mouse_inside;
m_mouse_inside = point_in_rect(me->m_pos, m_clip);
if (old_inside == false && m_mouse_inside == true)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseEnter;
handle_event(&e2);
}
if (old_inside == true && m_mouse_inside == false)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseLeave;
handle_event(&e2);
}
}
return kEventResult::Consumed;
}
}
switch (e->m_cat)
{
case kEventCategory::MouseEvent:
{
if (m_mouse_ignore)
break;
MouseEvent* me = static_cast<MouseEvent*>(e);
bool inside = point_in_rect(me->m_pos, m_clip);
bool inside_old = m_mouse_inside;
m_mouse_inside = inside;
switch (e->m_type)
{
case kEventType::MouseScroll:
case kEventType::MouseDownL:
case kEventType::MouseDownR:
case kEventType::MouseUpL:
case kEventType::MouseUpR:
if ((inside || m_mouse_captured) && ((handle_event(e) == kEventResult::Consumed) || m_force_mouse_capture))
return kEventResult::Consumed;
break;
case kEventType::MouseMove:
if (inside_old == false && inside == true)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseEnter;
handle_event(&e2);
}
if (inside || m_mouse_captured)
{
ret = handle_event(e);
if (m_force_mouse_capture)
ret = kEventResult::Consumed;
}
if (inside_old == true && inside == false)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseLeave;
handle_event(&e2);
}
break;
default:
if (handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
break;
}
case kEventCategory::GestureEvent:
{
if (m_mouse_ignore)
break;
GestureEvent* ge = static_cast<GestureEvent*>(e);
bool inside = point_in_rect(ge->m_pos, m_clip);
//bool inside_old = m_mouse_inside;
m_mouse_inside = inside;
if ((inside || m_mouse_captured) && handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
default:
if (handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
return ret;
} }
kEventResult Node::handle_event(Event* e) kEventResult Node::handle_event(Event* e)
@@ -622,101 +450,22 @@ bool Node::is_child(Node* o) const
void Node::mouse_capture() void Node::mouse_capture()
{ {
if (!m_parent || !m_manager) pp::panopainter::legacy_ui_node_mouse_capture(*this);
return;
auto root = m_manager->get_ref("main");
if (!root) return;
auto& c = root->current_mouse_capture;
auto& s = root->m_capture_stack;
// already owner of capture
if (c.get() == this || std::find_if(s.begin(), s.end(),
[this](const auto& a) { return a.get() == this; }) != s.end())
return;
if (c)
{
if (c->is_child_recursive(this))
{
// save on stack
s.push_back(c);
}
else
{
// cancel previous owner
MouseEvent e;
e.m_type = kEventType::MouseCancel;
c->handle_event(&e);
// TODO: only delete nodes on a different tree,
// so preserve direct parents of this
// also clear the whole stack
//s.clear();
s.push_back(c);
}
}
// make current
c = shared_from_this();
m_mouse_captured = true;
} }
void Node::mouse_release() void Node::mouse_release()
{ {
if (!m_parent || !m_manager) pp::panopainter::legacy_ui_node_mouse_release(*this);
return;
auto root = m_manager->get_ref("main");
if (!root) return;
auto& c = root->current_mouse_capture;
auto& s = root->m_capture_stack;
s.erase(std::remove_if(s.begin(), s.end(),
[this](const auto& a) { return a.get() == this; }), s.end());
if (c.get() == this)
{
if (s.empty())
{
c = nullptr;
}
else
{
c = s.back();
s.pop_back();
}
}
m_mouse_captured = false;
} }
void Node::key_capture() void Node::key_capture()
{ {
if (!m_parent || !m_manager) pp::panopainter::legacy_ui_node_key_capture(*this);
return;
auto root = m_manager->get_ref("main");
if (!root) return;
root->current_key_capture = shared_from_this();
m_key_captured = true;
} }
void Node::key_release() void Node::key_release()
{ {
if (!m_parent || !m_manager) pp::panopainter::legacy_ui_node_key_release(*this);
return;
auto root = m_manager->get_ref("main");
if (!root) return;
if (root->current_key_capture.get() == this)
root->current_key_capture = nullptr;
m_key_captured = false;
} }
Node&& Node::operator=(Node&& o) Node&& Node::operator=(Node&& o)

View File

@@ -599,84 +599,33 @@ void NodeStrokePreview::draw_stroke_immediate()
ortho_proj)); ortho_proj));
const bool copy_stroke_destination = pass_orchestration.copy_stroke_destination; const bool copy_stroke_destination = pass_orchestration.copy_stroke_destination;
pp::panopainter::setup_legacy_stroke_shader(pass_orchestration.stroke_shader); pp::panopainter::setup_legacy_stroke_shader(pass_orchestration.stroke_shader);
execute_stroke_draw_immediate_pass_sequence( const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_draw_immediate_shell<StrokeFrame>(
prepared_strokes.stroke,
prepared_strokes.dual_stroke,
*b, *b,
std::move(prepared_strokes.dual_brush),
pass_orchestration,
copy_stroke_destination,
zoom,
size);
m_rtt.unbindFramebuffer();
apply_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height);
apply_stroke_preview_clear_color(cc);
}
NodeStrokePreview::StrokeMainLivePassRequest
NodeStrokePreview::make_stroke_draw_immediate_main_live_pass_request(
Stroke& stroke,
const Brush& brush,
const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
bool copy_stroke_destination,
float zoom,
const glm::vec2& size)
{
return pp::panopainter::make_legacy_node_stroke_preview_main_live_pass_request<StrokeFrame>(
brush,
pass_orchestration, pass_orchestration,
m_tex, m_tex,
m_rtt_mixer, m_rtt_mixer,
m_rtt, m_rtt,
m_tex_background,
m_tex_dual,
m_tex_preview,
m_sampler_linear,
m_sampler_linear_repeat,
copy_stroke_destination, copy_stroke_destination,
size, size,
pp::panopainter::kLegacyNodeStrokePreviewStrokeTextureSlot, pp::panopainter::kLegacyNodeStrokePreviewStrokeTextureSlot,
[&] { [&] {
return stroke_draw_compute(stroke, zoom); if (!pass_orchestration.material.dual_pass.enabled) {
}, return;
[&](auto& frame) {
if (brush.m_tip_mix > 0.f)
{
stroke_draw_mix(xy(frame.m_mixer_rect), zw(frame.m_mixer_rect));
} }
frame.col = brush.m_blend_mode != 0 || brush.m_tip_mix > 0.f ? pp::panopainter::setup_legacy_stroke_dual_shader(pass_orchestration.material.dual_pass.uses_pattern);
glm::vec4 { .7, .4, .1, 1 } : bind_stroke_preview_dual_pass_textures(*prepared_strokes.dual_brush);
glm::vec4 { 0, 0, 0, 1 };
frame.flow = glm::max(frame.flow, m_min_flow);
},
[&](auto& frame) {
execute_stroke_draw_immediate_main_live_sample_pass(
brush,
copy_stroke_destination,
frame,
size);
},
[&](auto& frame) {
/*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex, copy_stroke_destination);
},
[&] { set_active_texture_unit(stroke_preview_live_slots::kMixer); m_rtt_mixer.unbindTexture(); });
}
void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass(
Stroke& dual_stroke,
const Brush& brush,
const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
std::shared_ptr<Brush> dual_brush,
bool copy_stroke_destination,
float zoom,
const glm::vec2& size)
{
(void)pass_orchestration;
(void)dual_brush;
pp::panopainter::execute_legacy_stroke_preview_live_pass( pp::panopainter::execute_legacy_stroke_preview_live_pass(
[&] { [&] {
m_rtt.clear(); m_rtt.clear();
}, },
[&] { [&] {
return stroke_draw_compute(dual_stroke, zoom); return stroke_draw_compute(prepared_strokes.dual_stroke, zoom);
}, },
[](auto& frame) { [](auto& frame) {
frame.col = { 0, 0, 0, 1 }; frame.col = { 0, 0, 0, 1 };
@@ -697,41 +646,10 @@ void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass(
pp::panopainter::copy_legacy_node_stroke_preview_framebuffer_to_texture( pp::panopainter::copy_legacy_node_stroke_preview_framebuffer_to_texture(
m_tex_dual, m_tex_dual,
size, size,
stroke_preview_composite_slots::kStroke); pp::panopainter::kLegacyNodeStrokePreviewStrokeTextureSlot);
}); });
}
void NodeStrokePreview::execute_stroke_draw_immediate_pass_sequence(
Stroke& stroke,
Stroke& dual_stroke,
const Brush& brush,
std::shared_ptr<Brush> dual_brush,
const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
bool copy_stroke_destination,
float zoom,
const glm::vec2& size)
{
const auto& material = pass_orchestration.material;
const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_immediate_pass_sequence(
pp::panopainter::LegacyNodeStrokePreviewImmediatePassSequenceRequest {
.execute_dual_pass = [&] {
if (!material.dual_pass.enabled) {
return;
}
pp::panopainter::setup_legacy_stroke_dual_shader(
material.dual_pass.uses_pattern);
bind_stroke_preview_dual_pass_textures(*dual_brush);
execute_stroke_draw_immediate_dual_pass(
dual_stroke,
brush,
pass_orchestration,
std::move(dual_brush),
copy_stroke_destination,
zoom,
size);
}, },
.capture_background = [&] { [&] {
execute_stroke_preview_background_capture_pass( execute_stroke_preview_background_capture_pass(
size, size,
pass_orchestration.background_colorize, pass_orchestration.background_colorize,
@@ -740,48 +658,21 @@ void NodeStrokePreview::execute_stroke_draw_immediate_pass_sequence(
m_plane.draw_fill(); m_plane.draw_fill();
}); });
}, },
.execute_main_live_pass = [&]() -> bool {
return pp::panopainter::execute_legacy_node_stroke_preview_main_live_pass(
make_stroke_draw_immediate_main_live_pass_request(
stroke,
brush,
pass_orchestration,
copy_stroke_destination,
zoom,
size));
},
.execute_final_composite = [&]() -> bool {
return pp::panopainter::execute_legacy_node_stroke_preview_final_composite(
size,
glm::vec2(brush.m_pattern_scale),
brush,
material.composite_pass,
m_tex_background,
m_tex,
m_tex_dual,
m_tex_preview,
m_sampler_linear,
m_sampler_linear_repeat,
[&] { [&] {
brush.m_pattern_texture ? brush.m_pattern_texture->bind() : unbind_texture_2d(); return stroke_draw_compute(prepared_strokes.stroke, zoom);
}, },
[&] { [&](auto& frame) {
m_plane.draw_fill(); if (b->m_tip_mix > 0.f)
}); {
}, stroke_draw_mix(xy(frame.m_mixer_rect), zw(frame.m_mixer_rect));
});
assert(sequence_ok);
} }
void NodeStrokePreview::execute_stroke_draw_immediate_main_live_sample_pass( frame.col = b->m_blend_mode != 0 || b->m_tip_mix > 0.f ?
const Brush& brush, glm::vec4 { .7, .4, .1, 1 } :
bool copy_stroke_destination, glm::vec4 { 0, 0, 0, 1 };
const StrokeFrame& frame, frame.flow = glm::max(frame.flow, m_min_flow);
const glm::vec2& size) },
{ [&](auto& frame) {
(void)brush;
(void)copy_stroke_destination;
(void)size;
pp::panopainter::use_legacy_stroke_shader(); pp::panopainter::use_legacy_stroke_shader();
pp::panopainter::apply_legacy_stroke_sample_uniforms( pp::panopainter::apply_legacy_stroke_sample_uniforms(
pp::panopainter::LegacyStrokeSampleUniforms { pp::panopainter::LegacyStrokeSampleUniforms {
@@ -789,6 +680,26 @@ void NodeStrokePreview::execute_stroke_draw_immediate_main_live_sample_pass(
.alpha = frame.flow, .alpha = frame.flow,
.opacity = frame.opacity, .opacity = frame.opacity,
}); });
},
[&](auto& frame) {
/*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex, copy_stroke_destination);
},
[&] {
set_active_texture_unit(3U);
m_rtt_mixer.unbindTexture();
},
[&] {
b->m_pattern_texture ? b->m_pattern_texture->bind() : unbind_texture_2d();
},
[&] {
m_plane.draw_fill();
});
assert(sequence_ok);
m_rtt.unbindFramebuffer();
apply_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height);
apply_stroke_preview_clear_color(cc);
} }
Image NodeStrokePreview::render_to_image() Image NodeStrokePreview::render_to_image()

View File

@@ -30,15 +30,6 @@ class NodeStrokePreview : public NodeBorder
static Sampler m_sampler_mipmap; static Sampler m_sampler_mipmap;
static DynamicShape m_brush_shape; static DynamicShape m_brush_shape;
Texture2D m_tex_preview; Texture2D m_tex_preview;
void execute_stroke_draw_immediate_pass_sequence(
Stroke& stroke,
Stroke& dual_stroke,
const Brush& brush,
std::shared_ptr<Brush> dual_brush,
const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
bool copy_stroke_destination,
float zoom,
const glm::vec2& size);
public: public:
using parent = NodeBorder; using parent = NodeBorder;
static std::atomic_int s_instances; static std::atomic_int s_instances;
@@ -65,26 +56,6 @@ public:
// return rect {origin, size} // return rect {origin, size}
glm::vec4 stroke_draw_samples(std::array<vertex_t, 4>& P, Texture2D& blend_tex, bool copy_stroke_destination); glm::vec4 stroke_draw_samples(std::array<vertex_t, 4>& P, Texture2D& blend_tex, bool copy_stroke_destination);
std::vector<StrokeFrame> stroke_draw_compute(Stroke& stroke, float zoom) const; std::vector<StrokeFrame> stroke_draw_compute(Stroke& stroke, float zoom) const;
StrokeMainLivePassRequest make_stroke_draw_immediate_main_live_pass_request(
Stroke& stroke,
const Brush& brush,
const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
bool copy_stroke_destination,
float zoom,
const glm::vec2& size);
void execute_stroke_draw_immediate_main_live_sample_pass(
const Brush& brush,
bool copy_stroke_destination,
const StrokeFrame& frame,
const glm::vec2& size);
void execute_stroke_draw_immediate_dual_pass(
Stroke& dual_stroke,
const Brush& brush,
const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
std::shared_ptr<Brush> dual_brush,
bool copy_stroke_destination,
float zoom,
const glm::vec2& size);
void draw_stroke(); void draw_stroke();
void draw_stroke_immediate(); void draw_stroke_immediate();
Image render_to_image(); Image render_to_image();