diff --git a/.gitmodules b/.gitmodules
index 712ff4f..4613c87 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,3 +22,6 @@
[submodule "libs/poly2tri"]
path = libs/poly2tri
url = https://github.com/greenm01/poly2tri
+[submodule "libs/base64"]
+ path = libs/base64
+ url = https://github.com/tkislan/base64
diff --git a/data/layout.xml b/data/layout.xml
index dbff9cd..cd76871 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -285,6 +285,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -472,9 +499,13 @@
-
+
-
+
+
+
+
+
diff --git a/engine.vcxproj b/engine.vcxproj
index 79859e0..e8f836f 100644
--- a/engine.vcxproj
+++ b/engine.vcxproj
@@ -76,7 +76,7 @@
true
- libs\glm;libs\glew-2.0.0\include;libs\stb;libs\tinyxml2;libs\yoga;libs\curl-win\include;libs\jpeg;libs\wacom;C:\Users\omar\Downloads\BugTrap-master\BugTrap-master\source\Client;libs\poly2tri\poly2tri;$(IncludePath)
+ libs\glm;libs\glew-2.0.0\include;libs\stb;libs\tinyxml2;libs\yoga;libs\curl-win\include;libs\jpeg;libs\wacom;C:\Users\omar\Downloads\BugTrap-master\BugTrap-master\source\Client;libs\poly2tri\poly2tri;libs\base64;$(IncludePath)
libs\curl-win\lib\dll-$(Configuration)-$(PlatformShortName);libs\glew-2.0.0\lib\Release\$(Platform);C:\Users\omar\Downloads\BugTrap-master\BugTrap-master\bin;$(LibraryPath)
@@ -86,7 +86,7 @@
false
- libs\glm;libs\glew-2.0.0\include;libs\stb;libs\tinyxml2;libs\yoga;libs\curl-win\include;libs\jpeg;libs\wacom;C:\Users\omar\Downloads\BugTrap-master\BugTrap-master\source\Client;libs\poly2tri\poly2tri;$(IncludePath)
+ libs\glm;libs\glew-2.0.0\include;libs\stb;libs\tinyxml2;libs\yoga;libs\curl-win\include;libs\jpeg;libs\wacom;C:\Users\omar\Downloads\BugTrap-master\BugTrap-master\source\Client;libs\poly2tri\poly2tri;libs\base64;$(IncludePath)
libs\curl-win\lib\dll-$(Configuration)-$(PlatformShortName);libs\glew-2.0.0\lib\Release\$(Platform);C:\Users\omar\Downloads\BugTrap-master\BugTrap-master\bin;$(LibraryPath)
@@ -170,6 +170,7 @@
+
@@ -194,6 +195,7 @@
+
@@ -304,6 +306,7 @@
+
diff --git a/engine.vcxproj.filters b/engine.vcxproj.filters
index 8e1b527..accbd16 100644
--- a/engine.vcxproj.filters
+++ b/engine.vcxproj.filters
@@ -228,6 +228,12 @@
Source Files
+
+ Source Files\ui
+
+
+ Source Files
+
@@ -404,6 +410,9 @@
Header Files\ui
+
+ Header Files\ui
+
diff --git a/engine/app.cpp b/engine/app.cpp
index 80c5ce9..7f67c2f 100644
--- a/engine/app.cpp
+++ b/engine/app.cpp
@@ -63,13 +63,6 @@ void App::initLog()
LogRemote::I.file_init();
}
-static size_t curl_data_handler(void *contents, size_t size, size_t nmemb, void *userp)
-{
- auto buffer = reinterpret_cast(userp);
- buffer->append((char*)contents, size * nmemb);
- return size * nmemb;
-}
-
void App::upload(std::string filename)
{
CURL *curl;
@@ -94,7 +87,7 @@ void App::upload(std::string filename)
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler);
- auto err = curl_easy_perform(curl); //here it crashes
+ auto err = curl_easy_perform(curl);
std::cout << "\n\nUPLOAD RESULT\n" << res << "\n\n\n";
curl_easy_cleanup(curl);
}
diff --git a/engine/app.h b/engine/app.h
index c455d4a..0ecacad 100644
--- a/engine/app.h
+++ b/engine/app.h
@@ -126,6 +126,10 @@ public:
void dialog_browse();
void dialog_export();
void dialog_layer_rename();
+
+ void cloud_upload_all();
+ void cloud_browse();
+
void brush_update();
void cmd_convert(std::string pano_path, std::string out_path);
diff --git a/engine/app_cloud.cpp b/engine/app_cloud.cpp
new file mode 100644
index 0000000..208b017
--- /dev/null
+++ b/engine/app_cloud.cpp
@@ -0,0 +1,93 @@
+#include "pch.h"
+#include "app.h"
+#include "util.h"
+#include "node_progress_bar.h"
+#include "node_dialog_cloud.h"
+
+using namespace ui;
+
+void App::cloud_upload_all()
+{
+ std::thread([] {
+ auto names = Asset::list_files(App::I.data_path, false, ".*\\.pano");
+
+ gl_state gl;
+ std::shared_ptr pb;
+ if (App::I.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();
+ }
+
+ int progress = 0;
+ int total = names.size();
+
+ for (const auto& n : names)
+ {
+ std::string path = App::I.data_path + "/" + n;
+ App::I.upload(path);
+
+ progress++;
+ float p = (float)progress / total * 100.f;
+ LOG("progress: %f", p);
+
+ if (App::I.layout.m_loaded)
+ {
+ App::I.async_start();
+ pb->m_progress->SetWidthP(p);
+ gl.save();
+ App::I.async_update();
+ gl.restore();
+ App::I.async_end();
+ }
+ }
+
+ if (App::I.layout.m_loaded)
+ {
+ App::I.async_start();
+ pb->destroy();
+ App::I.async_update();
+ App::I.async_end();
+ }
+ }).detach();
+}
+
+void App::cloud_browse()
+{
+ if (!canvas)
+ return;
+
+ // load thumbnail test
+ auto dialog = std::make_shared();
+ dialog->m_manager = &layout;
+ dialog->data_path = data_path;
+ dialog->init();
+ dialog->create();
+ dialog->loaded();
+
+ layout[main_id]->add_child(dialog);
+ layout[main_id]->update();
+
+ dialog->btn_ok->on_click = [this, dialog](Node*)
+ {
+// canvas->reset_camera();
+// layers->clear();
+// canvas->m_canvas->project_open(dialog->selected_path);
+// doc_name = dialog->selected_name;
+// if (auto docname = layout[main_id]->find("txt-docname"))
+// docname->set_text(("Panodoc: " + doc_name).c_str());
+// for (auto& i : canvas->m_canvas->m_order)
+// layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
+ dialog->destroy();
+ ActionManager::clear();
+ };
+}
\ No newline at end of file
diff --git a/engine/app_dialogs.cpp b/engine/app_dialogs.cpp
index 787602d..ffae787 100644
--- a/engine/app_dialogs.cpp
+++ b/engine/app_dialogs.cpp
@@ -2,6 +2,7 @@
#include "app.h"
#include "node_dialog_open.h"
#include "node_dialog_browse.h"
+#include "node_dialog_cloud.h"
void App::dialog_newdoc()
{
diff --git a/engine/app_layout.cpp b/engine/app_layout.cpp
index bb932d6..f24869d 100644
--- a/engine/app_layout.cpp
+++ b/engine/app_layout.cpp
@@ -424,60 +424,15 @@ void App::init_menu_file()
popup->mouse_release();
popup->destroy();
};
- popup->find("file-upload")->on_click = [this](Node*) {
+ popup->find("file-cloud-upload")->on_click = [this](Node*) {
+ cloud_upload_all();
+ popup->mouse_release();
+ popup->destroy();
+ };
+ popup->find("file-cloud-browse")->on_click = [this](Node*) {
+ cloud_browse();
popup->mouse_release();
popup->destroy();
- std::thread([] {
- auto names = Asset::list_files(App::I.data_path, false, ".*\\.pano");
-
- gl_state gl;
- std::shared_ptr pb;
- if (App::I.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();
- }
-
- int progress = 0;
- int total = names.size();
-
- for (const auto& n : names)
- {
- std::string path = App::I.data_path + "/" + n;
- App::I.upload(path);
-
- progress++;
- float p = (float)progress / total * 100.f;
- LOG("progress: %f", p);
-
- if (App::I.layout.m_loaded)
- {
- App::I.async_start();
- pb->m_progress->SetWidthP(p);
- gl.save();
- App::I.async_update();
- gl.restore();
- App::I.async_end();
- }
- }
-
- if (App::I.layout.m_loaded)
- {
- App::I.async_start();
- pb->destroy();
- App::I.async_update();
- App::I.async_end();
- }
- }).detach();
};
};
}
diff --git a/engine/image.h b/engine/image.h
index 7781500..1a414fb 100644
--- a/engine/image.h
+++ b/engine/image.h
@@ -4,8 +4,8 @@ namespace ui {
class Image
{
- std::unique_ptr m_data;
public:
+ std::unique_ptr m_data;
int width = 0;
int height = 0;
int comp = 4;
@@ -19,6 +19,10 @@ public:
comp = 4;
m_data = std::make_unique(size());
}
+ void copy_from(const uint8_t* data)
+ {
+ std::copy(data, data + size(), m_data.get());
+ }
void flip();
void create() { m_data = std::make_unique(size()); }
};
diff --git a/engine/log.cpp b/engine/log.cpp
index cedb8dc..9412997 100644
--- a/engine/log.cpp
+++ b/engine/log.cpp
@@ -4,13 +4,6 @@
LogRemote LogRemote::I;
-static size_t data_handler(void *contents, size_t size, size_t nmemb, void *userp)
-{
- auto buffer = reinterpret_cast(userp);
- buffer->append((char*)contents, size * nmemb);
- return size * nmemb;
-}
-
void LogRemote::start()
{
if (m_running || m_error)
@@ -43,7 +36,7 @@ void LogRemote::net_init()
return;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &this->readBuffer);
//curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, data_handler);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
}
std::string LogRemote::net_request(std::string cmd, std::string data /*= ""*/)
diff --git a/engine/node.cpp b/engine/node.cpp
index 4efe034..d92d45b 100644
--- a/engine/node.cpp
+++ b/engine/node.cpp
@@ -25,6 +25,7 @@
#include "node_canvas.h"
#include "node_scroll.h"
#include "node_dialog_browse.h"
+#include "node_dialog_cloud.h"
void Node::watch(std::function observer)
{
@@ -797,6 +798,8 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node)
CASE(kWidget::Scroll, NodeScroll);
CASE(kWidget::DialogBrowse, NodeDialogBrowse);
CASE(kWidget::DialogBrowseItem, NodeDialogBrowseItem);
+ CASE(kWidget::DialogCloud, NodeDialogCloud);
+ CASE(kWidget::DialogCloudItem, NodeDialogCloudItem);
#undef CASE
case kWidget::Ref:
{
diff --git a/engine/node.h b/engine/node.h
index 5d702e2..93f271d 100644
--- a/engine/node.h
+++ b/engine/node.h
@@ -71,6 +71,8 @@ enum class kWidget : uint16_t
Scroll = const_hash("scroll"),
DialogBrowse = const_hash("dialog-browse"),
DialogBrowseItem = const_hash("dialog-browse-item"),
+ DialogCloud = const_hash("dialog-cloud"),
+ DialogCloudItem = const_hash("dialog-cloud-item"),
};
class Node
diff --git a/engine/node_dialog_cloud.cpp b/engine/node_dialog_cloud.cpp
new file mode 100644
index 0000000..54a1803
--- /dev/null
+++ b/engine/node_dialog_cloud.cpp
@@ -0,0 +1,210 @@
+#include "pch.h"
+#include "log.h"
+#include "node_dialog_cloud.h"
+#include "canvas.h"
+#include "node_image_texture.h"
+#include "asset.h"
+#include "node_message_box.h"
+#include "app.h"
+#include "image.h"
+
+Node* NodeDialogCloud::clone_instantiate() const
+{
+ return new NodeDialogCloud();
+}
+
+void NodeDialogCloud::clone_finalize(Node* dest) const
+{
+ NodeDialogCloud* n = static_cast(dest);
+ n->init_controls();
+}
+
+void NodeDialogCloud::init()
+{
+ auto tpl = static_cast(init_template("dialog-cloud"));
+ m_color = tpl->m_color;
+ m_border_color = tpl->m_border_color;;
+ m_thinkness = tpl->m_thinkness;;
+ init_controls();
+}
+
+void NodeDialogCloud::init_controls()
+{
+ btn_ok = find("btn-ok");
+ btn_cancel = find("btn-cancel");
+ btn_cancel->on_click = [this](Node*) {
+ destroy();
+ };
+ container = find("files-list");
+ std::thread(&NodeDialogCloud::load_thumbs_thread, this).detach();
+/*
+ auto names = Asset::list_files(data_path, false, ".*\\.pano");
+ 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;
+ 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;
+ };
+
+ // load thumb
+ ui::Image thumb = ui::Canvas::I->thumbnail_read(node->m_path);
+ auto image_tex = node->find("thumb-tex");
+ image_tex->tex.destroy();
+ image_tex->tex.create(thumb);
+
+ container->add_child(node);
+ }
+*/
+}
+
+void NodeDialogCloud::loaded()
+{
+}
+
+std::vector split(const std::string& subject, char d)
+{
+ std::vector ret;
+ int start = 0;
+ int n = subject.find_first_of(d);
+ while (n != std::string::npos)
+ {
+ ret.push_back(subject.substr(start, n - start));
+ start = n + 1;
+ n = subject.find_first_of(d, start);
+ }
+ ret.push_back(subject.substr(start));
+ return ret;
+}
+
+void NodeDialogCloud::load_thumbs_thread()
+{
+ CURL *curl = curl_easy_init();
+ std::string res;
+ if (curl)
+ {
+ 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");
+ auto err = curl_easy_perform(curl);
+ LOG("CLOUD LIST: %s", res.c_str());
+
+ auto names = split(res, ',');
+ for (const auto& n : names)
+ {
+ res.clear();
+ std::string url = "http://omigamedev.ddns.net:8080/panoview/cloud-info.php?file=" + n;
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+ auto err = curl_easy_perform(curl);
+ auto info = split(res, ',');
+ int width = atoi(info[0].c_str());
+ int height = atoi(info[1].c_str());
+ int comp = atoi(info[2].c_str());
+ assert(comp == 4);
+ std::string rgb;
+ rgb.resize(Base64::DecodedLength(info[3]));
+ Base64::Decode(info[3], &rgb);
+ ui::Image thumb;
+ thumb.create(width, height);
+ 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_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;
+// };
+
+ // load thumb
+// ui::Image thumb = ui::Canvas::I->thumbnail_read(node->m_path);
+
+ }
+ curl_easy_cleanup(curl);
+ }
+}
+
+//////////////////////////////////////////////////////////////////
+
+Node* NodeDialogCloudItem::clone_instantiate() const
+{
+ return new NodeDialogCloudItem;
+}
+void NodeDialogCloudItem::clone_finalize(Node* dest) const
+{
+ NodeDialogCloudItem* n = static_cast(dest);
+ n->init_controls();
+}
+void NodeDialogCloudItem::init()
+{
+ auto tpl = static_cast(init_template("dialog-cloud-item"));
+ m_color = tpl->m_color;
+ m_border_color = tpl->m_border_color;
+ m_thinkness = tpl->m_thinkness;
+ init_controls();
+}
+void NodeDialogCloudItem::init_controls()
+{
+ m_text = find("title");
+}
+void NodeDialogCloudItem::loaded()
+{
+
+}
+void NodeDialogCloudItem::draw()
+{
+ auto c = m_selected ? m_color_selected : m_color_normal;
+ m_thinkness = m_selected ? 1.f : 0.f;
+ m_color = m_mouse_inside ? m_color_hover : c;
+ NodeBorder::draw();
+}
+kEventResult NodeDialogCloudItem::handle_event(Event* e)
+{
+ NodeBorder::handle_event(e);
+ switch (e->m_type)
+ {
+ case kEventType::MouseEnter:
+ break;
+ case kEventType::MouseLeave:
+ break;
+ case kEventType::MouseDownL:
+ m_selected = true;
+ if (on_selected)
+ on_selected(this);
+ break;
+ case kEventType::MouseUpL:
+ break;
+ default:
+ return kEventResult::Available;
+ break;
+ }
+ return kEventResult::Consumed;
+}
diff --git a/engine/node_dialog_cloud.h b/engine/node_dialog_cloud.h
new file mode 100644
index 0000000..46d9357
--- /dev/null
+++ b/engine/node_dialog_cloud.h
@@ -0,0 +1,47 @@
+#pragma once
+#include "node_border.h"
+#include "node_button.h"
+#include "node_image_texture.h"
+#include "node_text.h"
+#include "node_text_input.h"
+
+class NodeDialogCloudItem : public NodeBorder
+{
+public:
+ NodeText* m_text;
+ NodeImageTexture* m_thumb;
+ glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1);
+ glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1);
+ glm::vec4 m_color_hover = glm::vec4(.5, .5, .5, 1);
+ bool m_selected = false;
+ std::string m_path;
+ std::string m_file_name;
+ std::function on_selected;
+ virtual Node* clone_instantiate() const override;
+ virtual void clone_finalize(Node* dest) const override;
+ virtual void init() override;
+ void init_controls();
+ virtual void loaded() override;
+ virtual void draw() override;
+ virtual kEventResult handle_event(Event* e) override;
+};
+
+class NodeDialogCloud : public NodeBorder
+{
+public:
+ NodeButton* btn_cancel;
+ NodeButton* btn_ok;
+ NodeButton* btn_delete;
+ NodeDialogCloudItem* current = nullptr;
+ Node* container;
+ std::string selected_path;
+ std::string selected_file;
+ std::string selected_name;
+ std::string data_path;
+ virtual Node* clone_instantiate() const override;
+ virtual void clone_finalize(Node* dest) const override;
+ virtual void init() override;
+ void init_controls();
+ virtual void loaded() override;
+ void load_thumbs_thread();
+};
diff --git a/engine/pch.h b/engine/pch.h
index 6aa8331..0a61143 100644
--- a/engine/pch.h
+++ b/engine/pch.h
@@ -105,6 +105,7 @@
#include
#include
#include
+#include
#endif
#include
diff --git a/engine/util.cpp b/engine/util.cpp
index 48ad39e..4ba3805 100644
--- a/engine/util.cpp
+++ b/engine/util.cpp
@@ -125,6 +125,13 @@ void check_OpenGLError(const char* stmt, const char* fname, int line)
}
}
+size_t curl_data_handler(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ auto buffer = reinterpret_cast(userp);
+ buffer->append((char*)contents, size * nmemb);
+ return size * nmemb;
+}
+
#ifdef _DEBUG
#define GL(stmt) stmt; CheckOpenGLError(#stmt, __FILE__, __LINE__);
#else
diff --git a/engine/util.h b/engine/util.h
index 90e6f65..1901088 100644
--- a/engine/util.h
+++ b/engine/util.h
@@ -16,6 +16,7 @@ glm::vec4 rand_color();
glm::vec3 convert_hsv2rgb(const glm::vec3 c);
glm::vec3 convert_rgb2hsv(const glm::vec3 c);
+size_t curl_data_handler(void *contents, size_t size, size_t nmemb, void *userp);
void check_OpenGLError(const char* stmt, const char* fname, int line);
template struct cbuffer
diff --git a/libs/base64 b/libs/base64
new file mode 160000
index 0000000..4d1fdf9
--- /dev/null
+++ b/libs/base64
@@ -0,0 +1 @@
+Subproject commit 4d1fdf97c94fa46eaee11cf4d561f8406217dc78