466 lines
14 KiB
Objective-C
466 lines
14 KiB
Objective-C
//
|
|
// GameViewController.m
|
|
// PanoPainter
|
|
//
|
|
// Created by Omar Mohamed Ali Mudhir on 07/05/17.
|
|
// Copyright © 2017 Omar Mohamed Ali Mudhir. All rights reserved.
|
|
//
|
|
|
|
#include "pch.h"
|
|
#import "GameViewController.h"
|
|
#import <OpenGLES/ES3/glext.h>
|
|
#include "app.h"
|
|
#import "objc_utils.h"
|
|
|
|
std::deque<std::packaged_task<void()>> tasklist;
|
|
std::mutex task_mutex;
|
|
|
|
@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;
|
|
}
|
|
@property (strong, nonatomic) EAGLContext *context;
|
|
|
|
- (void)setupGL;
|
|
- (void)tearDownGL;
|
|
@end
|
|
|
|
//std::map<
|
|
bool pen_down = false;
|
|
int t_count = 0;
|
|
glm::vec2 t_pos;
|
|
int lock_count = 0;
|
|
NSThread* lock_thread;
|
|
std::recursive_mutex lock_mutex;
|
|
|
|
@implementation GameViewController
|
|
- (void)async_lock
|
|
{
|
|
lock_mutex.lock();
|
|
// if (lock_thread != [NSThread currentThread] || lock_count == 0)
|
|
// [gl_lock lock];
|
|
// lock_thread = [NSThread currentThread];
|
|
// lock_count++;
|
|
[EAGLContext setCurrentContext:self.context];
|
|
GLKView* view = (GLKView*)self.view;
|
|
[view bindDrawable];
|
|
}
|
|
- (void)async_unlock
|
|
{
|
|
// lock_count--;
|
|
// if (lock_count == 0)
|
|
// [gl_lock unlock];
|
|
lock_mutex.unlock();
|
|
}
|
|
- (void)async_swap
|
|
{
|
|
[self.context presentRenderbuffer:GL_RENDERBUFFER];
|
|
}
|
|
|
|
-(void)display_file:(std::string)filename
|
|
{
|
|
NSString* filePath = [NSString stringWithUTF8String:filename.c_str()];
|
|
NSURL *url = [NSURL fileURLWithPath:filePath];
|
|
UIDocumentInteractionController *popup = [UIDocumentInteractionController interactionControllerWithURL:url];
|
|
[popup setDelegate:self];
|
|
[popup presentPreviewAnimated:YES];
|
|
}
|
|
|
|
-(UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller
|
|
{
|
|
return self;
|
|
}
|
|
|
|
|
|
- (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])
|
|
App::I.key_char(cstr[0]);
|
|
NSLog(@"%@", text);
|
|
// Do something with the typed character
|
|
}
|
|
- (void)deleteBackward {
|
|
App::I.key_char('\b');
|
|
// Handle the delete key
|
|
}
|
|
- (BOOL)hasText {
|
|
// Return whether there's any text present
|
|
return YES;
|
|
}
|
|
- (BOOL)canBecomeFirstResponder {
|
|
return YES;
|
|
}
|
|
|
|
// Call this method somewhere in your view controller setup code.
|
|
- (void)registerForKeyboardNotifications
|
|
{
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWillBeShown:)
|
|
name:UIKeyboardWillShowNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWillBeHidden:)
|
|
name:UIKeyboardWillHideNotification object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWasShown:)
|
|
name:UIKeyboardDidShowNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
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
|
|
{
|
|
App::I.redraw = true;
|
|
App::I.animate = true;
|
|
}
|
|
- (void)keyboardWasHidden:(NSNotification*)aNotification
|
|
{
|
|
App::I.redraw = true;
|
|
App::I.animate = false;
|
|
[self unregisterForKeyboardNotifications];
|
|
}
|
|
|
|
// Called when the UIKeyboardDidShowNotification is sent.
|
|
- (void)keyboardWasShown:(NSNotification*)aNotification
|
|
{
|
|
NSDictionary* info = [aNotification userInfo];
|
|
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
|
|
|
|
GLKView *view = (GLKView *)self.view;
|
|
CGRect frame = view.frame;
|
|
frame.size.height -= kbSize.height;
|
|
view.frame = frame;
|
|
|
|
[self async_lock];
|
|
App::I.resize(frame.size.width * view.contentScaleFactor,
|
|
frame.size.height * view.contentScaleFactor);
|
|
App::I.animate = false;
|
|
[self async_unlock];
|
|
}
|
|
|
|
// Called when the UIKeyboardWillHideNotification is sent
|
|
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
|
|
{
|
|
CGRect frame = [[UIScreen mainScreen] bounds];
|
|
self.view.frame = frame;
|
|
[self async_lock];
|
|
App::I.resize(frame.size.width * self.view.contentScaleFactor,
|
|
frame.size.height * self.view.contentScaleFactor);
|
|
App::I.animate = true;
|
|
[self async_unlock];
|
|
}
|
|
|
|
- (void)reset_touch
|
|
{
|
|
[self async_lock];
|
|
if (t_count == 2)
|
|
App::I.gesture_end();
|
|
else
|
|
App::I.mouse_cancel(0);
|
|
[self async_unlock];
|
|
t_count = 0;
|
|
}
|
|
|
|
std::set<UITouch*> ignored_touch;
|
|
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
|
{
|
|
UITouch *touch = [[event allTouches] anyObject];
|
|
|
|
CGPoint touchLocation = [touch locationInView:self.view];
|
|
float scale = self.view.contentScaleFactor;
|
|
|
|
kEventSource source = touch.type == UITouchType::UITouchTypeStylus ? kEventSource::Stylus : kEventSource::Touch;
|
|
if (source == kEventSource::Stylus)
|
|
pen_down = true;
|
|
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([touchLocation, scale, f=touch.force, source] {
|
|
App::I.mouse_down(0, touchLocation.x * scale, touchLocation.y * scale, f, source, 0);
|
|
});
|
|
t_count = 1;
|
|
t_pos = {touchLocation.x * scale, touchLocation.y * scale};
|
|
}
|
|
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
|
{
|
|
NSUInteger n = [[event allTouches] count];
|
|
float scale = self.view.contentScaleFactor;
|
|
|
|
std::vector<UITouch*> valid_touches;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
auto t = [[[event allTouches] allObjects] objectAtIndex:i];
|
|
auto it = ignored_touch.find(t);
|
|
if (it == ignored_touch.end())
|
|
{
|
|
valid_touches.push_back(t);
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"ignored moved");
|
|
}
|
|
}
|
|
n = valid_touches.size();
|
|
if (n == 0)
|
|
return;
|
|
|
|
UITouch* t0 = nullptr;
|
|
UITouch* t1 = nullptr;
|
|
|
|
t0 = valid_touches[0];
|
|
CGPoint tl0 = [t0 locationInView:self.view];
|
|
glm::vec2 p0 = glm::vec2(tl0.x * scale, tl0.y * scale);
|
|
glm::vec2 p1;
|
|
if (n > 1)
|
|
{
|
|
t1 = valid_touches[1];
|
|
CGPoint tl1 = [t1 locationInView:self.view];
|
|
p1 = glm::vec2(tl1.x * scale, tl1.y * scale);
|
|
}
|
|
|
|
if (pen_down)
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([tt0=t0.type,tt1=t1.type,p0,p1,f0=t0.force,f1=t1.force] {
|
|
if (tt0 == UITouchType::UITouchTypeStylus)
|
|
App::I.mouse_move(p0.x, p0.y, f0, kEventSource::Stylus, 0);
|
|
if (tt1 == UITouchType::UITouchTypeStylus)
|
|
App::I.mouse_move(p1.x, p1.y, f1, kEventSource::Stylus, 0);
|
|
});
|
|
}
|
|
else if (n == 2)
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([c=t_count, p0, p1] {
|
|
if (c == 1)
|
|
{
|
|
App::I.mouse_cancel(0);
|
|
App::I.gesture_start(p0, p1);
|
|
}
|
|
else
|
|
App::I.gesture_move(p0, p1);
|
|
});
|
|
t_count = 2;
|
|
}
|
|
else if (n == 1)
|
|
{
|
|
UITouch *touch = valid_touches[0];
|
|
CGPoint touchLocation = [touch locationInView:self.view];
|
|
float force = touch.type == UITouchType::UITouchTypeStylus ? touch.force : 1.0f;
|
|
//AppDelegate* app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
|
|
//force = [app sonarpen_pressure];
|
|
if (t_count == 2)
|
|
{
|
|
//App::I.gesture_end();
|
|
//App::I.mouse_down(0, touchLocation.x * scale, touchLocation.y * scale, force);
|
|
}
|
|
else
|
|
{
|
|
kEventSource source = touch.type == UITouchType::UITouchTypeStylus ? kEventSource::Stylus : kEventSource::Touch;
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([touchLocation, scale, source, force] {
|
|
App::I.mouse_move(touchLocation.x * scale, touchLocation.y * scale, force, source, 0);
|
|
});
|
|
}
|
|
}
|
|
t_pos = {tl0.x * scale, tl0.y * scale};
|
|
}
|
|
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
|
{
|
|
UITouch *touch = [[event allTouches] anyObject];
|
|
CGPoint touchLocation = [touch locationInView:self.view];
|
|
float scale = self.view.contentScaleFactor;
|
|
|
|
if (ignored_touch.count(touch))
|
|
{
|
|
ignored_touch.erase(touch);
|
|
NSLog(@"remove ignored");
|
|
}
|
|
|
|
kEventSource source = touch.type == UITouchType::UITouchTypeStylus ? kEventSource::Stylus : kEventSource::Touch;
|
|
pen_down = false;
|
|
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([tc=t_count, touchLocation, scale, source] {
|
|
if (tc == 2)
|
|
App::I.gesture_end();
|
|
else
|
|
App::I.mouse_up(0, touchLocation.x * scale, touchLocation.y * scale, source, 0);
|
|
});
|
|
|
|
t_count = 0;
|
|
t_pos = {touchLocation.x * scale, touchLocation.y * scale};
|
|
}
|
|
|
|
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
|
|
{
|
|
[self async_lock];
|
|
App::I.resize(size.width * self.view.contentScaleFactor,
|
|
size.height * self.view.contentScaleFactor);
|
|
[self async_unlock];
|
|
}
|
|
|
|
- (void)viewDidAppear:(BOOL)animated
|
|
{
|
|
App::I.redraw = true;
|
|
[self resignFirstResponder];
|
|
//[self registerForKeyboardNotifications];
|
|
}
|
|
|
|
- (void)viewDidLoad
|
|
{
|
|
[super viewDidLoad];
|
|
App::I.initLog();
|
|
App::I.ios_view = self;
|
|
|
|
self.preferredFramesPerSecond = 60;
|
|
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
|
|
|
|
if (!self.context) {
|
|
NSLog(@"Failed to create ES context");
|
|
}
|
|
|
|
gl_lock = [[NSLock alloc] init];
|
|
|
|
GLKView *view = (GLKView *)self.view;
|
|
view.context = self.context;
|
|
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
|
|
glview = view;
|
|
|
|
App::I.width = view.frame.size.width * view.contentScaleFactor;
|
|
App::I.height = view.frame.size.height * view.contentScaleFactor;
|
|
|
|
[self setupGL];
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self tearDownGL];
|
|
|
|
if ([EAGLContext currentContext] == self.context) {
|
|
[EAGLContext setCurrentContext:nil];
|
|
}
|
|
}
|
|
|
|
- (void)didReceiveMemoryWarning
|
|
{
|
|
[super didReceiveMemoryWarning];
|
|
|
|
if ([self isViewLoaded] && ([[self view] window] == nil)) {
|
|
self.view = nil;
|
|
|
|
[self tearDownGL];
|
|
|
|
if ([EAGLContext currentContext] == self.context) {
|
|
[EAGLContext setCurrentContext:nil];
|
|
}
|
|
self.context = nil;
|
|
}
|
|
|
|
// Dispose of any resources that can be recreated.
|
|
}
|
|
|
|
- (BOOL)prefersStatusBarHidden {
|
|
return YES;
|
|
}
|
|
|
|
- (void)setupGL
|
|
{
|
|
[EAGLContext setCurrentContext:self.context];
|
|
App::I.init();
|
|
}
|
|
|
|
- (void)tearDownGL
|
|
{
|
|
[EAGLContext setCurrentContext:self.context];
|
|
}
|
|
|
|
#pragma mark - GLKView and GLKViewController delegate methods
|
|
|
|
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
|
|
{
|
|
static auto time = std::chrono::steady_clock::now();
|
|
static double elapsed = 0;
|
|
auto now = std::chrono::steady_clock::now();
|
|
auto dt = std::chrono::duration<float>(now - time);
|
|
time = now;
|
|
elapsed += dt.count();
|
|
|
|
std::deque<std::packaged_task<void()>> working_list;
|
|
if (!tasklist.empty())
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
working_list = std::move(tasklist);
|
|
}
|
|
|
|
App::I.tick(dt.count());
|
|
|
|
[self async_lock];
|
|
if (!(App::I.redraw || App::I.animate || !working_list.empty()))
|
|
{
|
|
[self.context presentRenderbuffer:GL_FRAMEBUFFER];
|
|
[self async_unlock];
|
|
return;
|
|
}
|
|
|
|
while (!working_list.empty())
|
|
{
|
|
working_list.front()();
|
|
working_list.pop_front();
|
|
}
|
|
App::I.clear();
|
|
App::I.update(elapsed);
|
|
[self.context presentRenderbuffer:GL_FRAMEBUFFER];
|
|
[self async_unlock];
|
|
elapsed = 0;
|
|
}
|
|
|
|
@end
|