diff --git a/data/layout.xml b/data/layout.xml
index 2eb4c93..add29cd 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -173,6 +173,7 @@
+
diff --git a/engine/app.cpp b/engine/app.cpp
index 142aa34..a82af8b 100644
--- a/engine/app.cpp
+++ b/engine/app.cpp
@@ -477,9 +477,20 @@ void App::initLayout()
button->on_click = [this,button](Node*) {
if (canvas)
{
- canvas->m_canvas->open_project(data_path);
- for (auto& i : canvas->m_canvas->m_order)
- layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
+ //canvas->m_canvas->project_open(data_path);
+ //for (auto& i : canvas->m_canvas->m_order)
+ // layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
+
+ // load thumbnail test
+ auto open_dialog = layout[const_hash("popup-dialog-open")]->m_children[0]->clone();
+ layout[main_id]->add_child(open_dialog);
+ layout[main_id]->update();
+ if (open_dialog)
+ {
+ Image thumb = canvas->m_canvas->thumbnail_read(data_path);
+ auto image_tex = open_dialog->find("thumb-tex");
+ image_tex->tex.create(thumb);
+ }
}
};
}
@@ -488,7 +499,7 @@ void App::initLayout()
button->on_click = [this,button](Node*) {
if (canvas)
{
- canvas->m_canvas->save_project(data_path);
+ canvas->m_canvas->project_save(data_path);
}
};
}
diff --git a/engine/canvas.cpp b/engine/canvas.cpp
index c1e3608..32b24b4 100644
--- a/engine/canvas.cpp
+++ b/engine/canvas.cpp
@@ -212,7 +212,7 @@ void ui::Canvas::stroke_draw()
}
void ui::Canvas::stroke_commit()
{
- if (!m_dirty)
+ if (!m_dirty || m_layers.empty())
return;
m_dirty = false;
@@ -503,7 +503,7 @@ void ui::Canvas::save(std::string data_path)
glActiveTexture(GL_TEXTURE0);
}
-void ui::Canvas::save_project(std::string data_path)
+void ui::Canvas::project_save(std::string data_path)
{
static char name[128];
sprintf(name, "%s/latlong.pano", data_path.c_str());
@@ -513,6 +513,14 @@ void ui::Canvas::save_project(std::string data_path)
LOG("cannot write project to %s", name);
return;
}
+
+ // load thumbnail
+ Image thumb = thumbnail_generate(64, 64);
+ fwrite(&thumb.width, sizeof(int), 1, fp);
+ fwrite(&thumb.height, sizeof(int), 1, fp);
+ fwrite(&thumb.comp, sizeof(int), 1, fp);
+ fwrite(thumb.data(), thumb.size(), 1, fp);
+
fwrite(&m_width, sizeof(int), 1, fp);
fwrite(&m_height, sizeof(int), 1, fp);
@@ -538,7 +546,7 @@ void ui::Canvas::save_project(std::string data_path)
LOG("project saved to %s", name);
}
-void ui::Canvas::open_project(std::string data_path)
+void ui::Canvas::project_open(std::string data_path)
{
static char name[128];
sprintf(name, "%s/latlong.pano", data_path.c_str());
@@ -548,6 +556,14 @@ void ui::Canvas::open_project(std::string data_path)
LOG("cannot write project to %s", name);
return;
}
+
+ // skip thumbnail
+ Image thumb;
+ fread(&thumb.width, sizeof(int), 1, fp);
+ fread(&thumb.height, sizeof(int), 1, fp);
+ fread(&thumb.comp, sizeof(int), 1, fp);
+ fseek(fp, thumb.size(), SEEK_CUR);
+
fread(&m_width, sizeof(int), 1, fp);
fread(&m_height, sizeof(int), 1, fp);
@@ -581,8 +597,86 @@ void ui::Canvas::open_project(std::string data_path)
fclose(fp);
LOG("project restore from %s", name);
}
+ui::Image ui::Canvas::thumbnail_generate(int w, int h)
+{
+ // save viewport and clear color states
+ GLint vp[4];
+ GLfloat cc[4];
+ glGetIntegerv(GL_VIEWPORT, vp);
+ glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
+ GLboolean blend = glIsEnabled(GL_BLEND);
+ // prepare common states
+ glViewport(0, 0, w, h);
+ RTT fb;
+ fb.create(w, h);
+ fb.bindFramebuffer();
+ fb.clear({ 1, 1, 1, 1 });
+ ui::Plane m_face_plane;
+ m_face_plane.create<1>(2, 2);
+
+ // recalculate because of different aspect ratio than the m_proj matrix
+ glm::mat4 proj = glm::perspective(glm::radians(m_cam_fov), (float)w / (float)h, 0.1f, 1000.f);
+
+ glEnable(GL_BLEND);
+ for (int plane_index = 0; plane_index < 6; plane_index++)
+ {
+ auto plane_mvp = proj * m_mv * m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1));
+
+ ui::ShaderManager::use(kShader::Checkerboard);
+ ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp);
+ m_face_plane.draw_fill();
+
+ ui::ShaderManager::use(kShader::TextureAlpha);
+ ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
+ ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp);
+ for (auto layer_index : m_order)
+ {
+ ui::ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index].m_opacity);
+ m_layers[layer_index].m_rtt[plane_index].bindTexture();
+ m_face_plane.draw_fill();
+ m_layers[layer_index].m_rtt[plane_index].unbindTexture();
+ }
+ }
+
+ fb.unbindFramebuffer();
+
+ // read the rendered image
+ ui::Image image;
+ image.create(w, h);
+ fb.readTextureData((uint8_t*)image.data());
+
+ fb.destroy();
+
+ // restore viewport and clear color states
+ blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
+ glViewport(vp[0], vp[1], vp[2], vp[3]);
+ glClearColor(cc[0], cc[1], cc[2], cc[3]);
+ glActiveTexture(GL_TEXTURE0);
+
+ return std::move(image);
+}
+ui::Image ui::Canvas::thumbnail_read(std::string data_path)
+{
+ static char name[128];
+ sprintf(name, "%s/latlong.pano", data_path.c_str());
+ FILE* fp = fopen(name, "rb");
+ if (!fp)
+ {
+ LOG("cannot read project %s", name);
+ return {}; // return empty image
+ }
+ Image thumb;
+ fread(&thumb.width, sizeof(int), 1, fp);
+ fread(&thumb.height, sizeof(int), 1, fp);
+ fread(&thumb.comp, sizeof(int), 1, fp);
+ thumb.create();
+ fread((uint8_t*)thumb.data(), thumb.size(), 1, fp);
+ fclose(fp);
+ LOG("project thumbnail read from %s", name);
+ return std::move(thumb);
+}
///////////////////////////////////////////////////////////////////////////////////////////
void ui::Layer::destroy()
diff --git a/engine/canvas.h b/engine/canvas.h
index 689d44b..15d7c9c 100644
--- a/engine/canvas.h
+++ b/engine/canvas.h
@@ -84,8 +84,11 @@ public:
void snapshot_restore();
void clear_context();
void save(std::string data_path);
- void save_project(std::string data_path);
- void open_project(std::string data_path);
+ void project_save(std::string data_path);
+ void project_open(std::string data_path);
+ ui::Image thumbnail_generate(int w, int h);
+ ui::Image thumbnail_read(std::string data_path);
+ void preview_generate();
};
diff --git a/engine/image.h b/engine/image.h
index 3ffec44..e6da6fa 100644
--- a/engine/image.h
+++ b/engine/image.h
@@ -6,12 +6,20 @@ class Image
{
std::unique_ptr m_data;
public:
- int width;
- int height;
- int comp;
+ int width = 0;
+ int height = 0;
+ int comp = 4;
bool load(std::string filename);
const uint8_t* data() const { return m_data.get(); }
int size() const { return width * height * comp; }
+ void create(int w, int h)
+ {
+ width = w;
+ height = h;
+ comp = 4;
+ m_data = std::make_unique(size());
+ }
+ void create() { m_data = std::make_unique(size()); }
};
}
diff --git a/engine/layout.cpp b/engine/layout.cpp
index c054c2e..08928c8 100644
--- a/engine/layout.cpp
+++ b/engine/layout.cpp
@@ -448,6 +448,7 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node)
CASE(kWidget::Node, Node);
CASE(kWidget::Border, NodeBorder);
CASE(kWidget::Image, NodeImage);
+ CASE(kWidget::ImageTexture, NodeImageTexture);
CASE(kWidget::Icon, NodeIcon);
CASE(kWidget::Text, NodeText);
CASE(kWidget::TextInput, NodeTextInput);
@@ -474,6 +475,8 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node)
auto id = const_hash(ids);
auto& ref = (*m_manager)[id]->m_children[0];
auto n = ref->clone();
+ n->m_nodeID_s = ids;
+ n->m_nodeID = id;
add_child(n);
break;
}
diff --git a/engine/layout.h b/engine/layout.h
index 6b23ba5..615e215 100644
--- a/engine/layout.h
+++ b/engine/layout.h
@@ -58,6 +58,7 @@ enum class kWidget : uint16_t
Text = const_hash("text"),
TextInput = const_hash("text-input"),
Image = const_hash("image"),
+ ImageTexture = const_hash("image-texture"),
Icon = const_hash("icon"),
Button = const_hash("button"),
ButtonCustom = const_hash("button-custom"),
@@ -547,6 +548,39 @@ public:
}
};
+class NodeImageTexture : public Node
+{
+public:
+ Texture2D tex;
+ virtual Node* clone_instantiate() const override { return new NodeImageTexture(); }
+ virtual void clone_copy(Node* dest) const override
+ {
+ Node::clone_copy(dest);
+ NodeImageTexture* n = static_cast(dest);
+ n->tex = tex;
+ }
+ // TODO: maybe we can save the texture data and restore later
+ //virtual void restore_context() override
+ //{
+ // Node::restore_context();
+ // create();
+ //}
+ virtual void draw() override
+ {
+ using namespace ui;
+ tex.bind();
+ NodeImage::m_sampler.bind(0);
+ glEnable(GL_BLEND);
+ ui::ShaderManager::use(kShader::Texture);
+ ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
+ ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
+ NodeImage::m_plane.draw_fill();
+ NodeImage::m_sampler.unbind();
+ tex.unbind();
+ glDisable(GL_BLEND);
+ }
+};
+
class NodeButton : public Node
{
public:
@@ -1928,10 +1962,6 @@ public:
//glm::mat4 proj = glm::ortho(0.f, box.z, 0.f, box.w, -1000.f, 1000.f);
glm::mat4 proj = glm::perspective(glm::radians(m_canvas->m_cam_fov), box.z / box.w, 0.1f, 1000.f);
glm::mat4 camera = glm::eulerAngleXY(m_canvas->m_cam_rot.y, m_canvas->m_cam_rot.x);
- glm::mat4 transform =
- glm::translate(glm::vec3(m_pan + m_size * 0.5f * zoom, -1)) * // pan
- glm::scale(glm::vec3(zoom * m_zoom_canvas, zoom * m_zoom_canvas, 1)) *
- glm::eulerAngleY(glm::radians(0.f));
m_canvas->m_mv = camera;
m_canvas->m_proj = proj;