add file picking for osx and ios and implement equirectangular import

This commit is contained in:
2017-12-09 09:07:42 +00:00
parent d18b1103bb
commit f4cd7fdc62
20 changed files with 218 additions and 8 deletions

View File

@@ -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;

View File

@@ -14,4 +14,5 @@
- (void)async_lock;
- (void)async_unlock;
- (void)async_swap;
- (std::string)pick_file;
@end

View File

@@ -9,7 +9,7 @@
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
@interface GameViewController : GLKViewController <UIKeyInput>
@interface GameViewController : GLKViewController <UIKeyInput,UIImagePickerControllerDelegate,UINavigationControllerDelegate>
{
@public GLKView* glview;
}
@@ -17,5 +17,7 @@
- (void)async_lock;
- (void)async_unlock;
- (void)async_swap;
- (void)pick_photo:(std::function<void(std::string)>) callback;
- (void)registerForKeyboardNotifications;
- (void)unregisterForKeyboardNotifications;
@end

View File

@@ -11,6 +11,15 @@
#import <OpenGLES/ES3/glext.h>
#include "app.h"
@interface GameImagePicker : UIImagePickerController
{
@public std::promise<std::string> promise;
@public std::function<void(std::string)> 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<void(std::string)>) 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<GameImagePicker*>(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

View File

@@ -531,6 +531,10 @@
<icon icon="page_add" width="20"/>
<text text="New Pano" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-import" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="page_add" width="20"/>
<text text="Import" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-open" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="page_add" width="20"/>
<text text="Open" margin="0 0 0 5" font-face="arial" font-size="11"/>

View File

@@ -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)
{

View File

@@ -82,6 +82,7 @@ public:
struct android_app* and_app;
struct engine* and_engine;
#endif
void pick_image(std::function<void(std::string path)> callback);
void showKeyboard();
void hideKeyboard();
void initLog();

View File

@@ -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<void(std::string path)> 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;

View File

@@ -350,6 +350,13 @@ void App::init_menu_file()
popup->mouse_release();
popup->destroy();
};
popup->find<NodeButtonCustom>("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<NodeButtonCustom>("file-open")->on_click = [this](Node*) {
dialog_open();
popup->mouse_release();

View File

@@ -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);

View File

@@ -131,6 +131,8 @@ public:
void snapshot_restore();
void snap_history(const std::vector<int>& 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);

View File

@@ -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<uint8_t[]>(buffer);
return true;
}
void Image::flip()
{
auto flipped = std::make_unique<uint8_t[]>(width*height*4);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -86,6 +86,7 @@
#include <vector>
#include <random>
#include <thread>
#include <future>
#include <fstream>
#include <iostream>
#include <algorithm>

View File

@@ -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];

View File

@@ -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<int rings, int sectors>
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));
}
};
}

View File

@@ -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();

View File

@@ -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); }