From f4cd7fdc62b68a4209accd09b46f91d3d57af171 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sat, 9 Dec 2017 09:07:42 +0000 Subject: [PATCH] add file picking for osx and ios and implement equirectangular import --- PanoPainter-OSX/main.cpp | 23 ++++++++++++++ PanoPainter-OSX/main.h | 1 + PanoPainter/GameViewController.h | 6 ++-- PanoPainter/GameViewController.m | 51 +++++++++++++++++++++++++++++++- data/layout.xml | 4 +++ engine/app.cpp | 4 +-- engine/app.h | 1 + engine/app_events.cpp | 15 ++++++++++ engine/app_layout.cpp | 7 +++++ engine/canvas.cpp | 39 ++++++++++++++++++++++++ engine/canvas.h | 2 ++ engine/image.cpp | 9 ++++++ engine/image.h | 1 + engine/node_canvas.cpp | 4 +-- engine/node_color_quad.cpp | 2 +- engine/pch.h | 1 + engine/shape.cpp | 31 +++++++++++++++++++ engine/shape.h | 14 +++++++++ engine/texture.cpp | 10 +++++++ engine/texture.h | 1 + 20 files changed, 218 insertions(+), 8 deletions(-) diff --git a/PanoPainter-OSX/main.cpp b/PanoPainter-OSX/main.cpp index 8b2c9d4..6c85f52 100644 --- a/PanoPainter-OSX/main.cpp +++ b/PanoPainter-OSX/main.cpp @@ -26,6 +26,29 @@ { CGLFlushDrawable([glctx CGLContextObj]); } +- (std::string)pick_file +{ + NSOpenPanel *panel = [NSOpenPanel openPanel]; + [panel setCanChooseFiles:YES]; + [panel setCanChooseDirectories:NO]; + [panel setAllowsMultipleSelection:NO]; // yes if more than one dir is allowed + + NSArray* fileTypes = [NSArray arrayWithObjects:@"png", @"PNG", @"jpg", @"JPG", @"jpeg", nil]; + [panel setAllowedFileTypes:fileTypes]; + + NSInteger clicked = [panel runModal]; + + std::string ret; + if (clicked == NSFileHandlingPanelOKButton) + { + for (NSURL *url in [panel URLs]) + { + LOG("selected file: %s", [[url path] cStringUsingEncoding:NSUTF8StringEncoding]); + ret = [[url path] cStringUsingEncoding:NSUTF8StringEncoding]; + } + } + return ret; +} - (instancetype)initWithFrame:(NSRect)frameRect { gl_ready = false; diff --git a/PanoPainter-OSX/main.h b/PanoPainter-OSX/main.h index 8b3ecac..0abc5ed 100644 --- a/PanoPainter-OSX/main.h +++ b/PanoPainter-OSX/main.h @@ -14,4 +14,5 @@ - (void)async_lock; - (void)async_unlock; - (void)async_swap; +- (std::string)pick_file; @end diff --git a/PanoPainter/GameViewController.h b/PanoPainter/GameViewController.h index 8462543..824fc1c 100644 --- a/PanoPainter/GameViewController.h +++ b/PanoPainter/GameViewController.h @@ -9,7 +9,7 @@ #import #import -@interface GameViewController : GLKViewController +@interface GameViewController : GLKViewController { @public GLKView* glview; } @@ -17,5 +17,7 @@ - (void)async_lock; - (void)async_unlock; - (void)async_swap; - +- (void)pick_photo:(std::function) callback; +- (void)registerForKeyboardNotifications; +- (void)unregisterForKeyboardNotifications; @end diff --git a/PanoPainter/GameViewController.m b/PanoPainter/GameViewController.m index 0278b5c..28a6bd2 100644 --- a/PanoPainter/GameViewController.m +++ b/PanoPainter/GameViewController.m @@ -11,6 +11,15 @@ #import #include "app.h" +@interface GameImagePicker : UIImagePickerController +{ + @public std::promise promise; + @public std::function callback; +} +@end +@implementation GameImagePicker +@end + @interface GameViewController () { NSLock* gl_lock; } @@ -48,6 +57,33 @@ NSThread* lock_thread; [self.context presentRenderbuffer:GL_RENDERBUFFER]; } +- (void)pick_photo:(std::function) callback +{ + GameImagePicker *picker = [[GameImagePicker alloc] init]; + picker.delegate = self; + picker.allowsEditing = NO; + picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; + picker->callback = callback; + [self presentViewController:picker animated:YES completion:NULL]; +} + +- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info +{ + NSString *chosenImage = [info[UIImagePickerControllerImageURL] path]; + GameImagePicker* p = static_cast(picker); + std::string path = [chosenImage cStringUsingEncoding:NSUTF8StringEncoding]; + p->callback(path); + [picker dismissViewControllerAnimated:YES completion:^{ + [self keyboardWillBeHidden:nil]; + }]; +} +- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker +{ + [picker dismissViewControllerAnimated:YES completion:^{ + [self keyboardWillBeHidden:nil]; + }]; +} + - (void)insertText:(NSString *)text { if (const char* cstr = [text cStringUsingEncoding:NSASCIIStringEncoding]) @@ -84,6 +120,18 @@ NSThread* lock_thread; selector:@selector(keyboardWasHidden:) name:UIKeyboardDidHideNotification object:nil]; } +- (void)unregisterForKeyboardNotifications +{ + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIKeyboardWillHideNotification object:nil]; + + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIKeyboardDidShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIKeyboardDidHideNotification object:nil]; +} - (void)keyboardWillBeShown:(NSNotification*)aNotification { @@ -94,6 +142,7 @@ NSThread* lock_thread; { App::I.redraw = true; App::I.animate = false; + [self unregisterForKeyboardNotifications]; } // Called when the UIKeyboardDidShowNotification is sent. @@ -233,7 +282,7 @@ NSThread* lock_thread; { App::I.redraw = true; [self resignFirstResponder]; - [self registerForKeyboardNotifications]; + //[self registerForKeyboardNotifications]; } - (void)viewDidLoad diff --git a/data/layout.xml b/data/layout.xml index c4385af..136c0fa 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -531,6 +531,10 @@ + + + + diff --git a/engine/app.cpp b/engine/app.cpp index b209a13..1441fe2 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -259,8 +259,8 @@ void App::update(float dt) 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); + //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 a334411..8cfe018 100644 --- a/engine/app.h +++ b/engine/app.h @@ -82,6 +82,7 @@ public: struct android_app* and_app; struct engine* and_engine; #endif + void pick_image(std::function callback); void showKeyboard(); void hideKeyboard(); void initLog(); diff --git a/engine/app_events.cpp b/engine/app_events.cpp index cd5c831..682213c 100644 --- a/engine/app_events.cpp +++ b/engine/app_events.cpp @@ -22,6 +22,7 @@ void App::showKeyboard() LOG("show keyboard"); redraw = true; #ifdef __IOS__ + [ios_view registerForKeyboardNotifications]; [ios_view becomeFirstResponder]; #elif __ANDROID__ displayKeyboard(and_app, true); @@ -34,11 +35,25 @@ void App::hideKeyboard() redraw = true; #ifdef __IOS__ [ios_view resignFirstResponder]; + [ios_view unregisterForKeyboardNotifications]; #elif __ANDROID__ displayKeyboard(and_app, false); #endif } +void App::pick_image(std::function callback) +{ + redraw = true; +#ifdef __IOS__ + [ios_view pick_photo:callback]; +#elif __OSX__ + std::string path = [osx_view pick_file]; + callback(path); +#elif __ANDROID__ + //displayKeyboard(and_app, false); +#endif +} + bool App::mouse_down(int button, float x, float y, float pressure, kEventSource source) { redraw = true; diff --git a/engine/app_layout.cpp b/engine/app_layout.cpp index 3c477df..2803126 100644 --- a/engine/app_layout.cpp +++ b/engine/app_layout.cpp @@ -350,6 +350,13 @@ void App::init_menu_file() popup->mouse_release(); popup->destroy(); }; + popup->find("file-import")->on_click = [this](Node*) { + App::I.pick_image([](std::string path){ + Canvas::I->import_equirectangular(path); + }); + popup->mouse_release(); + popup->destroy(); + }; popup->find("file-open")->on_click = [this](Node*) { dialog_open(); popup->mouse_release(); diff --git a/engine/canvas.cpp b/engine/canvas.cpp index 280b581..b8e7c6c 100644 --- a/engine/canvas.cpp +++ b/engine/canvas.cpp @@ -800,6 +800,45 @@ void ui::Canvas::clear_context() } }; +void ui::Canvas::import_equirectangular(std::string file_path) +{ + std::thread t(&ui::Canvas::import_equirectangular_thread, this, file_path); + t.detach(); +} + +void ui::Canvas::import_equirectangular_thread(std::string file_path) +{ + Texture2D tex; + gl_state gl; + App::I.async_start(); + gl.save(); + snap_history({0,1,2,3,4,5}); + tex.load_file(file_path); + ui::Sphere sphere; + glDisable(GL_DEPTH_TEST); + sphere.create<64, 64>(2.f); + draw_objects([&](const glm::mat4& camera, const glm::mat4& proj){ + m_sampler.bind(0); + glActiveTexture(GL_TEXTURE0); + tex.bind(); + ShaderManager::use(ui::kShader::Texture); + ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera * + glm::eulerAngleY(glm::radians(-90.f)) * glm::scale(glm::vec3(1,-1,1))); + sphere.draw_fill(); + tex.unbind(); + m_sampler.unbind(); + }); + for (int i = 0; i < 6; i++) + { + m_dirty_box[i] = glm::vec4(0, 0, m_width, m_height); + m_dirty_face[i] = true; + } + App::I.async_update(); + gl.restore(); + App::I.async_end(); +} + void ui::Canvas::export_equirectangular(std::string file_path) { std::thread t(&ui::Canvas::export_equirectangular_thread, this, file_path); diff --git a/engine/canvas.h b/engine/canvas.h index 4e8b1ff..ccc429e 100644 --- a/engine/canvas.h +++ b/engine/canvas.h @@ -131,6 +131,8 @@ public: void snapshot_restore(); void snap_history(const std::vector& planes); void clear_context(); + void import_equirectangular(std::string file_path); + void import_equirectangular_thread(std::string file_path); void export_equirectangular(std::string file_path); void export_equirectangular_thread(std::string file_path); void export_anim(std::string data_path); diff --git a/engine/image.cpp b/engine/image.cpp index 9f5cd36..e3679a2 100644 --- a/engine/image.cpp +++ b/engine/image.cpp @@ -23,6 +23,15 @@ bool Image::load(std::string filename) return true; } +bool Image::load_file(std::string filename) +{ + stbi_set_flip_vertically_on_load(false); + uint8_t* buffer = stbi_load(filename.c_str(), &width, &height, nullptr, 4); + comp = 4; + m_data = std::unique_ptr(buffer); + return true; +} + void Image::flip() { auto flipped = std::make_unique(width*height*4); diff --git a/engine/image.h b/engine/image.h index 1a414fb..f4e44ae 100644 --- a/engine/image.h +++ b/engine/image.h @@ -10,6 +10,7 @@ public: int height = 0; int comp = 4; bool load(std::string filename); + bool load_file(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) diff --git a/engine/node_canvas.cpp b/engine/node_canvas.cpp index dbdbb79..6dd5ae3 100644 --- a/engine/node_canvas.cpp +++ b/engine/node_canvas.cpp @@ -125,7 +125,7 @@ void NodeCanvas::draw() ui::ShaderManager::u_int(kShaderUniform::TexStroke, 1); ui::ShaderManager::u_int(kShaderUniform::TexMask, 2); ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); - ui::ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[m_canvas->m_current_layer_idx].m_alpha_locked); + ui::ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index].m_alpha_locked); ui::ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active); ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); glActiveTexture(GL_TEXTURE0); @@ -151,7 +151,7 @@ void NodeCanvas::draw() ui::ShaderManager::u_int(kShaderUniform::TexMask, 2); //ui::ShaderManager::u_int(kShaderUniform::TexStencil, 3); ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); - ui::ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[m_canvas->m_current_layer_idx].m_alpha_locked); + ui::ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index].m_alpha_locked); ui::ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active); ui::ShaderManager::u_int(kShaderUniform::BlendMode, m_canvas->m_current_stroke->m_brush.m_blend_mode); ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); diff --git a/engine/node_color_quad.cpp b/engine/node_color_quad.cpp index ad9026a..a7457f0 100644 --- a/engine/node_color_quad.cpp +++ b/engine/node_color_quad.cpp @@ -94,6 +94,6 @@ void NodeColorQuad::draw() using namespace ui; ui::ShaderManager::use(kShader::ColorQuad); ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); - ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); + ui::ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(convert_rgb2hsv(glm::vec3(m_color)), 1)); m_plane.draw_fill(); } diff --git a/engine/pch.h b/engine/pch.h index 41c2419..1a6380a 100644 --- a/engine/pch.h +++ b/engine/pch.h @@ -86,6 +86,7 @@ #include #include #include +#include #include #include #include diff --git a/engine/shape.cpp b/engine/shape.cpp index 698b459..9ecafae 100644 --- a/engine/shape.cpp +++ b/engine/shape.cpp @@ -456,6 +456,37 @@ void Slice9::create_impl(float w, float h, float r, float tr, GLushort *idx, Sha *idx++ = 12; // D *idx++ = 0; // A } +void Sphere::create_impl(int rings, int sectors, float radius, GLushort *idx, Shape::vertex_t *vertices) +{ + count[0] = rings * sectors * 6; + count[1] = 0; + ioff[0] = (GLvoid*)0; + ioff[1] = (GLvoid*)0; + + float const R = 1.f / (float)(rings-1); + float const S = 1.f / (float)(sectors-1); + int r, s; + + auto v = vertices; + for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) { + float const y = (float)sin( -M_PI_2 + M_PI * r * R ); + float const x = (float)cos(2*M_PI * s * S) * (float)sin( M_PI * r * R ); + float const z = (float)sin(2*M_PI * s * S) * (float)sin( M_PI * r * R ); + + *v++ = { glm::vec4(x, y, z, 1) * radius, glm::vec2(s*S, r*R) }; + } + + auto i = idx; + for(r = 0; r < rings-1; r++) for(s = 0; s < sectors-1; s++) { + *i++ = r * sectors + s; + *i++ = r * sectors + (s+1); + *i++ = (r+1) * sectors + (s+1); + + *i++ = r * sectors + s; + *i++ = (r+1) * sectors + (s+1); + *i++ = (r+1) * sectors + s; + } +} void ui::LineSegment::update_vertices(const glm::vec4 data[2]) { static vertex_t vertices[2]; diff --git a/engine/shape.h b/engine/shape.h index 238e65c..e39bbed 100644 --- a/engine/shape.h +++ b/engine/shape.h @@ -194,4 +194,18 @@ public: } }; +class Sphere : public Shape +{ + void create_impl(int rings, int sectors, float radius, GLushort* idx, vertex_t* vertices); +public: + template + bool create(float radius) + { + static GLushort idx[rings * sectors * 6]; + static vertex_t vertices[rings * sectors]; + create_impl(rings, sectors, radius, idx, vertices); + return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices)); + } +}; + } diff --git a/engine/texture.cpp b/engine/texture.cpp index ef1ea3d..031fbe2 100644 --- a/engine/texture.cpp +++ b/engine/texture.cpp @@ -82,6 +82,16 @@ bool Texture2D::load(std::string filename) return false; return create(img); } + +bool Texture2D::load_file(std::string filename) +{ + LOG("load texture %s", filename.c_str()); + ui::Image img; + if (!img.load_file(filename)) + return false; + return create(img); +} + void Texture2D::update(const uint8_t* data) { bind(); diff --git a/engine/texture.h b/engine/texture.h index a3c7314..82b4547 100644 --- a/engine/texture.h +++ b/engine/texture.h @@ -13,6 +13,7 @@ public: bool create(const ui::Image& img); void assign(GLuint tex, int w = -1, int h = -1, GLuint internal_format = GL_RGBA8, GLuint format = GL_RGBA); bool load(std::string filename); + bool load_file(std::string filename); void destroy() { if (m_tex) LOG("TEX destroy %d", m_tex); glDeleteTextures(1, &m_tex); } void bind() const { glBindTexture(GL_TEXTURE_2D, m_tex); } void unbind() const { glBindTexture(GL_TEXTURE_2D, 0); }