// // KrakenView.m // Kraken // // Created by Kearwood Gilbert on 2017-04-27. // Copyright © 2017 Kearwood Software. All rights reserved. // #import #import #import #import "KrakenView.h" #import "KREngine-common.h" #import "KREngine.h" @interface KrakenView() { GLint framebufferWidth; GLint framebufferHeight; GLuint defaultFramebuffer; GLuint colorRenderbuffer; GLuint depthRenderbuffer; CFTimeInterval lastTimestamp; struct { unsigned int preRender:1; unsigned int postRender:1; } delegateRespondsTo; } @property (nonatomic, unsafe_unretained) CADisplayLink *__unsafe_unretained displayLink; @property (nonatomic, strong) EAGLContext *context; @end @implementation KrakenView + (Class)layerClass { return [CAEAGLLayer class]; } - (id)initWithCoder:(NSCoder*)coder { self = [super initWithCoder:coder]; if (self) { lastTimestamp = 0.0; delegateRespondsTo.preRender = 0; delegateRespondsTo.postRender = 0; [self startAnimation]; } return self; } - (void)dealloc { [self stopAnimation]; } - (void)setDelegate:(id )aDelegate { if (_delegate != aDelegate) { _delegate = aDelegate; delegateRespondsTo.preRender = [_delegate respondsToSelector:@selector(preRender:withDeltaTime:)]; delegateRespondsTo.postRender = [_delegate respondsToSelector:@selector(postRender:withDeltaTime:)]; } } - (void)startAnimation { CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; eaglLayer.contentsScale = [[UIScreen mainScreen] scale]; eaglLayer.opaque = TRUE; eaglLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking: [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8 }; KRContext::activateRenderContext(); //EAGLContext *aContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; EAGLContext *aContext = [EAGLContext currentContext]; if (!aContext) { NSLog(@"Failed to create ES context"); } else if (![EAGLContext setCurrentContext:aContext]) { NSLog(@"Failed to set ES context current"); } self.context = aContext; [self createFrameBuffer]; CADisplayLink *aDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawFrame)]; [aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; self.displayLink = aDisplayLink; } - (void)stopAnimation { [self.displayLink invalidate]; self.displayLink = nil; if (self.context) { [EAGLContext setCurrentContext:self.context]; if (defaultFramebuffer) { GLDEBUG(glDeleteFramebuffers(1, &defaultFramebuffer)); defaultFramebuffer = 0; } if (colorRenderbuffer) { GLDEBUG(glDeleteRenderbuffers(1, &colorRenderbuffer)); colorRenderbuffer = 0; } if (depthRenderbuffer) { GLDEBUG(glDeleteRenderbuffers(1, &depthRenderbuffer)); depthRenderbuffer = 0; } [EAGLContext setCurrentContext:nil]; self.context = nil; } } - (void)drawFrame { CFTimeInterval timeStamp = self.displayLink.timestamp; CFTimeInterval deltaTime; if (lastTimestamp == 0.0) { deltaTime = 0.0; } else { deltaTime = timeStamp - lastTimestamp; } lastTimestamp = timeStamp; KRContext *context = KREngine.sharedInstance.context; if (self.delegate && delegateRespondsTo.preRender) { [self.delegate preRender:context withDeltaTime:deltaTime]; } // ---- Render the Buffer ---- [EAGLContext setCurrentContext:self.context]; if (self.scene) { GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer)); [[KREngine sharedInstance] renderScene: self.scene WithDeltaTime: deltaTime AndWidth: framebufferWidth AndHeight: framebufferHeight AndDefaultFBO: defaultFramebuffer]; } else { GLDEBUG(glClearColor(0.0f, 0.0f, 1.0f, 1.0f)); GLDEBUG(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } // ---- Present the Buffer ---- #if GL_EXT_discard_framebuffer GLenum attachments[2] = {GL_DEPTH_ATTACHMENT}; GLDEBUG(glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments)); #endif GLDEBUG(glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer)); BOOL success = [self.context presentRenderbuffer:GL_RENDERBUFFER]; if (self.delegate && delegateRespondsTo.postRender) { [self.delegate postRender:context withDeltaTime:deltaTime]; } } - (void)createFrameBuffer { // Create default framebuffer object. GLDEBUG(glGenFramebuffers(1, &defaultFramebuffer)); GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer)); // Create color render buffer and allocate backing store. GLDEBUG(glGenRenderbuffers(1, &colorRenderbuffer)); GLDEBUG(glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer)); [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer]; GLDEBUG(glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth)); GLDEBUG(glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight)); // Attach color render buffer GLDEBUG(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer)); // Create depth render buffer and allocate backing store. GLDEBUG(glGenRenderbuffers(1, &depthRenderbuffer)); GLDEBUG(glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer)); GLDEBUG(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, framebufferWidth, framebufferHeight)); // Attach depth render buffer GLDEBUG(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer)); } @end