| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- //
- // GameView.m
- // danmaku
- //
- // Created by Sam Jaffe on 5/22/19.
- // Copyright © 2019 Sam Jaffe. All rights reserved.
- //
- #import "GameView.h"
- #include <memory>
- #include <OpenGL/gl3.h>
- #include "danmaku/world.hpp"
- #include "game/engine/events.hpp"
- #include "game/engine/game_dispatch.hpp"
- #include "game/graphics/renderer.hpp"
- namespace env { namespace detail {
- extern void resize_screen(math::vec2i const&);
- }}
- @implementation GameView {
- std::shared_ptr<graphics::direct_renderer> renderer;
- std::shared_ptr<engine::game_dispatch> game;
- }
- - (id)initWithFrame:(NSRect)aRect {
- NSOpenGLPixelFormatAttribute attr[] = {
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFAOpenGLProfile,
- NSOpenGLProfileVersion3_2Core,
- 0
- };
-
- // create pixel format
- NSOpenGLPixelFormat *nsglFormat = [[NSOpenGLPixelFormat alloc]
- initWithAttributes:attr];
- // create the context...
- if (!(self = [super initWithFrame:aRect pixelFormat:nsglFormat])) {
- return nil;
- }
- using graphics::direct_renderer;
- using graphics::driver;
- renderer = std::make_shared<direct_renderer>(driver::openGL);
- game = std::make_shared<engine::game_dispatch>(renderer);
- auto world = danmaku::world::load_world("scripts/level/world.json", game);
- game->register_scene(world);
- game->activate_scene("light-1");
- // make the context current
- NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:nsglFormat
- shareContext:nil];
- [self setOpenGLContext:context];
- [self setPixelFormat:nsglFormat];
- [[self openGLContext] makeCurrentContext];
- return self;
- }
- - (void)prepareOpenGL {
- [super prepareOpenGL];
- // enable vertical sychronization to refresh rate
- const GLint vals = 0x01;
- [[self openGLContext] setValues:&vals forParameter:NSOpenGLCPSwapInterval];
-
- const GLint opaque = 0;
- [[self openGLContext] setValues:&opaque
- forParameter:NSOpenGLCPSurfaceOpacity];
- }
- - (void)drawRect:(NSRect)dirtyRect {
- // glClear(GL_COLOR_BUFFER_BIT);
- // glLoadIdentity();
-
- env::detail::resize_screen({{(int)dirtyRect.size.width,
- (int)dirtyRect.size.height}});
- if (game) {
- renderer->clear();
- game->render();
- }
- // glFlush();
- // the correct way to do double buffering on Mac is this:
- [[self openGLContext] flushBuffer];
-
- int err;
- if ((err = glGetError()) != 0) {
- NSLog(@"glGetError(): %d", err);
- }
- }
- - (void)reshape {
- [super reshape];
-
- const CGSize dims = [[self window] frame].size;
- if (dims.width == 0 || dims.height == 0) return;
-
- [self setFrame:NSRect{0, 0, dims.width, dims.height}];
-
- // window resize; width and height are in pixel coordinates
- // but they are floats
- float screen_w = dims.width;
- float screen_h = dims.height;
-
- // glViewport(0,0, screen_w, screen_h);
-
- // here I cast floats to ints; most systems use integer coordinate systems
- env::detail::resize_screen({{(int)screen_w, (int)screen_h}});
- }
- - (BOOL)acceptsFirstResponder {
- return YES;
- }
- - (void) press_key:(int)key forEvent:(NSEvent *)evt ns_key:(int)local
- down:(BOOL)pressed {
- if ([evt keyCode] == local) {
- game->process_key_event(key, pressed);
- }
- }
- - (void) toggle_key:(int)key forEvent:(NSEvent *)evt ns_key:(int)local {
- NSUInteger flags = [evt modifierFlags] &
- NSEventModifierFlagDeviceIndependentFlagsMask;
- game->process_key_event(key, flags & local);
- }
- - (void)keyDown:(NSEvent *)theEvent {
- if (!game) {
- // This is a really ugly hack because, for dumb reasons, there are two
- // GameView objects, one that I initialize, and one that I don't
- return [(GameView*)[[self window] delegate] keyDown:theEvent];
- }
- if ([theEvent isARepeat])
- return;
-
- NSString *str = [theEvent charactersIgnoringModifiers];
- unichar c = [str characterAtIndex:0];
-
- // only ASCII please
- if (c < ' ' || c > '~') {
- c = 0;
- }
-
- //(unsigned long)[theEvent modifierFlags]
- using namespace engine::keys;
- [self toggle_key:MOD_SHIFT forEvent:theEvent
- ns_key:NSEventModifierFlagShift];
- [self toggle_key:MOD_CONTROL forEvent:theEvent
- ns_key:NSEventModifierFlagControl];
- [self toggle_key:MOD_ALT forEvent:theEvent
- ns_key:NSEventModifierFlagOption];
- [self toggle_key:MOD_COMMAND forEvent:theEvent
- ns_key:NSEventModifierFlagCommand];
- [self press_key:UP_ARROW forEvent:theEvent ns_key:0x007e down:YES];
- [self press_key:DOWN_ARROW forEvent:theEvent ns_key:0x007d down:YES];
- [self press_key:LEFT_ARROW forEvent:theEvent ns_key:0x007b down:YES];
- [self press_key:RIGHT_ARROW forEvent:theEvent ns_key:0x007c down:YES];
- NSLog(@"keyDown -> { 0x%04x, 0x%08lx, 0x%02x=%c }", [theEvent keyCode],
- [theEvent modifierFlags], c, c);
- game->process_key_event(c, true);
- }
- - (void)keyUp:(NSEvent *)theEvent {
- if (!game) {
- // This is a really ugly hack because, for dumb reasons, there are two
- // GameView objects, one that I initialize, and one that I don't
- return [(GameView*)[[self window] delegate] keyDown:theEvent];
- }
- NSLog(@"keyUp -> { 0x%04x, 0x%08lx }", [theEvent keyCode],
- [theEvent modifierFlags]);
- NSString *str = [theEvent charactersIgnoringModifiers];
- unichar c = [str characterAtIndex:0];
-
- // only ASCII please
- if (c < ' ' || c > '~') {
- c = 0;
- }
-
- using namespace engine::keys;
- [self press_key:UP_ARROW forEvent:theEvent ns_key:0x007e down:NO];
- [self press_key:DOWN_ARROW forEvent:theEvent ns_key:0x007d down:NO];
- [self press_key:LEFT_ARROW forEvent:theEvent ns_key:0x007b down:NO];
- [self press_key:RIGHT_ARROW forEvent:theEvent ns_key:0x007c down:NO];
-
- game->process_key_event(c, false);
- }
- static NSTimer *timer = nil;
- static NSTimer *frameTimer = nil;
- static NSTimeInterval const FRAME_INTERVAL = 1.0 / 60.0;
- - (void)windowDidResignMain:(NSNotification *)notification {
- NSLog(@"window did resign main");
- [timer invalidate];
- [frameTimer invalidate];
-
- [self setNeedsDisplay:YES];
- }
- - (void)windowDidBecomeMain:(NSNotification *)notification {
- NSLog(@"window did become main");
- game->set_current_timestamp();
- [self setNeedsDisplay:YES];
-
- // TODO (sjaffe): Become able to change framerate
- // TODO (sjaffe): Link this with env::fps::v60, etc.
- timer = [NSTimer timerWithTimeInterval:FRAME_INTERVAL
- target:self
- selector:@selector(timerEvent:)
- userInfo:nil
- repeats:YES];
-
- frameTimer = [NSTimer timerWithTimeInterval:FRAME_INTERVAL
- target:self
- selector:@selector(renderEvent:)
- userInfo:nil
- repeats:YES];
-
-
- [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
- [[NSRunLoop currentRunLoop] addTimer:frameTimer forMode:NSDefaultRunLoopMode];
- }
- - (void)renderEvent:(NSTimer *)t {
- game->render();
- [[self openGLContext] flushBuffer];
- }
- - (void)timerEvent:(NSTimer *)t {
- game->update();
- [self setNeedsDisplay:YES];
- }
- - (void) awakeFromNib
- {
- NSOpenGLPixelFormatAttribute attributes [] = {
- NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)24,
- NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
- (NSOpenGLPixelFormatAttribute)0
- };
-
- NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc]
- initWithAttributes:attributes];
- [self setPixelFormat:pf];
- }
- @end
|