Extract app frame and export dialog helpers
This commit is contained in:
@@ -92,6 +92,7 @@ set(PP_PANOPAINTER_APP_SOURCES
|
|||||||
src/app_cloud.cpp
|
src/app_cloud.cpp
|
||||||
src/app_commands.cpp
|
src/app_commands.cpp
|
||||||
src/app_dialogs.cpp
|
src/app_dialogs.cpp
|
||||||
|
src/app_dialogs_export.cpp
|
||||||
src/app_dialogs_info_openers.cpp
|
src/app_dialogs_info_openers.cpp
|
||||||
src/app_events.cpp
|
src/app_events.cpp
|
||||||
src/app_layout.cpp
|
src/app_layout.cpp
|
||||||
@@ -100,6 +101,7 @@ set(PP_PANOPAINTER_APP_SOURCES
|
|||||||
src/app_layout_tools_menu.cpp
|
src/app_layout_tools_menu.cpp
|
||||||
src/app_shaders.cpp
|
src/app_shaders.cpp
|
||||||
src/app_vr.cpp
|
src/app_vr.cpp
|
||||||
|
src/legacy_app_frame_services.cpp
|
||||||
src/legacy_app_dialog_services.cpp
|
src/legacy_app_dialog_services.cpp
|
||||||
src/legacy_app_dialog_services.h
|
src/legacy_app_dialog_services.h
|
||||||
src/legacy_app_preference_services.cpp
|
src/legacy_app_preference_services.cpp
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ Current hotspot files:
|
|||||||
- `src/main.cpp`: 1117 lines
|
- `src/main.cpp`: 1117 lines
|
||||||
- `src/node_panel_brush.cpp`: 1197 lines
|
- `src/node_panel_brush.cpp`: 1197 lines
|
||||||
- `src/node_stroke_preview.cpp`: 933 lines
|
- `src/node_stroke_preview.cpp`: 933 lines
|
||||||
- `src/node_canvas.cpp`: 953 lines
|
- `src/node_canvas.cpp`: 897 lines
|
||||||
- `src/app.cpp`: 950 lines
|
- `src/app.cpp`: 502 lines
|
||||||
- `src/app_dialogs.cpp`: 789 lines
|
- `src/app_dialogs.cpp`: 441 lines
|
||||||
|
|
||||||
Current architecture mismatches that must be treated as real blockers:
|
Current architecture mismatches that must be treated as real blockers:
|
||||||
|
|
||||||
@@ -139,7 +139,10 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
`src/app_layout_about_layer_menu.cpp` and `App::init_menu_about()` plus
|
`src/app_layout_about_layer_menu.cpp` and `App::init_menu_about()` plus
|
||||||
`App::init_menu_layer()` are now thin call-throughs, while the informational
|
`App::init_menu_layer()` are now thin call-throughs, while the informational
|
||||||
overlay opener family now also lives in `src/app_dialogs_info_openers.cpp`
|
overlay opener family now also lives in `src/app_dialogs_info_openers.cpp`
|
||||||
and the corresponding `App::dialog_*` entrypoints are thinner.
|
and the corresponding `App::dialog_*` entrypoints are thinner, while the
|
||||||
|
export/video/PPBR dialog family now also lives in
|
||||||
|
`src/app_dialogs_export.cpp` and those `App::dialog_*` entrypoints are
|
||||||
|
thinner too.
|
||||||
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
|
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
|
||||||
global singleton reach, raw observer pointers, retained static worker
|
global singleton reach, raw observer pointers, retained static worker
|
||||||
ownership in several app families, and ad hoc mutex/condition-variable
|
ownership in several app families, and ad hoc mutex/condition-variable
|
||||||
@@ -172,7 +175,12 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
while `App::rec_loop()` now delegates worker-iteration orchestration into
|
while `App::rec_loop()` now delegates worker-iteration orchestration into
|
||||||
the retained recording bridge, `App::update_rec_frames()` now delegates
|
the retained recording bridge, `App::update_rec_frames()` now delegates
|
||||||
recording label refresh through that same retained recording path, and the
|
recording label refresh through that same retained recording path, and the
|
||||||
canvas state-management cluster for picking, clear/clear-all, layer
|
UI observer math, repeated UI child traversal, and canvas toolbar refresh
|
||||||
|
now live in `src/legacy_app_frame_services.cpp` instead of staying inline in
|
||||||
|
`src/app.cpp`, while the larger document/export/save/open/thumbnail
|
||||||
|
document-IO cluster now lives in `src/legacy_canvas_document_io_services.cpp`
|
||||||
|
and `src/app.cpp` is materially thinner,
|
||||||
|
while the canvas state-management cluster for picking, clear/clear-all, layer
|
||||||
add/remove/order/lookups, animation frame control, resize, and snapshot
|
add/remove/order/lookups, animation frame control, resize, and snapshot
|
||||||
save/restore now lives in `src/legacy_canvas_state_services.cpp` instead of
|
save/restore now lives in `src/legacy_canvas_state_services.cpp` instead of
|
||||||
`src/canvas.cpp`, while the larger import/export/save/open/thumbnail
|
`src/canvas.cpp`, while the larger import/export/save/open/thumbnail
|
||||||
|
|||||||
@@ -199,7 +199,12 @@ Current slice:
|
|||||||
- `NodeCanvas` non-`draw_merged` per-layer temporary erase/paint, layer-texture,
|
- `NodeCanvas` non-`draw_merged` per-layer temporary erase/paint, layer-texture,
|
||||||
and blend setup now also route through
|
and blend setup now also route through
|
||||||
`make_legacy_canvas_draw_merge_layer_path_gl_execution(...)`, but the node
|
`make_legacy_canvas_draw_merge_layer_path_gl_execution(...)`, but the node
|
||||||
still owns the remaining draw lambdas and broader renderer-state shell.
|
still owned the remaining draw lambdas and broader renderer-state shell.
|
||||||
|
- `NodeCanvas` now routes that remaining non-`draw_merged` per-layer blend,
|
||||||
|
composite, debug-outline, and frame callback assembly through the local
|
||||||
|
`make_node_canvas_layer_path_execution(...)` helper, which materially thins
|
||||||
|
`NodeCanvas::draw()` even though the broader draw loop still lives in
|
||||||
|
`src/node_canvas.cpp`.
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/node_stroke_preview.cpp`
|
- `src/node_stroke_preview.cpp`
|
||||||
@@ -322,7 +327,7 @@ Mini-model packet:
|
|||||||
|
|
||||||
#### ARC-APP-002 - Split `app_dialogs.cpp` Into Workflow Adapters And Widget Openers
|
#### ARC-APP-002 - Split `app_dialogs.cpp` Into Workflow Adapters And Widget Openers
|
||||||
|
|
||||||
Status: Ready
|
Status: In Progress
|
||||||
|
|
||||||
Why now:
|
Why now:
|
||||||
`src/app_dialogs.cpp` still mixes document workflow decisions, export routing,
|
`src/app_dialogs.cpp` still mixes document workflow decisions, export routing,
|
||||||
@@ -334,6 +339,10 @@ Current slice:
|
|||||||
and the corresponding `App::dialog_*` entrypoints are now thin call-throughs,
|
and the corresponding `App::dialog_*` entrypoints are now thin call-throughs,
|
||||||
but document/export workflow and retained dialog execution are still inline in
|
but document/export workflow and retained dialog execution are still inline in
|
||||||
`src/app_dialogs.cpp`.
|
`src/app_dialogs.cpp`.
|
||||||
|
- Export, video-export, and PPBR export entrypoints now also live in
|
||||||
|
`src/app_dialogs_export.cpp`, and the corresponding `App::dialog_*`
|
||||||
|
entrypoints are now thin call-throughs, but new/open/save/browse/resize and
|
||||||
|
retained dialog execution are still inline in `src/app_dialogs.cpp`.
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/app_dialogs.cpp`
|
- `src/app_dialogs.cpp`
|
||||||
@@ -364,11 +373,19 @@ Mini-model packet:
|
|||||||
|
|
||||||
#### ARC-APP-003 - Reduce `app.cpp` To Frame, Queue, And Composition Shell
|
#### ARC-APP-003 - Reduce `app.cpp` To Frame, Queue, And Composition Shell
|
||||||
|
|
||||||
Status: Ready
|
Status: In Progress
|
||||||
|
|
||||||
Why now:
|
Why now:
|
||||||
`src/app.cpp` still carries startup, frame flow, queue draining, recording,
|
`src/app.cpp` still carries startup, frame flow, queue draining, recording,
|
||||||
observer math, and composition logic in one 950-line file.
|
and composition logic in one 502-line file.
|
||||||
|
|
||||||
|
Current slice:
|
||||||
|
- UI observer math now routes through `src/legacy_app_frame_services.cpp`
|
||||||
|
instead of staying inline in `src/app.cpp`.
|
||||||
|
- The repeated UI child traversal in `App::draw()` now routes through the same
|
||||||
|
retained helper.
|
||||||
|
- Canvas toolbar refresh in `App::update()` now also routes through that helper
|
||||||
|
file, materially shrinking `src/app.cpp`.
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/app.cpp`
|
- `src/app.cpp`
|
||||||
|
|||||||
131
src/app.cpp
131
src/app.cpp
@@ -33,37 +33,6 @@
|
|||||||
App* App::I = nullptr; // singleton
|
App* App::I = nullptr; // singleton
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
pp::app::CanvasToolMode canvas_tool_mode_from_canvas_mode(kCanvasMode mode) noexcept
|
|
||||||
{
|
|
||||||
switch (mode) {
|
|
||||||
case kCanvasMode::Draw:
|
|
||||||
return pp::app::CanvasToolMode::draw;
|
|
||||||
case kCanvasMode::Erase:
|
|
||||||
return pp::app::CanvasToolMode::erase;
|
|
||||||
case kCanvasMode::Line:
|
|
||||||
return pp::app::CanvasToolMode::line;
|
|
||||||
case kCanvasMode::Camera:
|
|
||||||
return pp::app::CanvasToolMode::camera;
|
|
||||||
case kCanvasMode::Grid:
|
|
||||||
return pp::app::CanvasToolMode::grid;
|
|
||||||
case kCanvasMode::Copy:
|
|
||||||
return pp::app::CanvasToolMode::copy;
|
|
||||||
case kCanvasMode::Cut:
|
|
||||||
return pp::app::CanvasToolMode::cut;
|
|
||||||
case kCanvasMode::Fill:
|
|
||||||
return pp::app::CanvasToolMode::fill;
|
|
||||||
case kCanvasMode::MaskFree:
|
|
||||||
return pp::app::CanvasToolMode::mask_free;
|
|
||||||
case kCanvasMode::MaskLine:
|
|
||||||
return pp::app::CanvasToolMode::mask_line;
|
|
||||||
case kCanvasMode::FloodFill:
|
|
||||||
return pp::app::CanvasToolMode::flood_fill;
|
|
||||||
default:
|
|
||||||
return pp::app::CanvasToolMode::draw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply_app_viewport(pp::renderer::gl::OpenGlViewportRect viewport)
|
void apply_app_viewport(pp::renderer::gl::OpenGlViewportRect viewport)
|
||||||
{
|
{
|
||||||
const auto status = pp::renderer::gl::apply_opengl_viewport(
|
const auto status = pp::renderer::gl::apply_opengl_viewport(
|
||||||
@@ -215,6 +184,9 @@ namespace pp::panopainter
|
|||||||
{
|
{
|
||||||
bool process_legacy_recording_worker_iteration(App& app);
|
bool process_legacy_recording_worker_iteration(App& app);
|
||||||
void update_legacy_recording_frame_label(App& app);
|
void update_legacy_recording_frame_label(App& app);
|
||||||
|
bool update_legacy_app_ui_observer(App& app, Node* n);
|
||||||
|
void watch_legacy_app_ui_children(App& app, const std::function<bool(Node*)>& observer, bool skip_first_main_child);
|
||||||
|
void update_legacy_canvas_toolbar(App& app);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool App::check_license()
|
bool App::check_license()
|
||||||
@@ -359,67 +331,7 @@ void App::async_swap()
|
|||||||
|
|
||||||
bool App::update_ui_observer(Node *n)
|
bool App::update_ui_observer(Node *n)
|
||||||
{
|
{
|
||||||
std::vector<pp::app::AppUiObserverParentClip> parent_clips;
|
return pp::panopainter::update_legacy_app_ui_observer(*this, n);
|
||||||
if (n) {
|
|
||||||
for (Node* p = n->m_parent; p; p = p->m_parent) {
|
|
||||||
parent_clips.push_back(pp::app::AppUiObserverParentClip {
|
|
||||||
.clip = pp::app::AppUiObserverRect {
|
|
||||||
.x = p->m_clip_uncut.x,
|
|
||||||
.y = p->m_clip_uncut.y,
|
|
||||||
.width = p->m_clip_uncut.z,
|
|
||||||
.height = p->m_clip_uncut.w,
|
|
||||||
},
|
|
||||||
.padding_top = YGNodeLayoutGetPadding(p->y_node, YGEdgeTop),
|
|
||||||
.padding_right = YGNodeLayoutGetPadding(p->y_node, YGEdgeRight),
|
|
||||||
.padding_bottom = YGNodeLayoutGetPadding(p->y_node, YGEdgeBottom),
|
|
||||||
.padding_left = YGNodeLayoutGetPadding(p->y_node, YGEdgeLeft),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto plan = pp::app::plan_app_ui_observer(
|
|
||||||
n != nullptr,
|
|
||||||
n && n->m_display,
|
|
||||||
n && n->m_on_screen,
|
|
||||||
n
|
|
||||||
? pp::app::AppUiObserverRect {
|
|
||||||
.x = n->m_clip_uncut.x,
|
|
||||||
.y = n->m_clip_uncut.y,
|
|
||||||
.width = n->m_clip_uncut.z,
|
|
||||||
.height = n->m_clip_uncut.w,
|
|
||||||
}
|
|
||||||
: pp::app::AppUiObserverRect {},
|
|
||||||
parent_clips,
|
|
||||||
height,
|
|
||||||
zoom,
|
|
||||||
off_x,
|
|
||||||
off_y);
|
|
||||||
if (!plan) {
|
|
||||||
LOG("UI observer plan failed: %s", plan.status().message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!n)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (plan.value().notify_leave_screen)
|
|
||||||
n->handle_on_screen(true, false);
|
|
||||||
if (plan.value().notify_enter_screen)
|
|
||||||
n->handle_on_screen(false, true);
|
|
||||||
n->m_on_screen = plan.value().next_on_screen;
|
|
||||||
|
|
||||||
if (!plan.value().draw_node)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
apply_app_scissor(pp::renderer::gl::OpenGlScissorRect {
|
|
||||||
.enabled = 1U,
|
|
||||||
.x = plan.value().scissor_x,
|
|
||||||
.y = plan.value().scissor_y,
|
|
||||||
.width = plan.value().scissor_width,
|
|
||||||
.height = plan.value().scissor_height,
|
|
||||||
});
|
|
||||||
n->draw();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::draw(float dt)
|
void App::draw(float dt)
|
||||||
@@ -446,11 +358,7 @@ void App::draw(float dt)
|
|||||||
.height = static_cast<std::int32_t>(uirtt.getHeight()),
|
.height = static_cast<std::int32_t>(uirtt.getHeight()),
|
||||||
});
|
});
|
||||||
apply_app_scissor_test(true);
|
apply_app_scissor_test(true);
|
||||||
for (int i = 1; i < layout[main_id]->m_children.size(); i++)
|
pp::panopainter::watch_legacy_app_ui_children(*this, observer, true);
|
||||||
layout[main_id]->m_children[i]->watch(observer);
|
|
||||||
for (int i = 0; layout_designer.get(main_id) && i < layout_designer[main_id]->m_children.size(); i++)
|
|
||||||
layout_designer[main_id]->m_children[i]->watch(observer);
|
|
||||||
//msgbox->watch(observer);
|
|
||||||
apply_app_scissor_test(false);
|
apply_app_scissor_test(false);
|
||||||
uirtt.unbindFramebuffer();
|
uirtt.unbindFramebuffer();
|
||||||
}
|
}
|
||||||
@@ -465,11 +373,7 @@ void App::draw(float dt)
|
|||||||
.height = static_cast<std::int32_t>(height),
|
.height = static_cast<std::int32_t>(height),
|
||||||
});
|
});
|
||||||
apply_app_scissor_test(true);
|
apply_app_scissor_test(true);
|
||||||
for (int i = 0; i < layout[main_id]->m_children.size(); i++)
|
pp::panopainter::watch_legacy_app_ui_children(*this, observer, false);
|
||||||
layout[main_id]->m_children[i]->watch(observer);
|
|
||||||
for (int i = 0; layout_designer.get(main_id) && i < layout_designer[main_id]->m_children.size(); i++)
|
|
||||||
layout_designer[main_id]->m_children[i]->watch(observer);
|
|
||||||
//msgbox->watch(observer);
|
|
||||||
apply_app_scissor_test(false);
|
apply_app_scissor_test(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,28 +396,7 @@ void App::update(float dt)
|
|||||||
if (!update_plan.refresh_canvas_toolbar)
|
if (!update_plan.refresh_canvas_toolbar)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
{
|
pp::panopainter::update_legacy_canvas_toolbar(*this);
|
||||||
auto mode = Canvas::I->m_current_mode;
|
|
||||||
|
|
||||||
CanvasModePen* pm = (CanvasModePen*)canvas->m_canvas->modes[(int)kCanvasMode::Draw][0];
|
|
||||||
const auto toolbar = pp::app::plan_canvas_tool_button_state(
|
|
||||||
canvas_tool_mode_from_canvas_mode(mode),
|
|
||||||
pm && pm->m_picking,
|
|
||||||
canvas->m_canvas->m_touch_lock);
|
|
||||||
layout[main_id]->find<NodeButtonCustom>("btn-pick")->set_active(toolbar.pick_active);
|
|
||||||
layout[main_id]->find<NodeButtonCustom>("btn-touchlock")->set_active(toolbar.touch_lock_active);
|
|
||||||
|
|
||||||
layout[main_id]->find<NodeButtonCustom>("btn-pen")->set_active(toolbar.pen_active);
|
|
||||||
layout[main_id]->find<NodeButtonCustom>("btn-erase")->set_active(toolbar.erase_active);
|
|
||||||
layout[main_id]->find<NodeButton>("btn-cam")->set_active(toolbar.camera_active);
|
|
||||||
layout[main_id]->find<NodeButtonCustom>("btn-line")->set_active(toolbar.line_active);
|
|
||||||
layout[main_id]->find<NodeButton>("btn-grid")->set_active(toolbar.grid_active);
|
|
||||||
layout[main_id]->find<NodeButton>("btn-copy")->set_active(toolbar.copy_active);
|
|
||||||
layout[main_id]->find<NodeButton>("btn-cut")->set_active(toolbar.cut_active);
|
|
||||||
layout[main_id]->find<NodeButtonCustom>("btn-mask-free")->set_active(toolbar.mask_free_active);
|
|
||||||
layout[main_id]->find<NodeButtonCustom>("btn-mask-line")->set_active(toolbar.mask_line_active);
|
|
||||||
layout[main_id]->find<NodeButtonCustom>("btn-bucket")->set_active(toolbar.flood_fill_active);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::terminate()
|
void App::terminate()
|
||||||
|
|||||||
@@ -16,13 +16,6 @@
|
|||||||
#include "node_dialog_browse.h"
|
#include "node_dialog_browse.h"
|
||||||
#include "node_dialog_resize.h"
|
#include "node_dialog_resize.h"
|
||||||
#include "node_dialog_cloud.h"
|
#include "node_dialog_cloud.h"
|
||||||
#include "node_dialog_export_ppbr.h"
|
|
||||||
|
|
||||||
#include <codec_api.h>
|
|
||||||
#define MP4V2_NO_STDINT_DEFS
|
|
||||||
#include <mp4v2/mp4v2.h>
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#ifdef __QUEST__
|
#ifdef __QUEST__
|
||||||
#include "oculus_vr.h"
|
#include "oculus_vr.h"
|
||||||
@@ -34,95 +27,18 @@ void open_changelog_dialog(App& app);
|
|||||||
void open_about_dialog(App& app);
|
void open_about_dialog(App& app);
|
||||||
void open_whatsnew_dialog(App& app, bool force_show);
|
void open_whatsnew_dialog(App& app, bool force_show);
|
||||||
void open_shortcuts_dialog(App& app);
|
void open_shortcuts_dialog(App& app);
|
||||||
|
void open_document_export_dialog(App& app, std::string ext);
|
||||||
|
void open_document_export_layers_dialog(App& app);
|
||||||
|
void open_document_export_anim_frames_dialog(App& app);
|
||||||
|
void open_document_export_depth_dialog(App& app);
|
||||||
|
void open_document_export_cube_faces_dialog(App& app);
|
||||||
|
void open_document_timelapse_export_dialog(App& app);
|
||||||
|
void open_document_export_mp4_dialog(App& app);
|
||||||
|
void open_ppbr_export_dialog(App& app);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] bool can_start_document_export(App& app, bool requires_license)
|
|
||||||
{
|
|
||||||
const auto decision = pp::app::plan_document_export_start(
|
|
||||||
requires_license,
|
|
||||||
!requires_license || app.check_license(),
|
|
||||||
app.canvas != nullptr);
|
|
||||||
|
|
||||||
switch (decision) {
|
|
||||||
case pp::app::DocumentExportStartDecision::start_now:
|
|
||||||
return true;
|
|
||||||
case pp::app::DocumentExportStartDecision::show_license_disabled:
|
|
||||||
{
|
|
||||||
const auto plan = pp::app::plan_document_export_license_disabled_dialog();
|
|
||||||
app.message_box(plan.title, plan.message, plan.show_cancel);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case pp::app::DocumentExportStartDecision::unavailable_no_canvas:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void start_document_export_collection(
|
|
||||||
App& app,
|
|
||||||
pp::app::DocumentExportCollectionKind kind)
|
|
||||||
{
|
|
||||||
const auto plan = pp::app::plan_document_export_collection_target(
|
|
||||||
kind,
|
|
||||||
app.uses_work_directory_document_export_collections());
|
|
||||||
const auto success_kind = pp::app::document_export_collection_success_kind(kind);
|
|
||||||
|
|
||||||
if (plan.destination == pp::app::DocumentExportCollectionDestination::work_directory_collection) {
|
|
||||||
const auto target = pp::app::make_document_export_collection_target(
|
|
||||||
app.work_path,
|
|
||||||
app.doc_name,
|
|
||||||
plan.suffix);
|
|
||||||
if (!target) {
|
|
||||||
const auto dialog = pp::app::plan_document_export_failure_dialog(success_kind, target.status().message);
|
|
||||||
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_export_collection(
|
|
||||||
app,
|
|
||||||
plan.kind,
|
|
||||||
target.value());
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(
|
|
||||||
kind == pp::app::DocumentExportCollectionKind::layers
|
|
||||||
? pp::app::DocumentExportExecutionKind::layers_collection
|
|
||||||
: pp::app::DocumentExportExecutionKind::animation_frames_collection),
|
|
||||||
status.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.pick_dir([
|
|
||||||
&app,
|
|
||||||
kind = plan.kind,
|
|
||||||
success_kind
|
|
||||||
](std::string path) {
|
|
||||||
const auto target = pp::app::make_document_export_stem_target(path, app.doc_name);
|
|
||||||
if (!target) {
|
|
||||||
const auto dialog = pp::app::plan_document_export_failure_dialog(success_kind, target.status().message);
|
|
||||||
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_export_stem(
|
|
||||||
app,
|
|
||||||
kind,
|
|
||||||
target.value());
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(
|
|
||||||
kind == pp::app::DocumentExportCollectionKind::layers
|
|
||||||
? pp::app::DocumentExportExecutionKind::layers_stem
|
|
||||||
: pp::app::DocumentExportExecutionKind::animation_frames_stem),
|
|
||||||
status.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void wire_document_browse_dialog_actions(
|
void wire_document_browse_dialog_actions(
|
||||||
App& app,
|
App& app,
|
||||||
const std::shared_ptr<NodeDialogBrowse>& dialog,
|
const std::shared_ptr<NodeDialogBrowse>& dialog,
|
||||||
@@ -453,59 +369,22 @@ void App::dialog_save()
|
|||||||
|
|
||||||
void App::dialog_export(std::string ext)
|
void App::dialog_export(std::string ext)
|
||||||
{
|
{
|
||||||
if (!can_start_document_export(*this, true))
|
pp::panopainter::open_document_export_dialog(*this, ext);
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: use picker
|
|
||||||
const auto target = pp::app::make_document_export_file_target(work_path, doc_name, ext);
|
|
||||||
if (!target) {
|
|
||||||
const auto dialog = pp::app::plan_document_export_failure_dialog(
|
|
||||||
pp::app::DocumentExportSuccessKind::equirectangular,
|
|
||||||
target.status().message);
|
|
||||||
message_box(dialog.title, dialog.message, dialog.show_cancel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_export_file(*this, target.value());
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(
|
|
||||||
pp::app::DocumentExportExecutionKind::equirectangular_file),
|
|
||||||
status.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_export_layers()
|
void App::dialog_export_layers()
|
||||||
{
|
{
|
||||||
if (!can_start_document_export(*this, true))
|
pp::panopainter::open_document_export_layers_dialog(*this);
|
||||||
return;
|
|
||||||
|
|
||||||
start_document_export_collection(
|
|
||||||
*this,
|
|
||||||
pp::app::DocumentExportCollectionKind::layers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_export_anim_frames()
|
void App::dialog_export_anim_frames()
|
||||||
{
|
{
|
||||||
if (!can_start_document_export(*this, true))
|
pp::panopainter::open_document_export_anim_frames_dialog(*this);
|
||||||
return;
|
|
||||||
|
|
||||||
start_document_export_collection(
|
|
||||||
*this,
|
|
||||||
pp::app::DocumentExportCollectionKind::animation_frames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_export_depth()
|
void App::dialog_export_depth()
|
||||||
{
|
{
|
||||||
if (!can_start_document_export(*this, true))
|
pp::panopainter::open_document_export_depth_dialog(*this);
|
||||||
return;
|
|
||||||
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_export_depth(*this, doc_name);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::depth),
|
|
||||||
status.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_resize()
|
void App::dialog_resize()
|
||||||
@@ -551,15 +430,7 @@ void App::dialog_resize()
|
|||||||
|
|
||||||
void App::dialog_export_cube_faces()
|
void App::dialog_export_cube_faces()
|
||||||
{
|
{
|
||||||
if (!can_start_document_export(*this, false))
|
pp::panopainter::open_document_export_cube_faces_dialog(*this);
|
||||||
return;
|
|
||||||
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_export_cube_faces(*this, doc_name);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::cube_faces),
|
|
||||||
status.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_layer_rename()
|
void App::dialog_layer_rename()
|
||||||
@@ -613,169 +484,17 @@ void App::dialog_preset_download()
|
|||||||
|
|
||||||
void App::dialog_ppbr_export()
|
void App::dialog_ppbr_export()
|
||||||
{
|
{
|
||||||
auto* overlay_anchor = layout[main_id];
|
pp::panopainter::open_ppbr_export_dialog(*this);
|
||||||
if (!overlay_anchor) {
|
|
||||||
LOG("PPBR export dialog open failed: main layout anchor is missing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogExportPPBR>(*this);
|
|
||||||
|
|
||||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
|
||||||
if (!overlay) {
|
|
||||||
LOG("PPBR export dialog open failed: %s", overlay.status().message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto overlay_handle = overlay.value();
|
|
||||||
|
|
||||||
const auto close_dialog = [overlay_anchor, overlay_handle]() {
|
|
||||||
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
|
|
||||||
(void)close_status;
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog->btn_ok->on_click = [this, dialog] (Node*) {
|
|
||||||
const auto request = pp::panopainter::make_legacy_brush_package_export_request(*dialog);
|
|
||||||
|
|
||||||
if (uses_prepared_file_writes())
|
|
||||||
{
|
|
||||||
pick_file_save("ppbr", "exported-brushes",
|
|
||||||
[this, dialog, request] (std::string path) {
|
|
||||||
const auto status = pp::panopainter::execute_legacy_brush_package_export(
|
|
||||||
*this,
|
|
||||||
*dialog,
|
|
||||||
request,
|
|
||||||
path,
|
|
||||||
pp::panopainter::LegacyBrushPackageExportMode::inline_export_only);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG("PPBR export failed: %s", status.message);
|
|
||||||
},
|
|
||||||
[dialog] (const std::string& path, bool saved) {
|
|
||||||
(void)path;
|
|
||||||
pp::panopainter::complete_legacy_brush_package_export(*dialog, saved);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pick_file_save({ "ppbr" }, [this, dialog, request] (std::string path) {
|
|
||||||
const auto status = pp::panopainter::execute_legacy_brush_package_export(
|
|
||||||
*this,
|
|
||||||
*dialog,
|
|
||||||
request,
|
|
||||||
path,
|
|
||||||
pp::panopainter::LegacyBrushPackageExportMode::desktop_async_close_and_message);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG("PPBR export failed: %s", status.message);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
dialog->btn_cancel->on_click = [close_dialog](Node*)
|
|
||||||
{
|
|
||||||
close_dialog();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_timelapse_export()
|
void App::dialog_timelapse_export()
|
||||||
{
|
{
|
||||||
if (!can_start_document_export(*this, false))
|
pp::panopainter::open_document_timelapse_export_dialog(*this);
|
||||||
return;
|
|
||||||
|
|
||||||
if (uses_prepared_file_writes())
|
|
||||||
{
|
|
||||||
const auto target = pp::app::make_document_export_suggested_name(doc_name, "-timelapse");
|
|
||||||
if (!target) {
|
|
||||||
const auto dialog = pp::app::plan_document_export_failure_dialog(
|
|
||||||
pp::app::DocumentExportSuccessKind::timelapse,
|
|
||||||
target.status().message);
|
|
||||||
message_box(dialog.title, dialog.message, dialog.show_cancel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pick_file_save("mp4", target.value().name,
|
|
||||||
[this](std::string path) {
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
|
||||||
*this,
|
|
||||||
pp::app::DocumentVideoExportKind::timelapse,
|
|
||||||
path,
|
|
||||||
false);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(
|
|
||||||
pp::app::DocumentExportExecutionKind::timelapse),
|
|
||||||
status.message);
|
|
||||||
},
|
|
||||||
[](const std::string& path, bool saved) {
|
|
||||||
(void)path;
|
|
||||||
(void)saved;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pick_file_save({ "mp4" }, [this](std::string path) {
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
|
||||||
*this,
|
|
||||||
pp::app::DocumentVideoExportKind::timelapse,
|
|
||||||
path,
|
|
||||||
true);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::timelapse),
|
|
||||||
status.message);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_export_mp4()
|
void App::dialog_export_mp4()
|
||||||
{
|
{
|
||||||
if (!can_start_document_export(*this, false))
|
pp::panopainter::open_document_export_mp4_dialog(*this);
|
||||||
return;
|
|
||||||
|
|
||||||
if (uses_prepared_file_writes())
|
|
||||||
{
|
|
||||||
const auto target = pp::app::make_document_export_suggested_name(doc_name, "-animation");
|
|
||||||
if (!target) {
|
|
||||||
const auto dialog = pp::app::plan_document_export_failure_dialog(
|
|
||||||
pp::app::DocumentExportSuccessKind::animation_mp4,
|
|
||||||
target.status().message);
|
|
||||||
message_box(dialog.title, dialog.message, dialog.show_cancel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pick_file_save("mp4", target.value().name,
|
|
||||||
[this](std::string path) {
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
|
||||||
*this,
|
|
||||||
pp::app::DocumentVideoExportKind::animation_mp4,
|
|
||||||
path,
|
|
||||||
false);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(
|
|
||||||
pp::app::DocumentExportExecutionKind::animation_mp4),
|
|
||||||
status.message);
|
|
||||||
},
|
|
||||||
[](const std::string& path, bool saved) {
|
|
||||||
(void)path;
|
|
||||||
(void)saved;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pick_file_save({ "mp4" }, [this](std::string path) {
|
|
||||||
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
|
||||||
*this,
|
|
||||||
pp::app::DocumentVideoExportKind::animation_mp4,
|
|
||||||
path,
|
|
||||||
true);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(
|
|
||||||
"%s: %s",
|
|
||||||
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::animation_mp4),
|
|
||||||
status.message);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_whatsnew(bool force_show)
|
void App::dialog_whatsnew(bool force_show)
|
||||||
|
|||||||
316
src/app_dialogs_export.cpp
Normal file
316
src/app_dialogs_export.cpp
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "app_core/app_dialog.h"
|
||||||
|
#include "app_core/document_export.h"
|
||||||
|
#include "legacy_brush_package_export_services.h"
|
||||||
|
#include "legacy_document_export_services.h"
|
||||||
|
#include "legacy_ui_overlay_services.h"
|
||||||
|
#include "node_dialog_export_ppbr.h"
|
||||||
|
|
||||||
|
#include <codec_api.h>
|
||||||
|
#define MP4V2_NO_STDINT_DEFS
|
||||||
|
#include <mp4v2/mp4v2.h>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] bool can_start_document_export(App& app, bool requires_license)
|
||||||
|
{
|
||||||
|
const auto decision = pp::app::plan_document_export_start(
|
||||||
|
requires_license,
|
||||||
|
!requires_license || app.check_license(),
|
||||||
|
app.canvas != nullptr);
|
||||||
|
|
||||||
|
switch (decision) {
|
||||||
|
case pp::app::DocumentExportStartDecision::start_now:
|
||||||
|
return true;
|
||||||
|
case pp::app::DocumentExportStartDecision::show_license_disabled:
|
||||||
|
{
|
||||||
|
const auto plan = pp::app::plan_document_export_license_disabled_dialog();
|
||||||
|
app.message_box(plan.title, plan.message, plan.show_cancel);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case pp::app::DocumentExportStartDecision::unavailable_no_canvas:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_document_export_collection(
|
||||||
|
App& app,
|
||||||
|
pp::app::DocumentExportCollectionKind kind)
|
||||||
|
{
|
||||||
|
const auto plan = pp::app::plan_document_export_collection_target(
|
||||||
|
kind,
|
||||||
|
app.uses_work_directory_document_export_collections());
|
||||||
|
const auto success_kind = pp::app::document_export_collection_success_kind(kind);
|
||||||
|
|
||||||
|
if (plan.destination == pp::app::DocumentExportCollectionDestination::work_directory_collection) {
|
||||||
|
const auto target = pp::app::make_document_export_collection_target(
|
||||||
|
app.work_path,
|
||||||
|
app.doc_name,
|
||||||
|
plan.suffix);
|
||||||
|
if (!target) {
|
||||||
|
const auto dialog = pp::app::plan_document_export_failure_dialog(success_kind, target.status().message);
|
||||||
|
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto status = pp::panopainter::execute_legacy_document_export_collection(
|
||||||
|
app,
|
||||||
|
plan.kind,
|
||||||
|
target.value());
|
||||||
|
if (!status.ok())
|
||||||
|
LOG(
|
||||||
|
"%s: %s",
|
||||||
|
pp::app::document_export_execution_log_message(
|
||||||
|
kind == pp::app::DocumentExportCollectionKind::layers
|
||||||
|
? pp::app::DocumentExportExecutionKind::layers_collection
|
||||||
|
: pp::app::DocumentExportExecutionKind::animation_frames_collection),
|
||||||
|
status.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.pick_dir([
|
||||||
|
&app,
|
||||||
|
kind = plan.kind,
|
||||||
|
success_kind
|
||||||
|
](std::string path) {
|
||||||
|
const auto target = pp::app::make_document_export_stem_target(path, app.doc_name);
|
||||||
|
if (!target) {
|
||||||
|
const auto dialog = pp::app::plan_document_export_failure_dialog(success_kind, target.status().message);
|
||||||
|
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto status = pp::panopainter::execute_legacy_document_export_stem(
|
||||||
|
app,
|
||||||
|
kind,
|
||||||
|
target.value());
|
||||||
|
if (!status.ok())
|
||||||
|
LOG(
|
||||||
|
"%s: %s",
|
||||||
|
pp::app::document_export_execution_log_message(
|
||||||
|
kind == pp::app::DocumentExportCollectionKind::layers
|
||||||
|
? pp::app::DocumentExportExecutionKind::layers_stem
|
||||||
|
: pp::app::DocumentExportExecutionKind::animation_frames_stem),
|
||||||
|
status.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_document_video_export(
|
||||||
|
App& app,
|
||||||
|
pp::app::DocumentVideoExportKind kind,
|
||||||
|
pp::app::DocumentExportSuccessKind success_kind,
|
||||||
|
const char* suffix,
|
||||||
|
pp::app::DocumentExportExecutionKind execution_kind)
|
||||||
|
{
|
||||||
|
if (!can_start_document_export(app, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (app.uses_prepared_file_writes())
|
||||||
|
{
|
||||||
|
const auto target = pp::app::make_document_export_suggested_name(app.doc_name, suffix);
|
||||||
|
if (!target) {
|
||||||
|
const auto dialog = pp::app::plan_document_export_failure_dialog(
|
||||||
|
success_kind,
|
||||||
|
target.status().message);
|
||||||
|
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.pick_file_save("mp4", target.value().name,
|
||||||
|
[&app, kind, execution_kind](std::string path) {
|
||||||
|
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
||||||
|
app,
|
||||||
|
kind,
|
||||||
|
path,
|
||||||
|
false);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG(
|
||||||
|
"%s: %s",
|
||||||
|
pp::app::document_export_execution_log_message(execution_kind),
|
||||||
|
status.message);
|
||||||
|
},
|
||||||
|
[](const std::string& path, bool saved) {
|
||||||
|
(void)path;
|
||||||
|
(void)saved;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.pick_file_save({ "mp4" }, [&app, kind, execution_kind](std::string path) {
|
||||||
|
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
||||||
|
app,
|
||||||
|
kind,
|
||||||
|
path,
|
||||||
|
true);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG(
|
||||||
|
"%s: %s",
|
||||||
|
pp::app::document_export_execution_log_message(execution_kind),
|
||||||
|
status.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void open_document_export_dialog(App& app, std::string ext)
|
||||||
|
{
|
||||||
|
if (!can_start_document_export(app, true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: use picker
|
||||||
|
const auto target = pp::app::make_document_export_file_target(app.work_path, app.doc_name, ext);
|
||||||
|
if (!target) {
|
||||||
|
const auto dialog = pp::app::plan_document_export_failure_dialog(
|
||||||
|
pp::app::DocumentExportSuccessKind::equirectangular,
|
||||||
|
target.status().message);
|
||||||
|
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto status = pp::panopainter::execute_legacy_document_export_file(app, target.value());
|
||||||
|
if (!status.ok())
|
||||||
|
LOG(
|
||||||
|
"%s: %s",
|
||||||
|
pp::app::document_export_execution_log_message(
|
||||||
|
pp::app::DocumentExportExecutionKind::equirectangular_file),
|
||||||
|
status.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_document_export_layers_dialog(App& app)
|
||||||
|
{
|
||||||
|
if (!can_start_document_export(app, true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
start_document_export_collection(
|
||||||
|
app,
|
||||||
|
pp::app::DocumentExportCollectionKind::layers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_document_export_anim_frames_dialog(App& app)
|
||||||
|
{
|
||||||
|
if (!can_start_document_export(app, true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
start_document_export_collection(
|
||||||
|
app,
|
||||||
|
pp::app::DocumentExportCollectionKind::animation_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_document_export_depth_dialog(App& app)
|
||||||
|
{
|
||||||
|
if (!can_start_document_export(app, true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto status = pp::panopainter::execute_legacy_document_export_depth(app, app.doc_name);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG(
|
||||||
|
"%s: %s",
|
||||||
|
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::depth),
|
||||||
|
status.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_document_export_cube_faces_dialog(App& app)
|
||||||
|
{
|
||||||
|
if (!can_start_document_export(app, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto status = pp::panopainter::execute_legacy_document_export_cube_faces(app, app.doc_name);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG(
|
||||||
|
"%s: %s",
|
||||||
|
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::cube_faces),
|
||||||
|
status.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_document_timelapse_export_dialog(App& app)
|
||||||
|
{
|
||||||
|
start_document_video_export(
|
||||||
|
app,
|
||||||
|
pp::app::DocumentVideoExportKind::timelapse,
|
||||||
|
pp::app::DocumentExportSuccessKind::timelapse,
|
||||||
|
"-timelapse",
|
||||||
|
pp::app::DocumentExportExecutionKind::timelapse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_document_export_mp4_dialog(App& app)
|
||||||
|
{
|
||||||
|
start_document_video_export(
|
||||||
|
app,
|
||||||
|
pp::app::DocumentVideoExportKind::animation_mp4,
|
||||||
|
pp::app::DocumentExportSuccessKind::animation_mp4,
|
||||||
|
"-animation",
|
||||||
|
pp::app::DocumentExportExecutionKind::animation_mp4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_ppbr_export_dialog(App& app)
|
||||||
|
{
|
||||||
|
auto* overlay_anchor = app.layout[app.main_id];
|
||||||
|
if (!overlay_anchor) {
|
||||||
|
LOG("PPBR export dialog open failed: main layout anchor is missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogExportPPBR>(app);
|
||||||
|
|
||||||
|
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
||||||
|
if (!overlay) {
|
||||||
|
LOG("PPBR export dialog open failed: %s", overlay.status().message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto overlay_handle = overlay.value();
|
||||||
|
|
||||||
|
const auto close_dialog = [overlay_anchor, overlay_handle]() {
|
||||||
|
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
|
||||||
|
(void)close_status;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog->btn_ok->on_click = [&app, dialog] (Node*) {
|
||||||
|
const auto request = pp::panopainter::make_legacy_brush_package_export_request(*dialog);
|
||||||
|
|
||||||
|
if (app.uses_prepared_file_writes())
|
||||||
|
{
|
||||||
|
app.pick_file_save("ppbr", "exported-brushes",
|
||||||
|
[&app, dialog, request] (std::string path) {
|
||||||
|
const auto status = pp::panopainter::execute_legacy_brush_package_export(
|
||||||
|
app,
|
||||||
|
*dialog,
|
||||||
|
request,
|
||||||
|
path,
|
||||||
|
pp::panopainter::LegacyBrushPackageExportMode::inline_export_only);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG("PPBR export failed: %s", status.message);
|
||||||
|
},
|
||||||
|
[dialog] (const std::string& path, bool saved) {
|
||||||
|
(void)path;
|
||||||
|
pp::panopainter::complete_legacy_brush_package_export(*dialog, saved);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.pick_file_save({ "ppbr" }, [&app, dialog, request] (std::string path) {
|
||||||
|
const auto status = pp::panopainter::execute_legacy_brush_package_export(
|
||||||
|
app,
|
||||||
|
*dialog,
|
||||||
|
request,
|
||||||
|
path,
|
||||||
|
pp::panopainter::LegacyBrushPackageExportMode::desktop_async_close_and_message);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG("PPBR export failed: %s", status.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
dialog->btn_cancel->on_click = [close_dialog](Node*)
|
||||||
|
{
|
||||||
|
close_dialog();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pp::panopainter
|
||||||
164
src/legacy_app_frame_services.cpp
Normal file
164
src/legacy_app_frame_services.cpp
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include "app.h"
|
||||||
|
#include "app_core/app_frame.h"
|
||||||
|
#include "app_core/canvas_tool_ui.h"
|
||||||
|
#include "legacy_ui_gl_dispatch.h"
|
||||||
|
#include "renderer_gl/opengl_capabilities.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
pp::app::CanvasToolMode canvas_tool_mode_from_canvas_mode(kCanvasMode mode) noexcept
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case kCanvasMode::Draw:
|
||||||
|
return pp::app::CanvasToolMode::draw;
|
||||||
|
case kCanvasMode::Erase:
|
||||||
|
return pp::app::CanvasToolMode::erase;
|
||||||
|
case kCanvasMode::Line:
|
||||||
|
return pp::app::CanvasToolMode::line;
|
||||||
|
case kCanvasMode::Camera:
|
||||||
|
return pp::app::CanvasToolMode::camera;
|
||||||
|
case kCanvasMode::Grid:
|
||||||
|
return pp::app::CanvasToolMode::grid;
|
||||||
|
case kCanvasMode::Copy:
|
||||||
|
return pp::app::CanvasToolMode::copy;
|
||||||
|
case kCanvasMode::Cut:
|
||||||
|
return pp::app::CanvasToolMode::cut;
|
||||||
|
case kCanvasMode::Fill:
|
||||||
|
return pp::app::CanvasToolMode::fill;
|
||||||
|
case kCanvasMode::MaskFree:
|
||||||
|
return pp::app::CanvasToolMode::mask_free;
|
||||||
|
case kCanvasMode::MaskLine:
|
||||||
|
return pp::app::CanvasToolMode::mask_line;
|
||||||
|
case kCanvasMode::FloodFill:
|
||||||
|
return pp::app::CanvasToolMode::flood_fill;
|
||||||
|
default:
|
||||||
|
return pp::app::CanvasToolMode::draw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_legacy_app_scissor(pp::renderer::gl::OpenGlScissorRect scissor)
|
||||||
|
{
|
||||||
|
const auto status = pp::renderer::gl::apply_opengl_scissor_rect(
|
||||||
|
scissor,
|
||||||
|
pp::renderer::gl::OpenGlScissorDispatch {
|
||||||
|
.enable = pp::legacy::ui_gl::enable_opengl_state,
|
||||||
|
.disable = pp::legacy::ui_gl::disable_opengl_state,
|
||||||
|
.scissor = pp::legacy::ui_gl::set_opengl_scissor,
|
||||||
|
});
|
||||||
|
if (!status.ok())
|
||||||
|
LOG("OpenGL scissor failed: %s", status.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void watch_layout_children(LayoutManager* layout, uint16_t main_id, int start_index, const std::function<bool(Node*)>& observer)
|
||||||
|
{
|
||||||
|
if (!layout)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (auto* root = layout->get(main_id)) {
|
||||||
|
for (int i = start_index; i < static_cast<int>(root->m_children.size()); ++i)
|
||||||
|
root->m_children[i]->watch(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pp::panopainter
|
||||||
|
{
|
||||||
|
|
||||||
|
bool update_legacy_app_ui_observer(App& app, Node* n)
|
||||||
|
{
|
||||||
|
std::vector<pp::app::AppUiObserverParentClip> parent_clips;
|
||||||
|
if (n) {
|
||||||
|
for (Node* p = n->m_parent; p; p = p->m_parent) {
|
||||||
|
parent_clips.push_back(pp::app::AppUiObserverParentClip {
|
||||||
|
.clip = pp::app::AppUiObserverRect {
|
||||||
|
.x = p->m_clip_uncut.x,
|
||||||
|
.y = p->m_clip_uncut.y,
|
||||||
|
.width = p->m_clip_uncut.z,
|
||||||
|
.height = p->m_clip_uncut.w,
|
||||||
|
},
|
||||||
|
.padding_top = YGNodeLayoutGetPadding(p->y_node, YGEdgeTop),
|
||||||
|
.padding_right = YGNodeLayoutGetPadding(p->y_node, YGEdgeRight),
|
||||||
|
.padding_bottom = YGNodeLayoutGetPadding(p->y_node, YGEdgeBottom),
|
||||||
|
.padding_left = YGNodeLayoutGetPadding(p->y_node, YGEdgeLeft),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto plan = pp::app::plan_app_ui_observer(
|
||||||
|
n != nullptr,
|
||||||
|
n && n->m_display,
|
||||||
|
n && n->m_on_screen,
|
||||||
|
n
|
||||||
|
? pp::app::AppUiObserverRect {
|
||||||
|
.x = n->m_clip_uncut.x,
|
||||||
|
.y = n->m_clip_uncut.y,
|
||||||
|
.width = n->m_clip_uncut.z,
|
||||||
|
.height = n->m_clip_uncut.w,
|
||||||
|
}
|
||||||
|
: pp::app::AppUiObserverRect {},
|
||||||
|
parent_clips,
|
||||||
|
app.height,
|
||||||
|
app.zoom,
|
||||||
|
app.off_x,
|
||||||
|
app.off_y);
|
||||||
|
if (!plan) {
|
||||||
|
LOG("UI observer plan failed: %s", plan.status().message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (plan.value().notify_leave_screen)
|
||||||
|
n->handle_on_screen(true, false);
|
||||||
|
if (plan.value().notify_enter_screen)
|
||||||
|
n->handle_on_screen(false, true);
|
||||||
|
n->m_on_screen = plan.value().next_on_screen;
|
||||||
|
|
||||||
|
if (!plan.value().draw_node)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
apply_legacy_app_scissor(pp::renderer::gl::OpenGlScissorRect {
|
||||||
|
.enabled = 1U,
|
||||||
|
.x = plan.value().scissor_x,
|
||||||
|
.y = plan.value().scissor_y,
|
||||||
|
.width = plan.value().scissor_width,
|
||||||
|
.height = plan.value().scissor_height,
|
||||||
|
});
|
||||||
|
n->draw();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void watch_legacy_app_ui_children(App& app, const std::function<bool(Node*)>& observer, bool skip_first_main_child)
|
||||||
|
{
|
||||||
|
watch_layout_children(&app.layout, app.main_id, skip_first_main_child ? 1 : 0, observer);
|
||||||
|
watch_layout_children(&app.layout_designer, app.main_id, 0, observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_legacy_canvas_toolbar(App& app)
|
||||||
|
{
|
||||||
|
const auto mode = Canvas::I->m_current_mode;
|
||||||
|
auto* pm = app.canvas->m_canvas->get_mode<CanvasModePen>();
|
||||||
|
const auto toolbar = pp::app::plan_canvas_tool_button_state(
|
||||||
|
canvas_tool_mode_from_canvas_mode(mode),
|
||||||
|
pm && pm->m_picking,
|
||||||
|
app.canvas->m_canvas->m_touch_lock);
|
||||||
|
app.layout[app.main_id]->find<NodeButtonCustom>("btn-pick")->set_active(toolbar.pick_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButtonCustom>("btn-touchlock")->set_active(toolbar.touch_lock_active);
|
||||||
|
|
||||||
|
app.layout[app.main_id]->find<NodeButtonCustom>("btn-pen")->set_active(toolbar.pen_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButtonCustom>("btn-erase")->set_active(toolbar.erase_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButton>("btn-cam")->set_active(toolbar.camera_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButtonCustom>("btn-line")->set_active(toolbar.line_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButton>("btn-grid")->set_active(toolbar.grid_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButton>("btn-copy")->set_active(toolbar.copy_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButton>("btn-cut")->set_active(toolbar.cut_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButtonCustom>("btn-mask-free")->set_active(toolbar.mask_free_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButtonCustom>("btn-mask-line")->set_active(toolbar.mask_line_active);
|
||||||
|
app.layout[app.main_id]->find<NodeButtonCustom>("btn-bucket")->set_active(toolbar.flood_fill_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -811,6 +811,82 @@ inline void execute_legacy_canvas_draw_merge_layer_path(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename BindBlenderFramebuffer,
|
||||||
|
typename ClearBlenderFramebuffer,
|
||||||
|
typename UnbindBlenderFramebuffer,
|
||||||
|
typename BindSampler,
|
||||||
|
typename BindNearestSampler,
|
||||||
|
typename BindStencilSampler,
|
||||||
|
typename SetActiveTextureUnit,
|
||||||
|
typename BindTemporaryTexture,
|
||||||
|
typename UnbindTemporaryTexture,
|
||||||
|
typename BindSmaskTexture,
|
||||||
|
typename UnbindSmaskTexture,
|
||||||
|
typename BindTemporaryDualTexture,
|
||||||
|
typename UnbindTemporaryDualTexture,
|
||||||
|
typename BindPatternTexture,
|
||||||
|
typename DrawFace,
|
||||||
|
typename BindBlenderTexture,
|
||||||
|
typename UnbindBlenderTexture,
|
||||||
|
typename BindDestinationTexture,
|
||||||
|
typename UnbindDestinationTexture,
|
||||||
|
typename CopyDestinationFramebuffer,
|
||||||
|
typename DrawDebugOutline,
|
||||||
|
typename DrawFrame>
|
||||||
|
[[nodiscard]] inline LegacyCanvasDrawMergeLayerPathExecution make_legacy_canvas_draw_merge_layer_path_gl_execution(
|
||||||
|
const LegacyCanvasDrawMergeLayerPathGlUniforms& uniforms,
|
||||||
|
BindBlenderFramebuffer&& bind_blender_framebuffer,
|
||||||
|
ClearBlenderFramebuffer&& clear_blender_framebuffer,
|
||||||
|
UnbindBlenderFramebuffer&& unbind_blender_framebuffer,
|
||||||
|
BindSampler&& bind_sampler,
|
||||||
|
BindNearestSampler&& bind_nearest_sampler,
|
||||||
|
BindStencilSampler&& bind_stencil_sampler,
|
||||||
|
SetActiveTextureUnit&& set_active_texture_unit,
|
||||||
|
BindTemporaryTexture&& bind_temporary_texture,
|
||||||
|
UnbindTemporaryTexture&& unbind_temporary_texture,
|
||||||
|
BindSmaskTexture&& bind_smask_texture,
|
||||||
|
UnbindSmaskTexture&& unbind_smask_texture,
|
||||||
|
BindTemporaryDualTexture&& bind_temporary_dual_texture,
|
||||||
|
UnbindTemporaryDualTexture&& unbind_temporary_dual_texture,
|
||||||
|
BindPatternTexture&& bind_pattern_texture,
|
||||||
|
DrawFace&& draw_face,
|
||||||
|
BindBlenderTexture&& bind_blender_texture,
|
||||||
|
UnbindBlenderTexture&& unbind_blender_texture,
|
||||||
|
BindDestinationTexture&& bind_destination_texture,
|
||||||
|
UnbindDestinationTexture&& unbind_destination_texture,
|
||||||
|
CopyDestinationFramebuffer&& copy_destination_framebuffer,
|
||||||
|
DrawDebugOutline&& draw_debug_outline,
|
||||||
|
DrawFrame&& draw_frame)
|
||||||
|
{
|
||||||
|
return make_legacy_canvas_draw_merge_layer_path_gl_execution(
|
||||||
|
uniforms,
|
||||||
|
LegacyCanvasDrawMergeLayerPathGlExecution {
|
||||||
|
.bind_blender_framebuffer = std::forward<BindBlenderFramebuffer>(bind_blender_framebuffer),
|
||||||
|
.clear_blender_framebuffer = std::forward<ClearBlenderFramebuffer>(clear_blender_framebuffer),
|
||||||
|
.unbind_blender_framebuffer = std::forward<UnbindBlenderFramebuffer>(unbind_blender_framebuffer),
|
||||||
|
.bind_sampler = std::forward<BindSampler>(bind_sampler),
|
||||||
|
.bind_nearest_sampler = std::forward<BindNearestSampler>(bind_nearest_sampler),
|
||||||
|
.bind_stencil_sampler = std::forward<BindStencilSampler>(bind_stencil_sampler),
|
||||||
|
.set_active_texture_unit = std::forward<SetActiveTextureUnit>(set_active_texture_unit),
|
||||||
|
.bind_temporary_texture = std::forward<BindTemporaryTexture>(bind_temporary_texture),
|
||||||
|
.unbind_temporary_texture = std::forward<UnbindTemporaryTexture>(unbind_temporary_texture),
|
||||||
|
.bind_smask_texture = std::forward<BindSmaskTexture>(bind_smask_texture),
|
||||||
|
.unbind_smask_texture = std::forward<UnbindSmaskTexture>(unbind_smask_texture),
|
||||||
|
.bind_temporary_dual_texture = std::forward<BindTemporaryDualTexture>(bind_temporary_dual_texture),
|
||||||
|
.unbind_temporary_dual_texture = std::forward<UnbindTemporaryDualTexture>(unbind_temporary_dual_texture),
|
||||||
|
.bind_pattern_texture = std::forward<BindPatternTexture>(bind_pattern_texture),
|
||||||
|
.draw_face = std::forward<DrawFace>(draw_face),
|
||||||
|
.bind_blender_texture = std::forward<BindBlenderTexture>(bind_blender_texture),
|
||||||
|
.unbind_blender_texture = std::forward<UnbindBlenderTexture>(unbind_blender_texture),
|
||||||
|
.bind_destination_texture = std::forward<BindDestinationTexture>(bind_destination_texture),
|
||||||
|
.unbind_destination_texture = std::forward<UnbindDestinationTexture>(unbind_destination_texture),
|
||||||
|
.copy_destination_framebuffer = std::forward<CopyDestinationFramebuffer>(copy_destination_framebuffer),
|
||||||
|
.draw_debug_outline = std::forward<DrawDebugOutline>(draw_debug_outline),
|
||||||
|
.draw_frame = std::forward<DrawFrame>(draw_frame),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template <typename PlanOnionRange, typename ShouldDrawPlane, typename VisitLayerPlane, typename LogOnionRangeFailure>
|
template <typename PlanOnionRange, typename ShouldDrawPlane, typename VisitLayerPlane, typename LogOnionRangeFailure>
|
||||||
inline void execute_legacy_canvas_draw_layer_traversal(
|
inline void execute_legacy_canvas_draw_layer_traversal(
|
||||||
size_t layer_count,
|
size_t layer_count,
|
||||||
|
|||||||
@@ -231,6 +231,170 @@ void run_canvas_tool_mode(pp::app::CanvasToolMode mode)
|
|||||||
LOG("Canvas input tool action failed: %s", status.message);
|
LOG("Canvas input tool action failed: %s", status.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* NodeCanvas::clone_instantiate() const
|
Node* NodeCanvas::clone_instantiate() const
|
||||||
@@ -467,161 +631,14 @@ void NodeCanvas::draw()
|
|||||||
glm::eulerAngleYXZ(yaw, pitch, roll) *
|
glm::eulerAngleYXZ(yaw, pitch, roll) *
|
||||||
m_canvas->m_plane_transform[plane_index] *
|
m_canvas->m_plane_transform[plane_index] *
|
||||||
glm::translate(glm::vec3(0, 0, -1));
|
glm::translate(glm::vec3(0, 0, -1));
|
||||||
|
const auto layer_path_execution = make_node_canvas_layer_path_execution(
|
||||||
const auto draw_layer_frame = pp::panopainter::make_legacy_canvas_draw_merge_layer_frame_draw(
|
*this,
|
||||||
m_canvas->m_layers[layer_index].get(),
|
layer_index,
|
||||||
&m_face_plane,
|
|
||||||
set_active_texture_unit,
|
|
||||||
plane_index,
|
plane_index,
|
||||||
m_canvas->m_layers[layer_index]->m_opacity);
|
plane_mvp_z,
|
||||||
|
b.get(),
|
||||||
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
copy_blend_destination,
|
||||||
if (b->m_pattern_flipx)
|
m_canvas->m_cam_fov < 20.f);
|
||||||
patt_scale.x *= -1.f;
|
|
||||||
if (b->m_pattern_flipy)
|
|
||||||
patt_scale.y *= -1.f;
|
|
||||||
|
|
||||||
const auto layer_path_execution = pp::panopainter::make_legacy_canvas_draw_merge_layer_path_gl_execution(
|
|
||||||
{
|
|
||||||
.temporary_erase = {
|
|
||||||
.mvp = plane_mvp_z,
|
|
||||||
.texture_slot = 0,
|
|
||||||
.stroke_texture_slot = 1,
|
|
||||||
.mask_texture_slot = 2,
|
|
||||||
.mask_enabled = m_canvas->m_smask_active,
|
|
||||||
},
|
|
||||||
.temporary_paint = {
|
|
||||||
.resolution = Canvas::I->m_size,
|
|
||||||
.pattern = {
|
|
||||||
.scale = patt_scale,
|
|
||||||
.invert = static_cast<float>(b->m_pattern_invert),
|
|
||||||
.brightness = b->m_pattern_brightness,
|
|
||||||
.contrast = b->m_pattern_contrast,
|
|
||||||
.depth = b->m_pattern_depth,
|
|
||||||
.blend_mode = b->m_pattern_blend_mode,
|
|
||||||
.offset = Canvas::I->m_pattern_offset,
|
|
||||||
},
|
|
||||||
.mvp = plane_mvp_z,
|
|
||||||
.layer_alpha = 1.0f,
|
|
||||||
.alpha_lock = m_canvas->m_layers[layer_index]->m_alpha_locked,
|
|
||||||
.mask_enabled = m_canvas->m_smask_active,
|
|
||||||
.use_fragcoord = false,
|
|
||||||
.blend_mode = b->m_blend_mode,
|
|
||||||
.use_dual = b->m_dual_enabled,
|
|
||||||
.dual_blend_mode = b->m_dual_blend_mode,
|
|
||||||
.dual_alpha = b->m_dual_opacity,
|
|
||||||
.use_pattern = b->m_pattern_enabled && !b->m_pattern_eachsample,
|
|
||||||
},
|
|
||||||
.layer_texture = {
|
|
||||||
.mvp = plane_mvp_z,
|
|
||||||
.texture_slot = 0,
|
|
||||||
.alpha = 1.f,
|
|
||||||
.highlight = m_canvas->m_layers[layer_index]->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 = m_canvas->m_layers[layer_index]->m_blend_mode,
|
|
||||||
.alpha = 1.f,
|
|
||||||
},
|
|
||||||
.copy_destination = copy_blend_destination,
|
|
||||||
},
|
|
||||||
.use_nearest_sampler = m_canvas->m_cam_fov < 20.f,
|
|
||||||
.use_dual_texture = b->m_dual_enabled,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.bind_blender_framebuffer = [&] {
|
|
||||||
m_blender_rtt.bindFramebuffer();
|
|
||||||
},
|
|
||||||
.clear_blender_framebuffer = [&] {
|
|
||||||
m_blender_rtt.clear();
|
|
||||||
},
|
|
||||||
.unbind_blender_framebuffer = [&] {
|
|
||||||
m_blender_rtt.unbindFramebuffer();
|
|
||||||
},
|
|
||||||
.bind_sampler = [&](int unit) {
|
|
||||||
m_sampler.bind(unit);
|
|
||||||
},
|
|
||||||
.bind_nearest_sampler = [&](int unit) {
|
|
||||||
m_sampler_nearest.bind(unit);
|
|
||||||
},
|
|
||||||
.bind_stencil_sampler = [&](int unit) {
|
|
||||||
m_sampler_stencil.bind(unit);
|
|
||||||
},
|
|
||||||
.set_active_texture_unit = [&](int unit) {
|
|
||||||
set_active_texture_unit(unit);
|
|
||||||
},
|
|
||||||
.bind_temporary_texture = [&] {
|
|
||||||
m_canvas->m_tmp[plane_index].bindTexture();
|
|
||||||
},
|
|
||||||
.unbind_temporary_texture = [&] {
|
|
||||||
m_canvas->m_tmp[plane_index].unbindTexture();
|
|
||||||
},
|
|
||||||
.bind_smask_texture = [&] {
|
|
||||||
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
|
||||||
},
|
|
||||||
.unbind_smask_texture = [&] {
|
|
||||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
|
||||||
},
|
|
||||||
.bind_temporary_dual_texture = [&] {
|
|
||||||
m_canvas->m_tmp_dual[plane_index].bindTexture();
|
|
||||||
},
|
|
||||||
.unbind_temporary_dual_texture = [&] {
|
|
||||||
m_canvas->m_tmp_dual[plane_index].unbindTexture();
|
|
||||||
},
|
|
||||||
.bind_pattern_texture = [&] {
|
|
||||||
b->m_pattern_texture ?
|
|
||||||
b->m_pattern_texture->bind() :
|
|
||||||
unbind_texture_2d();
|
|
||||||
},
|
|
||||||
.draw_face = [&] {
|
|
||||||
m_face_plane.draw_fill();
|
|
||||||
},
|
|
||||||
.bind_blender_texture = [&] {
|
|
||||||
m_blender_rtt.bindTexture();
|
|
||||||
},
|
|
||||||
.unbind_blender_texture = [&] {
|
|
||||||
m_blender_rtt.unbindTexture();
|
|
||||||
},
|
|
||||||
.bind_destination_texture = [&] {
|
|
||||||
m_blender_bg.bind();
|
|
||||||
},
|
|
||||||
.unbind_destination_texture = [&] {
|
|
||||||
m_blender_bg.unbind();
|
|
||||||
},
|
|
||||||
.copy_destination_framebuffer = [&] {
|
|
||||||
copy_framebuffer_to_texture_2d(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
m_blender_bg.size().x,
|
|
||||||
m_blender_bg.size().y);
|
|
||||||
},
|
|
||||||
.draw_debug_outline =
|
|
||||||
#ifdef _DEBUG
|
|
||||||
[&] {
|
|
||||||
auto bb = m_canvas->m_layers[layer_index]->box(plane_index) / (float)m_canvas->m_layers[layer_index]->w;
|
|
||||||
glm::vec2 bbmin = xy(bb);
|
|
||||||
glm::vec2 bbsz = zw(bb) - xy(bb);
|
|
||||||
pp::panopainter::configure_legacy_ui_color_shader(
|
|
||||||
plane_mvp_z
|
|
||||||
* glm::translate(glm::vec3(bbmin * 2.f, 0))
|
|
||||||
* glm::translate(glm::vec3(-1, -1, 0))
|
|
||||||
* glm::scale(glm::vec3(bbsz, 1))
|
|
||||||
* glm::translate(glm::vec3(1, 1, 0)),
|
|
||||||
{ 1, 0, 0, 1 });
|
|
||||||
m_face_plane.draw_stroke();
|
|
||||||
},
|
|
||||||
#else
|
|
||||||
[] {
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
.draw_frame = draw_layer_frame,
|
|
||||||
});
|
|
||||||
|
|
||||||
pp::panopainter::execute_legacy_canvas_draw_merge_layer_path(
|
pp::panopainter::execute_legacy_canvas_draw_merge_layer_path(
|
||||||
m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||||
|
|||||||
Reference in New Issue
Block a user