Skip to content

Instantly share code, notes, and snippets.

@tibewww
Created November 17, 2016 14:41
Show Gist options
  • Save tibewww/3eb3284219958d4e551ce58c599e1912 to your computer and use it in GitHub Desktop.
Save tibewww/3eb3284219958d4e551ce58c599e1912 to your computer and use it in GitHub Desktop.
//
// HTYGLKVC.m
// HTY360Player
//
// Created by  on 11/8/15.
// Copyright © 2015 Hanton. All rights reserved.
//
#import "HTYGLKVC.h"
#import "GLProgram.h"
#import "HTY360PlayerVC.h"
#import <CoreMotion/CoreMotion.h>
#define MAX_OVERTURE 95.0
#define MIN_OVERTURE 25.0
#define DEFAULT_OVERTURE 85.0
#define ES_PI (3.14159265f)
#define ROLL_CORRECTION ES_PI/2.0
// Color Conversion Constants (YUV to RGB) including adjustment from 16-235/16-240 (video range)
// BT.709, which is the standard for HDTV.
static const GLfloat kColorConversion709[] = {
1.164, 1.164, 1.164,
0.0, -0.213, 2.112,
1.793, -0.533, 0.0,
};
// Uniform index.
enum {
UNIFORM_MODELVIEWPROJECTION_MATRIX,
UNIFORM_Y,
UNIFORM_UV,
UNIFORM_COLOR_CONVERSION_MATRIX,
NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];
@interface HTYGLKVC () {
GLKMatrix4 _modelViewProjectionMatrix;
GLuint _vertexArrayID;
GLuint _vertexBufferID;
GLuint _vertexIndicesBufferID;
GLuint _vertexTexCoordID;
GLuint _vertexTexCoordAttributeIndex;
float _fingerRotationX;
float _fingerRotationY;
float _savedGyroRotationX;
float _savedGyroRotationY;
CGFloat _overture;
int _numIndices;
CMMotionManager *_motionManager;
CMAttitude *_referenceAttitude;
CVOpenGLESTextureRef _lumaTexture;
CVOpenGLESTextureRef _chromaTexture;
CVOpenGLESTextureCacheRef _videoTextureCache;
const GLfloat *_preferredConversion;
}
@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) GLProgram *program;
@property (strong, nonatomic) NSMutableArray *currentTouches;
- (void)setupGL;
- (void)tearDownGL;
- (void)buildProgram;
@end
@implementation HTYGLKVC
@dynamic view;
- (void)viewDidLoad {
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(@"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectOrientation) name:UIDeviceOrientationDidChangeNotification object:nil];
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
[view addGestureRecognizer:pinchRecognizer];
UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)];
singleTapRecognizer.numberOfTapsRequired = 1;
[view addGestureRecognizer:singleTapRecognizer];
self.preferredFramesPerSecond = 30.0f;
_overture = DEFAULT_OVERTURE;
// Set the default conversion to BT.709, which is the standard for HDTV.
_preferredConversion = kColorConversion709;
[self setupGL];
[self startDeviceMotion];
}
-(UIInterfaceOrientationMask) supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
-(void) detectOrientation {
// _referenceAttitude = nil;
}
- (void)dealloc {
[self stopDeviceMotion];
[self.view deleteDrawable];
[self tearDownGL];
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
self.context = 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.
}
#pragma mark generate sphere
int esGenSphere ( int numSlices, float radius, float **vertices, float **normals,
float **texCoords, uint16_t **indices, int *numVertices_out) {
int i;
int j;
int numParallels = numSlices / 2;
int numVertices = ( numParallels + 1 ) * ( numSlices + 1 );
int numIndices = numParallels * numSlices * 6;
float angleStep = (2.0f * ES_PI) / ((float) numSlices);
if ( vertices != NULL )
*vertices = (float*)malloc ( sizeof(float) * 3 * numVertices );
// Pas besoin des normals pour l'instant
// if ( normals != NULL )
// *normals = malloc ( sizeof(float) * 3 * numVertices );
if ( texCoords != NULL )
*texCoords = (float*)malloc ( sizeof(float) * 2 * numVertices );
if ( indices != NULL )
*indices = (uint16_t*)malloc ( sizeof(uint16_t) * numIndices );
for ( i = 0; i < numParallels + 1; i++ ) {
for ( j = 0; j < numSlices + 1; j++ ) {
int vertex = ( i * (numSlices + 1) + j ) * 3;
if ( vertices ) {
(*vertices)[vertex + 0] = radius * sinf ( angleStep * (float)i ) *
sinf ( angleStep * (float)j );
(*vertices)[vertex + 1] = radius * cosf ( angleStep * (float)i );
(*vertices)[vertex + 2] = radius * sinf ( angleStep * (float)i ) *
cosf ( angleStep * (float)j );
}
// if ( normals )
// {
// (*normals)[vertex + 0] = (*vertices)[vertex + 0] / radius;
// (*normals)[vertex + 1] = (*vertices)[vertex + 1] / radius;
// (*normals)[vertex + 2] = (*vertices)[vertex + 2] / radius;
// }
if (texCoords) {
int texIndex = ( i * (numSlices + 1) + j ) * 2;
(*texCoords)[texIndex + 0] = (float) j / (float) numSlices;
(*texCoords)[texIndex + 1] = 1.0f - ((float) i / (float) (numParallels));
}
}
}
// Generate the indices
if ( indices != NULL ) {
uint16_t *indexBuf = (*indices);
for ( i = 0; i < numParallels ; i++ ) {
for ( j = 0; j < numSlices; j++ ) {
*indexBuf++ = i * ( numSlices + 1 ) + j;
*indexBuf++ = ( i + 1 ) * ( numSlices + 1 ) + j;
*indexBuf++ = ( i + 1 ) * ( numSlices + 1 ) + ( j + 1 );
*indexBuf++ = i * ( numSlices + 1 ) + j;
*indexBuf++ = ( i + 1 ) * ( numSlices + 1 ) + ( j + 1 );
*indexBuf++ = i * ( numSlices + 1 ) + ( j + 1 );
}
}
}
if (numVertices_out) {
*numVertices_out = numVertices;
}
return numIndices;
}
#pragma mark setup gl
- (void)setupGL {
[EAGLContext setCurrentContext:self.context];
[self buildProgram];
GLfloat *vVertices = NULL;
GLfloat *vTextCoord = NULL;
GLushort *indices = NULL;
int numVertices = 0;
_numIndices = esGenSphere(200, 1.0f, &vVertices, NULL,
&vTextCoord, &indices, &numVertices);
glGenVertexArraysOES(1, &_vertexArrayID);
glBindVertexArrayOES(_vertexArrayID);
// Vertex
glGenBuffers(1, &_vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID);
glBufferData(GL_ARRAY_BUFFER,
numVertices*3*sizeof(GLfloat),
vVertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition,
3,
GL_FLOAT,
GL_FALSE,
sizeof(GLfloat) * 3,
NULL);
// Texture Coordinates
glGenBuffers(1, &_vertexTexCoordID);
glBindBuffer(GL_ARRAY_BUFFER, _vertexTexCoordID);
glBufferData(GL_ARRAY_BUFFER,
numVertices*2*sizeof(GLfloat),
vTextCoord,
GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(_vertexTexCoordAttributeIndex);
glVertexAttribPointer(_vertexTexCoordAttributeIndex,
2,
GL_FLOAT,
GL_FALSE,
sizeof(GLfloat) * 2,
NULL);
//Indices
glGenBuffers(1, &_vertexIndicesBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vertexIndicesBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(GLushort) * _numIndices,
indices, GL_STATIC_DRAW);
if (!_videoTextureCache) {
CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, _context, NULL, &_videoTextureCache);
if (err != noErr) {
NSLog(@"Error at CVOpenGLESTextureCacheCreate %d", err);
return;
}
}
[_program use];
glUniform1i(uniforms[UNIFORM_Y], 0);
glUniform1i(uniforms[UNIFORM_UV], 1);
glUniformMatrix3fv(uniforms[UNIFORM_COLOR_CONVERSION_MATRIX], 1, GL_FALSE, _preferredConversion);
free(vVertices);
free(vTextCoord);
free(indices);
}
- (void)tearDownGL {
[EAGLContext setCurrentContext:self.context];
[self cleanUpTextures];
glDeleteBuffers(1, &_vertexBufferID);
glDeleteVertexArraysOES(1, &_vertexArrayID);
glDeleteBuffers(1, &_vertexTexCoordID);
_program = nil;
if (_videoTextureCache)
{
CFRelease(_videoTextureCache);
_videoTextureCache = NULL;
}
}
#pragma mark texture cleanup
- (void)cleanUpTextures {
if (_lumaTexture) {
CFRelease(_lumaTexture);
_lumaTexture = NULL;
}
if (_chromaTexture) {
CFRelease(_chromaTexture);
_chromaTexture = NULL;
}
// Periodic texture cache flush every frame
CVOpenGLESTextureCacheFlush(_videoTextureCache, 0);
}
#pragma mark device motion management
- (void)startDeviceMotion {
_isUsingMotion = NO;
_motionManager = [[CMMotionManager alloc] init];
_referenceAttitude = nil;
_motionManager.deviceMotionUpdateInterval = 1.0 / 60.0;
_motionManager.gyroUpdateInterval = 1.0f / 60;
_motionManager.showsDeviceMovementDisplay = YES;
[_motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical];
_referenceAttitude = _motionManager.deviceMotion.attitude; // Maybe nil actually. reset it later when we have data
_savedGyroRotationX = 0;
_savedGyroRotationY = 0;
//_isUsingMotion = YES;
}
- (void)stopDeviceMotion {
_fingerRotationX = _savedGyroRotationX-_referenceAttitude.roll- ROLL_CORRECTION;
_fingerRotationY = _savedGyroRotationY;
_isUsingMotion = NO;
[_motionManager stopDeviceMotionUpdates];
_motionManager = nil;
}
#pragma mark - GLKView and GLKViewController delegate methods
- (void)update {
float aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(_overture), aspect, 0.1f, 400.0f);
projectionMatrix = GLKMatrix4Rotate(projectionMatrix, ES_PI, 1.0f, 0.0f, 0.0f);
GLKMatrix4 modelViewMatrix = GLKMatrix4Identity;
modelViewMatrix = GLKMatrix4Scale(modelViewMatrix, 300.0, 300.0, 300.0);
// if(_isUsingMotion) {
CMDeviceMotion *d = _motionManager.deviceMotion;
if (d != nil) {
CMAttitude *attitude = d.attitude;
if (_referenceAttitude != nil) {
[attitude multiplyByInverseOfAttitude:_referenceAttitude];
} else {
//NSLog(@"was nil : set new attitude", nil);
_referenceAttitude = d.attitude;
}
float cRoll = -fabs(attitude.roll); // Up/Down en landscape
float cYaw = attitude.yaw; // Left/ Right en landscape -> pas besoin de prendre l'opposé
float cPitch = attitude.pitch; // Depth en landscape -> pas besoin de prendre l'opposé
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationLandscapeRight ){
cPitch = cPitch*-1; // correct depth when in landscape right
}
if (YES) {
modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, cRoll); // Up/Down axis
modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, cPitch);
modelViewMatrix = GLKMatrix4RotateZ(modelViewMatrix, cYaw);
modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, ROLL_CORRECTION);
modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, ES_PI);
modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, _fingerRotationX);
modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, _fingerRotationY);
_savedGyroRotationX = cRoll + ROLL_CORRECTION + _fingerRotationX;
_savedGyroRotationY = cPitch + _fingerRotationY;
}
/*
else {
float deviceOrientationRadians = 0.0f;
int mul = 1;
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationPortrait) {
deviceOrientationRadians = -M_PI_2;
mul = -1;
}
if (orientation == UIDeviceOrientationPortraitUpsideDown) {
deviceOrientationRadians = M_PI_2;
mul = 1;
}
GLKMatrix4 baseRotation = GLKMatrix4MakeRotation(deviceOrientationRadians, 1.0f, 0.0f, 0.0f);//up down
CMRotationMatrix a = attitude.rotationMatrix; // COL 1 : Gauche droite, COL 2: haut bas
GLKMatrix4 deviceMatrix = GLKMatrix4Make(mul*a.m11, mul*a.m12, a.m13, 0.0f,
mul*a.m21, mul*a.m22, a.m23, 0.0f,
mul*a.m31, mul*a.m32, a.m33, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
deviceMatrix = GLKMatrix4Multiply(baseRotation, deviceMatrix);
modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, _fingerRotationX);
modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, _fingerRotationY);
modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, deviceMatrix);
}
*/
}
// } else {
// modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, _fingerRotationX);
// modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, _fingerRotationY);
// }
_modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
[_program use];
glBindVertexArrayOES(_vertexArrayID);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
CVPixelBufferRef pixelBuffer = [self.videoPlayerController retrievePixelBufferToDraw];
CVReturn err;
if (pixelBuffer != NULL) {
int frameWidth = (int)CVPixelBufferGetWidth(pixelBuffer);
int frameHeight = (int)CVPixelBufferGetHeight(pixelBuffer);
if (!_videoTextureCache) {
NSLog(@"No video texture cache");
CVPixelBufferRelease(pixelBuffer);
return;
}
[self cleanUpTextures];
// Y-plane
glActiveTexture(GL_TEXTURE0);
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
_videoTextureCache,
pixelBuffer,
NULL,
GL_TEXTURE_2D,
GL_RED_EXT,
frameWidth,
frameHeight,
GL_RED_EXT,
GL_UNSIGNED_BYTE,
0,
&_lumaTexture);
if (err) {
NSLog(@"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);
}
glBindTexture(CVOpenGLESTextureGetTarget(_lumaTexture), CVOpenGLESTextureGetName(_lumaTexture));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// UV-plane.
glActiveTexture(GL_TEXTURE1);
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
_videoTextureCache,
pixelBuffer,
NULL,
GL_TEXTURE_2D,
GL_RG_EXT,
frameWidth / 2,
frameHeight / 2,
GL_RG_EXT,
GL_UNSIGNED_BYTE,
1,
&_chromaTexture);
if (err) {
NSLog(@"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);
}
glBindTexture(CVOpenGLESTextureGetTarget(_chromaTexture), CVOpenGLESTextureGetName(_chromaTexture));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
CVPixelBufferRelease(pixelBuffer);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawElements ( GL_TRIANGLES, _numIndices,
GL_UNSIGNED_SHORT, 0 );
}
glBindVertexArrayOES(0);
glUseProgram(0);
}
#pragma mark - OpenGL Program
- (void)buildProgram {
_program = [[GLProgram alloc]
initWithVertexShaderFilename:@"Shader"
fragmentShaderFilename:@"Shader"];
[_program addAttribute:@"position"];
[_program addAttribute:@"texCoord"];
if (![_program link]) {
NSString *programLog = [_program programLog];
NSLog(@"Program link log: %@", programLog);
NSString *fragmentLog = [_program fragmentShaderLog];
NSLog(@"Fragment shader compile log: %@", fragmentLog);
NSString *vertexLog = [_program vertexShaderLog];
NSLog(@"Vertex shader compile log: %@", vertexLog);
_program = nil;
NSAssert(NO, @"Falied to link HalfSpherical shaders");
}
_vertexTexCoordAttributeIndex = [_program attributeIndex:@"texCoord"];
uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = [_program uniformIndex:@"modelViewProjectionMatrix"];
uniforms[UNIFORM_Y] = [_program uniformIndex:@"SamplerY"];
uniforms[UNIFORM_UV] = [_program uniformIndex:@"SamplerUV"];
uniforms[UNIFORM_COLOR_CONVERSION_MATRIX] = [_program uniformIndex:@"colorConversionMatrix"];
}
#pragma mark - touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(_isUsingMotion) return;
for (UITouch *touch in touches) {
[_currentTouches addObject:touch];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(_isUsingMotion) return;
UITouch *touch = [touches anyObject];
float distX = [touch locationInView:touch.view].x -
[touch previousLocationInView:touch.view].x;
float distY = [touch locationInView:touch.view].y -
[touch previousLocationInView:touch.view].y;
distX *= -0.005;
distY *= -0.005;
_fingerRotationX += distY * _overture / 100;
_fingerRotationY -= distX * _overture / 100;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (_isUsingMotion) return;
for (UITouch *touch in touches) {
[_currentTouches removeObject:touch];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
[_currentTouches removeObject:touch];
}
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer {
_overture /= recognizer.scale;
if (_overture > MAX_OVERTURE)
_overture = MAX_OVERTURE;
if(_overture<MIN_OVERTURE)
_overture = MIN_OVERTURE;
}
- (void)handleSingleTapGesture:(UITapGestureRecognizer *)recognizer {
[_videoPlayerController toggleControls];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment