From f2a73a905d6b40db312e41dea728b4a4e6a98b87 Mon Sep 17 00:00:00 2001 From: Omar Date: Sat, 5 Oct 2019 21:08:40 +0200 Subject: [PATCH] add webgl support --- .gitignore | 3 + data/layout.xml | 1 - src/app.cpp | 83 ++++++++++++++++++++++- src/app.h | 4 +- src/app_events.cpp | 2 +- src/app_shaders.cpp | 2 +- src/asset.cpp | 6 ++ src/canvas.cpp | 2 +- src/canvas.h | 4 ++ src/log.cpp | 8 +++ src/log.h | 2 + src/node.cpp | 2 +- src/node_dialog_cloud.cpp | 2 + src/node_panel_grid.cpp | 2 +- src/node_panel_quick.cpp | 4 ++ src/pch.h | 23 ++++++- src/texture.cpp | 2 +- webgl/CMakeLists.txt | 136 ++++++++++++++++++++++++++++++++++++++ webgl/src/main.cpp | 93 ++++++++++++++++++++++++++ 19 files changed, 370 insertions(+), 11 deletions(-) create mode 100644 webgl/CMakeLists.txt create mode 100644 webgl/src/main.cpp diff --git a/.gitignore b/.gitignore index 40e4fa6..fc82edf 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ linux/CMakeFiles/ linux/cmake_install.cmake linux/CMakeCache.txt linux/Makefile + +webgl/build +webgl/.vscode diff --git a/data/layout.xml b/data/layout.xml index ba2b2f2..75025cc 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -23,7 +23,6 @@ - diff --git a/src/app.cpp b/src/app.cpp index b126558..a19e2e8 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -250,6 +250,7 @@ void App::initLog() LOG("load preferences failed"); } +#if WITH_CURL int progress_callback_download(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { @@ -265,9 +266,11 @@ int progress_callback_upload(void *clientp, curl_off_t dltotal, progress((float)ulnow / (float)ultotal); return 0; } +#endif //CURL void App::download(std::string url, std::string dest_filepath, std::function progress) { +#if WITH_CURL CURL *curl = curl_easy_init(); if (curl) { @@ -289,12 +292,14 @@ void App::download(std::string url, std::string dest_filepath, std::function progress) { +#if WITH_CURL CURL *curl; struct curl_httppost *formpost = NULL; @@ -362,6 +369,7 @@ void App::upload(std::string filename, std::string name, std::function working_list; + + // move the task list locally to free the queue for other threads + { + std::unique_lock lock(render_task_mutex); + if (render_tasklist.empty()) + return; + working_list = std::move(render_tasklist); + } + + // execute the tasks + if (!working_list.empty()) + { + async_start(); + while (!working_list.empty()) + { + //LOG("render task %d", count); + //LOG("task %s", working_list.front().name.c_str()); + count++; + working_list.front()(); + working_list.pop_front(); + } + async_end(); + } +} + void App::render_thread_main() { BT_SetTerminate(); @@ -868,6 +908,45 @@ void App::render_thread_main() } } +void App::ui_thread_tick() +{ + ui_thread_id = std::this_thread::get_id(); + ui_running = true; + + std::deque working_list; + + // move the task list locally to free the queue for other threads + { + std::unique_lock lock(ui_task_mutex); + working_list = std::move(ui_tasklist); + } + + // execute the tasks + if (!working_list.empty()) + { + while (!working_list.empty()) + { + //LOG("ui task %d", count); + working_list.front()(); + working_list.pop_front(); + } + } + + tick(0); + + if (redraw) + { + update(0); + render_task([this] + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + clear(); + draw(0); + async_swap(); + }); + } +} + void App::ui_thread_main() { BT_SetTerminate(); diff --git a/src/app.h b/src/app.h index 1d16046..d8282ad 100644 --- a/src/app.h +++ b/src/app.h @@ -155,7 +155,7 @@ public: #elif defined(__OSX__) && defined(__OBJC__) View* osx_view; AppOSX* osx_app; -#elif __LINUX__ +#elif __LINUX__ || __WEB__ GLFWwindow* glfw_window; #endif @@ -294,6 +294,7 @@ public: static std::thread render_thread; static std::thread::id render_thread_id; static bool render_running; + void render_thread_tick(); void render_thread_main(); void render_thread_start(); void render_thread_stop(); @@ -365,6 +366,7 @@ public: static std::thread ui_thread; static std::thread::id ui_thread_id; static bool ui_running; + void ui_thread_tick(); void ui_thread_main(); void ui_thread_start(); void ui_thread_stop(); diff --git a/src/app_events.cpp b/src/app_events.cpp index 36e6968..c5c57ac 100644 --- a/src/app_events.cpp +++ b/src/app_events.cpp @@ -193,7 +193,7 @@ void App::pick_file(std::vector types, std::functionon_progress(p); return cont ? 0 : 1; } +#endif //CURL bool Asset::open_url(const std::string& url, std::function progress /*= nullptr*/) { +#if WITH_CURL CURL* curl = curl_easy_init(); if (curl) { @@ -204,6 +209,7 @@ bool Asset::open_url(const std::string& url, std::function progress return true; } } +#endif // CURL return false; } diff --git a/src/canvas.cpp b/src/canvas.cpp index 71522c5..f9b1e66 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -1577,7 +1577,7 @@ bool Canvas::create(int width, int height) m_tex[i].create(width, height, GL_RGBA8); m_tex2[i].create(width, height, GL_RGBA8); } -#if defined(__IOS__) || defined(__ANDROID__) +#if defined(__GLES__) m_sampler_brush.create(); #else m_sampler_brush.create(GL_LINEAR, GL_CLAMP_TO_BORDER); diff --git a/src/canvas.h b/src/canvas.h index 45c6095..a9c5106 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -9,7 +9,11 @@ #include "canvas_modes.h" #include +#if __WEB__ +#define CANVAS_RES 512 +#else #define CANVAS_RES 1536 +#endif struct PPIThumb { diff --git a/src/log.cpp b/src/log.cpp index aa7797b..67c0646 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -6,6 +6,7 @@ LogRemote LogRemote::I; void LogRemote::start() { +#if WITH_CURL if (m_running || m_error) return; // already running @@ -27,6 +28,7 @@ void LogRemote::start() net_close(); LOG("NET thread loop exit"); }); +#endif //CURL } void LogRemote::stop() @@ -39,6 +41,7 @@ void LogRemote::stop() void LogRemote::net_init() { +#if WITH_CURL if (!(curl = curl_easy_init())) return; curl_easy_setopt(curl, CURLOPT_WRITEDATA, &this->readBuffer); @@ -48,10 +51,12 @@ void LogRemote::net_init() #ifdef __ANDROID__ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); #endif +#endif //CURL } std::string LogRemote::net_request(std::string cmd, std::string data /*= ""*/) { readBuffer.clear(); +#if WITH_CURL curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); auto url = m_url + cmd; @@ -63,13 +68,16 @@ std::string LogRemote::net_request(std::string cmd, std::string data /*= ""*/) m_running = false; m_error = true; } +#endif //CURL return readBuffer; } void LogRemote::net_close() { +#if WITH_CURL if (curl) curl_easy_cleanup(curl); curl = nullptr; +#endif //CURL m_running = false; } void LogRemote::file_init() diff --git a/src/log.h b/src/log.h index ef50a03..4e4f29e 100644 --- a/src/log.h +++ b/src/log.h @@ -23,8 +23,10 @@ public: BlockingQueue m_mq; // Store messages until the file is open std::vector m_tmp; +#if WITH_CURL CURL *curl = nullptr; CURLcode res; +#endif std::string readBuffer; std::string m_url = "http://omigamedev.ddns.net:8083"; int m_session; diff --git a/src/node.cpp b/src/node.cpp index 92bdd99..fdfc7a4 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -1322,7 +1322,7 @@ void Node::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) void Node::load_internal(const tinyxml2::XMLElement* x_node) { m_name = x_node->Name(); - //LOG("node %s", m_name.c_str()); + //LOG("load_internal node %s", m_name.c_str()); init(); diff --git a/src/node_dialog_cloud.cpp b/src/node_dialog_cloud.cpp index 47a20dd..7b13ab0 100644 --- a/src/node_dialog_cloud.cpp +++ b/src/node_dialog_cloud.cpp @@ -48,6 +48,7 @@ void NodeDialogCloud::removed(Node* parent) void NodeDialogCloud::load_thumbs_thread() { +#if WITH_CURL BT_SetTerminate(); CURL *curl = curl_easy_init(); std::string res; @@ -144,6 +145,7 @@ void NodeDialogCloud::load_thumbs_thread() } curl_easy_cleanup(curl); } +#endif //CURL } ////////////////////////////////////////////////////////////////// diff --git a/src/node_panel_grid.cpp b/src/node_panel_grid.cpp index a8d6586..6a15668 100644 --- a/src/node_panel_grid.cpp +++ b/src/node_panel_grid.cpp @@ -147,7 +147,7 @@ void NodePanelGrid::init_controls() if (texres == m_texture.size().x) return; -#if defined(__IOS__) || defined(__ANDROID__) +#if defined(__GLES__) m_texture.create(texres, texres); m_texture.create_mipmaps(); #else diff --git a/src/node_panel_quick.cpp b/src/node_panel_quick.cpp index 3d5c04d..88274db 100644 --- a/src/node_panel_quick.cpp +++ b/src/node_panel_quick.cpp @@ -177,8 +177,12 @@ void NodePanelQuick::init_controls() NodeButtonCustom* NodePanelQuick::init_button_brush(const std::string& name, bool szp, bool flp) { + LOG("init_button_brush %s", name.c_str()); auto button = find(name.c_str()); + if (!button) + LOG("couldn't find button %s", name.c_str()); button->on_click = std::bind(&this_class::handle_button_brush_click, this, std::placeholders::_1); + LOG("button has %d children", button->m_children.size()); auto pr = static_cast(button->m_children[0].get()); pr->m_brush = std::make_shared(); pr->m_brush->m_tip_size_pressure = szp; diff --git a/src/pch.h b/src/pch.h index 483a7c1..8cf074d 100644 --- a/src/pch.h +++ b/src/pch.h @@ -103,6 +103,24 @@ #define __block #define BT_SetTerminate void +#elif defined(EMSCRIPTEN) + + #define GLFW_INCLUDE_ES3 + #include + #include + #include + #include + + #define __WEB__ 1 + #define SHADER_VERSION "#version 300 es\n" + #define PP_OS "web" + #define __GLES__ 1 + #define __WEBGL__ 1 + #define __block + #define BT_SetTerminate void + + //#define GL_RGBA8 GL_RGBA + #endif #define SIXPLETTE(I) {I, I, I, I, I, I} @@ -155,5 +173,8 @@ #include #include #include -#include #include + +#ifndef EMSCRIPTEN +#include +#endif \ No newline at end of file diff --git a/src/texture.cpp b/src/texture.cpp index 1b66569..118b9b8 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -299,7 +299,7 @@ void Sampler::set_border(glm::vec4 rgba) { App::I->render_task([this, rgba] { -#if USE_SAMPLER && !defined(__IOS__) && !defined(__ANDROID__) +#if USE_SAMPLER && !defined(__GLES__) glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, glm::value_ptr(rgba)); #endif // USE_SAMPLER }); diff --git a/webgl/CMakeLists.txt b/webgl/CMakeLists.txt new file mode 100644 index 0000000..4e4cc27 --- /dev/null +++ b/webgl/CMakeLists.txt @@ -0,0 +1,136 @@ +cmake_minimum_required(VERSION 3.4.1) +project(panopainter) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ + -std=c++14 -O2 \ + -s USE_GLFW=3 \ + -s USE_WEBGL2=1 \ + -s FULL_ES3=1 \ + -s USE_PTHREADS=1 \ + -s ASSERTIONS=2 \ + -s TOTAL_MEMORY=64Mb \ + -s ALLOW_MEMORY_GROWTH=1 \ + -s WASM_MEM_MAX=512MB \ +") +add_executable(panopainter + src/main.cpp + ../libs/yoga/yoga/log.cpp + ../libs/yoga/yoga/Utils.cpp + ../libs/yoga/yoga/YGConfig.cpp + ../libs/yoga/yoga/YGEnums.cpp + ../libs/yoga/yoga/YGLayout.cpp + ../libs/yoga/yoga/YGMarker.cpp + ../libs/yoga/yoga/YGNode.cpp + ../libs/yoga/yoga/YGNodePrint.cpp + ../libs/yoga/yoga/YGStyle.cpp + ../libs/yoga/yoga/YGValue.cpp + ../libs/yoga/yoga/Yoga.cpp + ../libs/tinyxml2/tinyxml2.cpp + ../libs/jpeg/jpgd.cpp + ../libs/jpeg/jpge.cpp + ../libs/poly2tri/poly2tri/common/shapes.cc + ../libs/poly2tri/poly2tri/sweep/advancing_front.cc + ../libs/poly2tri/poly2tri/sweep/cdt.cc + ../libs/poly2tri/poly2tri/sweep/sweep_context.cc + ../libs/poly2tri/poly2tri/sweep/sweep.cc + ../libs/fmt/src/format.cc + ../src/pch.cpp + ../src/util.cpp + ../src/rtt.cpp + ../src/bezier.cpp + ../src/asset.cpp + ../src/image.cpp + ../src/texture.cpp + ../src/font.cpp + ../src/shader.cpp + ../src/shape.cpp + ../src/app.cpp + ../src/app_cloud.cpp + ../src/app_dialogs.cpp + ../src/app_events.cpp + ../src/app_layout.cpp + ../src/app_shaders.cpp + ../src/app_vr.cpp + ../src/brush.cpp + ../src/canvas.cpp + ../src/canvas_layer.cpp + ../src/canvas_actions.cpp + ../src/canvas_modes.cpp + ../src/log.cpp + ../src/action.cpp + ../src/layout.cpp + ../src/version.cpp + ../src/node.cpp + ../src/node_about.cpp + ../src/node_border.cpp + ../src/node_button.cpp + ../src/node_button_custom.cpp + ../src/node_canvas.cpp + ../src/node_checkbox.cpp + ../src/node_color_quad.cpp + ../src/node_colorwheel.cpp + ../src/node_combobox.cpp + ../src/node_changelog.cpp + ../src/node_dialog_browse.cpp + ../src/node_dialog_cloud.cpp + ../src/node_dialog_open.cpp + ../src/node_dialog_picker.cpp + ../src/node_dialog_layer_rename.cpp + ../src/node_dialog_resize.cpp + ../src/node_icon.cpp + ../src/node_image.cpp + ../src/node_image_texture.cpp + ../src/node_message_box.cpp + ../src/node_panel_brush.cpp + ../src/node_panel_color.cpp + ../src/node_panel_grid.cpp + ../src/node_panel_floating.cpp + ../src/node_panel_layer.cpp + ../src/node_panel_stroke.cpp + ../src/node_panel_quick.cpp + ../src/node_popup_menu.cpp + ../src/node_progress_bar.cpp + ../src/node_settings.cpp + ../src/node_slider.cpp + ../src/node_stroke_preview.cpp + ../src/node_text.cpp + ../src/node_text_input.cpp + ../src/node_tool_bucket.cpp + ../src/node_usermanual.cpp + ../src/node_viewport.cpp + ../src/node_scroll.cpp + ../src/abr.cpp + ../src/binary_stream.cpp + ../src/serializer.cpp + ../src/settings.cpp + ../src/node_input_box.cpp + ../src/node_dialog_export_ppbr.cpp +) +set_target_properties(panopainter PROPERTIES SUFFIX ".html") +set_target_properties(panopainter PROPERTIES LINK_FLAGS "\ + --embed-file data \ + -s USE_GLFW=3 \ + -s USE_WEBGL2=1 \ + -s FULL_ES3=1 \ + -s USE_PTHREADS=1 \ + -s ASSERTIONS=2 \ + -s TOTAL_MEMORY=64Mb \ + -s ALLOW_MEMORY_GROWTH=1 \ + -s WASM_MEM_MAX=512MB \ +") +target_include_directories(panopainter PRIVATE + src + ../src + ../libs/glm + ../libs/tinyxml2 + ../libs/yoga + ../libs/stb + ../libs/jpeg + ../libs/poly2tri/poly2tri + ../libs/base64 + ../libs/sqlite3 + ../libs/nanort + ../libs/hash-library + ../libs/fmt/include + ../libs/glad/include + ../libs/tinyfiledialogs +) diff --git a/webgl/src/main.cpp b/webgl/src/main.cpp new file mode 100644 index 0000000..e484e17 --- /dev/null +++ b/webgl/src/main.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include + +App app; +GLFWwindow* wnd; +float theta = 0; +glm::vec2 g_cursor_pos; + +void main_loop() +{ + app.render_thread_tick(); + app.ui_thread_tick(); + + // theta += 0.1f; + // float r = sinf(theta); + // glClearColor(r, 0.9f, 0.9f, 1.0f); + // glClear(GL_COLOR_BUFFER_BIT); + // app.tick(0); + // app.update(0); + // app.draw(0); + // SDL_GL_SwapWindow(wnd); +} + +int main() +{ + if (glfwInit() != GL_TRUE) + printf("Failed to init GLFW"); + wnd = glfwCreateWindow(800, 600, "hello", nullptr, nullptr); + glfwMakeContextCurrent(wnd); + + glfwSetCursorPosCallback(wnd, [](GLFWwindow* wnd, double x, double y){ + g_cursor_pos = glm::vec2(x, y); + app.ui_task_async([=]{ + app.mouse_move(x, y, 1.f, kEventSource::Mouse, false); + }); + }); + glfwSetMouseButtonCallback(wnd, [](GLFWwindow* wnd, int button, int action, int mods){ + app.ui_task_async([=]{ + if (action == GLFW_PRESS) + app.mouse_down(button, g_cursor_pos.x, g_cursor_pos.y, 1.f, kEventSource::Mouse, false); + else if (action == GLFW_RELEASE) + app.mouse_up(button, g_cursor_pos.x, g_cursor_pos.y, kEventSource::Mouse, false); + }); + }); + glfwSetWindowSizeCallback(wnd, [](GLFWwindow* wnd, int width, int height){ + app.ui_task_async([=]{ + app.resize(width, height); + }); + }); + glfwSetWindowCloseCallback(wnd, [](GLFWwindow* wnd){ + app.ui_task([] { + if (!app.request_close()) + glfwSetWindowShouldClose(app.glfw_window, GLFW_FALSE); + }); + }); + glfwSetWindowRefreshCallback(wnd, [](GLFWwindow* wnd){ + app.ui_task_async([=]{ + app.redraw = true; + }, true); + }); + + printf("GL version: %s\n", glGetString(GL_VERSION)); + printf("GL vendor: %s\n", glGetString(GL_VENDOR)); + printf("GL renderer: %s\n", glGetString(GL_RENDERER)); + printf("GLSL version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); + + App::I = &app; + app.initLog(); + app.create(); + app.width = 800; + app.height = 600; + app.glfw_window = wnd; + + // app.render_thread_tick(); + // app.ui_thread_tick(); + app.ui_task_async([]{ + app.init(); + }); + + LOG("start threads"); + //app.render_thread_start(); + // app.ui_thread_start(); + + emscripten_set_main_loop(main_loop, 0, true); + printf("hello world 003\n"); + return 0; +} +