// // 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 #include "app.h" #import "objc_utils.h" std::deque> tasklist; std::mutex task_mutex; @interface GameImagePicker : UIImagePickerController { @public std::promise promise; @public std::function 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; @implementation GameViewController - (void)async_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]; } - (void)async_swap { [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]) 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 ignored_touch; - (void)touchesBegan:(NSSet *)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; // if (touch.majorRadius > 25.f) // ignored_touch.insert(touch); // NSLog(@"touch down size %f", touch.majorRadius); // [self async_lock]; std::lock_guard 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); }); // [self async_unlock]; t_count = 1; t_pos = {touchLocation.x * scale, touchLocation.y * scale}; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { NSUInteger n = [[event allTouches] count]; float scale = self.view.contentScaleFactor; std::vector 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); } //[self async_lock]; if (pen_down) { std::lock_guard 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); if (tt1 == UITouchType::UITouchTypeStylus) App::I.mouse_move(p1.x, p1.y, f1, kEventSource::Stylus); }); } else if (n == 2) { std::lock_guard 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]; //auto p = glm::vec2(touchLocation.x * scale, touchLocation.y * scale); float force = touch.type == UITouchType::UITouchTypeStylus ? touch.force : 1.0f; 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 lock(task_mutex); tasklist.emplace_back([touchLocation, scale, source, force] { App::I.mouse_move(touchLocation.x * scale, touchLocation.y * scale, force, source); }); } } //[self async_unlock]; t_pos = {tl0.x * scale, tl0.y * scale}; } -(void)touchesEnded:(NSSet *)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; // [self async_lock]; // if (t_count == 2) // App::I.gesture_end(); // else // App::I.mouse_up(0, touchLocation.x * scale, touchLocation.y * scale, source); // [self async_unlock]; std::lock_guard 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); }); t_count = 0; t_pos = {touchLocation.x * scale, touchLocation.y * scale}; } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)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)update { } - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { static auto time = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now(); auto dt = std::chrono::duration(now - time); std::deque> working_list; if (!tasklist.empty()) { std::lock_guard lock(task_mutex); working_list = std::move(tasklist); } if (!(App::I.redraw || App::I.animate || !working_list.empty())) return; [self async_lock]; while (!working_list.empty()) { working_list.front()(); working_list.pop_front(); } App::I.clear(); App::I.update(dt.count()); [self.context presentRenderbuffer:GL_FRAMEBUFFER]; [self async_unlock]; time = now; } @end