Extract dialog, VR, and canvas draw 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_info_openers.cpp
|
||||||
src/app_events.cpp
|
src/app_events.cpp
|
||||||
src/app_layout.cpp
|
src/app_layout.cpp
|
||||||
src/app_layout_about_layer_menu.cpp
|
src/app_layout_about_layer_menu.cpp
|
||||||
|
|||||||
@@ -83,12 +83,12 @@ Current hotspot files:
|
|||||||
- `src/app_layout.cpp`: 1249 lines
|
- `src/app_layout.cpp`: 1249 lines
|
||||||
- `src/canvas_modes.cpp`: 1798 lines
|
- `src/canvas_modes.cpp`: 1798 lines
|
||||||
- `src/node.cpp`: 1551 lines
|
- `src/node.cpp`: 1551 lines
|
||||||
- `src/main.cpp`: 1218 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`: 968 lines
|
- `src/node_canvas.cpp`: 953 lines
|
||||||
- `src/app.cpp`: 950 lines
|
- `src/app.cpp`: 950 lines
|
||||||
- `src/app_dialogs.cpp`: 908 lines
|
- `src/app_dialogs.cpp`: 789 lines
|
||||||
|
|
||||||
Current architecture mismatches that must be treated as real blockers:
|
Current architecture mismatches that must be treated as real blockers:
|
||||||
|
|
||||||
@@ -124,8 +124,11 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
per-layer render-path orchestration now also route through retained
|
per-layer render-path orchestration now also route through retained
|
||||||
draw-merge helpers even though the broader node draw loop is still inline,
|
draw-merge helpers even though the broader node draw loop is still inline,
|
||||||
with the non-`draw_merged` outer layer/plane traversal now also routing
|
with the non-`draw_merged` outer layer/plane traversal now also routing
|
||||||
through `execute_legacy_canvas_draw_layer_traversal(...)` while the heavier
|
through `execute_legacy_canvas_draw_layer_traversal(...)`, while the heavier
|
||||||
per-layer GL setup and draw lambdas still live in `src/node_canvas.cpp`.
|
per-layer GL setup now also routes through
|
||||||
|
`make_legacy_canvas_draw_merge_layer_path_gl_execution(...)` even though the
|
||||||
|
remaining draw lambdas and broader node draw loop still live in
|
||||||
|
`src/node_canvas.cpp`.
|
||||||
- `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files
|
- `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files
|
||||||
rather than thin composition/binding surfaces, even though tools-menu binding
|
rather than thin composition/binding surfaces, even though tools-menu binding
|
||||||
plus nested panels/options submenu wiring now live in
|
plus nested panels/options submenu wiring now live in
|
||||||
@@ -134,7 +137,9 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
live in `src/app_layout_file_menu.cpp` and `App::init_menu_file()` is now a
|
live in `src/app_layout_file_menu.cpp` and `App::init_menu_file()` is now a
|
||||||
thin call-through, while about-menu and layer-menu wiring now also live in
|
thin call-through, while about-menu and layer-menu wiring now also live in
|
||||||
`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.
|
`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`
|
||||||
|
and the corresponding `App::dialog_*` entrypoints are thinner.
|
||||||
- `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
|
||||||
@@ -161,6 +166,9 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
`src/main.cpp`, while Win32 pointer API loading, stylus/ink timer decay,
|
`src/main.cpp`, while Win32 pointer API loading, stylus/ink timer decay,
|
||||||
Wintab packet reset, and `WM_POINTERUPDATE` pen/touch handling now also live
|
Wintab packet reset, and `WM_POINTERUPDATE` pen/touch handling now also live
|
||||||
in `src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`,
|
in `src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`,
|
||||||
|
while the retained Win32 VR/HMD shell now also routes through
|
||||||
|
`src/platform_windows/windows_vr_shell.h` instead of staying inline in
|
||||||
|
`src/main.cpp`,
|
||||||
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
|
||||||
|
|||||||
@@ -195,8 +195,11 @@ Current slice:
|
|||||||
broader draw-loop and renderer-state shell sequencing.
|
broader draw-loop and renderer-state shell sequencing.
|
||||||
- `NodeCanvas` outer non-`draw_merged` layer/plane traversal, onion-range
|
- `NodeCanvas` outer non-`draw_merged` layer/plane traversal, onion-range
|
||||||
failure handling, and visit payload setup now also route through
|
failure handling, and visit payload setup now also route through
|
||||||
`execute_legacy_canvas_draw_layer_traversal(...)`, but the node still owns
|
`execute_legacy_canvas_draw_layer_traversal(...)`.
|
||||||
the heavier per-layer GL setup and draw lambdas.
|
- `NodeCanvas` non-`draw_merged` per-layer temporary erase/paint, layer-texture,
|
||||||
|
and blend setup now also route through
|
||||||
|
`make_legacy_canvas_draw_merge_layer_path_gl_execution(...)`, but the node
|
||||||
|
still owns the remaining draw lambdas and broader renderer-state shell.
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/node_stroke_preview.cpp`
|
- `src/node_stroke_preview.cpp`
|
||||||
@@ -325,6 +328,13 @@ Why now:
|
|||||||
`src/app_dialogs.cpp` still mixes document workflow decisions, export routing,
|
`src/app_dialogs.cpp` still mixes document workflow decisions, export routing,
|
||||||
dialog construction, and overlay ownership.
|
dialog construction, and overlay ownership.
|
||||||
|
|
||||||
|
Current slice:
|
||||||
|
- Informational overlay opener paths for user manual, changelog, about,
|
||||||
|
what's-new, and shortcuts now live in `src/app_dialogs_info_openers.cpp`,
|
||||||
|
and the corresponding `App::dialog_*` entrypoints are now thin call-throughs,
|
||||||
|
but document/export workflow and retained dialog execution are still inline in
|
||||||
|
`src/app_dialogs.cpp`.
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/app_dialogs.cpp`
|
- `src/app_dialogs.cpp`
|
||||||
- `src/legacy_app_dialog_services.*`
|
- `src/legacy_app_dialog_services.*`
|
||||||
@@ -417,6 +427,10 @@ Current slice:
|
|||||||
reset, and `WM_POINTERUPDATE` pen/touch handling now live in
|
reset, and `WM_POINTERUPDATE` pen/touch handling now live in
|
||||||
`src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`,
|
`src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`,
|
||||||
but broader retained Win32 shell state is still open
|
but broader retained Win32 shell state is still open
|
||||||
|
- the retained Win32 VR/HMD shell, including worker start/stop and VR FPS
|
||||||
|
state, now routes through `src/platform_windows/windows_vr_shell.h` instead
|
||||||
|
of staying inline in `src/main.cpp`, but broader retained Win32 shell state
|
||||||
|
is still open
|
||||||
- prepared-file background work now runs through an `AppRuntime`-owned worker
|
- prepared-file background work now runs through an `AppRuntime`-owned worker
|
||||||
queue instead of a retained static worker in `src/app_events.cpp`
|
queue instead of a retained static worker in `src/app_events.cpp`
|
||||||
- canvas async import/export/save/open background work now also runs through an
|
- canvas async import/export/save/open background work now also runs through an
|
||||||
|
|||||||
@@ -11,18 +11,12 @@
|
|||||||
#include "legacy_document_export_services.h"
|
#include "legacy_document_export_services.h"
|
||||||
#include "legacy_document_layer_services.h"
|
#include "legacy_document_layer_services.h"
|
||||||
#include "legacy_document_session_services.h"
|
#include "legacy_document_session_services.h"
|
||||||
#include "legacy_preference_storage.h"
|
|
||||||
#include "legacy_ui_overlay_services.h"
|
#include "legacy_ui_overlay_services.h"
|
||||||
#include "node_dialog_open.h"
|
#include "node_dialog_open.h"
|
||||||
#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_about.h"
|
|
||||||
#include "node_changelog.h"
|
|
||||||
#include "node_usermanual.h"
|
|
||||||
#include "node_dialog_export_ppbr.h"
|
#include "node_dialog_export_ppbr.h"
|
||||||
#include "node_remote_page.h"
|
|
||||||
#include "node_shorcuts.h"
|
|
||||||
|
|
||||||
#include <codec_api.h>
|
#include <codec_api.h>
|
||||||
#define MP4V2_NO_STDINT_DEFS
|
#define MP4V2_NO_STDINT_DEFS
|
||||||
@@ -34,6 +28,14 @@
|
|||||||
#include "oculus_vr.h"
|
#include "oculus_vr.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
void open_usermanual_dialog(App& app);
|
||||||
|
void open_changelog_dialog(App& app);
|
||||||
|
void open_about_dialog(App& app);
|
||||||
|
void open_whatsnew_dialog(App& app, bool force_show);
|
||||||
|
void open_shortcuts_dialog(App& app);
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] bool can_start_document_export(App& app, bool requires_license)
|
[[nodiscard]] bool can_start_document_export(App& app, bool requires_license)
|
||||||
@@ -208,80 +210,17 @@ std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
|
|||||||
|
|
||||||
void App::dialog_usermanual()
|
void App::dialog_usermanual()
|
||||||
{
|
{
|
||||||
auto* overlay_anchor = layout[main_id];
|
pp::panopainter::open_usermanual_dialog(*this);
|
||||||
if (!overlay_anchor) {
|
|
||||||
LOG("User manual dialog open failed: main layout anchor is missing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeUserManual>(*this);
|
|
||||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
|
||||||
if (!overlay) {
|
|
||||||
LOG("User manual 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 = [close_dialog](Node*) {
|
|
||||||
close_dialog();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_changelog()
|
void App::dialog_changelog()
|
||||||
{
|
{
|
||||||
auto* overlay_anchor = layout[main_id];
|
pp::panopainter::open_changelog_dialog(*this);
|
||||||
if (!overlay_anchor) {
|
|
||||||
LOG("Changelog dialog open failed: main layout anchor is missing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeChangelog>(*this);
|
|
||||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
|
||||||
if (!overlay) {
|
|
||||||
LOG("Changelog 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 = [close_dialog](Node*) {
|
|
||||||
close_dialog();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_about()
|
void App::dialog_about()
|
||||||
{
|
{
|
||||||
auto* overlay_anchor = layout[main_id];
|
pp::panopainter::open_about_dialog(*this);
|
||||||
if (!overlay_anchor) {
|
|
||||||
LOG("About dialog open failed: main layout anchor is missing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeAbout>(*this);
|
|
||||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
|
||||||
if (!overlay) {
|
|
||||||
LOG("About 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 = [close_dialog](Node*) {
|
|
||||||
close_dialog();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::continue_document_workflow_after_optional_save(std::function<void()> action)
|
void App::continue_document_workflow_after_optional_save(std::function<void()> action)
|
||||||
@@ -841,68 +780,10 @@ void App::dialog_export_mp4()
|
|||||||
|
|
||||||
void App::dialog_whatsnew(bool force_show)
|
void App::dialog_whatsnew(bool force_show)
|
||||||
{
|
{
|
||||||
auto* overlay_anchor = layout[main_id];
|
pp::panopainter::open_whatsnew_dialog(*this, force_show);
|
||||||
if (!overlay_anchor) {
|
|
||||||
LOG("What's new dialog open failed: main layout anchor is missing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto overlay_handle = std::make_shared<pp::ui::NodeHandle>();
|
|
||||||
const auto open_overlay = [overlay_anchor, overlay_handle](const std::shared_ptr<NodeRemotePage>& page) {
|
|
||||||
if (overlay_handle->valid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, page);
|
|
||||||
if (!overlay) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*overlay_handle = overlay.value();
|
|
||||||
};
|
|
||||||
const auto close_overlay = [overlay_anchor, overlay_handle]() {
|
|
||||||
if (!overlay_handle->valid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, *overlay_handle);
|
|
||||||
(void)close_status;
|
|
||||||
*overlay_handle = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
auto whatsnew = std::make_shared<NodeRemotePage>();
|
|
||||||
whatsnew->set_manager(&layout);
|
|
||||||
whatsnew->init();
|
|
||||||
std::string url = fmt::format("https://panopainter.com/app-content/whatsnew/?version={}", g_version_build);
|
|
||||||
whatsnew->load_url(url, [whatsnew, force_show, open_overlay](bool success) {
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
int last_id = pp::panopainter::legacy_whatsnew_id_or(0);
|
|
||||||
if (force_show || (whatsnew->m_page_id <= g_version_build && whatsnew->m_page_id > last_id))
|
|
||||||
{
|
|
||||||
whatsnew->set_title(fmt::format("What's new in version {}", g_version_number));
|
|
||||||
if (!force_show)
|
|
||||||
open_overlay(whatsnew);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
whatsnew->add_button("Reload", 120, [whatsnew](Node*) {
|
|
||||||
whatsnew->reload();
|
|
||||||
});
|
|
||||||
whatsnew->add_button("Read Later", 120, [whatsnew, close_overlay](Node*) {
|
|
||||||
pp::panopainter::clear_legacy_whatsnew_id();
|
|
||||||
pp::panopainter::save_legacy_preferences();
|
|
||||||
close_overlay();
|
|
||||||
});
|
|
||||||
whatsnew->add_button("Close", 100, [whatsnew, close_overlay](Node*) {
|
|
||||||
pp::panopainter::set_legacy_whatsnew_id(whatsnew->m_page_id);
|
|
||||||
pp::panopainter::save_legacy_preferences();
|
|
||||||
close_overlay();
|
|
||||||
});
|
|
||||||
if (force_show)
|
|
||||||
open_overlay(whatsnew);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_shortcuts()
|
void App::dialog_shortcuts()
|
||||||
{
|
{
|
||||||
(void)pp::panopainter::add_legacy_overlay_node<NodeShortcuts>(*this);
|
pp::panopainter::open_shortcuts_dialog(*this);
|
||||||
}
|
}
|
||||||
|
|||||||
128
src/app_dialogs_info_openers.cpp
Normal file
128
src/app_dialogs_info_openers.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "legacy_preference_storage.h"
|
||||||
|
#include "legacy_ui_overlay_services.h"
|
||||||
|
#include "node_about.h"
|
||||||
|
#include "node_changelog.h"
|
||||||
|
#include "node_remote_page.h"
|
||||||
|
#include "node_shorcuts.h"
|
||||||
|
#include "node_usermanual.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename DialogNode>
|
||||||
|
void open_info_dialog(App& app, const char* log_name)
|
||||||
|
{
|
||||||
|
auto* overlay_anchor = app.layout[app.main_id];
|
||||||
|
if (!overlay_anchor) {
|
||||||
|
LOG("%s dialog open failed: main layout anchor is missing", log_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dialog = make_legacy_overlay_node<DialogNode>(app);
|
||||||
|
const auto overlay = open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
||||||
|
if (!overlay) {
|
||||||
|
LOG("%s dialog open failed: %s", log_name, overlay.status().message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto overlay_handle = overlay.value();
|
||||||
|
|
||||||
|
const auto close_dialog = [overlay_anchor, overlay_handle]() {
|
||||||
|
const auto close_status = close_legacy_overlay_node(*overlay_anchor, overlay_handle);
|
||||||
|
(void)close_status;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog->btn_ok->on_click = [close_dialog](Node*) {
|
||||||
|
close_dialog();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void open_usermanual_dialog(App& app)
|
||||||
|
{
|
||||||
|
open_info_dialog<NodeUserManual>(app, "User manual");
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_changelog_dialog(App& app)
|
||||||
|
{
|
||||||
|
open_info_dialog<NodeChangelog>(app, "Changelog");
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_about_dialog(App& app)
|
||||||
|
{
|
||||||
|
open_info_dialog<NodeAbout>(app, "About");
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_whatsnew_dialog(App& app, bool force_show)
|
||||||
|
{
|
||||||
|
auto* overlay_anchor = app.layout[app.main_id];
|
||||||
|
if (!overlay_anchor) {
|
||||||
|
LOG("What's new dialog open failed: main layout anchor is missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto overlay_handle = std::make_shared<pp::ui::NodeHandle>();
|
||||||
|
const auto open_overlay = [overlay_anchor, overlay_handle](const std::shared_ptr<NodeRemotePage>& page) {
|
||||||
|
if (overlay_handle->valid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto overlay = open_legacy_overlay_node_with_handle(*overlay_anchor, page);
|
||||||
|
if (!overlay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*overlay_handle = overlay.value();
|
||||||
|
};
|
||||||
|
const auto close_overlay = [overlay_anchor, overlay_handle]() {
|
||||||
|
if (!overlay_handle->valid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto close_status = close_legacy_overlay_node(*overlay_anchor, *overlay_handle);
|
||||||
|
(void)close_status;
|
||||||
|
*overlay_handle = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto whatsnew = std::make_shared<NodeRemotePage>();
|
||||||
|
whatsnew->set_manager(&app.layout);
|
||||||
|
whatsnew->init();
|
||||||
|
std::string url = fmt::format("https://panopainter.com/app-content/whatsnew/?version={}", g_version_build);
|
||||||
|
whatsnew->load_url(url, [whatsnew, force_show, open_overlay](bool success) {
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
int last_id = legacy_whatsnew_id_or(0);
|
||||||
|
if (force_show || (whatsnew->m_page_id <= g_version_build && whatsnew->m_page_id > last_id))
|
||||||
|
{
|
||||||
|
whatsnew->set_title(fmt::format("What's new in version {}", g_version_number));
|
||||||
|
if (!force_show)
|
||||||
|
open_overlay(whatsnew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
whatsnew->add_button("Reload", 120, [whatsnew](Node*) {
|
||||||
|
whatsnew->reload();
|
||||||
|
});
|
||||||
|
whatsnew->add_button("Read Later", 120, [whatsnew, close_overlay](Node*) {
|
||||||
|
clear_legacy_whatsnew_id();
|
||||||
|
save_legacy_preferences();
|
||||||
|
close_overlay();
|
||||||
|
});
|
||||||
|
whatsnew->add_button("Close", 100, [whatsnew, close_overlay](Node*) {
|
||||||
|
set_legacy_whatsnew_id(whatsnew->m_page_id);
|
||||||
|
save_legacy_preferences();
|
||||||
|
close_overlay();
|
||||||
|
});
|
||||||
|
if (force_show)
|
||||||
|
open_overlay(whatsnew);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_shortcuts_dialog(App& app)
|
||||||
|
{
|
||||||
|
(void)add_legacy_overlay_node<NodeShortcuts>(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pp::panopainter
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "legacy_canvas_stroke_composite_services.h"
|
||||||
|
#include "legacy_canvas_stroke_erase_services.h"
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@@ -130,6 +132,40 @@ struct LegacyCanvasDrawMergeLayerPathExecution {
|
|||||||
std::function<void(int, float)> draw_frame;
|
std::function<void(int, float)> draw_frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LegacyCanvasDrawMergeLayerPathGlUniforms {
|
||||||
|
LegacyStrokeEraseUniforms temporary_erase;
|
||||||
|
LegacyStrokeCompositeUniforms temporary_paint;
|
||||||
|
LegacyCanvasDrawMergeTextureAlphaUniforms layer_texture;
|
||||||
|
LegacyCanvasDrawMergeLayerBlendUniforms blend;
|
||||||
|
bool use_nearest_sampler = false;
|
||||||
|
bool use_dual_texture = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LegacyCanvasDrawMergeLayerPathGlExecution {
|
||||||
|
std::function<void()> bind_blender_framebuffer;
|
||||||
|
std::function<void()> clear_blender_framebuffer;
|
||||||
|
std::function<void()> unbind_blender_framebuffer;
|
||||||
|
std::function<void(int)> bind_sampler;
|
||||||
|
std::function<void(int)> bind_nearest_sampler;
|
||||||
|
std::function<void(int)> bind_stencil_sampler;
|
||||||
|
std::function<void(int)> set_active_texture_unit;
|
||||||
|
std::function<void()> bind_temporary_texture;
|
||||||
|
std::function<void()> unbind_temporary_texture;
|
||||||
|
std::function<void()> bind_smask_texture;
|
||||||
|
std::function<void()> unbind_smask_texture;
|
||||||
|
std::function<void()> bind_temporary_dual_texture;
|
||||||
|
std::function<void()> unbind_temporary_dual_texture;
|
||||||
|
std::function<void()> bind_pattern_texture;
|
||||||
|
std::function<void()> draw_face;
|
||||||
|
std::function<void()> bind_blender_texture;
|
||||||
|
std::function<void()> unbind_blender_texture;
|
||||||
|
std::function<void()> bind_destination_texture;
|
||||||
|
std::function<void()> unbind_destination_texture;
|
||||||
|
std::function<void()> copy_destination_framebuffer;
|
||||||
|
std::function<void()> draw_debug_outline;
|
||||||
|
std::function<void(int, float)> draw_frame;
|
||||||
|
};
|
||||||
|
|
||||||
struct LegacyCanvasDrawLayerVisit {
|
struct LegacyCanvasDrawLayerVisit {
|
||||||
size_t layer_index = 0;
|
size_t layer_index = 0;
|
||||||
int plane_index = 0;
|
int plane_index = 0;
|
||||||
@@ -679,6 +715,102 @@ inline void execute_legacy_canvas_draw_merge_layer_path(
|
|||||||
execution.draw_debug_outline();
|
execution.draw_debug_outline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline LegacyCanvasDrawMergeLayerPathExecution make_legacy_canvas_draw_merge_layer_path_gl_execution(
|
||||||
|
const LegacyCanvasDrawMergeLayerPathGlUniforms& uniforms,
|
||||||
|
const LegacyCanvasDrawMergeLayerPathGlExecution& execution)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.bind_blender_framebuffer = execution.bind_blender_framebuffer,
|
||||||
|
.clear_blender_framebuffer = execution.clear_blender_framebuffer,
|
||||||
|
.unbind_blender_framebuffer = execution.unbind_blender_framebuffer,
|
||||||
|
.prepare_temporary_erase = [uniforms, execution] {
|
||||||
|
execution.bind_sampler(0);
|
||||||
|
execution.bind_sampler(1);
|
||||||
|
execution.bind_sampler(2);
|
||||||
|
setup_legacy_stroke_erase_shader(uniforms.temporary_erase);
|
||||||
|
execution.set_active_texture_unit(1);
|
||||||
|
execution.bind_temporary_texture();
|
||||||
|
execution.set_active_texture_unit(2);
|
||||||
|
execution.bind_smask_texture();
|
||||||
|
},
|
||||||
|
.cleanup_temporary_erase = [execution] {
|
||||||
|
execution.set_active_texture_unit(2);
|
||||||
|
execution.unbind_smask_texture();
|
||||||
|
execution.set_active_texture_unit(1);
|
||||||
|
execution.unbind_temporary_texture();
|
||||||
|
},
|
||||||
|
.prepare_temporary_paint = [uniforms, execution] {
|
||||||
|
execution.bind_sampler(0);
|
||||||
|
execution.bind_sampler(1);
|
||||||
|
execution.bind_sampler(2);
|
||||||
|
execution.bind_sampler(3);
|
||||||
|
execution.bind_stencil_sampler(4);
|
||||||
|
setup_legacy_stroke_composite_shader(uniforms.temporary_paint);
|
||||||
|
execution.set_active_texture_unit(1);
|
||||||
|
execution.bind_temporary_texture();
|
||||||
|
execution.set_active_texture_unit(2);
|
||||||
|
execution.bind_smask_texture();
|
||||||
|
execution.set_active_texture_unit(3);
|
||||||
|
if (uniforms.use_dual_texture) {
|
||||||
|
execution.bind_temporary_dual_texture();
|
||||||
|
}
|
||||||
|
execution.set_active_texture_unit(4);
|
||||||
|
execution.bind_pattern_texture();
|
||||||
|
},
|
||||||
|
.cleanup_temporary_paint = [uniforms, execution] {
|
||||||
|
execution.set_active_texture_unit(3);
|
||||||
|
if (uniforms.use_dual_texture) {
|
||||||
|
execution.unbind_temporary_dual_texture();
|
||||||
|
}
|
||||||
|
execution.set_active_texture_unit(2);
|
||||||
|
execution.unbind_smask_texture();
|
||||||
|
execution.set_active_texture_unit(1);
|
||||||
|
execution.unbind_temporary_texture();
|
||||||
|
},
|
||||||
|
.prepare_layer_texture = [uniforms, execution] {
|
||||||
|
if (uniforms.use_nearest_sampler) {
|
||||||
|
execution.bind_nearest_sampler(0);
|
||||||
|
} else {
|
||||||
|
execution.bind_sampler(0);
|
||||||
|
}
|
||||||
|
setup_legacy_canvas_draw_merge_texture_alpha_shader(uniforms.layer_texture);
|
||||||
|
},
|
||||||
|
.cleanup_layer_texture = [] {
|
||||||
|
},
|
||||||
|
.draw_blend = [uniforms, execution] {
|
||||||
|
execute_legacy_canvas_draw_merge_layer_blend(
|
||||||
|
uniforms.blend,
|
||||||
|
{
|
||||||
|
.unbind_merge_framebuffer = execution.unbind_blender_framebuffer,
|
||||||
|
.bind_samplers = [execution] {
|
||||||
|
execution.bind_sampler(0);
|
||||||
|
execution.bind_sampler(2);
|
||||||
|
},
|
||||||
|
.bind_merge_texture = [execution] {
|
||||||
|
execution.set_active_texture_unit(0);
|
||||||
|
execution.bind_blender_texture();
|
||||||
|
},
|
||||||
|
.bind_destination_texture = [execution] {
|
||||||
|
execution.set_active_texture_unit(2);
|
||||||
|
execution.bind_destination_texture();
|
||||||
|
},
|
||||||
|
.copy_destination_framebuffer = execution.copy_destination_framebuffer,
|
||||||
|
.draw = execution.draw_face,
|
||||||
|
.unbind_destination_texture = [execution] {
|
||||||
|
execution.set_active_texture_unit(2);
|
||||||
|
execution.unbind_destination_texture();
|
||||||
|
},
|
||||||
|
.unbind_merge_texture = [execution] {
|
||||||
|
execution.set_active_texture_unit(0);
|
||||||
|
execution.unbind_blender_texture();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
.draw_debug_outline = execution.draw_debug_outline,
|
||||||
|
.draw_frame = execution.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,
|
||||||
|
|||||||
113
src/main.cpp
113
src/main.cpp
@@ -7,20 +7,18 @@
|
|||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include "keymap.h"
|
#include "keymap.h"
|
||||||
#include "hmd.h"
|
|
||||||
#include "legacy_gl_runtime_dispatch.h"
|
#include "legacy_gl_runtime_dispatch.h"
|
||||||
#include "legacy_preference_storage.h"
|
#include "legacy_preference_storage.h"
|
||||||
#include "renderer_gl/opengl_capabilities.h"
|
#include "renderer_gl/opengl_capabilities.h"
|
||||||
#include "platform_windows/windows_platform_services.h"
|
#include "platform_windows/windows_platform_services.h"
|
||||||
#include "platform_windows/windows_splash.h"
|
#include "platform_windows/windows_splash.h"
|
||||||
#include "platform_windows/windows_stylus_input.h"
|
#include "platform_windows/windows_stylus_input.h"
|
||||||
|
#include "platform_windows/windows_vr_shell.h"
|
||||||
#include "../resource.h"
|
#include "../resource.h"
|
||||||
|
|
||||||
#include <shellscalingapi.h>
|
#include <shellscalingapi.h>
|
||||||
#include <WbemCli.h>
|
#include <WbemCli.h>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
#include "wacom.h"
|
#include "wacom.h"
|
||||||
#include "abr.h"
|
#include "abr.h"
|
||||||
|
|
||||||
@@ -32,9 +30,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <memory>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <stop_token>
|
|
||||||
|
|
||||||
#define WM_USER_CLOSE (WM_USER + 1)
|
#define WM_USER_CLOSE (WM_USER + 1)
|
||||||
#define WM_USER_WAKEUP (WM_USER + 2)
|
#define WM_USER_WAKEUP (WM_USER + 2)
|
||||||
@@ -55,11 +51,8 @@ struct RetainedState
|
|||||||
bool keys[256]{};
|
bool keys[256]{};
|
||||||
std::map<kKey, int> vkey_map;
|
std::map<kKey, int> vkey_map;
|
||||||
wchar_t window_title[512]{};
|
wchar_t window_title[512]{};
|
||||||
std::jthread hmd_renderer;
|
|
||||||
bool sandboxed = false;
|
bool sandboxed = false;
|
||||||
std::mutex hmd_render_mutex;
|
pp::platform::windows::VrShellState vr;
|
||||||
std::condition_variable hmd_render_cv;
|
|
||||||
std::unique_ptr<Vive> vive;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RetainedState& retained_state()
|
RetainedState& retained_state()
|
||||||
@@ -125,9 +118,7 @@ void pp_windows_enqueue_main_task(std::packaged_task<void()> task)
|
|||||||
enqueue_main_task_bridge(std::move(task));
|
enqueue_main_task_bridge(std::move(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<int> vr_frames{0};
|
|
||||||
std::atomic<int> running{-1};
|
std::atomic<int> running{-1};
|
||||||
std::atomic_bool vr_running{false};
|
|
||||||
|
|
||||||
#ifdef USE_RENDERDOC
|
#ifdef USE_RENDERDOC
|
||||||
RENDERDOC_API_1_4_0* rdoc_api = NULL;
|
RENDERDOC_API_1_4_0* rdoc_api = NULL;
|
||||||
@@ -230,8 +221,8 @@ void win32_update_stylus(float dt)
|
|||||||
void win32_update_fps(int frames)
|
void win32_update_fps(int frames)
|
||||||
{
|
{
|
||||||
static wchar_t title_fps[512];
|
static wchar_t title_fps[512];
|
||||||
const int vr_fps = vr_frames.load(std::memory_order_relaxed);
|
|
||||||
auto& state = retained_state();
|
auto& state = retained_state();
|
||||||
|
const int vr_fps = pp::platform::windows::current_vr_fps(state.vr);
|
||||||
if (App::I->vr_active)
|
if (App::I->vr_active)
|
||||||
swprintf_s(title_fps, L"%s - %d fps - %d vr fps", state.window_title, frames, vr_fps);
|
swprintf_s(title_fps, L"%s - %d fps - %d vr fps", state.window_title, frames, vr_fps);
|
||||||
else
|
else
|
||||||
@@ -520,101 +511,13 @@ void init_vk_map()
|
|||||||
bool win32_vr_start()
|
bool win32_vr_start()
|
||||||
{
|
{
|
||||||
auto& state = retained_state();
|
auto& state = retained_state();
|
||||||
if (state.sandboxed)
|
return pp::platform::windows::start_vr_shell(state.vr, state.sandboxed, running);
|
||||||
return false;
|
|
||||||
|
|
||||||
state.vive = std::make_unique<Vive>();
|
|
||||||
state.vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) { App::I->vr_draw(proj, view, pose); };
|
|
||||||
if (!state.vive->Initialize())
|
|
||||||
{
|
|
||||||
state.vive.reset();
|
|
||||||
LOG("VR: failed to initialize vive");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.hmd_renderer.joinable())
|
|
||||||
{
|
|
||||||
state.hmd_renderer.request_stop();
|
|
||||||
state.hmd_renderer.join();
|
|
||||||
}
|
|
||||||
state.hmd_renderer = std::jthread([&state](std::stop_token stop_token) {
|
|
||||||
if (!state.vive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
BT_SetTerminate();
|
|
||||||
LOG("start hmd render thread");
|
|
||||||
App::I->has_vr = true;
|
|
||||||
vr_running.store(true, std::memory_order_relaxed);
|
|
||||||
|
|
||||||
state.vive->on_analog_button = std::bind(&App::vr_analog, App::I, std::placeholders::_1,
|
|
||||||
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
|
||||||
state.vive->on_button = std::bind(&App::vr_digital, App::I, std::placeholders::_1,
|
|
||||||
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
|
||||||
|
|
||||||
App::I->render_task([] {
|
|
||||||
App::I->vr_draw_ui();
|
|
||||||
});
|
|
||||||
|
|
||||||
const float target_tick_rate = 90;
|
|
||||||
auto t0 = GetTickCount64();
|
|
||||||
float one_sec_timer = 0;
|
|
||||||
int frames = 0;
|
|
||||||
while (!stop_token.stop_requested() && vr_running.load(std::memory_order_relaxed) && running.load(std::memory_order_relaxed) == 1 && state.vive->Valid())
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(state.hmd_render_mutex);
|
|
||||||
auto t1 = GetTickCount64();
|
|
||||||
float dt = (float)(t1 - t0) / 1000.0f;
|
|
||||||
|
|
||||||
one_sec_timer += dt;
|
|
||||||
if (one_sec_timer >= 1.f)
|
|
||||||
{
|
|
||||||
one_sec_timer = 0;
|
|
||||||
vr_frames.store(frames, std::memory_order_relaxed);
|
|
||||||
frames = 0;
|
|
||||||
}
|
|
||||||
frames++;
|
|
||||||
|
|
||||||
state.vive->Update();
|
|
||||||
App::I->vr_active = state.vive->m_active;
|
|
||||||
App::I->vr_controllers[0] = state.vive->m_controllers[0];
|
|
||||||
App::I->vr_head = state.vive->m_pose;
|
|
||||||
App::I->vr_update(dt);
|
|
||||||
|
|
||||||
if (vr_running.load(std::memory_order_relaxed) && state.vive->m_active)
|
|
||||||
{
|
|
||||||
App::I->render_task([&state] {
|
|
||||||
state.vive->Draw();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const int framerate = (1.f / target_tick_rate) * 1000;
|
|
||||||
const int diff = framerate - (t1 - t0);
|
|
||||||
//state.hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff));
|
|
||||||
t0 = t1;
|
|
||||||
}
|
|
||||||
App::I->vr_active = false;
|
|
||||||
App::I->has_vr = false;
|
|
||||||
vr_running.store(false, std::memory_order_relaxed);
|
|
||||||
state.vive->Terminate();
|
|
||||||
LOG("hmd renderer terminated");
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void win32_vr_stop()
|
void win32_vr_stop()
|
||||||
{
|
{
|
||||||
auto& state = retained_state();
|
auto& state = retained_state();
|
||||||
if (state.vive)
|
pp::platform::windows::stop_vr_shell(state.vr);
|
||||||
{
|
|
||||||
vr_running.store(false, std::memory_order_relaxed);
|
|
||||||
if (state.hmd_renderer.joinable())
|
|
||||||
{
|
|
||||||
state.hmd_renderer.request_stop();
|
|
||||||
state.hmd_renderer.join();
|
|
||||||
}
|
|
||||||
state.vive->Terminate();
|
|
||||||
state.vive.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void win32_save_window_state()
|
void win32_save_window_state()
|
||||||
@@ -947,11 +850,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
|
|||||||
{
|
{
|
||||||
case WM_USER_CLOSE:
|
case WM_USER_CLOSE:
|
||||||
running.store(0, std::memory_order_relaxed);
|
running.store(0, std::memory_order_relaxed);
|
||||||
if (state.hmd_renderer.joinable())
|
pp::platform::windows::request_stop_and_join_vr_thread(state.vr);
|
||||||
{
|
|
||||||
state.hmd_renderer.request_stop();
|
|
||||||
state.hmd_renderer.join();
|
|
||||||
}
|
|
||||||
App::I->runtime().ui_thread_stop();
|
App::I->runtime().ui_thread_stop();
|
||||||
App::I->runtime().render_thread_stop();
|
App::I->runtime().render_thread_stop();
|
||||||
App::I->terminate();
|
App::I->terminate();
|
||||||
|
|||||||
@@ -475,14 +475,62 @@ void NodeCanvas::draw()
|
|||||||
plane_index,
|
plane_index,
|
||||||
m_canvas->m_layers[layer_index]->m_opacity);
|
m_canvas->m_layers[layer_index]->m_opacity);
|
||||||
|
|
||||||
pp::panopainter::execute_legacy_canvas_draw_merge_layer_path(
|
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
||||||
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,
|
if (b->m_pattern_flipx)
|
||||||
m_canvas->m_current_stroke && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
patt_scale.x *= -1.f;
|
||||||
use_blend,
|
if (b->m_pattern_flipy)
|
||||||
visit.first_frame,
|
patt_scale.y *= -1.f;
|
||||||
visit.last_frame,
|
|
||||||
[&](int frame) {
|
const auto layer_path_execution = pp::panopainter::make_legacy_canvas_draw_merge_layer_path_gl_execution(
|
||||||
return pp::app::animation_onion_frame_alpha(onion_range, frame);
|
{
|
||||||
|
.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 = [&] {
|
.bind_blender_framebuffer = [&] {
|
||||||
@@ -494,139 +542,65 @@ void NodeCanvas::draw()
|
|||||||
.unbind_blender_framebuffer = [&] {
|
.unbind_blender_framebuffer = [&] {
|
||||||
m_blender_rtt.unbindFramebuffer();
|
m_blender_rtt.unbindFramebuffer();
|
||||||
},
|
},
|
||||||
.prepare_temporary_erase = [&] {
|
.bind_sampler = [&](int unit) {
|
||||||
m_sampler.bind(0);
|
m_sampler.bind(unit);
|
||||||
m_sampler.bind(1);
|
|
||||||
m_sampler.bind(2);
|
|
||||||
|
|
||||||
//ShaderManager::u_vec2(kShaderUniform::Resolution, zw(m_canvas->m_box) / zoom);
|
|
||||||
//ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index]->m_alpha_locked);
|
|
||||||
pp::panopainter::setup_legacy_stroke_erase_shader(
|
|
||||||
pp::panopainter::LegacyStrokeEraseUniforms {
|
|
||||||
.mvp = plane_mvp_z,
|
|
||||||
.texture_slot = 0,
|
|
||||||
.stroke_texture_slot = 1,
|
|
||||||
.mask_texture_slot = 2,
|
|
||||||
.mask_enabled = m_canvas->m_smask_active,
|
|
||||||
});
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_canvas->m_tmp[plane_index].bindTexture();
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
|
||||||
},
|
},
|
||||||
.cleanup_temporary_erase = [&] {
|
.bind_nearest_sampler = [&](int unit) {
|
||||||
set_active_texture_unit(2);
|
m_sampler_nearest.bind(unit);
|
||||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
},
|
||||||
set_active_texture_unit(1);
|
.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();
|
m_canvas->m_tmp[plane_index].unbindTexture();
|
||||||
},
|
},
|
||||||
.prepare_temporary_paint = [&] {
|
.bind_smask_texture = [&] {
|
||||||
m_sampler.bind(0);
|
|
||||||
m_sampler.bind(1);
|
|
||||||
m_sampler.bind(2);
|
|
||||||
m_sampler.bind(3);
|
|
||||||
m_sampler_stencil.bind(4);
|
|
||||||
|
|
||||||
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
|
||||||
if (b->m_pattern_flipx) patt_scale.x *= -1.f;
|
|
||||||
if (b->m_pattern_flipy) patt_scale.y *= -1.f;
|
|
||||||
|
|
||||||
pp::panopainter::setup_legacy_stroke_composite_shader(
|
|
||||||
pp::panopainter::LegacyStrokeCompositeUniforms {
|
|
||||||
.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,
|
|
||||||
});
|
|
||||||
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_canvas->m_tmp[plane_index].bindTexture();
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
||||||
set_active_texture_unit(3);
|
},
|
||||||
if (b->m_dual_enabled)
|
.unbind_smask_texture = [&] {
|
||||||
m_canvas->m_tmp_dual[plane_index].bindTexture();
|
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
||||||
set_active_texture_unit(4);
|
},
|
||||||
|
.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 ?
|
||||||
b->m_pattern_texture->bind() :
|
b->m_pattern_texture->bind() :
|
||||||
unbind_texture_2d();
|
unbind_texture_2d();
|
||||||
},
|
},
|
||||||
.cleanup_temporary_paint = [&] {
|
.draw_face = [&] {
|
||||||
set_active_texture_unit(3);
|
|
||||||
if (b->m_dual_enabled)
|
|
||||||
m_canvas->m_tmp_dual[plane_index].unbindTexture();
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_canvas->m_tmp[plane_index].unbindTexture();
|
|
||||||
},
|
|
||||||
.prepare_layer_texture = [&] {
|
|
||||||
m_canvas->m_cam_fov < 20.f ? m_sampler_nearest.bind(0) : m_sampler.bind(0);
|
|
||||||
pp::panopainter::setup_legacy_canvas_draw_merge_texture_alpha_shader(
|
|
||||||
pp::panopainter::LegacyCanvasDrawMergeTextureAlphaUniforms {
|
|
||||||
.mvp = plane_mvp_z,
|
|
||||||
.texture_slot = 0,
|
|
||||||
.alpha = 1.f,
|
|
||||||
.highlight = m_canvas->m_layers[layer_index]->m_hightlight,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
.cleanup_layer_texture = [&] {
|
|
||||||
},
|
|
||||||
.draw_blend = [&] {
|
|
||||||
m_sampler.bind(0);
|
|
||||||
m_sampler.bind(2);
|
|
||||||
|
|
||||||
pp::panopainter::setup_legacy_canvas_draw_merge_texture_blend_shader(
|
|
||||||
pp::panopainter::LegacyCanvasDrawMergeTextureBlendUniforms {
|
|
||||||
.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,
|
|
||||||
});
|
|
||||||
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
m_blender_rtt.bindTexture();
|
|
||||||
if (copy_blend_destination)
|
|
||||||
{
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
m_blender_bg.bind();
|
|
||||||
copy_framebuffer_to_texture_2d(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
m_blender_bg.size().x,
|
|
||||||
m_blender_bg.size().y);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_face_plane.draw_fill();
|
m_face_plane.draw_fill();
|
||||||
|
},
|
||||||
if (copy_blend_destination)
|
.bind_blender_texture = [&] {
|
||||||
{
|
m_blender_rtt.bindTexture();
|
||||||
set_active_texture_unit(2);
|
},
|
||||||
m_blender_bg.unbind();
|
.unbind_blender_texture = [&] {
|
||||||
}
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
m_blender_rtt.unbindTexture();
|
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 =
|
.draw_debug_outline =
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
[&] {
|
[&] {
|
||||||
@@ -648,6 +622,17 @@ void NodeCanvas::draw()
|
|||||||
#endif
|
#endif
|
||||||
.draw_frame = draw_layer_frame,
|
.draw_frame = draw_layer_frame,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||||
|
use_blend,
|
||||||
|
visit.first_frame,
|
||||||
|
visit.last_frame,
|
||||||
|
[&](int frame) {
|
||||||
|
return pp::app::animation_onion_frame_alpha(onion_range, frame);
|
||||||
|
},
|
||||||
|
layer_path_execution);
|
||||||
},
|
},
|
||||||
[&](const char* message) {
|
[&](const char* message) {
|
||||||
LOG("NodeCanvas onion frame range failed: %s", message);
|
LOG("NodeCanvas onion frame range failed: %s", message);
|
||||||
|
|||||||
141
src/platform_windows/windows_vr_shell.h
Normal file
141
src/platform_windows/windows_vr_shell.h
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "app.h"
|
||||||
|
#include "hmd.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace pp::platform::windows {
|
||||||
|
|
||||||
|
struct VrShellState final {
|
||||||
|
std::jthread hmd_renderer;
|
||||||
|
std::mutex hmd_render_mutex;
|
||||||
|
std::condition_variable hmd_render_cv;
|
||||||
|
std::unique_ptr<Vive> vive;
|
||||||
|
std::atomic<int> vr_frames{0};
|
||||||
|
std::atomic_bool vr_running{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline int current_vr_fps(const VrShellState& state)
|
||||||
|
{
|
||||||
|
return state.vr_frames.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void request_stop_and_join_vr_thread(VrShellState& state)
|
||||||
|
{
|
||||||
|
if (state.hmd_renderer.joinable())
|
||||||
|
{
|
||||||
|
state.hmd_renderer.request_stop();
|
||||||
|
state.hmd_renderer.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool start_vr_shell(VrShellState& state, bool sandboxed, std::atomic<int>& running)
|
||||||
|
{
|
||||||
|
if (sandboxed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.vive = std::make_unique<Vive>();
|
||||||
|
state.vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) {
|
||||||
|
App::I->vr_draw(proj, view, pose);
|
||||||
|
};
|
||||||
|
if (!state.vive->Initialize())
|
||||||
|
{
|
||||||
|
state.vive.reset();
|
||||||
|
LOG("VR: failed to initialize vive");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.hmd_renderer.joinable())
|
||||||
|
{
|
||||||
|
state.hmd_renderer.request_stop();
|
||||||
|
state.hmd_renderer.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
state.hmd_renderer = std::jthread([&state, &running](std::stop_token stop_token) {
|
||||||
|
if (!state.vive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BT_SetTerminate();
|
||||||
|
LOG("start hmd render thread");
|
||||||
|
App::I->has_vr = true;
|
||||||
|
state.vr_running.store(true, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
state.vive->on_analog_button = std::bind(&App::vr_analog, App::I, std::placeholders::_1,
|
||||||
|
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
||||||
|
state.vive->on_button = std::bind(&App::vr_digital, App::I, std::placeholders::_1,
|
||||||
|
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
||||||
|
|
||||||
|
App::I->render_task([] {
|
||||||
|
App::I->vr_draw_ui();
|
||||||
|
});
|
||||||
|
|
||||||
|
const float target_tick_rate = 90;
|
||||||
|
auto t0 = GetTickCount64();
|
||||||
|
float one_sec_timer = 0;
|
||||||
|
int frames = 0;
|
||||||
|
while (!stop_token.stop_requested() &&
|
||||||
|
state.vr_running.load(std::memory_order_relaxed) &&
|
||||||
|
running.load(std::memory_order_relaxed) == 1 &&
|
||||||
|
state.vive->Valid())
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(state.hmd_render_mutex);
|
||||||
|
auto t1 = GetTickCount64();
|
||||||
|
float dt = (float)(t1 - t0) / 1000.0f;
|
||||||
|
|
||||||
|
one_sec_timer += dt;
|
||||||
|
if (one_sec_timer >= 1.f)
|
||||||
|
{
|
||||||
|
one_sec_timer = 0;
|
||||||
|
state.vr_frames.store(frames, std::memory_order_relaxed);
|
||||||
|
frames = 0;
|
||||||
|
}
|
||||||
|
frames++;
|
||||||
|
|
||||||
|
state.vive->Update();
|
||||||
|
App::I->vr_active = state.vive->m_active;
|
||||||
|
App::I->vr_controllers[0] = state.vive->m_controllers[0];
|
||||||
|
App::I->vr_head = state.vive->m_pose;
|
||||||
|
App::I->vr_update(dt);
|
||||||
|
|
||||||
|
if (state.vr_running.load(std::memory_order_relaxed) && state.vive->m_active)
|
||||||
|
{
|
||||||
|
App::I->render_task([&state] {
|
||||||
|
state.vive->Draw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const int framerate = (1.f / target_tick_rate) * 1000;
|
||||||
|
const int diff = framerate - (t1 - t0);
|
||||||
|
(void)diff;
|
||||||
|
//state.hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff));
|
||||||
|
t0 = t1;
|
||||||
|
}
|
||||||
|
|
||||||
|
App::I->vr_active = false;
|
||||||
|
App::I->has_vr = false;
|
||||||
|
state.vr_running.store(false, std::memory_order_relaxed);
|
||||||
|
state.vive->Terminate();
|
||||||
|
LOG("hmd renderer terminated");
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void stop_vr_shell(VrShellState& state)
|
||||||
|
{
|
||||||
|
if (state.vive)
|
||||||
|
{
|
||||||
|
state.vr_running.store(false, std::memory_order_relaxed);
|
||||||
|
request_stop_and_join_vr_thread(state);
|
||||||
|
state.vive->Terminate();
|
||||||
|
state.vive.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user