Extract canvas stroke runtime and preview draw shells
This commit is contained in:
@@ -25,6 +25,8 @@ set(PP_LEGACY_PAINT_DOCUMENT_SOURCES
|
|||||||
src/legacy_canvas_layer_services.cpp
|
src/legacy_canvas_layer_services.cpp
|
||||||
src/legacy_canvas_stroke_commit_services.cpp
|
src/legacy_canvas_stroke_commit_services.cpp
|
||||||
src/legacy_canvas_stroke_live_services.cpp
|
src/legacy_canvas_stroke_live_services.cpp
|
||||||
|
src/legacy_canvas_stroke_runtime_services.cpp
|
||||||
|
src/legacy_canvas_stroke_runtime_services.h
|
||||||
src/legacy_canvas_document_io_services.cpp
|
src/legacy_canvas_document_io_services.cpp
|
||||||
src/legacy_canvas_object_draw_services.cpp
|
src/legacy_canvas_object_draw_services.cpp
|
||||||
src/legacy_canvas_object_draw_services.h
|
src/legacy_canvas_object_draw_services.h
|
||||||
@@ -160,6 +162,8 @@ set(PP_PANOPAINTER_UI_SOURCES
|
|||||||
src/legacy_brush_panel_services.h
|
src/legacy_brush_panel_services.h
|
||||||
src/legacy_document_animation_services.cpp
|
src/legacy_document_animation_services.cpp
|
||||||
src/legacy_document_animation_services.h
|
src/legacy_document_animation_services.h
|
||||||
|
src/legacy_node_canvas_draw_services.cpp
|
||||||
|
src/legacy_node_canvas_draw_services.h
|
||||||
src/legacy_node_canvas_state_services.cpp
|
src/legacy_node_canvas_state_services.cpp
|
||||||
src/legacy_node_canvas_state_services.h
|
src/legacy_node_canvas_state_services.h
|
||||||
src/legacy_brush_preset_panel_ui.cpp
|
src/legacy_brush_preset_panel_ui.cpp
|
||||||
@@ -190,6 +194,7 @@ set(PP_PANOPAINTER_UI_SOURCES
|
|||||||
src/node_panel_layer.cpp
|
src/node_panel_layer.cpp
|
||||||
src/node_panel_quick.cpp
|
src/node_panel_quick.cpp
|
||||||
src/node_panel_stroke.cpp
|
src/node_panel_stroke.cpp
|
||||||
|
src/legacy_node_stroke_preview_runtime_services.cpp
|
||||||
src/node_stroke_preview.cpp
|
src/node_stroke_preview.cpp
|
||||||
src/node_tool_bucket.cpp
|
src/node_tool_bucket.cpp
|
||||||
src/node_usermanual.cpp
|
src/node_usermanual.cpp
|
||||||
|
|||||||
@@ -79,15 +79,15 @@ What is still carrying too much live ownership:
|
|||||||
|
|
||||||
Current hotspot files:
|
Current hotspot files:
|
||||||
|
|
||||||
- `src/canvas.cpp`: 1271 lines
|
- `src/canvas.cpp`: 1010 lines
|
||||||
- `src/app_layout.cpp`: 125 lines
|
- `src/app_layout.cpp`: 125 lines
|
||||||
- `src/canvas_modes.cpp`: 720 lines
|
- `src/canvas_modes.cpp`: 720 lines
|
||||||
- `src/node.cpp`: 803 lines
|
- `src/node.cpp`: 803 lines
|
||||||
- `src/main.cpp`: 271 lines
|
- `src/main.cpp`: 271 lines
|
||||||
- `src/node_panel_brush.cpp`: 435 lines
|
- `src/node_panel_brush.cpp`: 435 lines
|
||||||
- `src/node_stroke_preview.cpp`: 751 lines
|
- `src/node_stroke_preview.cpp`: 607 lines
|
||||||
- `src/node_canvas.cpp`: 702 lines
|
- `src/node_canvas.cpp`: 219 lines
|
||||||
- `src/app.cpp`: 575 lines
|
- `src/app.cpp`: 502 lines
|
||||||
- `src/app_dialogs.cpp`: 168 lines
|
- `src/app_dialogs.cpp`: 168 lines
|
||||||
|
|
||||||
Current architecture mismatches that must be treated as real blockers:
|
Current architecture mismatches that must be treated as real blockers:
|
||||||
@@ -268,6 +268,11 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
`Canvas::flood_fill(...)`, and `Canvas::FloodData::apply()` now also route
|
`Canvas::flood_fill(...)`, and `Canvas::FloodData::apply()` now also route
|
||||||
through `src/legacy_canvas_layer_services.cpp` instead of staying inline in
|
through `src/legacy_canvas_layer_services.cpp` instead of staying inline in
|
||||||
`src/canvas.cpp`, which trims another coherent retained layer/fill workflow
|
`src/canvas.cpp`, which trims another coherent retained layer/fill workflow
|
||||||
|
pocket, while `Canvas::stroke_end(...)`, `Canvas::stroke_cancel(...)`,
|
||||||
|
`Canvas::stroke_draw_mix(...)`, `Canvas::stroke_draw_project(...)`,
|
||||||
|
`Canvas::stroke_update(...)`, and `Canvas::stroke_start(...)` now also route
|
||||||
|
through `src/legacy_canvas_stroke_runtime_services.*` instead of staying
|
||||||
|
inline in `src/canvas.cpp`, which trims another large retained stroke/runtime
|
||||||
pocket, while the
|
pocket, while the
|
||||||
`CanvasModeTransform` interaction family now also routes through
|
`CanvasModeTransform` interaction family now also routes through
|
||||||
`src/legacy_canvas_mode_transform.cpp` instead of staying inline in
|
`src/legacy_canvas_mode_transform.cpp` instead of staying inline in
|
||||||
@@ -302,6 +307,14 @@ 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 `NodeStrokePreview` retained lifecycle, worker-thread shell,
|
||||||
|
render-to-image path, on-screen handling, and preview texture ownership now
|
||||||
|
also route through `src/legacy_node_stroke_preview_runtime_services.cpp`
|
||||||
|
instead of staying inline in `src/node_stroke_preview.cpp`, while
|
||||||
|
`NodeCanvas::init()` plus the remaining `NodeCanvas::draw()` outer shell now
|
||||||
|
also route through `src/legacy_node_canvas_draw_services.*` instead of
|
||||||
|
staying inline in `src/node_canvas.cpp`, which materially reduces the live
|
||||||
|
node to a thinner controller surface around event routing and state wrappers,
|
||||||
while `Node::on_event(...)` plus mouse/key capture and release ownership now
|
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
|
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
|
`src/node.cpp`, which materially thins the base scene-graph event shell
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ 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
|
||||||
1271 lines, with `src/canvas_modes.cpp` still large at about 720 lines.
|
1010 lines, with `src/canvas_modes.cpp` now materially thinner and the next
|
||||||
|
remaining render-shell pressure shifting toward preview/canvas nodes.
|
||||||
|
|
||||||
Current slice:
|
Current slice:
|
||||||
- Canvas state-management helpers for picking, clear/clear-all, layer
|
- Canvas state-management helpers for picking, clear/clear-all, layer
|
||||||
@@ -135,6 +136,12 @@ Current slice:
|
|||||||
`src/legacy_canvas_layer_services.cpp` instead of staying inline in
|
`src/legacy_canvas_layer_services.cpp` instead of staying inline in
|
||||||
`src/canvas.cpp`, which trims another coherent retained layer/fill workflow
|
`src/canvas.cpp`, which trims another coherent retained layer/fill workflow
|
||||||
pocket from the live canvas shell.
|
pocket from the live canvas shell.
|
||||||
|
- `Canvas::stroke_end(...)`, `Canvas::stroke_cancel(...)`,
|
||||||
|
`Canvas::stroke_draw_mix(...)`, `Canvas::stroke_draw_project(...)`,
|
||||||
|
`Canvas::stroke_update(...)`, and `Canvas::stroke_start(...)` now also live
|
||||||
|
in `src/legacy_canvas_stroke_runtime_services.*` instead of staying inline in
|
||||||
|
`src/canvas.cpp`, which trims another large retained live stroke/runtime
|
||||||
|
pocket from the canvas shell.
|
||||||
- The `CanvasModePen` and `CanvasModeLine` interaction families now also live
|
- The `CanvasModePen` and `CanvasModeLine` interaction families now also live
|
||||||
in `src/legacy_canvas_mode_pen_line.cpp` instead of staying inline in
|
in `src/legacy_canvas_mode_pen_line.cpp` instead of staying inline in
|
||||||
`src/canvas_modes.cpp`, which materially thins another retained pen/line
|
`src/canvas_modes.cpp`, which materially thins another retained pen/line
|
||||||
@@ -307,6 +314,16 @@ Current slice:
|
|||||||
live in `src/legacy_node_canvas_state_services.*` instead of staying inline
|
live in `src/legacy_node_canvas_state_services.*` instead of staying inline
|
||||||
in `src/node_canvas.cpp`, which materially thins another retained
|
in `src/node_canvas.cpp`, which materially thins another retained
|
||||||
state/control pocket without reopening the draw path.
|
state/control pocket without reopening the draw path.
|
||||||
|
- `NodeStrokePreview` retained lifecycle, worker-thread shell, render-to-image,
|
||||||
|
on-screen handling, and preview texture ownership now also live in
|
||||||
|
`src/legacy_node_stroke_preview_runtime_services.cpp` instead of staying
|
||||||
|
inline in `src/node_stroke_preview.cpp`, which materially thins the preview
|
||||||
|
node around its runtime-facing shell even though live pass execution still
|
||||||
|
remains.
|
||||||
|
- `NodeCanvas::init()` plus the remaining `NodeCanvas::draw()` outer shell now
|
||||||
|
also live in `src/legacy_node_canvas_draw_services.*` instead of staying
|
||||||
|
inline in `src/node_canvas.cpp`, which materially reduces the live node to a
|
||||||
|
thinner controller surface around event routing and state wrappers.
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/node_stroke_preview.cpp`
|
- `src/node_stroke_preview.cpp`
|
||||||
|
|||||||
286
src/canvas.cpp
286
src/canvas.cpp
@@ -9,6 +9,7 @@
|
|||||||
#include "legacy_canvas_stroke_composite_services.h"
|
#include "legacy_canvas_stroke_composite_services.h"
|
||||||
#include "legacy_canvas_stroke_edge_services.h"
|
#include "legacy_canvas_stroke_edge_services.h"
|
||||||
#include "legacy_canvas_stroke_execution_services.h"
|
#include "legacy_canvas_stroke_execution_services.h"
|
||||||
|
#include "legacy_canvas_stroke_runtime_services.h"
|
||||||
#include "legacy_canvas_stroke_shader_services.h"
|
#include "legacy_canvas_stroke_shader_services.h"
|
||||||
#include "legacy_canvas_object_draw_services.h"
|
#include "legacy_canvas_object_draw_services.h"
|
||||||
#include "legacy_canvas_projection_services.h"
|
#include "legacy_canvas_projection_services.h"
|
||||||
@@ -252,12 +253,6 @@ void delete_canvas_renderbuffer(GLuint renderbuffer)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pp::panopainter::LegacyCanvasStrokeMixPassShell make_canvas_stroke_mix_pass_shell(
|
|
||||||
Canvas& canvas,
|
|
||||||
const glm::vec2& bb_min,
|
|
||||||
const glm::vec2& bb_sz);
|
|
||||||
|
|
||||||
Canvas* Canvas::I;
|
Canvas* Canvas::I;
|
||||||
std::vector<CanvasMode*> Canvas::modes[] = {
|
std::vector<CanvasMode*> Canvas::modes[] = {
|
||||||
{ new CanvasModePen, new CanvasModeBasicCamera }, // brush
|
{ new CanvasModePen, new CanvasModeBasicCamera }, // brush
|
||||||
@@ -307,114 +302,16 @@ glm::mat4 Canvas::m_plane_transform[6] = {
|
|||||||
glm::lookAt(glm::vec3(), { 0,-1, 0}, {0, 0, 1}), // bottom
|
glm::lookAt(glm::vec3(), { 0,-1, 0}, {0, 0, 1}), // bottom
|
||||||
};
|
};
|
||||||
|
|
||||||
void Canvas::stroke_end()
|
void Canvas::stroke_end() { pp::panopainter::legacy_canvas_stroke_end(*this); }
|
||||||
{
|
void Canvas::stroke_cancel() { pp::panopainter::legacy_canvas_stroke_cancel(*this); }
|
||||||
if (!m_current_stroke)
|
|
||||||
return;
|
|
||||||
if (m_current_stroke->has_sample())
|
|
||||||
{
|
|
||||||
m_commit_delayed = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_show_tmp = false;
|
|
||||||
stroke_commit();
|
|
||||||
m_current_stroke = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Canvas::stroke_cancel()
|
|
||||||
{
|
|
||||||
if (!m_current_stroke)
|
|
||||||
return;
|
|
||||||
m_current_stroke = nullptr;
|
|
||||||
m_show_tmp = false;
|
|
||||||
}
|
|
||||||
void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
|
void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
|
||||||
{
|
{
|
||||||
gl_state gl;
|
pp::panopainter::legacy_canvas_stroke_draw_mix(*this, bb_min, bb_sz);
|
||||||
gl.save();
|
|
||||||
const auto mix_shell = make_canvas_stroke_mix_pass_shell(*this, bb_min, bb_sz);
|
|
||||||
[[maybe_unused]] const auto mix_result = pp::panopainter::execute_legacy_canvas_stroke_mix_pass_shell(
|
|
||||||
mix_shell.setup.begin,
|
|
||||||
mix_shell.setup.end,
|
|
||||||
mix_shell.request);
|
|
||||||
|
|
||||||
gl.restore();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<std::vector<vertex_t>, 6> Canvas::stroke_draw_project(std::array<vertex_t, 4>& B, bool project_3d /*= false*/, glm::mat4 mv /*= glm::mat4(1)*/) const
|
std::array<std::vector<vertex_t>, 6> Canvas::stroke_draw_project(std::array<vertex_t, 4>& B, bool project_3d /*= false*/, glm::mat4 mv /*= glm::mat4(1)*/) const
|
||||||
{
|
{
|
||||||
// intersect P with the current face to clip diverging points from the plane
|
return pp::panopainter::legacy_canvas_stroke_draw_project(*this, B, project_3d, mv);
|
||||||
const auto unp_vp = zw(m_box);
|
|
||||||
const auto unp_inv = glm::inverse(m_proj * m_mv);
|
|
||||||
std::array<std::vector<vertex_t>, 6> ret;
|
|
||||||
for (int i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
struct ray_t {
|
|
||||||
glm::vec3 o;
|
|
||||||
glm::vec3 d;
|
|
||||||
vertex_t v;
|
|
||||||
ray_t(glm::vec3 o, glm::vec3 d, vertex_t v) : o(o), d(d), v(v) { }
|
|
||||||
};
|
|
||||||
std::vector<ray_t> rays;
|
|
||||||
if (project_3d)
|
|
||||||
{
|
|
||||||
rays.reserve(B.size());
|
|
||||||
for (auto const& b : B)
|
|
||||||
rays.emplace_back(glm::vec3(0), b.pos, b);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto P = poly_intersect(B.data(), B.data() + 4, m_plane_shape[i]);
|
|
||||||
rays.reserve(P.size());
|
|
||||||
for (auto const& p : P)
|
|
||||||
{
|
|
||||||
glm::vec3 ray_origin, ray_dir;
|
|
||||||
auto clip_space = glm::vec2(p.pos.x, unp_vp.y - p.pos.y - 1.f) / unp_vp * 2.f - 1.f;
|
|
||||||
auto wp0 = unp_inv * glm::vec4(clip_space, 0, 1);
|
|
||||||
auto wp1 = unp_inv * glm::vec4(clip_space, .5, 1);
|
|
||||||
ray_origin = xyz(wp0 / wp0.w);
|
|
||||||
ray_dir = glm::normalize(xyz(wp1 / wp1.w) - ray_origin);
|
|
||||||
rays.emplace_back(ray_origin, ray_dir, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]);
|
|
||||||
std::vector<vertex_t> face_ret;
|
|
||||||
face_ret.reserve(rays.size());
|
|
||||||
for (auto const& r : rays)
|
|
||||||
{
|
|
||||||
glm::vec3 hit;
|
|
||||||
float hit_t;
|
|
||||||
if (ray_intersect(r.o, r.d, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit, hit_t))
|
|
||||||
{
|
|
||||||
glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1);
|
|
||||||
|
|
||||||
//P[j].uvs2 = xy(P[j].pos) / glm::vec2(App::I->width, App::I->height);
|
|
||||||
vertex_t v;
|
|
||||||
v.pos.x = -(plane_local.x * 0.5f - 0.5f) * m_width;
|
|
||||||
v.pos.y = (plane_local.y * 0.5f + 0.5f) * m_height;
|
|
||||||
|
|
||||||
// Black magic - BEWARE!
|
|
||||||
// interpolation perspective correction, use the current camera projection to correct the interpolation
|
|
||||||
// because the new shape will have z fixed with an ortho projection when drawn to the face
|
|
||||||
// we need to imitate the same perspective as the once in the camera
|
|
||||||
// see: https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes
|
|
||||||
auto hit_cam = mv * glm::vec4(hit, 1);
|
|
||||||
v.pos.z = 0;
|
|
||||||
v.pos.w = hit_cam.z;
|
|
||||||
v.uvs = r.v.uvs * hit_cam.z;
|
|
||||||
v.uvs2 = r.v.uvs2 * hit_cam.z;
|
|
||||||
face_ret.emplace_back(v);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (face_ret.size() >= 3)
|
|
||||||
ret[i] = std::move(face_ret);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void execute_canvas_draw_merge_layer_composite(
|
static void execute_canvas_draw_merge_layer_composite(
|
||||||
@@ -842,96 +739,6 @@ static void execute_canvas_draw_merge_plane_final_composite(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pp::panopainter::LegacyCanvasStrokeMixPassShell make_canvas_stroke_mix_pass_shell(
|
|
||||||
Canvas& canvas,
|
|
||||||
const glm::vec2& bb_min,
|
|
||||||
const glm::vec2& bb_sz)
|
|
||||||
{
|
|
||||||
const auto layer_index = canvas.m_current_layer_idx;
|
|
||||||
auto& current_layer = *canvas.m_layers[layer_index];
|
|
||||||
std::array<glm::mat4, 6> plane_transform {};
|
|
||||||
std::copy(std::begin(Canvas::m_plane_transform), std::end(Canvas::m_plane_transform), plane_transform.begin());
|
|
||||||
const auto mix_planes = pp::panopainter::plan_legacy_canvas_stroke_mix_pass_planes(
|
|
||||||
current_layer.m_visible,
|
|
||||||
current_layer.m_opacity,
|
|
||||||
glm::scale(glm::vec3(1, -1, 1)) * canvas.m_proj * canvas.m_mv,
|
|
||||||
plane_transform,
|
|
||||||
[&](int plane_index) {
|
|
||||||
return current_layer.face(plane_index);
|
|
||||||
});
|
|
||||||
const auto& b = canvas.m_current_stroke->m_brush;
|
|
||||||
return pp::panopainter::make_legacy_canvas_stroke_mix_pass_shell(
|
|
||||||
[&] {
|
|
||||||
canvas.m_mixer.bindFramebuffer();
|
|
||||||
apply_canvas_viewport(0, 0, canvas.m_mixer.getWidth(), canvas.m_mixer.getHeight());
|
|
||||||
apply_canvas_capability(depth_test_state(), false);
|
|
||||||
apply_canvas_capability(scissor_test_state(), true);
|
|
||||||
apply_canvas_capability(blend_state(), false);
|
|
||||||
apply_canvas_scissor(
|
|
||||||
static_cast<std::int32_t>(bb_min.x),
|
|
||||||
static_cast<std::int32_t>(bb_min.y),
|
|
||||||
static_cast<std::int32_t>(bb_sz.x),
|
|
||||||
static_cast<std::int32_t>(bb_sz.y));
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
canvas.m_mixer.unbindFramebuffer();
|
|
||||||
},
|
|
||||||
"Canvas::stroke_draw_mix",
|
|
||||||
canvas.m_size,
|
|
||||||
mix_planes,
|
|
||||||
[&] {
|
|
||||||
canvas.m_sampler.bind(0);
|
|
||||||
canvas.m_sampler.bind(1);
|
|
||||||
canvas.m_sampler.bind(2);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
canvas.m_sampler.unbind();
|
|
||||||
},
|
|
||||||
[&](int plane_index, const glm::mat4& plane_mvp_z) {
|
|
||||||
(void)plane_index;
|
|
||||||
pp::panopainter::setup_legacy_stroke_composite_shader(
|
|
||||||
pp::panopainter::LegacyStrokeCompositeUniforms {
|
|
||||||
.resolution = canvas.m_size,
|
|
||||||
.mvp = plane_mvp_z,
|
|
||||||
.pattern_texture_slot = 3,
|
|
||||||
.layer_alpha = 1.0f,
|
|
||||||
.alpha_lock = false,
|
|
||||||
.mask_enabled = false,
|
|
||||||
.use_fragcoord = false,
|
|
||||||
.blend_mode = b->m_blend_mode,
|
|
||||||
.use_dual = false,
|
|
||||||
.use_pattern = false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[&](int plane_index) {
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
current_layer.rtt(plane_index).bindTexture();
|
|
||||||
},
|
|
||||||
[&](int plane_index) {
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
canvas.m_tmp[plane_index].bindTexture();
|
|
||||||
},
|
|
||||||
[&](int plane_index) {
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
canvas.m_smask.rtt(plane_index).bindTexture();
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
canvas.m_node->m_face_plane.draw_fill();
|
|
||||||
},
|
|
||||||
[&](int plane_index) {
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
canvas.m_smask.rtt(plane_index).unbindTexture();
|
|
||||||
},
|
|
||||||
[&](int plane_index) {
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
canvas.m_tmp[plane_index].unbindTexture();
|
|
||||||
},
|
|
||||||
[&](int plane_index) {
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
current_layer.rtt(plane_index).unbindTexture();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution
|
static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution
|
||||||
make_canvas_draw_merge_temporary_paint_request(
|
make_canvas_draw_merge_temporary_paint_request(
|
||||||
Canvas& canvas,
|
Canvas& canvas,
|
||||||
@@ -1157,87 +964,8 @@ void Canvas::draw_merge(bool draw_checkerboard, std::array<bool, 6> faces /*= SI
|
|||||||
draw_checkerboard);
|
draw_checkerboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::stroke_update(glm::vec3 point, float pressure)
|
void Canvas::stroke_update(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_update(*this, point, pressure); }
|
||||||
{
|
void Canvas::stroke_start(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_start(*this, point, pressure); }
|
||||||
m_current_stroke->add_point(point, pressure);
|
|
||||||
if (m_dual_stroke)
|
|
||||||
m_dual_stroke->add_point(point, pressure);
|
|
||||||
}
|
|
||||||
void Canvas::stroke_start(glm::vec3 point, float pressure)
|
|
||||||
{
|
|
||||||
assert(App::I->is_render_thread());
|
|
||||||
|
|
||||||
// need to commit this now before starting a new stroke
|
|
||||||
if (m_current_stroke && m_commit_delayed)
|
|
||||||
{
|
|
||||||
m_show_tmp = false;
|
|
||||||
m_commit_delayed = false;
|
|
||||||
stroke_commit();
|
|
||||||
m_current_stroke = nullptr;
|
|
||||||
m_dual_stroke = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_pattern_offset = m_current_brush->m_pattern_rand_offset ?
|
|
||||||
glm::vec2((rand()%1000)*0.001f, (rand()%1000)*0.001f) :
|
|
||||||
glm::vec2(0);
|
|
||||||
|
|
||||||
m_current_stroke = std::make_unique<Stroke>();
|
|
||||||
m_current_stroke->m_camera.rot = m_cam_rot;
|
|
||||||
m_current_stroke->m_camera.fov = m_cam_fov;
|
|
||||||
if (m_current_mode == kCanvasMode::Line)
|
|
||||||
m_current_stroke->m_filter_points = false;
|
|
||||||
m_current_stroke->randomize_prng();
|
|
||||||
m_current_stroke->start(m_current_brush);
|
|
||||||
m_current_stroke->add_point(point, pressure);
|
|
||||||
|
|
||||||
// Generate a brush for the dual-brush
|
|
||||||
if (m_current_brush->m_dual_enabled)
|
|
||||||
{
|
|
||||||
auto dual_brush = std::make_shared<Brush>();
|
|
||||||
dual_brush->m_tip_flow = m_current_brush->m_dual_flow;
|
|
||||||
dual_brush->m_tip_opacity = m_current_brush->m_dual_opacity;
|
|
||||||
dual_brush->m_tip_flipx = m_current_brush->m_dual_flipx;
|
|
||||||
dual_brush->m_tip_flipy = m_current_brush->m_dual_flipy;
|
|
||||||
dual_brush->m_tip_invert = m_current_brush->m_dual_invert;
|
|
||||||
dual_brush->m_blend_mode = m_current_brush->m_dual_blend_mode;
|
|
||||||
dual_brush->m_tip_randflipx = m_current_brush->m_dual_randflip;
|
|
||||||
dual_brush->m_tip_randflipy = m_current_brush->m_dual_randflip;
|
|
||||||
dual_brush->m_tip_size = m_current_brush->m_dual_size * m_current_brush->m_tip_size;
|
|
||||||
dual_brush->m_tip_spacing = m_current_brush->m_dual_spacing;
|
|
||||||
dual_brush->m_jitter_scatter = m_current_brush->m_dual_scatter;
|
|
||||||
dual_brush->m_jitter_scatter_bothaxis = m_current_brush->m_dual_scatter_bothaxis;
|
|
||||||
dual_brush->m_jitter_angle = m_current_brush->m_dual_rotate;
|
|
||||||
dual_brush->m_tip_texture = m_current_brush->m_dual_texture;
|
|
||||||
dual_brush->m_tip_aspect = m_current_brush->m_dual_aspect;
|
|
||||||
m_dual_stroke = std::make_unique<Stroke>();
|
|
||||||
m_dual_stroke->m_camera.rot = m_cam_rot;
|
|
||||||
m_dual_stroke->m_camera.fov = m_cam_fov;
|
|
||||||
m_dual_stroke->start(dual_brush);
|
|
||||||
m_dual_stroke->add_point(point, pressure);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const& l = m_layers[m_current_layer_idx];
|
|
||||||
for (int i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
m_dirty_box[i] = glm::vec4(m_width, m_height, 0, 0); // reset bounding box
|
|
||||||
m_dirty_face[i] = false;
|
|
||||||
|
|
||||||
m_tmp[i].bindFramebuffer();
|
|
||||||
m_tmp[i].clear({ 0, 0, 0, 0 });
|
|
||||||
m_tmp[i].unbindFramebuffer();
|
|
||||||
|
|
||||||
if (m_current_brush->m_dual_enabled)
|
|
||||||
{
|
|
||||||
m_tmp_dual[i].bindFramebuffer();
|
|
||||||
m_tmp_dual[i].clear({ 0, 0, 0, 0 });
|
|
||||||
m_tmp_dual[i].unbindFramebuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_mixer.bindFramebuffer();
|
|
||||||
m_mixer.clear();
|
|
||||||
m_mixer.unbindFramebuffer();
|
|
||||||
m_show_tmp = true;
|
|
||||||
}
|
|
||||||
void Canvas::destroy()
|
void Canvas::destroy()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
|
|||||||
336
src/legacy_canvas_stroke_runtime_services.cpp
Normal file
336
src/legacy_canvas_stroke_runtime_services.cpp
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include "canvas.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "legacy_canvas_stroke_runtime_services.h"
|
||||||
|
|
||||||
|
#include "legacy_canvas_stroke_composite_services.h"
|
||||||
|
#include "legacy_canvas_stroke_services.h"
|
||||||
|
#include "legacy_ui_gl_dispatch.h"
|
||||||
|
#include "renderer_gl/opengl_capabilities.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
GLenum depth_test_state()
|
||||||
|
{
|
||||||
|
return static_cast<GLenum>(pp::renderer::gl::depth_test_state());
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum scissor_test_state()
|
||||||
|
{
|
||||||
|
return static_cast<GLenum>(pp::renderer::gl::scissor_test_state());
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum blend_state()
|
||||||
|
{
|
||||||
|
return static_cast<GLenum>(pp::renderer::gl::blend_state());
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_active_texture_unit(std::uint32_t unit_index)
|
||||||
|
{
|
||||||
|
pp::legacy::ui_gl::activate_texture_unit(unit_index, "Canvas");
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
|
||||||
|
{
|
||||||
|
pp::legacy::ui_gl::apply_viewport(x, y, width, height, "Canvas");
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_canvas_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
|
||||||
|
{
|
||||||
|
pp::legacy::ui_gl::apply_scissor_rect(x, y, width, height, "Canvas");
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_canvas_capability(std::uint32_t capability, bool enabled)
|
||||||
|
{
|
||||||
|
pp::legacy::ui_gl::set_capability(capability, enabled, "Canvas");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
|
LegacyCanvasStrokeMixPassShell make_legacy_canvas_stroke_mix_pass_shell(
|
||||||
|
Canvas& canvas,
|
||||||
|
const glm::vec2& bb_min,
|
||||||
|
const glm::vec2& bb_sz)
|
||||||
|
{
|
||||||
|
const auto layer_index = canvas.m_current_layer_idx;
|
||||||
|
auto& current_layer = *canvas.m_layers[layer_index];
|
||||||
|
std::array<glm::mat4, 6> plane_transform {};
|
||||||
|
std::copy(std::begin(Canvas::m_plane_transform), std::end(Canvas::m_plane_transform), plane_transform.begin());
|
||||||
|
const auto mix_planes = pp::panopainter::plan_legacy_canvas_stroke_mix_pass_planes(
|
||||||
|
current_layer.m_visible,
|
||||||
|
current_layer.m_opacity,
|
||||||
|
glm::scale(glm::vec3(1, -1, 1)) * canvas.m_proj * canvas.m_mv,
|
||||||
|
plane_transform,
|
||||||
|
[&](int plane_index) {
|
||||||
|
return current_layer.face(plane_index);
|
||||||
|
});
|
||||||
|
const auto& b = canvas.m_current_stroke->m_brush;
|
||||||
|
return pp::panopainter::make_legacy_canvas_stroke_mix_pass_shell(
|
||||||
|
[&] {
|
||||||
|
canvas.m_mixer.bindFramebuffer();
|
||||||
|
apply_canvas_viewport(0, 0, canvas.m_mixer.getWidth(), canvas.m_mixer.getHeight());
|
||||||
|
apply_canvas_capability(depth_test_state(), false);
|
||||||
|
apply_canvas_capability(scissor_test_state(), true);
|
||||||
|
apply_canvas_capability(blend_state(), false);
|
||||||
|
apply_canvas_scissor(
|
||||||
|
static_cast<std::int32_t>(bb_min.x),
|
||||||
|
static_cast<std::int32_t>(bb_min.y),
|
||||||
|
static_cast<std::int32_t>(bb_sz.x),
|
||||||
|
static_cast<std::int32_t>(bb_sz.y));
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
canvas.m_mixer.unbindFramebuffer();
|
||||||
|
},
|
||||||
|
"Canvas::stroke_draw_mix",
|
||||||
|
canvas.m_size,
|
||||||
|
mix_planes,
|
||||||
|
[&] {
|
||||||
|
canvas.m_sampler.bind(0);
|
||||||
|
canvas.m_sampler.bind(1);
|
||||||
|
canvas.m_sampler.bind(2);
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
canvas.m_sampler.unbind();
|
||||||
|
},
|
||||||
|
[&](int plane_index, const glm::mat4& plane_mvp_z) {
|
||||||
|
(void)plane_index;
|
||||||
|
pp::panopainter::setup_legacy_stroke_composite_shader(
|
||||||
|
pp::panopainter::LegacyStrokeCompositeUniforms {
|
||||||
|
.resolution = canvas.m_size,
|
||||||
|
.mvp = plane_mvp_z,
|
||||||
|
.pattern_texture_slot = 3,
|
||||||
|
.layer_alpha = 1.0f,
|
||||||
|
.alpha_lock = false,
|
||||||
|
.mask_enabled = false,
|
||||||
|
.use_fragcoord = false,
|
||||||
|
.blend_mode = b->m_blend_mode,
|
||||||
|
.use_dual = false,
|
||||||
|
.use_pattern = false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[&](int plane_index) {
|
||||||
|
set_active_texture_unit(0);
|
||||||
|
current_layer.rtt(plane_index).bindTexture();
|
||||||
|
},
|
||||||
|
[&](int plane_index) {
|
||||||
|
set_active_texture_unit(1);
|
||||||
|
canvas.m_tmp[plane_index].bindTexture();
|
||||||
|
},
|
||||||
|
[&](int plane_index) {
|
||||||
|
set_active_texture_unit(2);
|
||||||
|
canvas.m_smask.rtt(plane_index).bindTexture();
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
canvas.m_node->m_face_plane.draw_fill();
|
||||||
|
},
|
||||||
|
[&](int plane_index) {
|
||||||
|
set_active_texture_unit(2);
|
||||||
|
canvas.m_smask.rtt(plane_index).unbindTexture();
|
||||||
|
},
|
||||||
|
[&](int plane_index) {
|
||||||
|
set_active_texture_unit(1);
|
||||||
|
canvas.m_tmp[plane_index].unbindTexture();
|
||||||
|
},
|
||||||
|
[&](int plane_index) {
|
||||||
|
set_active_texture_unit(0);
|
||||||
|
current_layer.rtt(plane_index).unbindTexture();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void legacy_canvas_stroke_end(Canvas& canvas)
|
||||||
|
{
|
||||||
|
if (!canvas.m_current_stroke)
|
||||||
|
return;
|
||||||
|
if (canvas.m_current_stroke->has_sample())
|
||||||
|
{
|
||||||
|
canvas.m_commit_delayed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
canvas.m_show_tmp = false;
|
||||||
|
canvas.stroke_commit();
|
||||||
|
canvas.m_current_stroke = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void legacy_canvas_stroke_cancel(Canvas& canvas)
|
||||||
|
{
|
||||||
|
if (!canvas.m_current_stroke)
|
||||||
|
return;
|
||||||
|
canvas.m_current_stroke = nullptr;
|
||||||
|
canvas.m_show_tmp = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void legacy_canvas_stroke_draw_mix(Canvas& canvas, const glm::vec2& bb_min, const glm::vec2& bb_sz)
|
||||||
|
{
|
||||||
|
gl_state gl;
|
||||||
|
gl.save();
|
||||||
|
const auto mix_shell = make_legacy_canvas_stroke_mix_pass_shell(canvas, bb_min, bb_sz);
|
||||||
|
[[maybe_unused]] const auto mix_result = pp::panopainter::execute_legacy_canvas_stroke_mix_pass_shell(
|
||||||
|
mix_shell.setup.begin,
|
||||||
|
mix_shell.setup.end,
|
||||||
|
mix_shell.request);
|
||||||
|
|
||||||
|
gl.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<std::vector<vertex_t>, 6> legacy_canvas_stroke_draw_project(
|
||||||
|
const Canvas& canvas,
|
||||||
|
std::array<vertex_t, 4>& B,
|
||||||
|
bool project_3d /*= false*/,
|
||||||
|
glm::mat4 mv /*= glm::mat4(1)*/)
|
||||||
|
{
|
||||||
|
const auto unp_vp = zw(canvas.m_box);
|
||||||
|
const auto unp_inv = glm::inverse(canvas.m_proj * canvas.m_mv);
|
||||||
|
std::array<std::vector<vertex_t>, 6> ret;
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
struct ray_t {
|
||||||
|
glm::vec3 o;
|
||||||
|
glm::vec3 d;
|
||||||
|
vertex_t v;
|
||||||
|
ray_t(glm::vec3 o, glm::vec3 d, vertex_t v) : o(o), d(d), v(v) { }
|
||||||
|
};
|
||||||
|
std::vector<ray_t> rays;
|
||||||
|
if (project_3d)
|
||||||
|
{
|
||||||
|
rays.reserve(B.size());
|
||||||
|
for (auto const& b : B)
|
||||||
|
rays.emplace_back(glm::vec3(0), b.pos, b);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto P = poly_intersect(B.data(), B.data() + 4, canvas.m_plane_shape[i]);
|
||||||
|
rays.reserve(P.size());
|
||||||
|
for (auto const& p : P)
|
||||||
|
{
|
||||||
|
glm::vec3 ray_origin, ray_dir;
|
||||||
|
auto clip_space = glm::vec2(p.pos.x, unp_vp.y - p.pos.y - 1.f) / unp_vp * 2.f - 1.f;
|
||||||
|
auto wp0 = unp_inv * glm::vec4(clip_space, 0, 1);
|
||||||
|
auto wp1 = unp_inv * glm::vec4(clip_space, .5, 1);
|
||||||
|
ray_origin = xyz(wp0 / wp0.w);
|
||||||
|
ray_dir = glm::normalize(xyz(wp1 / wp1.w) - ray_origin);
|
||||||
|
rays.emplace_back(ray_origin, ray_dir, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glm::mat4 plane_camera = glm::lookAt(canvas.m_plane_origin[i], canvas.m_plane_normal[i], canvas.m_plane_tangent[i]);
|
||||||
|
std::vector<vertex_t> face_ret;
|
||||||
|
face_ret.reserve(rays.size());
|
||||||
|
for (auto const& r : rays)
|
||||||
|
{
|
||||||
|
glm::vec3 hit;
|
||||||
|
float hit_t;
|
||||||
|
if (ray_intersect(r.o, r.d, canvas.m_plane_origin[i], canvas.m_plane_normal[i], canvas.m_plane_tangent[i], hit, hit_t))
|
||||||
|
{
|
||||||
|
glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1);
|
||||||
|
|
||||||
|
vertex_t v;
|
||||||
|
v.pos.x = -(plane_local.x * 0.5f - 0.5f) * canvas.m_width;
|
||||||
|
v.pos.y = (plane_local.y * 0.5f + 0.5f) * canvas.m_height;
|
||||||
|
|
||||||
|
auto hit_cam = mv * glm::vec4(hit, 1);
|
||||||
|
v.pos.z = 0;
|
||||||
|
v.pos.w = hit_cam.z;
|
||||||
|
v.uvs = r.v.uvs * hit_cam.z;
|
||||||
|
v.uvs2 = r.v.uvs2 * hit_cam.z;
|
||||||
|
face_ret.emplace_back(v);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (face_ret.size() >= 3)
|
||||||
|
ret[i] = std::move(face_ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void legacy_canvas_stroke_update(Canvas& canvas, glm::vec3 point, float pressure)
|
||||||
|
{
|
||||||
|
canvas.m_current_stroke->add_point(point, pressure);
|
||||||
|
if (canvas.m_dual_stroke)
|
||||||
|
canvas.m_dual_stroke->add_point(point, pressure);
|
||||||
|
}
|
||||||
|
|
||||||
|
void legacy_canvas_stroke_start(Canvas& canvas, glm::vec3 point, float pressure)
|
||||||
|
{
|
||||||
|
assert(App::I->is_render_thread());
|
||||||
|
|
||||||
|
if (canvas.m_current_stroke && canvas.m_commit_delayed)
|
||||||
|
{
|
||||||
|
canvas.m_show_tmp = false;
|
||||||
|
canvas.m_commit_delayed = false;
|
||||||
|
canvas.stroke_commit();
|
||||||
|
canvas.m_current_stroke = nullptr;
|
||||||
|
canvas.m_dual_stroke = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.m_pattern_offset = canvas.m_current_brush->m_pattern_rand_offset ?
|
||||||
|
glm::vec2((rand() % 1000) * 0.001f, (rand() % 1000) * 0.001f) :
|
||||||
|
glm::vec2(0);
|
||||||
|
|
||||||
|
canvas.m_current_stroke = std::make_unique<Stroke>();
|
||||||
|
canvas.m_current_stroke->m_camera.rot = canvas.m_cam_rot;
|
||||||
|
canvas.m_current_stroke->m_camera.fov = canvas.m_cam_fov;
|
||||||
|
if (canvas.m_current_mode == kCanvasMode::Line)
|
||||||
|
canvas.m_current_stroke->m_filter_points = false;
|
||||||
|
canvas.m_current_stroke->randomize_prng();
|
||||||
|
canvas.m_current_stroke->start(canvas.m_current_brush);
|
||||||
|
canvas.m_current_stroke->add_point(point, pressure);
|
||||||
|
|
||||||
|
if (canvas.m_current_brush->m_dual_enabled)
|
||||||
|
{
|
||||||
|
auto dual_brush = std::make_shared<Brush>();
|
||||||
|
dual_brush->m_tip_flow = canvas.m_current_brush->m_dual_flow;
|
||||||
|
dual_brush->m_tip_opacity = canvas.m_current_brush->m_dual_opacity;
|
||||||
|
dual_brush->m_tip_flipx = canvas.m_current_brush->m_dual_flipx;
|
||||||
|
dual_brush->m_tip_flipy = canvas.m_current_brush->m_dual_flipy;
|
||||||
|
dual_brush->m_tip_invert = canvas.m_current_brush->m_dual_invert;
|
||||||
|
dual_brush->m_blend_mode = canvas.m_current_brush->m_dual_blend_mode;
|
||||||
|
dual_brush->m_tip_randflipx = canvas.m_current_brush->m_dual_randflip;
|
||||||
|
dual_brush->m_tip_randflipy = canvas.m_current_brush->m_dual_randflip;
|
||||||
|
dual_brush->m_tip_size = canvas.m_current_brush->m_dual_size * canvas.m_current_brush->m_tip_size;
|
||||||
|
dual_brush->m_tip_spacing = canvas.m_current_brush->m_dual_spacing;
|
||||||
|
dual_brush->m_jitter_scatter = canvas.m_current_brush->m_dual_scatter;
|
||||||
|
dual_brush->m_jitter_scatter_bothaxis = canvas.m_current_brush->m_dual_scatter_bothaxis;
|
||||||
|
dual_brush->m_jitter_angle = canvas.m_current_brush->m_dual_rotate;
|
||||||
|
dual_brush->m_tip_texture = canvas.m_current_brush->m_dual_texture;
|
||||||
|
dual_brush->m_tip_aspect = canvas.m_current_brush->m_dual_aspect;
|
||||||
|
canvas.m_dual_stroke = std::make_unique<Stroke>();
|
||||||
|
canvas.m_dual_stroke->m_camera.rot = canvas.m_cam_rot;
|
||||||
|
canvas.m_dual_stroke->m_camera.fov = canvas.m_cam_fov;
|
||||||
|
canvas.m_dual_stroke->start(dual_brush);
|
||||||
|
canvas.m_dual_stroke->add_point(point, pressure);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
canvas.m_dirty_box[i] = glm::vec4(canvas.m_width, canvas.m_height, 0, 0);
|
||||||
|
canvas.m_dirty_face[i] = false;
|
||||||
|
|
||||||
|
canvas.m_tmp[i].bindFramebuffer();
|
||||||
|
canvas.m_tmp[i].clear({ 0, 0, 0, 0 });
|
||||||
|
canvas.m_tmp[i].unbindFramebuffer();
|
||||||
|
|
||||||
|
if (canvas.m_current_brush->m_dual_enabled)
|
||||||
|
{
|
||||||
|
canvas.m_tmp_dual[i].bindFramebuffer();
|
||||||
|
canvas.m_tmp_dual[i].clear({ 0, 0, 0, 0 });
|
||||||
|
canvas.m_tmp_dual[i].unbindFramebuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.m_mixer.bindFramebuffer();
|
||||||
|
canvas.m_mixer.clear();
|
||||||
|
canvas.m_mixer.unbindFramebuffer();
|
||||||
|
canvas.m_show_tmp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pp::panopainter
|
||||||
28
src/legacy_canvas_stroke_runtime_services.h
Normal file
28
src/legacy_canvas_stroke_runtime_services.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "legacy_canvas_stroke_execution_services.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Canvas;
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
|
[[nodiscard]] LegacyCanvasStrokeMixPassShell make_legacy_canvas_stroke_mix_pass_shell(
|
||||||
|
Canvas& canvas,
|
||||||
|
const glm::vec2& bb_min,
|
||||||
|
const glm::vec2& bb_sz);
|
||||||
|
|
||||||
|
void legacy_canvas_stroke_end(Canvas& canvas);
|
||||||
|
void legacy_canvas_stroke_cancel(Canvas& canvas);
|
||||||
|
void legacy_canvas_stroke_draw_mix(Canvas& canvas, const glm::vec2& bb_min, const glm::vec2& bb_sz);
|
||||||
|
[[nodiscard]] std::array<std::vector<vertex_t>, 6> legacy_canvas_stroke_draw_project(
|
||||||
|
const Canvas& canvas,
|
||||||
|
std::array<vertex_t, 4>& B,
|
||||||
|
bool project_3d = false,
|
||||||
|
glm::mat4 mv = glm::mat4(1));
|
||||||
|
void legacy_canvas_stroke_update(Canvas& canvas, glm::vec3 point, float pressure);
|
||||||
|
void legacy_canvas_stroke_start(Canvas& canvas, glm::vec3 point, float pressure);
|
||||||
|
|
||||||
|
} // namespace pp::panopainter
|
||||||
548
src/legacy_node_canvas_draw_services.cpp
Normal file
548
src/legacy_node_canvas_draw_services.cpp
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include "legacy_node_canvas_draw_services.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "app.h"
|
||||||
|
#include "legacy_canvas_draw_merge_services.h"
|
||||||
|
#include "legacy_preference_storage.h"
|
||||||
|
#include "legacy_ui_gl_dispatch.h"
|
||||||
|
#include "legacy_ui_overlay_services.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "node_panel_grid.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::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();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
|
void init_legacy_node_canvas_shell(NodeCanvas& node_canvas)
|
||||||
|
{
|
||||||
|
const auto preferences = read_legacy_canvas_preferences();
|
||||||
|
node_canvas.m_density = preferences.viewport_density;
|
||||||
|
node_canvas.m_cursor_visibility = (NodeCanvas::kCursorVisibility)preferences.cursor_mode;
|
||||||
|
|
||||||
|
node_canvas.m_mouse_ignore = false;
|
||||||
|
node_canvas.m_canvas = std::make_unique<Canvas>();
|
||||||
|
const int canvas_resolution = App::I->default_canvas_resolution();
|
||||||
|
node_canvas.m_canvas->create(canvas_resolution, canvas_resolution);
|
||||||
|
node_canvas.m_canvas->m_unsaved = false;
|
||||||
|
node_canvas.m_canvas->m_node = &node_canvas;
|
||||||
|
|
||||||
|
node_canvas.m_sampler.create();
|
||||||
|
//node_canvas.m_sampler.set_filter(pp::renderer::gl::linear_texture_filter(), pp::renderer::gl::nearest_texture_filter());
|
||||||
|
|
||||||
|
node_canvas.m_sampler_nearest.create(pp::renderer::gl::nearest_texture_filter());
|
||||||
|
|
||||||
|
node_canvas.m_sampler_linear.create(pp::renderer::gl::linear_texture_filter());
|
||||||
|
node_canvas.m_sampler_stencil.create(
|
||||||
|
pp::renderer::gl::linear_texture_filter(),
|
||||||
|
pp::renderer::gl::repeat_texture_wrap());
|
||||||
|
node_canvas.m_face_plane.create<1>(2, 2);
|
||||||
|
node_canvas.m_line.create();
|
||||||
|
CanvasMode::node = &node_canvas;
|
||||||
|
for (int i = 0; i < (int)kCanvasMode::COUNT; i++)
|
||||||
|
for (auto m : Canvas::modes[i])
|
||||||
|
m->init();
|
||||||
|
|
||||||
|
node_canvas.m_grid.create(1, 1, node_canvas.m_grid_divs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_legacy_node_canvas_shell(NodeCanvas& node_canvas)
|
||||||
|
{
|
||||||
|
// sanity checks
|
||||||
|
float zoom = node_canvas.root()->m_zoom;
|
||||||
|
if (zoom == 0.f)
|
||||||
|
zoom = 1.f;
|
||||||
|
auto box = node_canvas.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(node_canvas.m_canvas->m_cam_fov), box.z / box.w, 0.001f, 1000.f);
|
||||||
|
glm::mat4 camera = glm::translate(node_canvas.m_canvas->m_cam_pos) * node_canvas.m_canvas->m_cam_rot;
|
||||||
|
|
||||||
|
float pitch = 0;
|
||||||
|
if (auto slider = node_canvas.root()->find<NodeSliderH>("pitch-slider"))
|
||||||
|
pitch = (slider->get_value() - 0.5) * glm::half_pi<float>();
|
||||||
|
float yaw = 0;
|
||||||
|
if (auto slider = node_canvas.root()->find<NodeSliderH>("yaw-slider"))
|
||||||
|
yaw = (slider->get_value() - 0.5) * glm::half_pi<float>();
|
||||||
|
float roll = 0;
|
||||||
|
if (auto slider = node_canvas.root()->find<NodeSliderH>("roll-slider"))
|
||||||
|
roll = (slider->get_value() - 0.5) * glm::half_pi<float>();
|
||||||
|
|
||||||
|
prepare_legacy_node_canvas_draw_setup(node_canvas, box, c, proj, camera);
|
||||||
|
|
||||||
|
// NOTE: draw_merge has been disabled for worst performance
|
||||||
|
bool draw_merged = !(node_canvas.m_canvas->m_current_mode == kCanvasMode::Camera);
|
||||||
|
draw_merged = false;
|
||||||
|
|
||||||
|
execute_legacy_canvas_draw_node_canvas_shell(
|
||||||
|
node_canvas.m_density != 1.f,
|
||||||
|
draw_merged,
|
||||||
|
[&] {
|
||||||
|
node_canvas.m_rtt.bindFramebuffer();
|
||||||
|
clear_node_canvas_color_buffer({ 1.f, 1.f, 0.f, 0.f });
|
||||||
|
apply_node_canvas_viewport(0, 0, node_canvas.m_rtt.getWidth(), node_canvas.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(node_canvas, proj, camera);
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
execute_node_canvas_draw_unmerged_pass(node_canvas, proj, camera, c, yaw, pitch, roll);
|
||||||
|
});
|
||||||
|
|
||||||
|
execute_node_canvas_draw_merge_tail(node_canvas, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pp::panopainter
|
||||||
10
src/legacy_node_canvas_draw_services.h
Normal file
10
src/legacy_node_canvas_draw_services.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "node_canvas.h"
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
|
void init_legacy_node_canvas_shell(NodeCanvas& node_canvas);
|
||||||
|
void draw_legacy_node_canvas_shell(NodeCanvas& node_canvas);
|
||||||
|
|
||||||
|
} // namespace pp::panopainter
|
||||||
187
src/legacy_node_stroke_preview_runtime_services.cpp
Normal file
187
src/legacy_node_stroke_preview_runtime_services.cpp
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "node_stroke_preview.h"
|
||||||
|
#include "texture.h"
|
||||||
|
#include "shader.h"
|
||||||
|
#include "bezier.h"
|
||||||
|
#include "canvas.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "legacy_canvas_draw_merge_services.h"
|
||||||
|
#include "legacy_canvas_stroke_composite_services.h"
|
||||||
|
#include "legacy_canvas_stroke_execution_services.h"
|
||||||
|
#include "legacy_canvas_stroke_preview_services.h"
|
||||||
|
#include "legacy_canvas_stroke_shader_services.h"
|
||||||
|
#include "legacy_canvas_stroke_services.h"
|
||||||
|
#include "legacy_node_stroke_preview_execution_services.h"
|
||||||
|
#include "legacy_ui_gl_dispatch.h"
|
||||||
|
#include "paint_renderer/compositor.h"
|
||||||
|
#include "renderer_gl/opengl_capabilities.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stop_token>
|
||||||
|
|
||||||
|
std::atomic_int NodeStrokePreview::s_instances{ 0 };
|
||||||
|
std::atomic_bool NodeStrokePreview::s_running{ false };
|
||||||
|
std::mutex NodeStrokePreview::s_render_mutex;
|
||||||
|
BlockingQueue<std::shared_ptr<NodeStrokePreview>> NodeStrokePreview::s_queue;
|
||||||
|
|
||||||
|
RTT NodeStrokePreview::m_rtt;
|
||||||
|
RTT NodeStrokePreview::m_rtt_mixer;
|
||||||
|
Texture2D NodeStrokePreview::m_tex; // blending tmp texture
|
||||||
|
Texture2D NodeStrokePreview::m_tex_dual;
|
||||||
|
Texture2D NodeStrokePreview::m_tex_background;
|
||||||
|
Sampler NodeStrokePreview::m_sampler_linear;
|
||||||
|
Sampler NodeStrokePreview::m_sampler_linear_repeat;
|
||||||
|
Sampler NodeStrokePreview::m_sampler_mipmap;
|
||||||
|
DynamicShape NodeStrokePreview::m_brush_shape;
|
||||||
|
std::jthread NodeStrokePreview::s_renderer;
|
||||||
|
|
||||||
|
void NodeStrokePreview::terminate_renderer()
|
||||||
|
{
|
||||||
|
if (!s_renderer.joinable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
s_running = false;
|
||||||
|
s_renderer.request_stop();
|
||||||
|
s_queue.UnlockGetters();
|
||||||
|
s_renderer.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeStrokePreview::empty_queue()
|
||||||
|
{
|
||||||
|
s_queue.q.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeStrokePreview::restore_context()
|
||||||
|
{
|
||||||
|
NodeBorder::restore_context();
|
||||||
|
init_controls();
|
||||||
|
if (m_size.x > 0 && m_size.y > 0)
|
||||||
|
m_tex_preview.create(static_cast<int>(m_size.x), static_cast<int>(m_size.y));
|
||||||
|
draw_stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeStrokePreview::clear_context()
|
||||||
|
{
|
||||||
|
NodeBorder::clear_context();
|
||||||
|
m_tex_preview.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
Image NodeStrokePreview::render_to_image()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> _lock(s_render_mutex);
|
||||||
|
|
||||||
|
App::I->render_task([this] {
|
||||||
|
draw_stroke_immediate();
|
||||||
|
});
|
||||||
|
return m_tex_preview.get_image();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeStrokePreview::draw_stroke()
|
||||||
|
{
|
||||||
|
if (m_size.x == 0 || m_size.y == 0)
|
||||||
|
return;
|
||||||
|
std::unique_lock<std::mutex> queue_lock(s_queue.mutex);
|
||||||
|
if (!s_renderer.joinable())
|
||||||
|
{
|
||||||
|
s_running = true;
|
||||||
|
s_renderer = std::jthread([](std::stop_token stop_token) {
|
||||||
|
BT_SetTerminate();
|
||||||
|
|
||||||
|
m_sampler_linear.create();
|
||||||
|
m_sampler_linear_repeat.create(
|
||||||
|
pp::renderer::gl::linear_texture_filter(),
|
||||||
|
pp::renderer::gl::repeat_texture_wrap());
|
||||||
|
m_sampler_mipmap.create();
|
||||||
|
m_sampler_mipmap.set_filter(
|
||||||
|
pp::renderer::gl::linear_mipmap_linear_texture_filter(),
|
||||||
|
pp::renderer::gl::linear_texture_filter());
|
||||||
|
m_brush_shape.create();
|
||||||
|
while (s_running && !stop_token.stop_requested())
|
||||||
|
{
|
||||||
|
auto node = s_queue.Get();
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> _lock(s_render_mutex);
|
||||||
|
|
||||||
|
// if the brush is not already loaded, load it and then destroy it
|
||||||
|
bool to_unload = (node->m_brush->m_tip_texture == nullptr);
|
||||||
|
node->m_brush->preload();
|
||||||
|
|
||||||
|
App::I->render_task([node, to_unload]
|
||||||
|
{
|
||||||
|
gl_state gl;
|
||||||
|
gl.save();
|
||||||
|
|
||||||
|
node->m_brush->load();
|
||||||
|
node->draw_stroke_immediate();
|
||||||
|
if (to_unload)
|
||||||
|
node->m_brush->unload();
|
||||||
|
|
||||||
|
gl.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
node->app_redraw();
|
||||||
|
|
||||||
|
//std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_rtt.destroy();
|
||||||
|
m_rtt_mixer.destroy();
|
||||||
|
m_tex.destroy();
|
||||||
|
m_tex_dual.destroy();
|
||||||
|
m_tex_background.destroy();
|
||||||
|
m_brush_shape.destroy();
|
||||||
|
s_running = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
queue_lock.unlock();
|
||||||
|
s_queue.PostUnique(std::static_pointer_cast<NodeStrokePreview>(shared_from_this()), m_draw_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeStrokePreview::draw()
|
||||||
|
{
|
||||||
|
pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader(
|
||||||
|
pp::panopainter::LegacyCanvasDrawMergeTextureUniforms {
|
||||||
|
.mvp = m_mvp,
|
||||||
|
.texture_slot = 0,
|
||||||
|
});
|
||||||
|
m_tex_preview.bind();
|
||||||
|
m_sampler_linear.bind(0);
|
||||||
|
m_plane.draw_fill();
|
||||||
|
m_sampler_linear.unbind();
|
||||||
|
m_tex_preview.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
|
||||||
|
{
|
||||||
|
if (m_preview_size == (new_size * root()->m_zoom) || !m_brush)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_preview_size = new_size * root()->m_zoom;
|
||||||
|
|
||||||
|
if (m_on_screen)
|
||||||
|
draw_stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeStrokePreview::destroy()
|
||||||
|
{
|
||||||
|
m_tex_preview.destroy();
|
||||||
|
Node::destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeStrokePreview::handle_on_screen(bool old_visibility, bool new_visibility)
|
||||||
|
{
|
||||||
|
parent::handle_on_screen(old_visibility, new_visibility);
|
||||||
|
if (new_visibility)
|
||||||
|
{
|
||||||
|
draw_stroke();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_queue.Remove(std::static_pointer_cast<NodeStrokePreview>(shared_from_this()));
|
||||||
|
m_tex_preview.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,111 +10,16 @@
|
|||||||
#include "app_core/canvas_tool_ui.h"
|
#include "app_core/canvas_tool_ui.h"
|
||||||
#include "app_core/document_animation.h"
|
#include "app_core/document_animation.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "node_panel_grid.h"
|
#include "legacy_node_canvas_draw_services.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_node_canvas_state_services.h"
|
||||||
#include "legacy_preference_storage.h"
|
|
||||||
#include "legacy_ui_gl_dispatch.h"
|
|
||||||
#include "legacy_ui_overlay_services.h"
|
#include "legacy_ui_overlay_services.h"
|
||||||
#include "legacy_canvas_tool_services.h"
|
#include "legacy_canvas_tool_services.h"
|
||||||
#include "legacy_history_services.h"
|
#include "legacy_history_services.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "node_canvas.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 {
|
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
|
pp::app::CanvasHotkeyKey canvas_hotkey_key(kKey key) noexcept
|
||||||
{
|
{
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@@ -265,336 +170,6 @@ void run_canvas_tool_mode(pp::app::CanvasToolMode mode)
|
|||||||
return kEventResult::Consumed;
|
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
|
Node* NodeCanvas::clone_instantiate() const
|
||||||
@@ -604,34 +179,7 @@ Node* NodeCanvas::clone_instantiate() const
|
|||||||
|
|
||||||
void NodeCanvas::init()
|
void NodeCanvas::init()
|
||||||
{
|
{
|
||||||
const auto preferences = pp::panopainter::read_legacy_canvas_preferences();
|
pp::panopainter::init_legacy_node_canvas_shell(*this);
|
||||||
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()
|
void NodeCanvas::restore_context()
|
||||||
@@ -646,72 +194,7 @@ void NodeCanvas::clear_context()
|
|||||||
|
|
||||||
void NodeCanvas::draw()
|
void NodeCanvas::draw()
|
||||||
{
|
{
|
||||||
// sanity checks
|
pp::panopainter::draw_legacy_node_canvas_shell(*this);
|
||||||
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)
|
void NodeCanvas::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <stop_token>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -360,39 +359,6 @@ void execute_stroke_preview_background_capture_pass(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic_int NodeStrokePreview::s_instances{ 0 };
|
|
||||||
std::atomic_bool NodeStrokePreview::s_running{ false };
|
|
||||||
std::mutex NodeStrokePreview::s_render_mutex;
|
|
||||||
BlockingQueue<std::shared_ptr<NodeStrokePreview>> NodeStrokePreview::s_queue;
|
|
||||||
|
|
||||||
RTT NodeStrokePreview::m_rtt;
|
|
||||||
RTT NodeStrokePreview::m_rtt_mixer;
|
|
||||||
Texture2D NodeStrokePreview::m_tex; // blending tmp texture
|
|
||||||
Texture2D NodeStrokePreview::m_tex_dual;
|
|
||||||
Texture2D NodeStrokePreview::m_tex_background;
|
|
||||||
Sampler NodeStrokePreview::m_sampler_linear;
|
|
||||||
Sampler NodeStrokePreview::m_sampler_linear_repeat;
|
|
||||||
Sampler NodeStrokePreview::m_sampler_mipmap;
|
|
||||||
DynamicShape NodeStrokePreview::m_brush_shape;
|
|
||||||
std::jthread NodeStrokePreview::s_renderer;
|
|
||||||
|
|
||||||
|
|
||||||
void NodeStrokePreview::terminate_renderer()
|
|
||||||
{
|
|
||||||
if (!s_renderer.joinable())
|
|
||||||
return;
|
|
||||||
|
|
||||||
s_running = false;
|
|
||||||
s_renderer.request_stop();
|
|
||||||
s_queue.UnlockGetters();
|
|
||||||
s_renderer.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::empty_queue()
|
|
||||||
{
|
|
||||||
s_queue.q.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* NodeStrokePreview::clone_instantiate() const
|
Node* NodeStrokePreview::clone_instantiate() const
|
||||||
{
|
{
|
||||||
return new NodeStrokePreview();
|
return new NodeStrokePreview();
|
||||||
@@ -420,21 +386,6 @@ void NodeStrokePreview::init_controls()
|
|||||||
// Canvas::I->m_current_brush.m_tex_id = const_hash("data/thumbs/Round-Hard.png");
|
// Canvas::I->m_current_brush.m_tex_id = const_hash("data/thumbs/Round-Hard.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeStrokePreview::restore_context()
|
|
||||||
{
|
|
||||||
NodeBorder::restore_context();
|
|
||||||
init_controls();
|
|
||||||
if (m_size.x > 0 && m_size.y > 0)
|
|
||||||
m_tex_preview.create(static_cast<int>(m_size.x), static_cast<int>(m_size.y));
|
|
||||||
draw_stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::clear_context()
|
|
||||||
{
|
|
||||||
NodeBorder::clear_context();
|
|
||||||
m_tex_preview.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
|
void NodeStrokePreview::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
|
||||||
{
|
{
|
||||||
const auto& b = m_brush;
|
const auto& b = m_brush;
|
||||||
@@ -701,121 +652,3 @@ void NodeStrokePreview::draw_stroke_immediate()
|
|||||||
apply_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height);
|
apply_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height);
|
||||||
apply_stroke_preview_clear_color(cc);
|
apply_stroke_preview_clear_color(cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
Image NodeStrokePreview::render_to_image()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> _lock(s_render_mutex);
|
|
||||||
|
|
||||||
App::I->render_task([this] {
|
|
||||||
draw_stroke_immediate();
|
|
||||||
});
|
|
||||||
return m_tex_preview.get_image();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::draw_stroke()
|
|
||||||
{
|
|
||||||
if (m_size.x == 0 || m_size.y == 0)
|
|
||||||
return;
|
|
||||||
std::unique_lock<std::mutex> queue_lock(s_queue.mutex);
|
|
||||||
if (!s_renderer.joinable())
|
|
||||||
{
|
|
||||||
s_running = true;
|
|
||||||
s_renderer = std::jthread([](std::stop_token stop_token) {
|
|
||||||
BT_SetTerminate();
|
|
||||||
|
|
||||||
m_sampler_linear.create();
|
|
||||||
m_sampler_linear_repeat.create(
|
|
||||||
pp::renderer::gl::linear_texture_filter(),
|
|
||||||
pp::renderer::gl::repeat_texture_wrap());
|
|
||||||
m_sampler_mipmap.create();
|
|
||||||
m_sampler_mipmap.set_filter(
|
|
||||||
pp::renderer::gl::linear_mipmap_linear_texture_filter(),
|
|
||||||
pp::renderer::gl::linear_texture_filter());
|
|
||||||
m_brush_shape.create();
|
|
||||||
while (s_running && !stop_token.stop_requested())
|
|
||||||
{
|
|
||||||
auto node = s_queue.Get();
|
|
||||||
if (node)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> _lock(s_render_mutex);
|
|
||||||
|
|
||||||
// if the brush is not already loaded, load it and then destroy it
|
|
||||||
bool to_unload = (node->m_brush->m_tip_texture == nullptr);
|
|
||||||
node->m_brush->preload();
|
|
||||||
|
|
||||||
App::I->render_task([node, to_unload]
|
|
||||||
{
|
|
||||||
gl_state gl;
|
|
||||||
gl.save();
|
|
||||||
|
|
||||||
node->m_brush->load();
|
|
||||||
node->draw_stroke_immediate();
|
|
||||||
if (to_unload)
|
|
||||||
node->m_brush->unload();
|
|
||||||
|
|
||||||
gl.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
node->app_redraw();
|
|
||||||
|
|
||||||
//std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_rtt.destroy();
|
|
||||||
m_rtt_mixer.destroy();
|
|
||||||
m_tex.destroy();
|
|
||||||
m_tex_dual.destroy();
|
|
||||||
m_tex_background.destroy();
|
|
||||||
m_brush_shape.destroy();
|
|
||||||
s_running = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
queue_lock.unlock();
|
|
||||||
s_queue.PostUnique(std::static_pointer_cast<NodeStrokePreview>(shared_from_this()), m_draw_first);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::draw()
|
|
||||||
{
|
|
||||||
pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader(
|
|
||||||
pp::panopainter::LegacyCanvasDrawMergeTextureUniforms {
|
|
||||||
.mvp = m_mvp,
|
|
||||||
.texture_slot = 0,
|
|
||||||
});
|
|
||||||
m_tex_preview.bind();
|
|
||||||
m_sampler_linear.bind(0);
|
|
||||||
m_plane.draw_fill();
|
|
||||||
m_sampler_linear.unbind();
|
|
||||||
m_tex_preview.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
|
|
||||||
{
|
|
||||||
if (m_preview_size == (new_size * root()->m_zoom) || !m_brush)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_preview_size = new_size * root()->m_zoom;
|
|
||||||
|
|
||||||
if (m_on_screen)
|
|
||||||
draw_stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::destroy()
|
|
||||||
{
|
|
||||||
m_tex_preview.destroy();
|
|
||||||
Node::destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::handle_on_screen(bool old_visibility, bool new_visibility)
|
|
||||||
{
|
|
||||||
parent::handle_on_screen(old_visibility, new_visibility);
|
|
||||||
if (new_visibility)
|
|
||||||
{
|
|
||||||
draw_stroke();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s_queue.Remove(std::static_pointer_cast<NodeStrokePreview>(shared_from_this()));
|
|
||||||
m_tex_preview.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user