2015-07-17 00:40:46 +00:00
|
|
|
//
|
2015-07-26 19:25:11 +00:00
|
|
|
// CSCathodeRayView.m
|
|
|
|
// CLK
|
2015-07-17 00:40:46 +00:00
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 16/07/2015.
|
|
|
|
// Copyright © 2015 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2015-07-26 19:25:11 +00:00
|
|
|
#import "CSCathodeRayView.h"
|
2015-07-17 00:40:46 +00:00
|
|
|
@import CoreVideo;
|
2015-09-25 01:23:16 +00:00
|
|
|
@import GLKit;
|
2016-02-06 04:05:58 +00:00
|
|
|
//#import <OpenGL/gl3.h>
|
2016-02-05 03:28:50 +00:00
|
|
|
//#import <OpenGL/gl3ext.h>
|
|
|
|
//#import <libkern/OSAtomic.h>
|
2015-07-17 00:40:46 +00:00
|
|
|
|
2015-08-02 18:25:21 +00:00
|
|
|
|
2015-07-26 19:13:46 +00:00
|
|
|
@implementation CSCathodeRayView {
|
2016-02-05 03:28:50 +00:00
|
|
|
CVDisplayLinkRef _displayLink;
|
2016-01-19 04:55:52 +00:00
|
|
|
CGRect _aspectRatioCorrectedBounds;
|
2015-07-17 00:40:46 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 03:28:50 +00:00
|
|
|
/*- (GLuint)textureForImageNamed:(NSString *)name
|
2015-09-25 01:23:16 +00:00
|
|
|
{
|
|
|
|
NSImage *const image = [NSImage imageNamed:name];
|
|
|
|
NSBitmapImageRep *bitmapRepresentation = [[NSBitmapImageRep alloc] initWithData: [image TIFFRepresentation]];
|
|
|
|
|
|
|
|
GLuint textureName;
|
|
|
|
glGenTextures(1, &textureName);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)image.size.width, (GLsizei)image.size.height, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmapRepresentation.bitmapData);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
return textureName;
|
2016-02-05 03:28:50 +00:00
|
|
|
}*/
|
2015-09-25 01:23:16 +00:00
|
|
|
|
2015-07-17 01:16:21 +00:00
|
|
|
- (void)prepareOpenGL
|
2015-07-17 00:40:46 +00:00
|
|
|
{
|
2015-07-17 01:16:21 +00:00
|
|
|
// Synchronize buffer swaps with vertical refresh rate
|
|
|
|
GLint swapInt = 1;
|
|
|
|
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
2015-07-17 00:40:46 +00:00
|
|
|
|
2015-07-17 01:16:21 +00:00
|
|
|
// Create a display link capable of being used with all active displays
|
2016-02-05 03:28:50 +00:00
|
|
|
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
|
2015-07-17 01:16:21 +00:00
|
|
|
|
|
|
|
// Set the renderer output callback function
|
2016-02-05 03:28:50 +00:00
|
|
|
CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self));
|
2015-07-17 01:16:21 +00:00
|
|
|
|
|
|
|
// Set the display link for the current renderer
|
|
|
|
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
|
|
|
|
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
2016-02-05 03:28:50 +00:00
|
|
|
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
|
2015-07-26 19:13:46 +00:00
|
|
|
|
2015-09-25 01:23:16 +00:00
|
|
|
// install the shadow mask texture as the second texture
|
2016-02-05 03:28:50 +00:00
|
|
|
/* glActiveTexture(GL_TEXTURE1);
|
2015-09-25 01:23:16 +00:00
|
|
|
_shadowMaskTextureName = [self textureForImageNamed:@"ShadowMask"];
|
|
|
|
|
|
|
|
// otherwise, we'll be working on the first texture
|
2016-02-05 03:28:50 +00:00
|
|
|
glActiveTexture(GL_TEXTURE0);*/
|
2015-09-25 01:23:16 +00:00
|
|
|
|
2015-07-26 19:13:46 +00:00
|
|
|
// get the shader ready, set the clear colour
|
2015-07-26 19:25:11 +00:00
|
|
|
[self.openGLContext makeCurrentContext];
|
2015-07-26 19:55:19 +00:00
|
|
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
2015-07-26 19:13:46 +00:00
|
|
|
|
2015-07-17 01:16:21 +00:00
|
|
|
// Activate the display link
|
2016-02-05 03:28:50 +00:00
|
|
|
CVDisplayLinkStart(_displayLink);
|
2015-07-27 03:50:43 +00:00
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
2015-07-31 21:47:10 +00:00
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
2015-07-17 01:16:21 +00:00
|
|
|
}
|
2015-07-17 00:40:46 +00:00
|
|
|
|
2015-07-27 03:50:43 +00:00
|
|
|
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
|
2015-07-17 01:16:21 +00:00
|
|
|
{
|
2015-07-26 19:13:46 +00:00
|
|
|
CSCathodeRayView *view = (__bridge CSCathodeRayView *)displayLinkContext;
|
2015-07-17 02:14:40 +00:00
|
|
|
[view.delegate openGLView:view didUpdateToTime:*now];
|
2016-02-05 04:05:47 +00:00
|
|
|
[view drawViewOnlyIfDirty:YES];
|
2015-07-17 01:16:21 +00:00
|
|
|
return kCVReturnSuccess;
|
|
|
|
}
|
2015-07-28 01:15:10 +00:00
|
|
|
|
|
|
|
- (void)invalidate
|
|
|
|
{
|
2016-02-05 03:28:50 +00:00
|
|
|
CVDisplayLinkStop(_displayLink);
|
2015-07-28 01:15:10 +00:00
|
|
|
}
|
|
|
|
|
2015-07-17 01:16:21 +00:00
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
// Release the display link
|
2016-02-05 03:28:50 +00:00
|
|
|
CVDisplayLinkRelease(_displayLink);
|
2015-07-28 01:15:10 +00:00
|
|
|
|
|
|
|
// Release OpenGL buffers
|
2016-02-05 03:28:50 +00:00
|
|
|
// [self.openGLContext makeCurrentContext];
|
|
|
|
// glDeleteBuffers(1, &_arrayBuffer);
|
|
|
|
// glDeleteVertexArrays(1, &_vertexArray);
|
|
|
|
// glDeleteTextures(1, &_textureName);
|
|
|
|
// glDeleteTextures(1, &_shadowMaskTextureName);
|
|
|
|
// glDeleteProgram(_shaderProgram);
|
2015-07-17 00:40:46 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 03:28:50 +00:00
|
|
|
- (CGSize)backingSize
|
2016-01-07 04:14:36 +00:00
|
|
|
{
|
2016-02-05 03:28:50 +00:00
|
|
|
return [self convertSizeToBacking:self.bounds.size];
|
2016-01-07 04:14:36 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 02:51:53 +00:00
|
|
|
- (void)reshape
|
|
|
|
{
|
|
|
|
[super reshape];
|
|
|
|
|
2015-07-25 03:42:19 +00:00
|
|
|
[self.openGLContext makeCurrentContext];
|
2015-08-13 21:01:25 +00:00
|
|
|
CGLLockContext([[self openGLContext] CGLContextObj]);
|
|
|
|
|
2016-02-05 03:28:50 +00:00
|
|
|
CGSize viewSize = [self backingSize];
|
|
|
|
glViewport(0, 0, (GLsizei)viewSize.width, (GLsizei)viewSize.height);
|
2015-08-13 21:01:25 +00:00
|
|
|
|
2016-02-05 03:28:50 +00:00
|
|
|
// [self pushSizeUniforms];
|
2015-09-25 01:23:16 +00:00
|
|
|
|
2015-08-13 21:01:25 +00:00
|
|
|
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
2015-07-24 02:51:53 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 03:28:50 +00:00
|
|
|
//- (void)setFrameBounds:(CGRect)frameBounds
|
|
|
|
//{
|
|
|
|
// _frameBounds = frameBounds;
|
|
|
|
//
|
|
|
|
// [self.openGLContext makeCurrentContext];
|
|
|
|
// CGLLockContext([[self openGLContext] CGLContextObj]);
|
|
|
|
//
|
|
|
|
// [self pushSizeUniforms];
|
|
|
|
//
|
|
|
|
// CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//- (void)pushSizeUniforms
|
|
|
|
//{
|
|
|
|
// if(_shaderProgram)
|
|
|
|
// {
|
|
|
|
// NSPoint viewSize = [self backingViewSize];
|
|
|
|
// if(_windowSizeUniform >= 0)
|
|
|
|
// {
|
|
|
|
// glUniform2f(_windowSizeUniform, (GLfloat)viewSize.x, (GLfloat)viewSize.y);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// CGFloat outputAspectRatioMultiplier = (viewSize.x / viewSize.y) / (4.0 / 3.0);
|
|
|
|
//
|
|
|
|
//// NSLog(@"%0.2f v %0.2f", outputAspectRatio, desiredOutputAspectRatio);
|
|
|
|
// _aspectRatioCorrectedBounds = _frameBounds;
|
|
|
|
//
|
|
|
|
// CGFloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * _frameBounds.size.width;
|
|
|
|
// _aspectRatioCorrectedBounds.origin.x -= bonusWidth * 0.5f * _aspectRatioCorrectedBounds.size.width;
|
|
|
|
// _aspectRatioCorrectedBounds.size.width *= outputAspectRatioMultiplier;
|
|
|
|
//
|
|
|
|
// if(_boundsOriginUniform >= 0) glUniform2f(_boundsOriginUniform, (GLfloat)_aspectRatioCorrectedBounds.origin.x, (GLfloat)_aspectRatioCorrectedBounds.origin.y);
|
|
|
|
// if(_boundsSizeUniform >= 0) glUniform2f(_boundsSizeUniform, (GLfloat)_aspectRatioCorrectedBounds.size.width, (GLfloat)_aspectRatioCorrectedBounds.size.height);
|
|
|
|
// }
|
|
|
|
//}
|
2016-01-07 04:14:36 +00:00
|
|
|
|
2015-08-13 22:22:51 +00:00
|
|
|
- (void)awakeFromNib
|
2015-07-24 02:51:53 +00:00
|
|
|
{
|
|
|
|
NSOpenGLPixelFormatAttribute attributes[] =
|
|
|
|
{
|
|
|
|
NSOpenGLPFADoubleBuffer,
|
|
|
|
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
2015-09-11 01:30:39 +00:00
|
|
|
NSOpenGLPFASampleBuffers, 1,
|
2016-02-15 02:57:23 +00:00
|
|
|
NSOpenGLPFASamples, 16,
|
2015-07-24 02:51:53 +00:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
|
|
|
|
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// When we're using a CoreProfile context, crash if we call a legacy OpenGL function
|
|
|
|
// This will make it much more obvious where and when such a function call is made so
|
|
|
|
// that we can remove such calls.
|
|
|
|
// Without this we'd simply get GL_INVALID_OPERATION error for calling legacy functions
|
|
|
|
// but it would be more difficult to see where that function was called.
|
|
|
|
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
self.pixelFormat = pixelFormat;
|
|
|
|
self.openGLContext = context;
|
|
|
|
self.wantsBestResolutionOpenGLSurface = YES;
|
2016-01-07 04:14:36 +00:00
|
|
|
|
|
|
|
// establish default instance variable values
|
2016-02-05 03:28:50 +00:00
|
|
|
// self.frameBounds = CGRectMake(0.0, 0.0, 1.0, 1.0);
|
2015-07-24 02:51:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-26 19:13:46 +00:00
|
|
|
- (void)drawRect:(NSRect)dirtyRect
|
2015-08-13 21:01:25 +00:00
|
|
|
{
|
2016-02-05 04:05:47 +00:00
|
|
|
[self drawViewOnlyIfDirty:NO];
|
2015-08-13 21:01:25 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 04:05:47 +00:00
|
|
|
- (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty
|
2015-07-26 19:13:46 +00:00
|
|
|
{
|
|
|
|
[self.openGLContext makeCurrentContext];
|
2015-08-13 21:01:25 +00:00
|
|
|
CGLLockContext([[self openGLContext] CGLContextObj]);
|
2015-07-26 19:13:46 +00:00
|
|
|
|
2016-02-05 04:05:47 +00:00
|
|
|
[self.delegate openGLView:self drawViewOnlyIfDirty:onlyIfDirty];
|
2015-07-26 19:13:46 +00:00
|
|
|
|
2015-08-13 21:01:25 +00:00
|
|
|
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
|
|
|
|
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
2015-07-26 19:13:46 +00:00
|
|
|
}
|
|
|
|
|
2015-08-19 00:33:24 +00:00
|
|
|
#pragma mark - NSResponder
|
|
|
|
|
|
|
|
- (BOOL)acceptsFirstResponder
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)keyDown:(NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
[self.responderDelegate keyDown:theEvent];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)keyUp:(NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
[self.responderDelegate keyUp:theEvent];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)flagsChanged:(NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
[self.responderDelegate flagsChanged:theEvent];
|
|
|
|
}
|
|
|
|
|
2015-07-17 00:40:46 +00:00
|
|
|
@end
|