diff --git a/PanoPainter/GameViewController.m b/PanoPainter/GameViewController.m index 28a6bd2..db717cd 100644 --- a/PanoPainter/GameViewController.m +++ b/PanoPainter/GameViewController.m @@ -30,6 +30,7 @@ @end //std::map< +bool pen_down = false; int t_count = 0; glm::vec2 t_pos; int lock_count = 0; @@ -194,6 +195,8 @@ NSThread* lock_thread; float scale = self.view.contentScaleFactor; kEventSource source = touch.type == UITouchType::UITouchTypeStylus ? kEventSource::Stylus : kEventSource::Touch; + if (source == kEventSource::Stylus) + pen_down = true; [self async_lock]; App::I.mouse_down(0, touchLocation.x * scale, touchLocation.y * scale, touch.force, source); [self async_unlock]; @@ -220,7 +223,14 @@ NSThread* lock_thread; } [self async_lock]; - if (n == 2) + if (pen_down) + { + if (t0.type == UITouchType::UITouchTypeStylus) + App::I.mouse_move(p0.x, p0.y, t0.force, kEventSource::Stylus); + if (t1.type == UITouchType::UITouchTypeStylus) + App::I.mouse_move(p1.x, p1.y, t1.force, kEventSource::Stylus); + } + else if (n == 2) { if (t_count == 1) { @@ -258,6 +268,7 @@ NSThread* lock_thread; float scale = self.view.contentScaleFactor; kEventSource source = touch.type == UITouchType::UITouchTypeStylus ? kEventSource::Stylus : kEventSource::Touch; + pen_down = false; [self async_lock]; if (t_count == 2) diff --git a/engine/app.cpp b/engine/app.cpp index b209a13..583b3af 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -24,8 +24,8 @@ App App::I; // singleton void App::create() { - width = 800; - height = 500; + width = 1920/2; + height = 1080/2; } void App::clear() @@ -63,24 +63,46 @@ void App::initLog() LogRemote::I.file_init(); } -void App::download(std::string filename) +int progress_callback_download(void *clientp, curl_off_t dltotal, + curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +{ + std::function progress = *(std::function*)clientp; + progress((float)dlnow / (float)dltotal); + return 0; +} + +int progress_callback_upload(void *clientp, curl_off_t dltotal, + curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +{ + std::function progress = *(std::function*)clientp; + progress((float)ulnow / (float)ultotal); + return 0; +} + +void App::download(std::string filename, std::function progress) { CURL *curl = curl_easy_init(); if (curl) { auto dest = data_path + "/" + filename; FILE* fp = fopen(dest.c_str(), "wb"); - std::string url = "http://omigamedev.ddns.net:8080/panoview/cloud-dwl.php?file=" + filename; + std::string url = "http://omigamedev.com/panopainter/cloud/cloud-dwl.php?file=" + filename; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_write); + if (progress) + { + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback_download); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + } auto err = curl_easy_perform(curl); curl_easy_cleanup(curl); fclose(fp); } } -void App::upload(std::string filename, std::string name) +void App::upload(std::string filename, std::string name, std::function progress) { CURL *curl; @@ -100,11 +122,17 @@ void App::upload(std::string filename, std::string name) if (curl) { - std::string url = "http://omigamedev.ddns.net:8080/panoview/cloud-upl.php?name=" + name; + std::string url = "http://omigamedev.com/panopainter/cloud/cloud-upl.php?name=" + name; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler); + if (progress) + { + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback_upload); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + } auto err = curl_easy_perform(curl); std::cout << "\n\nUPLOAD RESULT\n" << res << "\n\n\n"; curl_easy_cleanup(curl); @@ -213,6 +241,11 @@ void App::async_update() #endif } +void App::async_redraw() +{ + redraw = true; +} + void App::async_end() { #if __OSX__ @@ -242,11 +275,17 @@ void App::update(float dt) //glViewport(0, 0, (GLsizei)width, (GLsizei)height); //glClear(GL_COLOR_BUFFER_BIT); + if (!canvas->m_mouse_captured) + { #if _WIN32 || __OSX__ - layout.reload(); + layout.reload(); #endif - if (auto* main = layout[main_id]) - main->update(width, height, zoom); + if (auto* main = layout[main_id]) + { + main->update(width, height, zoom); + stroke->update_controls(); + } + } static glm::vec4 color_button_normal{.1, .1, .1, 1}; static glm::vec4 color_button_hlight{ 1, .0, .0, 1}; @@ -256,11 +295,6 @@ void App::update(float dt) layout[main_id]->find("btn-touchlock")->set_color( canvas->m_canvas->m_touch_lock ? color_button_hlight : color_button_normal); - stroke->update_controls(); - auto pix = ui::Canvas::I->m_current_brush.m_tip_color; - auto hsv = convert_rgb2hsv(glm::vec3(pix[0], pix[1], pix[2])); - color->m_hue->m_value.y = hsv.x; - color->m_quad->m_value = glm::vec2(hsv.y, 1.f - hsv.z); auto observer = [this](Node* n) { diff --git a/engine/app.h b/engine/app.h index 8cfe018..6e32394 100644 --- a/engine/app.h +++ b/engine/app.h @@ -16,6 +16,7 @@ #include "node_scroll.h" #include "node_canvas.h" #include "node_dialog_layer_rename.h" +#include "node_progress_bar.h" #if defined(__OBJC__) && defined(__IOS__) #import @@ -97,6 +98,7 @@ public: void update(float dt); void async_start(); void async_update(); + void async_redraw(); void async_end(); void resize(float w, float h); bool mouse_down(int button, float x, float y, float pressure, kEventSource source); @@ -128,8 +130,10 @@ public: void cloud_upload(); void cloud_upload_all(); void cloud_browse(); - void upload(std::string filename, std::string name = ""); - void download(std::string filename); + void upload(std::string filename, std::string name = "", std::function progress = nullptr); + void download(std::string filename, std::function progress = nullptr); + + std::shared_ptr show_progress(const std::string& title); void brush_update(); void title_update(std::string name, int resolution); diff --git a/engine/app_cloud.cpp b/engine/app_cloud.cpp index c944cb5..864dbb7 100644 --- a/engine/app_cloud.cpp +++ b/engine/app_cloud.cpp @@ -25,8 +25,21 @@ void App::cloud_upload() std::thread([this] { std::string path = data_path + "/" + doc_name + ".pano"; Canvas::I->project_save_thread(path); - upload(path); + async_start(); + auto pb = show_progress("Uploading"); + async_redraw(); + async_end(); + + upload(path, doc_name, [this,pb](float p){ + async_start(); + pb->m_progress->SetWidthP(p * 100.f); + async_redraw(); + async_end(); + }); + + async_start(); + pb->destroy(); auto msgbox = new NodeMessageBox(); msgbox->m_manager = &layout; msgbox->init(); @@ -34,6 +47,7 @@ void App::cloud_upload() msgbox->m_message->set_text("This document has been succesfully uploaded."); layout[main_id]->add_child(msgbox); layout[main_id]->update(); + async_redraw(); async_end(); }).detach(); } @@ -41,24 +55,17 @@ void App::cloud_upload() void App::cloud_upload_all() { - std::thread([] { - auto names = Asset::list_files(App::I.data_path, false, ".*\\.pano"); + std::thread([this] { + auto names = Asset::list_files(data_path, false, ".*\\.pano"); gl_state gl; std::shared_ptr pb; - if (App::I.layout.m_loaded) + if (layout.m_loaded) { - App::I.async_start(); - pb = std::make_shared(); - pb->m_manager = &App::I.layout; - pb->init(); - pb->create(); - pb->loaded(); - pb->m_progress->SetWidthP(0); - pb->m_title->set_text("Export Pano Image"); - App::I.layout[App::I.main_id]->add_child(pb); - App::I.async_update(); - App::I.async_end(); + async_start(); + pb = show_progress("Export Pano Image"); + async_redraw(); + async_end(); } int progress = 0; @@ -66,30 +73,30 @@ void App::cloud_upload_all() for (const auto& n : names) { - std::string path = App::I.data_path + "/" + n; - App::I.upload(path); + std::string path = data_path + "/" + n; + upload(path); progress++; float p = (float)progress / total * 100.f; LOG("progress: %f", p); - if (App::I.layout.m_loaded) + if (layout.m_loaded) { - App::I.async_start(); + async_start(); pb->m_progress->SetWidthP(p); gl.save(); - App::I.async_update(); + async_redraw(); gl.restore(); - App::I.async_end(); + async_end(); } } - if (App::I.layout.m_loaded) + if (layout.m_loaded) { - App::I.async_start(); + async_start(); pb->destroy(); - App::I.async_update(); - App::I.async_end(); + async_redraw(); + async_end(); } }).detach(); } @@ -117,11 +124,18 @@ void App::cloud_browse() async_start(); auto* m = layout[main_id]->add_child(); m->m_title->set_text("Downloading"); - m->m_message->set_text("Download in progress, please wait..."); - async_update(); + m->m_message->set_text("Download in progress"); + async_redraw(); async_end(); - download(dialog->selected_file); + download(dialog->selected_file, [this,m](float p){ + static char progress[256]; + sprintf(progress, "Download in progress %.2f%%", p * 100.f); + async_start(); + m->m_message->set_text(progress); + async_redraw(); + async_end(); + }); async_start(); canvas->reset_camera(); @@ -137,7 +151,7 @@ void App::cloud_browse() layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str()); ActionManager::clear(); m->destroy(); - async_update(); + async_redraw(); async_end(); }).detach(); }; diff --git a/engine/app_dialogs.cpp b/engine/app_dialogs.cpp index ce6fd48..6dc5c0f 100644 --- a/engine/app_dialogs.cpp +++ b/engine/app_dialogs.cpp @@ -4,6 +4,19 @@ #include "node_dialog_browse.h" #include "node_dialog_cloud.h" +std::shared_ptr App::show_progress(const std::string& title) +{ + auto pb = std::make_shared(); + pb->m_manager = &layout; + pb->init(); + pb->create(); + pb->loaded(); + pb->m_progress->SetWidthP(0); + pb->m_title->set_text(title.c_str()); + layout[main_id]->add_child(pb); + return pb; +} + void App::dialog_newdoc() { if (canvas) diff --git a/engine/app_events.cpp b/engine/app_events.cpp index 682213c..84c8620 100644 --- a/engine/app_events.cpp +++ b/engine/app_events.cpp @@ -3,6 +3,8 @@ #ifdef __ANDROID__ void displayKeyboard(android_app* mApplication, bool pShow); +#elif _WIN32 +std::string win32_open_file(); #endif @@ -51,6 +53,10 @@ void App::pick_image(std::function callback) callback(path); #elif __ANDROID__ //displayKeyboard(and_app, false); +#elif _WIN32 + std::string path = win32_open_file(); + if (!path.empty()) + callback(path); #endif } diff --git a/engine/canvas.cpp b/engine/canvas.cpp index ef30858..3499ece 100644 --- a/engine/canvas.cpp +++ b/engine/canvas.cpp @@ -1400,8 +1400,11 @@ void ui::Canvas::project_open_thread(std::string file_path) fread(compressed.data(), 1, data_size, fp); int imgw, imgh, imgc; uint8_t* rgba = stbi_load_from_memory(compressed.data(), data_size, &imgw, &imgh, &imgc, 4); - std::copy(rgba, rgba + (imgw*imgh * 4), snap.image[plane_index].get()); - delete rgba; + if (rgba) + { + std::copy(rgba, rgba + (imgw*imgh * 4), snap.image[plane_index].get()); + delete rgba; + } } progress++; diff --git a/engine/canvas_modes.cpp b/engine/canvas_modes.cpp index d998957..90cdc03 100644 --- a/engine/canvas_modes.cpp +++ b/engine/canvas_modes.cpp @@ -98,6 +98,7 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc) node->mouse_release(); glm::vec4 pix = canvas->pick_get(loc); canvas->m_current_brush.m_tip_color = pix; + App::I.color->set_color(pix); } m_dragging = false; m_picking = false; @@ -109,6 +110,7 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { glm::vec4 pix = canvas->pick_get(loc); canvas->m_current_brush.m_tip_color = pix; + App::I.color->set_color(pix); } m_cur_pos = loc; break; diff --git a/engine/main.cpp b/engine/main.cpp index f8323dc..a1df199 100644 --- a/engine/main.cpp +++ b/engine/main.cpp @@ -81,6 +81,26 @@ void async_unlock() } } +std::string win32_open_file() +{ + OPENFILENAMEA ofn; + char fileName[MAX_PATH] = ""; + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hWnd; + ofn.lpstrFilter = "Image Files (*.jpg, *.png)\0*.jpg;*.png"; + ofn.lpstrFile = fileName; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrDefExt = ""; + ofn.lpstrInitialDir = "Missions\\"; + if (GetOpenFileNameA(&ofn) != NULL) + { + return fileName; + } + return ""; +} + struct async_locker { async_locker() { async_lock(); } diff --git a/engine/node.cpp b/engine/node.cpp index 31253e5..f4aaa29 100644 --- a/engine/node.cpp +++ b/engine/node.cpp @@ -186,6 +186,16 @@ void Node::loaded() } +void Node::added(Node* parent) +{ + +} + +void Node::removed(Node* parent) +{ + +} + const Node* Node::init_template(const char* id) { const auto& m_template = static_cast((*m_manager)[const_hash(id)]->m_children[0].get()); @@ -207,6 +217,7 @@ void Node::add_child(Node* n) n->parent = this; n->m_manager = m_manager; YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node)); + n->added(this); } void Node::add_child(Node* n, int index) @@ -215,6 +226,7 @@ void Node::add_child(Node* n, int index) n->parent = this; n->m_manager = m_manager; YGNodeInsertChild(y_node, n->y_node, index); + n->added(this); } void Node::add_child(std::shared_ptr n) @@ -223,6 +235,7 @@ void Node::add_child(std::shared_ptr n) n->parent = this; n->m_manager = m_manager; YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node)); + n->added(this); } void Node::add_child(std::shared_ptr n, int index) @@ -231,6 +244,7 @@ void Node::add_child(std::shared_ptr n, int index) n->parent = this; n->m_manager = m_manager; YGNodeInsertChild(y_node, n->y_node, index); + n->added(this); } void Node::remove_child(Node* n) @@ -238,6 +252,7 @@ void Node::remove_child(Node* n) auto i = std::find_if(m_children.begin(), m_children.end(), [=](auto& ptr) { return ptr.get() == n; }); if (i != m_children.end()) { + n->removed(this); YGNodeRemoveChild(y_node, n->y_node); m_children.erase(i); } @@ -246,7 +261,10 @@ void Node::remove_child(Node* n) void Node::remove_all_children() { for (auto& n : m_children) + { + n->removed(this); YGNodeRemoveChild(y_node, n->y_node); + } m_children.clear(); } diff --git a/engine/node.h b/engine/node.h index c6e3097..172ca67 100644 --- a/engine/node.h +++ b/engine/node.h @@ -179,6 +179,8 @@ public: template T* add_child() { auto* n = new T; + n->m_manager = m_manager; + n->parent = parent; n->init(); n->create(); n->loaded(); @@ -192,6 +194,8 @@ public: virtual void create(); virtual void init(); virtual void loaded(); + virtual void added(Node* parent); + virtual void removed(Node* parent); const Node* init_template(const char* id); void async_start(); void async_update(); diff --git a/engine/node_dialog_cloud.cpp b/engine/node_dialog_cloud.cpp index fc68a6d..0ed6137 100644 --- a/engine/node_dialog_cloud.cpp +++ b/engine/node_dialog_cloud.cpp @@ -43,40 +43,90 @@ void NodeDialogCloud::loaded() { } +void NodeDialogCloud::removed(Node* parent) +{ + closed = true; +} + void NodeDialogCloud::load_thumbs_thread() { CURL *curl = curl_easy_init(); std::string res; if (curl) { + async_start(); + auto* align = container->add_child(); + align->SetWidthP(100.f); + align->SetHeightP(100.f); + align->SetAlign(YGAlignCenter); + align->SetJustify(YGJustifyCenter); + auto* text = align->add_child(); + text->set_font(kFont::Arial_30); + text->set_text("Connecting to the server..."); + async_update(); + async_end(); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler); - curl_easy_setopt(curl, CURLOPT_URL, "http://omigamedev.ddns.net:8080/panoview/cloud-list.php"); + curl_easy_setopt(curl, CURLOPT_URL, "http://omigamedev.com/panopainter/cloud/cloud-list.php"); auto err = curl_easy_perform(curl); + if (err != CURLE_OK) { async_start(); - auto* align = container->add_child(); - align->SetWidthP(100.f); - align->SetHeightP(100.f); - align->SetAlign(YGAlignCenter); - align->SetJustify(YGJustifyCenter); - auto* t = align->add_child(); - t->set_font(kFont::Arial_30); - t->set_text("Could not connect to the server"); + text->set_text("Could not connect to the server"); async_update(); async_end(); return; } + async_start(); + align->destroy(); + async_end(); + LOG("CLOUD LIST: %s", res.c_str()); auto names = split(res, ','); + std::vector nodes; + + // create slots with name + App::I.async_start(); for (const auto& n : names) { + auto node = new NodeDialogCloudItem; + node->m_manager = m_manager; + node->init(); + node->m_text->set_text(n.c_str()); + node->m_path = data_path + "/" + n; + node->m_file_name = n; + container->add_child(node); + node->on_selected = [&](NodeDialogCloudItem* target) { + if (target == current) + return; + selected_path = target->m_path; + selected_file = target->m_file_name; + selected_name = selected_file.substr(0, selected_file.length() - 5); + if (current) + current->m_selected = false; + current = target; + }; + nodes.push_back(node); + } + App::I.async_update(); + App::I.async_end(); + + // load the icons + for (int i = 0; i < names.size(); i++) + { + const auto& n = names[i]; + auto* node = nodes[i]; + if (closed) + break; + res.clear(); - std::string url = "http://omigamedev.ddns.net:8080/panoview/cloud-info.php?file=" + n; + std::string url = "http://omigamedev.com/panopainter/cloud/cloud-info.php?file=" + n; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + LOG("%s", url.c_str()); auto err = curl_easy_perform(curl); if (err != CURLE_OK) break; // TODO: handle this error with a message or something @@ -93,29 +143,11 @@ void NodeDialogCloud::load_thumbs_thread() thumb.copy_from((uint8_t*)rgb.data()); App::I.async_start(); - auto node = new NodeDialogCloudItem; - node->m_manager = m_manager; - node->init(); - node->m_text->set_text(n.c_str()); - node->m_path = data_path + "/" + n; - node->m_file_name = n; auto image_tex = node->find("thumb-tex"); image_tex->tex.destroy(); image_tex->tex.create(thumb); - container->add_child(node); App::I.async_update(); App::I.async_end(); - - node->on_selected = [&](NodeDialogCloudItem* target) { - if (target == current) - return; - selected_path = target->m_path; - selected_file = target->m_file_name; - selected_name = selected_file.substr(0, selected_file.length() - 5); - if (current) - current->m_selected = false; - current = target; - }; } curl_easy_cleanup(curl); } diff --git a/engine/node_dialog_cloud.h b/engine/node_dialog_cloud.h index 46d9357..61b93e3 100644 --- a/engine/node_dialog_cloud.h +++ b/engine/node_dialog_cloud.h @@ -29,6 +29,7 @@ public: class NodeDialogCloud : public NodeBorder { public: + bool closed = false; NodeButton* btn_cancel; NodeButton* btn_ok; NodeButton* btn_delete; @@ -43,5 +44,6 @@ public: virtual void init() override; void init_controls(); virtual void loaded() override; + virtual void removed(Node* parent) override; void load_thumbs_thread(); }; diff --git a/engine/node_panel_color.cpp b/engine/node_panel_color.cpp index 80318e1..88f950d 100644 --- a/engine/node_panel_color.cpp +++ b/engine/node_panel_color.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "log.h" #include "node_panel_color.h" +#include "canvas.h" Node* NodePanelColor::clone_instantiate() const { @@ -27,6 +28,7 @@ void NodePanelColor::init_controls() m_base_color = m_quad->m_color = hue_color; float hue = convert_rgb2hsv(m_base_color).x; m_color = glm::vec4(convert_hsv2rgb(glm::vec3(hue, m_cursor.x, 1.f-m_cursor.y)), 1.f); + m_quad->m_color = hue_color; if (on_color_changed) on_color_changed(this, m_color); }; @@ -40,3 +42,17 @@ void NodePanelColor::init_controls() }; m_hue->set_value(0); } + +void NodePanelColor::set_color(glm::vec3 rgb) +{ + auto hsv = convert_rgb2hsv(rgb); + m_hue->m_value.y = hsv.x; + m_quad->m_value = glm::vec2(hsv.y, 1.f - hsv.z); + m_quad->m_color = glm::vec4(rgb, 1); + m_cursor = m_quad->m_value; +} + +void NodePanelColor::added(Node* parent) +{ + set_color(ui::Canvas::I->m_current_brush.m_tip_color); +} diff --git a/engine/node_panel_color.h b/engine/node_panel_color.h index ea3ad88..02fdf49 100644 --- a/engine/node_panel_color.h +++ b/engine/node_panel_color.h @@ -15,5 +15,7 @@ public: virtual Node* clone_instantiate() const override; virtual void clone_finalize(Node* dest) const override; virtual void init() override; + virtual void added(Node* parent) override; void init_controls(); + void set_color(glm::vec3 rgb); }; diff --git a/engine/node_slider.cpp b/engine/node_slider.cpp index 58938b4..bd9a7a8 100644 --- a/engine/node_slider.cpp +++ b/engine/node_slider.cpp @@ -93,7 +93,7 @@ kEventResult NodeSliderH::handle_event(Event* e) auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) * m_mask; m_value = pos / glm::max({ 1, 1 }, sz); if (on_value_changed) - on_value_changed(this, glm::length(m_value)); + on_value_changed(this, glm::length(m_value * m_mask)); } break; case kEventType::MouseCancel: @@ -103,7 +103,7 @@ kEventResult NodeSliderH::handle_event(Event* e) m_value = m_old_value; set_value(glm::length(m_value)); if (on_value_changed) - on_value_changed(this, glm::length(m_value)); + on_value_changed(this, glm::length(m_value * m_mask)); } dragging = false; break; @@ -143,6 +143,7 @@ void NodeSliderHue::init_controls() glm::vec4 NodeSliderHue::get_hue() { + m_color = glm::vec4(convert_hsv2rgb({ glm::length(m_value * m_mask), 1, 1 }), 1); return m_color; }