GameView.mm 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. //
  2. // GameView.m
  3. // danmaku
  4. //
  5. // Created by Sam Jaffe on 5/22/19.
  6. // Copyright © 2019 Sam Jaffe. All rights reserved.
  7. //
  8. #import "GameView.h"
  9. #include <memory>
  10. #include <OpenGL/gl3.h>
  11. #include "game/engine/events.hpp"
  12. #include "game/engine/game_dispatch.hpp"
  13. #include "game/graphics/renderer.hpp"
  14. namespace env { namespace detail {
  15. extern void resize_screen(math::vec2i const&);
  16. }}
  17. @implementation GameView {
  18. std::shared_ptr<graphics::direct_renderer> renderer;
  19. std::shared_ptr<engine::game_dispatch> game;
  20. };
  21. - (void)doInit {
  22. if (renderer) return;
  23. using namespace graphics;
  24. renderer = std::make_shared<direct_renderer>(driver::openGL);
  25. game = std::make_shared<engine::game_dispatch>(renderer);
  26. }
  27. - (id)initWithFrame:(NSRect)aRect {
  28. NSOpenGLPixelFormatAttribute attr[] = {
  29. NSOpenGLPFADoubleBuffer,
  30. 0
  31. };
  32. // create pixel format
  33. NSOpenGLPixelFormat *nsglFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
  34. // create the context...
  35. if (!(self = [super initWithFrame:aRect pixelFormat:nsglFormat])) {
  36. return nil;
  37. }
  38. // make the context current
  39. [[self openGLContext] makeCurrentContext];
  40. // rest of your init method ...
  41. [self doInit];
  42. return self;
  43. }
  44. - (void)prepareOpenGL {
  45. [self doInit];
  46. renderer->clear();
  47. // enable vertical sychronization to refresh rate
  48. const GLint vals = 0x01;
  49. [[self openGLContext] setValues:&vals forParameter:NSOpenGLCPSwapInterval];
  50. const GLint opaque = 0;
  51. [[self openGLContext] setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
  52. }
  53. - (void)drawRect:(NSRect)dirtyRect {
  54. // glClear(GL_COLOR_BUFFER_BIT);
  55. // glLoadIdentity();
  56. env::detail::resize_screen({{(int)dirtyRect.size.width,
  57. (int)dirtyRect.size.height}});
  58. game->render();
  59. // glFlush();
  60. // the correct way to do double buffering on Mac is this:
  61. [[self openGLContext] flushBuffer];
  62. int err;
  63. if ((err = glGetError()) != 0) {
  64. NSLog(@"glGetError(): %d", err);
  65. }
  66. }
  67. - (void)reshape {
  68. NSRect newFrame = {0, 0, [[self window] frame].size.width,
  69. [[self window] frame].size.height};
  70. [self setFrame:newFrame];
  71. // window resize; width and height are in pixel coordinates
  72. // but they are floats
  73. float screen_w = [self frame].size.width;
  74. float screen_h = [self frame].size.height;
  75. // glViewport(0,0, screen_w, screen_h);
  76. // here I cast floats to ints; most systems use integer coordinate systems
  77. env::detail::resize_screen({{(int)screen_w, (int)screen_h}});
  78. }
  79. - (BOOL)acceptsFirstResponder {
  80. return YES;
  81. }
  82. - (void) press_key:(int)key forEvent:(NSEvent *)evt ns_key:(int)local
  83. down:(BOOL)pressed {
  84. if ([evt keyCode] == local) {
  85. game->process_key_event(key, pressed);
  86. }
  87. }
  88. - (void) toggle_key:(int)key forEvent:(NSEvent *)evt ns_key:(int)local {
  89. NSUInteger flags = [evt modifierFlags] &
  90. NSEventModifierFlagDeviceIndependentFlagsMask;
  91. game->process_key_event(key, flags & local);
  92. }
  93. - (void)keyDown:(NSEvent *)theEvent {
  94. if ([theEvent isARepeat])
  95. return;
  96. NSString *str = [theEvent charactersIgnoringModifiers];
  97. unichar c = [str characterAtIndex:0];
  98. // only ASCII please
  99. if (c < ' ' || c > '~') {
  100. c = 0;
  101. }
  102. //(unsigned long)[theEvent modifierFlags]
  103. using namespace engine::keys;
  104. [self toggle_key:MOD_SHIFT forEvent:theEvent
  105. ns_key:NSEventModifierFlagShift];
  106. [self toggle_key:MOD_CONTROL forEvent:theEvent
  107. ns_key:NSEventModifierFlagControl];
  108. [self toggle_key:MOD_ALT forEvent:theEvent
  109. ns_key:NSEventModifierFlagOption];
  110. [self toggle_key:MOD_COMMAND forEvent:theEvent
  111. ns_key:NSEventModifierFlagCommand];
  112. [self press_key:UP_ARROW forEvent:theEvent ns_key:0x007e down:YES];
  113. [self press_key:DOWN_ARROW forEvent:theEvent ns_key:0x007d down:YES];
  114. [self press_key:LEFT_ARROW forEvent:theEvent ns_key:0x007b down:YES];
  115. [self press_key:RIGHT_ARROW forEvent:theEvent ns_key:0x007c down:YES];
  116. // NSLog(@"keyDown -> { 0x%04x, 0x%08lx, 0x%02x=%c }", [theEvent keyCode], [theEvent modifierFlags], c, c);
  117. game->process_key_event(c, true);
  118. }
  119. - (void)keyUp:(NSEvent *)theEvent {
  120. // NSLog(@"keyUp -> { 0x%04x, 0x%08lx }", [theEvent keyCode], [theEvent modifierFlags]);
  121. NSString *str = [theEvent charactersIgnoringModifiers];
  122. unichar c = [str characterAtIndex:0];
  123. // only ASCII please
  124. if (c < ' ' || c > '~') {
  125. c = 0;
  126. }
  127. using namespace engine::keys;
  128. [self press_key:UP_ARROW forEvent:theEvent ns_key:0x007e down:NO];
  129. [self press_key:DOWN_ARROW forEvent:theEvent ns_key:0x007d down:NO];
  130. [self press_key:LEFT_ARROW forEvent:theEvent ns_key:0x007b down:NO];
  131. [self press_key:RIGHT_ARROW forEvent:theEvent ns_key:0x007c down:NO];
  132. game->process_key_event(c, false);
  133. }
  134. static NSTimer *timer = nil;
  135. static NSTimer *frameTimer = nil;
  136. static NSTimeInterval const FRAME_INTERVAL = 1.0 / 60.0;
  137. - (void)windowDidResignMain:(NSNotification *)notification {
  138. NSLog(@"window did resign main");
  139. [timer invalidate];
  140. [frameTimer invalidate];
  141. // env::pause_clock();
  142. [self setNeedsDisplay:YES];
  143. }
  144. - (void)windowDidBecomeMain:(NSNotification *)notification {
  145. NSLog(@"window did become main");
  146. // env::resume_clock();
  147. [self setNeedsDisplay:YES];
  148. // TODO (sjaffe): Become able to change framerate
  149. // TODO (sjaffe): Link this with env::fps::v60, etc.
  150. timer = [NSTimer timerWithTimeInterval:FRAME_INTERVAL
  151. target:self
  152. selector:@selector(timerEvent:)
  153. userInfo:nil
  154. repeats:YES];
  155. frameTimer = [NSTimer timerWithTimeInterval:FRAME_INTERVAL
  156. target:self
  157. selector:@selector(renderEvent:)
  158. userInfo:nil
  159. repeats:YES];
  160. [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
  161. [[NSRunLoop currentRunLoop] addTimer:frameTimer forMode:NSDefaultRunLoopMode];
  162. }
  163. - (void)renderEvent:(NSTimer *)t {
  164. game->render();
  165. [[self openGLContext] flushBuffer];
  166. }
  167. - (void)timerEvent:(NSTimer *)t {
  168. game->update();
  169. [self setNeedsDisplay:YES];
  170. }
  171. - (void) awakeFromNib
  172. {
  173. NSOpenGLPixelFormatAttribute attributes [] = {
  174. NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)24,
  175. NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
  176. (NSOpenGLPixelFormatAttribute)0
  177. };
  178. NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
  179. [self setPixelFormat:pf];
  180. }
  181. @end