├── CardboardSDK ├── CBDStereoGLView.h ├── CBDStereoGLView.m ├── CBDViewController.h ├── CBDViewController.mm ├── Cardboard │ ├── CardboardDeviceParams.h │ ├── CardboardDeviceParams.mm │ ├── CardboardUnity.h │ ├── CardboardUnity.mm │ ├── Distortion.h │ ├── Distortion.mm │ ├── DistortionRenderer.h │ ├── DistortionRenderer.mm │ ├── Eye.h │ ├── Eye.mm │ ├── FieldOfView.h │ ├── FieldOfView.mm │ ├── GLStateBackup.h │ ├── GLStateBackup.mm │ ├── HeadMountedDisplay.h │ ├── HeadMountedDisplay.mm │ ├── HeadTransform.h │ ├── HeadTransform.mm │ ├── ScreenParams.h │ ├── ScreenParams.mm │ ├── Sensors │ │ ├── HeadTracker.h │ │ ├── HeadTracker.mm │ │ ├── Internal │ │ │ ├── Matrix3x3d.h │ │ │ ├── Matrix3x3d.mm │ │ │ ├── OrientationEKF.h │ │ │ ├── OrientationEKF.mm │ │ │ ├── SO3Util.h │ │ │ ├── SO3Util.mm │ │ │ ├── Vector3d.h │ │ │ └── Vector3d.mm │ │ ├── MagnetSensor.h │ │ └── MagnetSensor.mm │ ├── Viewport.h │ └── Viewport.mm ├── CardboardSDK.h └── Misc │ ├── DebugUtils.h │ ├── GLHelpers.h │ └── GLHelpers.m ├── README.md ├── SFOpenGLESPanorama.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── Jagie.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── Jagie.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── SFOpenGLESPanorama.xcscheme │ └── xcschememanagement.plist ├── SFOpenGLESPanorama ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── GameViewController.h ├── GameViewController.m ├── GameViewControllerForCardBoard.h ├── GameViewControllerForCardBoard.mm ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── Shaders │ ├── Shader.fsh │ └── Shader.vsh ├── back.jpeg ├── bottom.jpeg ├── front.jpeg ├── left.jpeg ├── main.m ├── right.jpeg └── top.jpeg ├── SFOpenGLESPanoramaTests ├── Info.plist └── SFOpenGLESPanoramaTests.m ├── s1.png └── s2.png /CardboardSDK/CBDStereoGLView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CBDStereoGLView.h 3 | // 4 | // Created by Ricardo Sánchez-Sáez on 01/02/2015. 5 | // 6 | 7 | #import 8 | 9 | #import "CBDViewController.h" 10 | 11 | 12 | @interface CBDStereoGLView : UIView 13 | 14 | - (instancetype)initWithFrame:(CGRect)frame 15 | context:(EAGLContext *)glContext; 16 | - (instancetype)initWithFrame:(CGRect)frame 17 | context:(EAGLContext *)glContext 18 | lock:(NSRecursiveLock *)lock; 19 | 20 | - (void)updateGLTextureForEye:(CBDEyeType)eyeType; 21 | - (void)renderTextureForEye:(CBDEyeType)eyeType; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /CardboardSDK/CBDStereoGLView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CBDStereoGLView.m 3 | // 4 | // Created by Ricardo Sánchez-Sáez on 01/02/2015. 5 | // 6 | 7 | #import "CBDStereoGLView.h" 8 | 9 | #import "GLHelpers.h" 10 | #import 11 | 12 | 13 | @interface UIScreen (SafeNativeScale) 14 | 15 | @property(nonatomic, readonly) CGFloat safeNativeScale; 16 | 17 | @end 18 | 19 | 20 | @implementation UIScreen (SafeNativeScale) 21 | 22 | - (CGFloat)safeNativeScale 23 | { 24 | return [self respondsToSelector:@selector(nativeScale)] ? self.nativeScale : self.scale; 25 | } 26 | 27 | @end 28 | 29 | 30 | @interface CBDStereoGLView () 31 | { 32 | GLubyte *_texturePixelBuffer; 33 | CGContextRef _bitmapContext; 34 | 35 | GLuint _leftTextureID; 36 | GLuint _rightTextureID; 37 | 38 | GLuint _programID; 39 | GLuint _vertexArrayID; 40 | 41 | GLuint _positionVertexBuffer; 42 | 43 | GLuint _positionLocation; 44 | GLuint _inputTextureCoordinateLocation; 45 | 46 | GLuint _uniformLocation; 47 | 48 | BOOL _leftTextureDataReady; 49 | BOOL _rightTextureDataReady; 50 | } 51 | 52 | @property (nonatomic) EAGLContext *glContext; 53 | @property (nonatomic) NSRecursiveLock *glLock; 54 | 55 | @end 56 | 57 | 58 | @implementation CBDStereoGLView 59 | 60 | - (instancetype)initWithFrame:(CGRect)frame context:(EAGLContext *)glContext 61 | { 62 | return [self initWithFrame:frame context:glContext lock:nil]; 63 | } 64 | 65 | 66 | - (instancetype)initWithFrame:(CGRect)frame context:(EAGLContext *)glContext lock:(NSRecursiveLock *)lock 67 | { 68 | self = [super initWithFrame:frame]; 69 | if (!self) { return nil; } 70 | 71 | _texturePixelBuffer = NULL; 72 | _bitmapContext = NULL; 73 | 74 | _leftTextureID = 0; 75 | _rightTextureID = 0; 76 | 77 | _programID = 0; 78 | _vertexArrayID = 0; 79 | 80 | _positionVertexBuffer = 0; 81 | 82 | _positionLocation = 0; 83 | _inputTextureCoordinateLocation = 0; 84 | 85 | _uniformLocation = 0; 86 | 87 | _leftTextureDataReady = NO; 88 | _rightTextureDataReady = NO; 89 | 90 | self.glLock = lock ? lock : [NSRecursiveLock new]; 91 | 92 | self.glContext = glContext; 93 | [self prepareForRendering]; 94 | 95 | return self; 96 | } 97 | 98 | - (void)dealloc 99 | { 100 | if (_bitmapContext) { CGContextRelease(_bitmapContext); } 101 | if (_texturePixelBuffer) { free(_texturePixelBuffer); } 102 | [self teardownGL]; 103 | } 104 | 105 | - (void)prepareForRendering 106 | { 107 | [self createBitmapContext]; 108 | [self setupGLProgram]; 109 | } 110 | 111 | - (void)setupGLProgram 112 | { 113 | [self.glLock lock]; 114 | 115 | EAGLContext* tmpContext = [EAGLContext currentContext]; 116 | [EAGLContext setCurrentContext:self.glContext]; 117 | 118 | // Program 119 | const GLchar *vertexShaderSource = 120 | "\ 121 | attribute vec2 position; \ 122 | attribute vec2 inputTextureCoordinate; \ 123 | varying vec2 textureCoordinate; \ 124 | \ 125 | void main() \ 126 | { \ 127 | gl_Position = vec4(position, 0.0, 1.0); \ 128 | textureCoordinate = inputTextureCoordinate; \ 129 | } \ 130 | "; 131 | 132 | const GLchar *fragmentShaderSource = 133 | "\ 134 | precision mediump float; \ 135 | varying vec2 textureCoordinate; \ 136 | uniform sampler2D textureSampler; \ 137 | \ 138 | void main() \ 139 | { \ 140 | gl_FragColor = texture2D(textureSampler, textureCoordinate); \ 141 | } \ 142 | "; 143 | 144 | GLuint vertexShader = 0; 145 | GLCompileShader(&vertexShader, GL_VERTEX_SHADER, vertexShaderSource); 146 | if (vertexShader == 0) { return; } 147 | 148 | GLuint pixelShader = 0; 149 | GLCompileShader(&pixelShader, GL_FRAGMENT_SHADER, fragmentShaderSource); 150 | if (pixelShader == 0) { return; } 151 | 152 | _programID = glCreateProgram(); 153 | if (_programID == 0) { return; } 154 | 155 | glAttachShader(_programID, vertexShader); 156 | GLCheckForError(); 157 | glAttachShader(_programID, pixelShader); 158 | GLCheckForError(); 159 | GLLinkProgram(_programID); 160 | GLint status; 161 | glGetProgramiv(_programID, GL_LINK_STATUS, &status); 162 | if (status == GL_FALSE) 163 | { 164 | GLchar message[256]; 165 | glGetProgramInfoLog(_programID, sizeof(message), 0, &message[0]); 166 | NSLog(@"Could not link program:\n%s", message); 167 | glDeleteProgram(_programID); 168 | _programID = 0; 169 | } 170 | 171 | 172 | // Buffers and VAO 173 | float vertices[] = { 174 | -1.0f, -1.0f, 0.0f, 0.0f, // Bottom-left 175 | 1.0f, -1.0f, 1.0f, 0.0f, // Bottom-right 176 | -1.0f, 1.0f, 0.0f, 1.0f, // Top-left 177 | 1.0f, 1.0f, 1.0f, 1.0f // Top-right 178 | }; 179 | 180 | glGenVertexArraysOES(1, &_vertexArrayID); 181 | glBindVertexArrayOES(_vertexArrayID); 182 | 183 | GLCheckForError(); 184 | 185 | _positionLocation = glGetAttribLocation(_programID, "position"); 186 | _inputTextureCoordinateLocation = glGetAttribLocation(_programID, "inputTextureCoordinate"); 187 | 188 | GLCheckForError(); 189 | 190 | glEnableVertexAttribArray(_positionLocation); 191 | glEnableVertexAttribArray(_inputTextureCoordinateLocation); 192 | 193 | GLCheckForError(); 194 | 195 | glGenBuffers(1, &_positionVertexBuffer); 196 | glBindBuffer(GL_ARRAY_BUFFER, _positionVertexBuffer); 197 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 198 | 199 | GLCheckForError(); 200 | 201 | // Set the position 202 | glVertexAttribPointer(_positionLocation, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), BUFFER_OFFSET(0)); 203 | glVertexAttribPointer(_inputTextureCoordinateLocation, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), BUFFER_OFFSET(2 * sizeof(float))); 204 | 205 | GLCheckForError(); 206 | 207 | _uniformLocation = glGetUniformLocation(_programID, "textureSampler"); 208 | 209 | GLCheckForError(); 210 | 211 | glBindVertexArrayOES(0); 212 | 213 | // Texture 214 | glGenTextures(1, &_leftTextureID); 215 | glBindTexture(GL_TEXTURE_2D, _leftTextureID); 216 | 217 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 218 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 219 | 220 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 221 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 222 | 223 | glGenTextures(1, &_rightTextureID); 224 | glBindTexture(GL_TEXTURE_2D, _rightTextureID); 225 | 226 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 227 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 228 | 229 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 230 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 231 | 232 | [EAGLContext setCurrentContext:tmpContext]; 233 | 234 | [self.glLock unlock]; 235 | } 236 | 237 | - (void)teardownGL 238 | { 239 | [self.glLock lock]; 240 | 241 | EAGLContext* tmpContext = [EAGLContext currentContext]; 242 | [EAGLContext setCurrentContext:self.glContext]; 243 | 244 | glDeleteTextures(1, &_leftTextureID); 245 | glDeleteTextures(1, &_rightTextureID); 246 | 247 | glDeleteVertexArraysOES(1, &_vertexArrayID); 248 | glDeleteBuffers(1, &_positionVertexBuffer); 249 | glDeleteProgram(_programID); 250 | 251 | [EAGLContext setCurrentContext:tmpContext]; 252 | 253 | [self.glLock unlock]; 254 | } 255 | 256 | - (void)createBitmapContext 257 | { 258 | CGFloat scale = [UIScreen mainScreen].safeNativeScale; 259 | size_t width = CGRectGetWidth(self.layer.bounds) * scale; 260 | size_t height = CGRectGetHeight(self.layer.bounds) * scale; 261 | 262 | if (_texturePixelBuffer) 263 | { 264 | free(_texturePixelBuffer); 265 | } 266 | _texturePixelBuffer = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte)); 267 | 268 | if (_bitmapContext) 269 | { 270 | CGContextRelease(_bitmapContext); 271 | } 272 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 273 | _bitmapContext = CGBitmapContextCreate(_texturePixelBuffer, 274 | width, height, 8, width * 4, colorSpace, 275 | kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); 276 | CGColorSpaceRelease(colorSpace); 277 | 278 | CGContextScaleCTM(_bitmapContext, scale, scale); 279 | } 280 | 281 | - (GLuint)textureIDForEye:(CBDEyeType)eyeType 282 | { 283 | GLuint textureID = 0; 284 | if (eyeType == CBDEyeTypeLeft || eyeType == CBDEyeTypeMonocular) 285 | { 286 | textureID = _leftTextureID; 287 | } 288 | else if (eyeType == CBDEyeTypeRight) 289 | { 290 | textureID = _rightTextureID; 291 | } 292 | return textureID; 293 | } 294 | 295 | - (BOOL)textureDataReadyForEye:(CBDEyeType)eyeType 296 | { 297 | BOOL textureDataReady = NO; 298 | if (eyeType == CBDEyeTypeLeft || eyeType == CBDEyeTypeMonocular) 299 | { 300 | textureDataReady = _leftTextureDataReady; 301 | } 302 | else if (eyeType == CBDEyeTypeRight) 303 | { 304 | textureDataReady = _rightTextureDataReady; 305 | } 306 | return textureDataReady; 307 | } 308 | 309 | - (void)setTextureDataReady:(BOOL)textureDataReady forEye:(CBDEyeType)eyeType 310 | { 311 | if (eyeType == CBDEyeTypeLeft || eyeType == CBDEyeTypeMonocular) 312 | { 313 | _leftTextureDataReady = textureDataReady; 314 | } 315 | else if (eyeType == CBDEyeTypeRight) 316 | { 317 | _rightTextureDataReady = textureDataReady; 318 | } 319 | } 320 | 321 | - (void)updateGLTextureForEye:(CBDEyeType)eyeType 322 | { 323 | GLuint textureID = [self textureIDForEye:eyeType]; 324 | if (textureID == 0 || !_bitmapContext) { return; } 325 | 326 | BOOL lockAcquired = [self.glLock tryLock]; 327 | if (!lockAcquired) { return; } 328 | 329 | EAGLContext* tmpContext = [EAGLContext currentContext]; 330 | [EAGLContext setCurrentContext:self.glContext]; 331 | 332 | size_t width = CGBitmapContextGetWidth(_bitmapContext); 333 | size_t height = CGBitmapContextGetHeight(_bitmapContext); 334 | 335 | CGRect rect = CGRectMake(0, 0, width, height); 336 | CGContextClearRect(_bitmapContext, rect); 337 | 338 | // Use presentationLayer because it takes CAAnimations into account 339 | // (Flush CATransaction to make sure the latest constraint 340 | // updates are reflected on the presentationLayer) 341 | [CATransaction flush]; 342 | [self.layer.presentationLayer renderInContext:_bitmapContext]; 343 | 344 | // Debug 345 | CGImageRef imageRef = CGBitmapContextCreateImage(_bitmapContext); 346 | __unused UIImage *image = [UIImage imageWithCGImage:imageRef]; 347 | CGImageRelease(imageRef); 348 | 349 | glBindTexture(GL_TEXTURE_2D, textureID); 350 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, 351 | GL_UNSIGNED_BYTE, _texturePixelBuffer); 352 | [self setTextureDataReady:YES forEye:eyeType]; 353 | 354 | [EAGLContext setCurrentContext:tmpContext]; 355 | 356 | GLCheckForError(); 357 | 358 | [self.glLock unlock]; 359 | } 360 | 361 | - (void)renderTextureForEye:(CBDEyeType)eyeType 362 | { 363 | GLuint textureID = [self textureIDForEye:eyeType]; 364 | BOOL textureDataSet = [self textureDataReadyForEye:eyeType]; 365 | if (textureID == 0 || _programID == 0 || !textureDataSet) { return; } 366 | 367 | BOOL lockAcquired = [self.glLock tryLock]; 368 | if (!lockAcquired) { return; } 369 | 370 | glUseProgram(_programID); 371 | glBindVertexArrayOES(_vertexArrayID); 372 | glUniform1i(_uniformLocation, 0); 373 | 374 | // Enable texture alpha blending 375 | glEnable(GL_BLEND); 376 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 377 | 378 | glActiveTexture(GL_TEXTURE0); 379 | glBindTexture(GL_TEXTURE_2D, textureID); 380 | 381 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 382 | 383 | GLCheckForError(); 384 | 385 | // Enable texture alpha blending 386 | glDisable(GL_BLEND); 387 | 388 | glBindVertexArrayOES(0); 389 | glUseProgram(0); 390 | 391 | [self.glLock unlock]; 392 | } 393 | 394 | @end 395 | -------------------------------------------------------------------------------- /CardboardSDK/CBDViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardboardViewController.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #import 7 | #import 8 | #import 9 | 10 | typedef NS_ENUM(NSInteger, CBDEyeType) 11 | { 12 | CBDEyeTypeMonocular, 13 | CBDEyeTypeLeft, 14 | CBDEyeTypeRight, 15 | }; 16 | 17 | @interface CBDEye : NSObject 18 | 19 | @property (nonatomic) CBDEyeType type; 20 | 21 | - (GLKMatrix4)eyeViewMatrix; 22 | - (GLKMatrix4)perspectiveMatrixWithZNear:(float)zNear zFar:(float)zFar; 23 | 24 | @end 25 | 26 | 27 | @protocol CBDStereoRendererDelegate 28 | 29 | - (void)setupRendererWithView:(GLKView *)glView; 30 | - (void)shutdownRendererWithView:(GLKView *)glView; 31 | - (void)renderViewDidChangeSize:(CGSize)size; 32 | 33 | - (void)prepareNewFrameWithHeadViewMatrix:(GLKMatrix4)headViewMatrix; 34 | - (void)drawEyeWithEye:(CBDEye *)eye; 35 | - (void)finishFrameWithViewportRect:(CGRect)viewPort; 36 | 37 | @optional 38 | 39 | - (void)magneticTriggerPressed; 40 | 41 | @end 42 | 43 | 44 | @interface CBDViewController : GLKViewController 45 | 46 | @property (nonatomic) GLKView *view; 47 | @property (nonatomic, readonly) NSRecursiveLock *glLock; 48 | 49 | @property (nonatomic, unsafe_unretained) id stereoRendererDelegate; 50 | @property (nonatomic) BOOL vrModeEnabled; 51 | @property (nonatomic) BOOL distortionCorrectionEnabled; 52 | @property (nonatomic) BOOL vignetteEnabled; 53 | @property (nonatomic) BOOL chromaticAberrationCorrectionEnabled; 54 | @property (nonatomic) BOOL restoreGLStateEnabled; 55 | @property (nonatomic) BOOL neckModelEnabled; 56 | 57 | @property (nonatomic,readonly) CMAttitude *attitude ; 58 | 59 | - (void)getFrameParameters:(float *)frameParemeters zNear:(float)zNear zFar:(float)zFar; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /CardboardSDK/CBDViewController.mm: -------------------------------------------------------------------------------- 1 | // 2 | // CardboardViewController.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #import "CBDViewController.h" 7 | 8 | #include "CardboardDeviceParams.h" 9 | #include "Distortion.h" 10 | #include "DistortionRenderer.h" 11 | #include "Eye.h" 12 | #include "FieldOfView.h" 13 | #include "HeadTracker.h" 14 | #include "HeadTransform.h" 15 | #include "HeadMountedDisplay.h" 16 | #include "MagnetSensor.h" 17 | #include "ScreenParams.h" 18 | #include "Viewport.h" 19 | 20 | #include "DebugUtils.h" 21 | 22 | #include "GLHelpers.h" 23 | 24 | 25 | @interface CBDEye () 26 | 27 | @property (nonatomic) CardboardSDK::Eye *eye; 28 | 29 | - (instancetype)initWithEye:(CardboardSDK::Eye *)eye; 30 | 31 | @end 32 | 33 | 34 | @implementation CBDEye 35 | 36 | - (instancetype)init 37 | { 38 | return [self initWithEye:nullptr]; 39 | } 40 | 41 | - (instancetype)initWithEye:(CardboardSDK::Eye *)eye 42 | { 43 | self = [super init]; 44 | if (!self) { return nil; } 45 | 46 | _eye = eye; 47 | 48 | return self; 49 | } 50 | 51 | - (CBDEyeType)type 52 | { 53 | CBDEyeType type = CBDEyeTypeMonocular; 54 | if (_eye->type() == CardboardSDK::Eye::TypeLeft) 55 | { 56 | type = CBDEyeTypeLeft; 57 | } 58 | else if (_eye->type() == CardboardSDK::Eye::TypeRight) 59 | { 60 | type = CBDEyeTypeRight; 61 | } 62 | return type; 63 | } 64 | 65 | - (GLKMatrix4)eyeViewMatrix 66 | { 67 | if (_eye != nullptr) 68 | { 69 | return _eye->eyeView(); 70 | } 71 | return GLKMatrix4Identity; 72 | } 73 | 74 | - (GLKMatrix4)perspectiveMatrixWithZNear:(float)zNear 75 | zFar:(float)zFar 76 | { 77 | if (_eye != nullptr) 78 | { 79 | return _eye->perspective(zNear, zFar); 80 | } 81 | return GLKMatrix4Identity; 82 | } 83 | 84 | @end 85 | 86 | 87 | @interface CBDViewController () 88 | { 89 | CardboardSDK::MagnetSensor *_magnetSensor; 90 | CardboardSDK::HeadTracker *_headTracker; 91 | CardboardSDK::HeadTransform *_headTransform; 92 | CardboardSDK::HeadMountedDisplay *_headMountedDisplay; 93 | 94 | CardboardSDK::Eye *_monocularEye; 95 | CardboardSDK::Eye *_leftEye; 96 | CardboardSDK::Eye *_rightEye; 97 | 98 | CardboardSDK::DistortionRenderer *_distortionRenderer; 99 | 100 | float _distortionCorrectionScale; 101 | 102 | float _zNear; 103 | float _zFar; 104 | 105 | BOOL _projectionChanged; 106 | 107 | BOOL _frameParamentersReady; 108 | } 109 | 110 | @property (nonatomic) NSRecursiveLock *glLock; 111 | 112 | @property (nonatomic) CBDEye *leftEyeWrapper; 113 | @property (nonatomic) CBDEye *rightEyeWrapper; 114 | 115 | @end 116 | 117 | 118 | @implementation CBDViewController 119 | 120 | @dynamic view; 121 | 122 | -(void)doInit{ 123 | 124 | // Do not allow the display to go into sleep 125 | [UIApplication sharedApplication].idleTimerDisabled = YES; 126 | 127 | self.delegate = self; 128 | 129 | _magnetSensor = new CardboardSDK::MagnetSensor(); 130 | _headTracker = new CardboardSDK::HeadTracker(); 131 | _headTransform = new CardboardSDK::HeadTransform(); 132 | _headMountedDisplay = new CardboardSDK::HeadMountedDisplay([UIScreen mainScreen]); 133 | 134 | _monocularEye = new CardboardSDK::Eye(CardboardSDK::Eye::TypeMonocular); 135 | _leftEye = new CardboardSDK::Eye(CardboardSDK::Eye::TypeLeft); 136 | _rightEye = new CardboardSDK::Eye(CardboardSDK::Eye::TypeRight); 137 | 138 | _distortionRenderer = new CardboardSDK::DistortionRenderer(); 139 | 140 | _distortionCorrectionScale = 1.0f; 141 | 142 | _vrModeEnabled = YES; 143 | _distortionCorrectionEnabled = YES; 144 | 145 | _zNear = 0.1f; 146 | _zFar = 100.0f; 147 | 148 | _projectionChanged = YES; 149 | 150 | _frameParamentersReady = NO; 151 | 152 | self.leftEyeWrapper = [CBDEye new]; 153 | self.rightEyeWrapper = [CBDEye new]; 154 | 155 | self.glLock = [NSRecursiveLock new]; 156 | 157 | _headTracker->startTracking([UIApplication sharedApplication].statusBarOrientation); 158 | _magnetSensor->start(); 159 | 160 | [[NSNotificationCenter defaultCenter] addObserver:self 161 | selector:@selector(magneticTriggerPressed:) 162 | name:CardboardSDK::CBDTriggerPressedNotification 163 | object:nil]; 164 | 165 | [[NSNotificationCenter defaultCenter] addObserver:self 166 | selector:@selector(orientationDidChange:) 167 | name:UIApplicationDidChangeStatusBarOrientationNotification 168 | object:nil]; 169 | } 170 | 171 | - (void)orientationDidChange:(NSNotification *)notification 172 | { 173 | _headTracker->updateDeviceOrientation([UIApplication sharedApplication].statusBarOrientation); 174 | } 175 | 176 | - (BOOL)prefersStatusBarHidden 177 | { 178 | return YES; 179 | } 180 | 181 | - (void)viewDidLoad 182 | { 183 | [super viewDidLoad]; 184 | [self doInit]; 185 | 186 | self.preferredFramesPerSecond = 60; 187 | self.view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 188 | if (!self.view.context) 189 | { 190 | NSLog(@"Failed to create OpenGL ES 2.0 context"); 191 | } 192 | self.view.drawableDepthFormat = GLKViewDrawableDepthFormat16; 193 | 194 | [self.stereoRendererDelegate setupRendererWithView:self.view]; 195 | } 196 | 197 | - (void)dealloc 198 | { 199 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 200 | 201 | [self.stereoRendererDelegate shutdownRendererWithView:self.view]; 202 | 203 | if (_magnetSensor != nullptr) { delete _magnetSensor; } 204 | if (_headTracker != nullptr) { delete _headTracker; } 205 | if (_headTransform != nullptr) { delete _headTransform; } 206 | if (_headMountedDisplay != nullptr) { delete _headMountedDisplay; } 207 | 208 | if (_monocularEye != nullptr) { delete _monocularEye; } 209 | if (_leftEye != nullptr) { delete _leftEye; } 210 | if (_rightEye != nullptr) { delete _rightEye; } 211 | 212 | if (_distortionRenderer != nullptr) { delete _distortionRenderer; } 213 | } 214 | 215 | - (BOOL)vignetteEnabled 216 | { 217 | return _distortionRenderer->vignetteEnabled(); 218 | } 219 | 220 | - (void)setVignetteEnabled:(BOOL)vignetteEnabled 221 | { 222 | _distortionRenderer->setVignetteEnabled(vignetteEnabled); 223 | } 224 | 225 | - (BOOL)chromaticAberrationCorrectionEnabled 226 | { 227 | return _distortionRenderer->chromaticAberrationEnabled(); 228 | } 229 | 230 | - (void)setChromaticAberrationCorrectionEnabled:(BOOL)chromaticAberrationCorrectionEnabled 231 | { 232 | _distortionRenderer->setChromaticAberrationEnabled(chromaticAberrationCorrectionEnabled); 233 | } 234 | 235 | - (BOOL)restoreGLStateEnabled 236 | { 237 | return _distortionRenderer->restoreGLStateEnabled(); 238 | } 239 | 240 | - (void)setRestoreGLStateEnabled:(BOOL)restoreGLStateEnabled 241 | { 242 | _distortionRenderer->setRestoreGLStateEnabled(restoreGLStateEnabled); 243 | } 244 | 245 | - (BOOL)neckModelEnabled 246 | { 247 | return _headTracker->neckModelEnabled(); 248 | } 249 | 250 | - (void)setNeckModelEnabled:(BOOL)neckModelEnabled 251 | { 252 | _headTracker->setNeckModelEnabled(neckModelEnabled); 253 | } 254 | 255 | - (void)magneticTriggerPressed:(NSNotification *)notification 256 | { 257 | if ([self.stereoRendererDelegate respondsToSelector:@selector(magneticTriggerPressed)]) 258 | { 259 | [self.stereoRendererDelegate magneticTriggerPressed]; 260 | } 261 | } 262 | 263 | - (void)glkViewController:(GLKViewController *)controller willPause:(BOOL)pause 264 | { 265 | if (pause) 266 | { 267 | _headTracker->stopTracking(); 268 | _magnetSensor->stop(); 269 | } 270 | else 271 | { 272 | _headTracker->startTracking([UIApplication sharedApplication].statusBarOrientation); 273 | _magnetSensor->start(); 274 | } 275 | } 276 | 277 | - (void)glkViewControllerUpdate:(GLKViewController *)controller 278 | { 279 | if (self.paused || !_headTracker->isReady()) { return; } 280 | 281 | [self calculateFrameParametersWithHeadTransform:_headTransform 282 | leftEye:_leftEye 283 | rightEye:_rightEye 284 | monocularEye:_monocularEye]; 285 | _frameParamentersReady = YES; 286 | } 287 | 288 | - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect 289 | { 290 | if (self.paused || !_headTracker->isReady() || !_frameParamentersReady) { return; } 291 | 292 | // glInsertEventMarkerEXT(0, "com.apple.GPUTools.event.debug-frame"); 293 | 294 | GLCheckForError(); 295 | 296 | BOOL lockAcquired = [_glLock tryLock]; 297 | if (!lockAcquired) { return; } 298 | 299 | if (self.vrModeEnabled) 300 | { 301 | if (_distortionCorrectionEnabled) 302 | { 303 | _distortionRenderer->beforeDrawFrame(); 304 | 305 | [self drawFrameWithHeadTransform:_headTransform 306 | leftEye:_leftEye 307 | rightEye:_rightEye]; 308 | 309 | GLCheckForError(); 310 | 311 | // Rebind original framebuffer 312 | [self.view bindDrawable]; 313 | _distortionRenderer->afterDrawFrame(); 314 | 315 | GLCheckForError(); 316 | } 317 | else 318 | { 319 | [self drawFrameWithHeadTransform:_headTransform 320 | leftEye:_leftEye 321 | rightEye:_rightEye]; 322 | } 323 | } 324 | else 325 | { 326 | [self drawFrameWithHeadTransform:_headTransform 327 | leftEye:_monocularEye 328 | rightEye:nullptr]; 329 | } 330 | 331 | [self finishFrameWithViewPort:_monocularEye->viewport()]; 332 | 333 | GLCheckForError(); 334 | 335 | [_glLock unlock]; 336 | } 337 | 338 | -(CMAttitude *)attitude{ 339 | return _headTracker->attitude; 340 | } 341 | 342 | - (void)calculateFrameParametersWithHeadTransform:(CardboardSDK::HeadTransform *)headTransform 343 | leftEye:(CardboardSDK::Eye *)leftEye 344 | rightEye:(CardboardSDK::Eye *)rightEye 345 | monocularEye:(CardboardSDK::Eye *)monocularEye 346 | { 347 | CardboardSDK::CardboardDeviceParams *cardboardDeviceParams = _headMountedDisplay->getCardboard(); 348 | 349 | headTransform->setHeadView(_headTracker->lastHeadView()); 350 | float halfInterLensDistance = cardboardDeviceParams->interLensDistance() * 0.5f; 351 | 352 | // NSLog(@"%@", NSStringFromGLKMatrix4(_headTracker->lastHeadView())); 353 | 354 | if (self.vrModeEnabled) 355 | { 356 | GLKMatrix4 leftEyeTranslate = GLKMatrix4MakeTranslation(halfInterLensDistance, 0, 0); 357 | GLKMatrix4 rightEyeTranslate = GLKMatrix4MakeTranslation(-halfInterLensDistance, 0, 0); 358 | 359 | leftEye->setEyeView( GLKMatrix4Multiply(leftEyeTranslate, headTransform->headView())); 360 | rightEye->setEyeView( GLKMatrix4Multiply(rightEyeTranslate, headTransform->headView())); 361 | } 362 | else 363 | { 364 | monocularEye->setEyeView(headTransform->headView()); 365 | } 366 | 367 | if (_projectionChanged) 368 | { 369 | CardboardSDK::ScreenParams *screenParams = _headMountedDisplay->getScreen(); 370 | monocularEye->viewport()->setViewport(0, 0, screenParams->width(), screenParams->height()); 371 | 372 | if (!self.vrModeEnabled) 373 | { 374 | [self updateMonocularFov:monocularEye->fov()]; 375 | } 376 | else if (_distortionCorrectionEnabled) 377 | { 378 | [self updateFovsWithLeftEyeFov:leftEye->fov() rightEyeFov:rightEye->fov()]; 379 | _distortionRenderer->fovDidChange(_headMountedDisplay, leftEye->fov(), rightEye->fov(), [self virtualEyeToScreenDistance]); 380 | } 381 | else 382 | { 383 | [self updateUndistortedFOVAndViewport]; 384 | } 385 | leftEye->setProjectionChanged(); 386 | rightEye->setProjectionChanged(); 387 | monocularEye->setProjectionChanged(); 388 | _projectionChanged = NO; 389 | } 390 | 391 | if (self.distortionCorrectionEnabled && _distortionRenderer->viewportsChanged()) 392 | { 393 | _distortionRenderer->updateViewports(leftEye->viewport(), rightEye->viewport()); 394 | } 395 | } 396 | 397 | - (void)updateMonocularFov:(CardboardSDK::FieldOfView *)monocularFov 398 | { 399 | CardboardSDK::ScreenParams *screenParams = _headMountedDisplay->getScreen(); 400 | const float monocularBottomFov = 22.5f; 401 | const float monocularLeftFov = GLKMathRadiansToDegrees( 402 | atanf( 403 | tanf(GLKMathDegreesToRadians(monocularBottomFov)) 404 | * screenParams->widthInMeters() 405 | / screenParams->heightInMeters())); 406 | monocularFov->setLeft(monocularLeftFov); 407 | monocularFov->setRight(monocularLeftFov); 408 | monocularFov->setBottom(monocularBottomFov); 409 | monocularFov->setTop(monocularBottomFov); 410 | } 411 | 412 | - (void)updateFovsWithLeftEyeFov:(CardboardSDK::FieldOfView *)leftEyeFov 413 | rightEyeFov:(CardboardSDK::FieldOfView *)rightEyeFov 414 | { 415 | CardboardSDK::CardboardDeviceParams *cardboardDeviceParams = _headMountedDisplay->getCardboard(); 416 | CardboardSDK::ScreenParams *screenParams = _headMountedDisplay->getScreen(); 417 | CardboardSDK::Distortion *distortion = cardboardDeviceParams->distortion(); 418 | float eyeToScreenDistance = [self virtualEyeToScreenDistance]; 419 | 420 | float outerDistance = (screenParams->widthInMeters() - cardboardDeviceParams->interLensDistance() ) / 2.0f; 421 | float innerDistance = cardboardDeviceParams->interLensDistance() / 2.0f; 422 | float bottomDistance = cardboardDeviceParams->verticalDistanceToLensCenter() - screenParams->borderSizeInMeters(); 423 | float topDistance = screenParams->heightInMeters() + screenParams->borderSizeInMeters() - cardboardDeviceParams->verticalDistanceToLensCenter(); 424 | 425 | float outerAngle = GLKMathRadiansToDegrees(atanf(distortion->distort(outerDistance / eyeToScreenDistance))); 426 | float innerAngle = GLKMathRadiansToDegrees(atanf(distortion->distort(innerDistance / eyeToScreenDistance))); 427 | float bottomAngle = GLKMathRadiansToDegrees(atanf(distortion->distort(bottomDistance / eyeToScreenDistance))); 428 | float topAngle = GLKMathRadiansToDegrees(atanf(distortion->distort(topDistance / eyeToScreenDistance))); 429 | 430 | leftEyeFov->setLeft(MIN(outerAngle, cardboardDeviceParams->maximumLeftEyeFOV()->left())); 431 | leftEyeFov->setRight(MIN(innerAngle, cardboardDeviceParams->maximumLeftEyeFOV()->right())); 432 | leftEyeFov->setBottom(MIN(bottomAngle, cardboardDeviceParams->maximumLeftEyeFOV()->bottom())); 433 | leftEyeFov->setTop(MIN(topAngle, cardboardDeviceParams->maximumLeftEyeFOV()->top())); 434 | 435 | rightEyeFov->setLeft(leftEyeFov->right()); 436 | rightEyeFov->setRight(leftEyeFov->left()); 437 | rightEyeFov->setBottom(leftEyeFov->bottom()); 438 | rightEyeFov->setTop(leftEyeFov->top()); 439 | } 440 | 441 | - (void)updateUndistortedFOVAndViewport 442 | { 443 | CardboardSDK::CardboardDeviceParams *cardboardDeviceParams = _headMountedDisplay->getCardboard(); 444 | CardboardSDK::ScreenParams *screenParams = _headMountedDisplay->getScreen(); 445 | 446 | float halfInterLensDistance = cardboardDeviceParams->interLensDistance() * 0.5f; 447 | float eyeToScreenDistance = [self virtualEyeToScreenDistance]; 448 | 449 | float left = screenParams->widthInMeters() / 2.0f - halfInterLensDistance; 450 | float right = halfInterLensDistance; 451 | float bottom = cardboardDeviceParams->verticalDistanceToLensCenter() - screenParams->borderSizeInMeters(); 452 | float top = screenParams->borderSizeInMeters() + screenParams->heightInMeters() - cardboardDeviceParams->verticalDistanceToLensCenter(); 453 | 454 | CardboardSDK::FieldOfView *leftEyeFov = _leftEye->fov(); 455 | leftEyeFov->setLeft(GLKMathRadiansToDegrees(atan2f(left, eyeToScreenDistance))); 456 | leftEyeFov->setRight(GLKMathRadiansToDegrees(atan2f(right, eyeToScreenDistance))); 457 | leftEyeFov->setBottom(GLKMathRadiansToDegrees(atan2f(bottom, eyeToScreenDistance))); 458 | leftEyeFov->setTop(GLKMathRadiansToDegrees(atan2f(top, eyeToScreenDistance))); 459 | 460 | CardboardSDK::FieldOfView *rightEyeFov = _rightEye->fov(); 461 | rightEyeFov->setLeft(leftEyeFov->right()); 462 | rightEyeFov->setRight(leftEyeFov->left()); 463 | rightEyeFov->setBottom(leftEyeFov->bottom()); 464 | rightEyeFov->setTop(leftEyeFov->top()); 465 | 466 | _leftEye->viewport()->setViewport(0, 0, screenParams->width() / 2, screenParams->height()); 467 | _rightEye->viewport()->setViewport(screenParams->width() / 2, 0, screenParams->width() / 2, screenParams->height()); 468 | } 469 | 470 | - (float)virtualEyeToScreenDistance 471 | { 472 | return _headMountedDisplay->getCardboard()->screenToLensDistance(); 473 | } 474 | 475 | - (void)viewDidLayoutSubviews 476 | { 477 | [super viewDidLayoutSubviews]; 478 | [self updateRenderViewSize:self.view.bounds.size]; 479 | } 480 | 481 | #pragma mark Stereo renderer methods 482 | 483 | - (void)updateRenderViewSize:(CGSize)size 484 | { 485 | if (self.vrModeEnabled) 486 | { 487 | [self.stereoRendererDelegate renderViewDidChangeSize:CGSizeMake(size.width / 2, size.height)]; 488 | } 489 | else 490 | { 491 | [self.stereoRendererDelegate renderViewDidChangeSize:CGSizeMake(size.width, size.height)]; 492 | } 493 | } 494 | 495 | - (void)drawFrameWithHeadTransform:(CardboardSDK::HeadTransform *)headTransform 496 | leftEye:(CardboardSDK::Eye *)leftEye 497 | rightEye:(CardboardSDK::Eye *)rightEye 498 | { 499 | GLCheckForError(); 500 | 501 | // NSLog(@"%@", NSStringFromGLKMatrix4(leftEyeParams->transform()->eyeView())); 502 | 503 | [self.stereoRendererDelegate prepareNewFrameWithHeadViewMatrix:headTransform->headView()]; 504 | 505 | GLCheckForError(); 506 | 507 | glEnable(GL_SCISSOR_TEST); 508 | leftEye->viewport()->setGLViewport(); 509 | leftEye->viewport()->setGLScissor(); 510 | 511 | GLCheckForError(); 512 | 513 | _leftEyeWrapper.eye = leftEye; 514 | [self.stereoRendererDelegate drawEyeWithEye:_leftEyeWrapper]; 515 | 516 | GLCheckForError(); 517 | 518 | if (rightEye == nullptr) { return; } 519 | 520 | rightEye->viewport()->setGLViewport(); 521 | rightEye->viewport()->setGLScissor(); 522 | 523 | GLCheckForError(); 524 | 525 | _rightEyeWrapper.eye = rightEye; 526 | [self.stereoRendererDelegate drawEyeWithEye:_rightEyeWrapper]; 527 | 528 | GLCheckForError(); 529 | } 530 | 531 | - (void)finishFrameWithViewPort:(CardboardSDK::Viewport *)viewport 532 | { 533 | viewport->setGLViewport(); 534 | viewport->setGLScissor(); 535 | [self.stereoRendererDelegate finishFrameWithViewportRect:viewport->toCGRect()]; 536 | } 537 | 538 | - (void)getFrameParameters:(float *)frameParemeters zNear:(float)zNear zFar:(float)zFar 539 | { 540 | [self calculateFrameParametersWithHeadTransform:_headTransform 541 | leftEye:_leftEye 542 | rightEye:_rightEye 543 | monocularEye:_monocularEye]; 544 | 545 | GLKMatrix4 headView = _headTransform->headView(); 546 | GLKMatrix4 leftEyeView = _leftEye->eyeView(); 547 | GLKMatrix4 leftEyePerspective = _leftEye->perspective(zNear, zFar); 548 | GLKMatrix4 rightEyeView = _rightEye->eyeView(); 549 | GLKMatrix4 rightEyePerspective = _rightEye->perspective(zNear, zFar); 550 | 551 | std::copy(headView.m, headView.m + 16, frameParemeters); 552 | std::copy(leftEyeView.m, leftEyeView.m + 16, frameParemeters + 16); 553 | std::copy(leftEyePerspective.m, leftEyePerspective.m + 16, frameParemeters + 32); 554 | std::copy(rightEyeView.m, rightEyeView.m + 16, frameParemeters + 48); 555 | std::copy(rightEyePerspective.m, rightEyePerspective.m + 16, frameParemeters + 64); 556 | } 557 | 558 | @end 559 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/CardboardDeviceParams.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardboardDeviceParams.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__CardboardDeviceParams__ 8 | #define __CardboardSDK_iOS__CardboardDeviceParams__ 9 | 10 | #import 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | class Distortion; 17 | class FieldOfView; 18 | 19 | 20 | class CardboardDeviceParams 21 | { 22 | public: 23 | CardboardDeviceParams(); 24 | CardboardDeviceParams(CardboardDeviceParams* params); 25 | ~CardboardDeviceParams(); 26 | 27 | NSString *vendor(); 28 | NSString *model(); 29 | 30 | float interLensDistance(); 31 | 32 | float verticalDistanceToLensCenter(); 33 | 34 | float screenToLensDistance(); 35 | 36 | FieldOfView *maximumLeftEyeFOV(); 37 | Distortion *distortion(); 38 | 39 | bool equals(CardboardDeviceParams *other); 40 | 41 | private: 42 | NSString *_vendor; 43 | NSString *_model; 44 | NSString *_version; 45 | float _interLensDistance; 46 | float _verticalDistanceToLensCenter; 47 | float _screenToLensDistance; 48 | 49 | FieldOfView *_maximumLeftEyeFOV; 50 | Distortion *_distortion; 51 | }; 52 | 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/CardboardDeviceParams.mm: -------------------------------------------------------------------------------- 1 | // 2 | // CardboardDeviceParams.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "CardboardDeviceParams.h" 8 | 9 | #include "Distortion.h" 10 | #include "FieldOfView.h" 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | CardboardDeviceParams::CardboardDeviceParams() : 17 | _vendor(@"com.google"), 18 | _model(@"cardboard"), 19 | _interLensDistance(0.06f), 20 | _verticalDistanceToLensCenter(0.035f), 21 | _screenToLensDistance(0.042f) 22 | { 23 | _distortion = new Distortion(); 24 | _maximumLeftEyeFOV = new FieldOfView(); 25 | } 26 | 27 | CardboardDeviceParams::CardboardDeviceParams(CardboardDeviceParams* params) 28 | { 29 | _vendor = params->_vendor; 30 | _model = params->_model; 31 | 32 | _interLensDistance = params->_interLensDistance; 33 | _verticalDistanceToLensCenter = params->_verticalDistanceToLensCenter; 34 | _screenToLensDistance = params->_screenToLensDistance; 35 | 36 | _maximumLeftEyeFOV = new FieldOfView(params->_maximumLeftEyeFOV); 37 | _distortion = new Distortion(params->_distortion); 38 | } 39 | 40 | CardboardDeviceParams::~CardboardDeviceParams() 41 | { 42 | if (_distortion != nullptr) { delete _distortion; } 43 | if (_maximumLeftEyeFOV != nullptr) { delete _maximumLeftEyeFOV; } 44 | } 45 | 46 | NSString *CardboardDeviceParams::vendor() 47 | { 48 | return _vendor; 49 | } 50 | 51 | NSString *CardboardDeviceParams::model() 52 | { 53 | return _model; 54 | } 55 | 56 | float CardboardDeviceParams::interLensDistance() 57 | { 58 | return _interLensDistance; 59 | } 60 | 61 | float CardboardDeviceParams::verticalDistanceToLensCenter() 62 | { 63 | return _verticalDistanceToLensCenter; 64 | } 65 | 66 | float CardboardDeviceParams::screenToLensDistance() 67 | { 68 | return _screenToLensDistance; 69 | } 70 | 71 | FieldOfView *CardboardDeviceParams::maximumLeftEyeFOV() 72 | { 73 | return _maximumLeftEyeFOV; 74 | } 75 | 76 | Distortion *CardboardDeviceParams::distortion() 77 | { 78 | return _distortion; 79 | } 80 | 81 | bool CardboardDeviceParams::equals(CardboardDeviceParams *other) 82 | { 83 | if (other == nullptr) 84 | { 85 | return false; 86 | } 87 | else if (other == this) 88 | { 89 | return true; 90 | } 91 | return 92 | ([vendor() isEqualToString:other->vendor()]) 93 | && ([model() isEqualToString:other->model()]) 94 | && (interLensDistance() == other->interLensDistance()) 95 | && (verticalDistanceToLensCenter() == other->verticalDistanceToLensCenter()) 96 | && (screenToLensDistance() == other->screenToLensDistance()) 97 | && (maximumLeftEyeFOV()->equals(other->maximumLeftEyeFOV())) 98 | && (distortion()->equals(other->distortion())); 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/CardboardUnity.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardboardUnity.h 3 | // CardboardSDK-iOS 4 | // 5 | // Created by Ricardo Sánchez-Sáez on 03/02/2015. 6 | // 7 | // 8 | 9 | #ifndef __CardboardSDK_iOS__CardboardUnity__ 10 | #define __CardboardSDK_iOS__CardboardUnity__ 11 | 12 | #endif /* defined(__CardboardSDK_iOS__CardboardUnity__) */ 13 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/CardboardUnity.mm: -------------------------------------------------------------------------------- 1 | // 2 | // CardboardUnity.cpp 3 | // CardboardSDK-iOS 4 | // 5 | // Created by Ricardo Sánchez-Sáez on 03/02/2015. 6 | // 7 | // 8 | 9 | #import "CardboardUnity.h" 10 | 11 | #import "CBDViewController.h" 12 | 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | 19 | static CBDViewController *cardboardViewController = nil; 20 | 21 | void _unity_getFrameParameters(float *frameParameters, float zNear, float zFar) 22 | { 23 | if (!cardboardViewController) 24 | { 25 | cardboardViewController = [CBDViewController new]; 26 | } 27 | 28 | [cardboardViewController getFrameParameters:frameParameters zNear:zNear zFar:zFar]; 29 | } 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Distortion.h: -------------------------------------------------------------------------------- 1 | // 2 | // Distortion.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__Distortion__ 8 | #define __CardboardSDK_iOS__Distortion__ 9 | 10 | #import 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | class Distortion 17 | { 18 | public: 19 | Distortion(); 20 | Distortion(Distortion *other); 21 | 22 | void setCoefficients(float *coefficients); 23 | float *coefficients(); 24 | 25 | float distortionFactor(float radius); 26 | float distort(float radius); 27 | float distortInverse(float radius); 28 | bool equals(Distortion *other); 29 | 30 | NSString *toString(); 31 | 32 | private: 33 | constexpr static int s_numberOfCoefficients = 2; 34 | float _coefficients[s_numberOfCoefficients]; 35 | }; 36 | 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Distortion.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Distortion.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "Distortion.h" 8 | 9 | 10 | namespace CardboardSDK 11 | { 12 | 13 | Distortion::Distortion() 14 | { 15 | _coefficients[0] = 0.441f; 16 | _coefficients[1] = 0.156f; 17 | } 18 | 19 | Distortion::Distortion(Distortion *other) 20 | { 21 | for (int i = 0; i < s_numberOfCoefficients; i++) 22 | { 23 | _coefficients[i] = other->_coefficients[i]; 24 | } 25 | } 26 | 27 | void Distortion::setCoefficients(float *coefficients) 28 | { 29 | for (int i = 0; i < s_numberOfCoefficients; i++) 30 | { 31 | _coefficients[i] = coefficients[i]; 32 | } 33 | } 34 | 35 | float *Distortion::coefficients() 36 | { 37 | return _coefficients; 38 | } 39 | 40 | float Distortion::distortionFactor(float radius) 41 | { 42 | float result = 1.0f; 43 | float rFactor = 1.0f; 44 | float squaredRadius = radius * radius; 45 | for (int i = 0; i < s_numberOfCoefficients; i++) 46 | { 47 | rFactor *= squaredRadius; 48 | result += _coefficients[i] * rFactor; 49 | } 50 | return result; 51 | } 52 | 53 | float Distortion::distort(float radius) 54 | { 55 | return radius * distortionFactor(radius); 56 | } 57 | 58 | float Distortion::distortInverse(float radius) 59 | { 60 | float r0 = radius / 0.9f; 61 | float r = radius * 0.9f; 62 | float dr0 = radius - distort(r0); 63 | while (fabsf(r - r0) > 0.0001f) 64 | { 65 | float dr = radius - distort(r); 66 | float r2 = r - dr * ((r - r0) / (dr - dr0)); 67 | r0 = r; 68 | r = r2; 69 | dr0 = dr; 70 | } 71 | return r; 72 | } 73 | 74 | bool Distortion::equals(Distortion *other) 75 | { 76 | if (other == nullptr) 77 | { 78 | return false; 79 | } 80 | else if (other == this) 81 | { 82 | return true; 83 | } 84 | 85 | for (int i = 0; i < s_numberOfCoefficients; i++) 86 | { 87 | if (_coefficients[i] != other->_coefficients[i]) 88 | { 89 | return false; 90 | } 91 | } 92 | 93 | return true; 94 | } 95 | 96 | NSString *Distortion::toString() 97 | { 98 | return [NSString stringWithFormat:@"{%f, %f}", _coefficients[0], _coefficients[1]]; 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/DistortionRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // DistortionRenderer.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__DistortionRenderer__ 8 | #define __CardboardSDK_iOS__DistortionRenderer__ 9 | 10 | #import 11 | #import 12 | 13 | 14 | namespace CardboardSDK 15 | { 16 | 17 | class Distortion; 18 | class Eye; 19 | class FieldOfView; 20 | class GLStateBackup; 21 | class HeadMountedDisplay; 22 | class Viewport; 23 | 24 | 25 | class DistortionRenderer 26 | { 27 | public: 28 | DistortionRenderer(); 29 | ~DistortionRenderer(); 30 | 31 | void beforeDrawFrame(); 32 | void afterDrawFrame(); 33 | 34 | void setResolutionScale(float scale); 35 | 36 | bool restoreGLStateEnabled(); 37 | void setRestoreGLStateEnabled(bool enabled); 38 | 39 | bool chromaticAberrationEnabled(); 40 | void setChromaticAberrationEnabled(bool enabled); 41 | 42 | bool vignetteEnabled(); 43 | void setVignetteEnabled(bool enabled); 44 | 45 | bool viewportsChanged(); 46 | void updateViewports(Viewport *leftViewport, 47 | Viewport *rightViewport); 48 | 49 | void fovDidChange(HeadMountedDisplay *hmd, 50 | FieldOfView *leftEyeFov, 51 | FieldOfView *rightEyeFov, 52 | float virtualEyeToScreenDistance); 53 | 54 | 55 | private: 56 | class DistortionMesh 57 | { 58 | public: 59 | int _indices; 60 | int _arrayBufferID; 61 | int _elementBufferID; 62 | 63 | DistortionMesh(); 64 | DistortionMesh(Distortion *distortionRed, 65 | Distortion *distortionGreen, 66 | Distortion *distortionBlue, 67 | float screenWidth, float screenHeight, 68 | float xEyeOffsetScreen, float yEyeOffsetScreen, 69 | float textureWidth, float textureHeight, 70 | float xEyeOffsetTexture, float yEyeOffsetTexture, 71 | float viewportXTexture, float viewportYTexture, 72 | float viewportWidthTexture, 73 | float viewportHeightTexture, 74 | bool vignetteEnabled); 75 | }; 76 | 77 | struct EyeViewport 78 | { 79 | public: 80 | float x; 81 | float y; 82 | float width; 83 | float height; 84 | float eyeX; 85 | float eyeY; 86 | 87 | NSString *toString(); 88 | }; 89 | 90 | struct ProgramHolder 91 | { 92 | public: 93 | ProgramHolder() : 94 | program(-1), 95 | positionLocation(-1), 96 | vignetteLocation(-1), 97 | redTextureCoordLocation(-1), 98 | greenTextureCoordLocation(-1), 99 | blueTextureCoordLocation(-1), 100 | uTextureCoordScaleLocation(-1), 101 | uTextureSamplerLocation(-1) {} 102 | 103 | GLint program; 104 | GLint positionLocation; 105 | GLint vignetteLocation; 106 | GLint redTextureCoordLocation; 107 | GLint greenTextureCoordLocation; 108 | GLint blueTextureCoordLocation; 109 | GLint uTextureCoordScaleLocation; 110 | GLint uTextureSamplerLocation; 111 | }; 112 | 113 | GLuint _textureID; 114 | GLuint _renderbufferID; 115 | GLuint _framebufferID; 116 | // int _originalFramebufferID; 117 | GLenum _textureFormat; 118 | GLenum _textureType; 119 | float _resolutionScale; 120 | bool _restoreGLStateEnabled; 121 | bool _chromaticAberrationCorrectionEnabled; 122 | bool _vignetteEnabled; 123 | DistortionMesh *_leftEyeDistortionMesh; 124 | DistortionMesh *_rightEyeDistortionMesh; 125 | GLStateBackup *_glStateBackup; 126 | GLStateBackup *_glStateBackupAberration; 127 | HeadMountedDisplay *_headMountedDisplay; 128 | EyeViewport *_leftEyeViewport; 129 | EyeViewport *_rightEyeViewport; 130 | bool _fovsChanged; 131 | bool _viewportsChanged; 132 | bool _textureFormatChanged; 133 | bool _drawingFrame; 134 | float _xPxPerTanAngle; 135 | float _yPxPerTanAngle; 136 | float _metersPerTanAngle; 137 | 138 | ProgramHolder *_programHolder; 139 | ProgramHolder *_programHolderAberration; 140 | 141 | EyeViewport *initViewportForEye(FieldOfView *eyeFieldOfView, float xOffsetM); 142 | 143 | void setTextureFormat(GLint textureFormat, GLint textureType); 144 | void updateTextureAndDistortionMesh(); 145 | void undistortTexture(GLint textureID); 146 | 147 | DistortionMesh *createDistortionMesh(EyeViewport *eyeViewport, 148 | float textureWidthTanAngle, 149 | float textureHeightTanAngle, 150 | float xEyeOffsetTanAngleScreen, 151 | float yEyeOffsetTanAngleScreen); 152 | void renderDistortionMesh(DistortionMesh *mesh, GLint textureID); 153 | float computeDistortionScale(Distortion *distortion, 154 | float screenWidthM, 155 | float interpupillaryDistanceM); 156 | int createTexture(GLint width, GLint height, GLint textureFormat, GLint textureType); 157 | int setupRenderTextureAndRenderbuffer(int width, int height); 158 | int createProgram(const GLchar *vertexSource, 159 | const GLchar *fragmentSource); 160 | ProgramHolder *createProgramHolder(bool aberrationCorrected); 161 | }; 162 | 163 | } 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Eye.h: -------------------------------------------------------------------------------- 1 | // 2 | // Eye.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__Eye__ 8 | #define __CardboardSDK_iOS__Eye__ 9 | 10 | #import 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | class FieldOfView; 17 | class Viewport; 18 | 19 | 20 | class Eye 21 | { 22 | public: 23 | 24 | typedef enum 25 | { 26 | TypeMonocular = 0, 27 | TypeLeft = 1, 28 | TypeRight = 2 29 | } Type; 30 | 31 | Eye(Type eye); 32 | ~Eye(); 33 | 34 | Type type(); 35 | 36 | GLKMatrix4 eyeView(); 37 | void setEyeView(GLKMatrix4 eyeView); 38 | GLKMatrix4 perspective(float zNear, float zFar); 39 | 40 | Viewport *viewport(); 41 | FieldOfView *fov(); 42 | 43 | void setProjectionChanged(); 44 | 45 | private: 46 | Type _type; 47 | GLKMatrix4 _eyeView; 48 | Viewport *_viewport; 49 | FieldOfView *_fov; 50 | bool _projectionChanged; 51 | GLKMatrix4 _perspective; 52 | float _lastZNear; 53 | float _lastZFar; 54 | }; 55 | 56 | } 57 | 58 | #endif -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Eye.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Eye.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "Eye.h" 8 | 9 | #include "FieldOfView.h" 10 | #include "Viewport.h" 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | Eye::Eye(Type eyeType) : 17 | _type(eyeType), 18 | _eyeView(GLKMatrix4Identity), 19 | _projectionChanged(true), 20 | _perspective(GLKMatrix4Identity), 21 | _lastZNear(0), 22 | _lastZFar(0) 23 | { 24 | _viewport = new Viewport(); 25 | _fov = new FieldOfView(); 26 | } 27 | 28 | Eye::~Eye() 29 | { 30 | if (_viewport != nullptr) { delete _viewport; } 31 | if (_fov != nullptr) { delete _fov; } 32 | } 33 | 34 | Eye::Type Eye::type() 35 | { 36 | return _type; 37 | } 38 | 39 | GLKMatrix4 Eye::eyeView() 40 | { 41 | return _eyeView; 42 | } 43 | 44 | void Eye::setEyeView(GLKMatrix4 eyeView) 45 | { 46 | _eyeView = eyeView; 47 | } 48 | 49 | GLKMatrix4 Eye::perspective(float zNear, float zFar) 50 | { 51 | if (!_projectionChanged && _lastZNear == zNear && _lastZFar == zFar) 52 | { 53 | return _perspective; 54 | } 55 | _perspective = fov()->toPerspectiveMatrix(zNear, zFar); 56 | _lastZNear = zNear; 57 | _lastZFar = zFar; 58 | _projectionChanged = false; 59 | return _perspective; 60 | } 61 | 62 | Viewport *Eye::viewport() 63 | { 64 | return _viewport; 65 | } 66 | 67 | FieldOfView *Eye::fov() 68 | { 69 | return _fov; 70 | } 71 | 72 | void Eye::setProjectionChanged() 73 | { 74 | _projectionChanged = true; 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/FieldOfView.h: -------------------------------------------------------------------------------- 1 | // 2 | // FieldOfView.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__FieldOfView__ 8 | #define __CardboardSDK_iOS__FieldOfView__ 9 | 10 | #import 11 | #import 12 | 13 | 14 | namespace CardboardSDK 15 | { 16 | 17 | class FieldOfView 18 | { 19 | public: 20 | FieldOfView(); 21 | FieldOfView(float left, float right, float bottom, float top); 22 | FieldOfView(FieldOfView *other); 23 | 24 | void setLeft(float left); 25 | float left(); 26 | 27 | void setRight(float right); 28 | float right(); 29 | 30 | void setBottom(float bottom); 31 | float bottom(); 32 | 33 | void setTop(float top); 34 | float top(); 35 | 36 | GLKMatrix4 toPerspectiveMatrix(float near, float far); 37 | 38 | bool equals(FieldOfView *other); 39 | NSString *toString(); 40 | 41 | private: 42 | constexpr static float s_defaultViewAngle = 40.0f; 43 | 44 | float _left; 45 | float _right; 46 | float _bottom; 47 | float _top; 48 | }; 49 | 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/FieldOfView.mm: -------------------------------------------------------------------------------- 1 | // 2 | // FieldOfView.cpp 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "FieldOfView.h" 8 | 9 | 10 | namespace CardboardSDK 11 | { 12 | 13 | FieldOfView::FieldOfView() : 14 | _left(s_defaultViewAngle), 15 | _right(s_defaultViewAngle), 16 | _bottom(s_defaultViewAngle), 17 | _top(s_defaultViewAngle) 18 | { 19 | } 20 | 21 | FieldOfView::FieldOfView(float left, float right, float bottom, float top) 22 | { 23 | _left = left; 24 | _right = right; 25 | _bottom = bottom; 26 | _top = top; 27 | } 28 | 29 | FieldOfView::FieldOfView(FieldOfView *other) 30 | { 31 | _left = other->_left; 32 | _right = other->_right; 33 | _bottom = other->_bottom; 34 | _top = other->_top; 35 | } 36 | 37 | void FieldOfView::setLeft(float left) 38 | { 39 | _left = left; 40 | } 41 | 42 | float FieldOfView::left() 43 | { 44 | return _left; 45 | } 46 | 47 | void FieldOfView::setRight(float right) 48 | { 49 | _right = right; 50 | } 51 | 52 | float FieldOfView::right() 53 | { 54 | return _right; 55 | } 56 | 57 | void FieldOfView::setBottom(float bottom) 58 | { 59 | _bottom = bottom; 60 | } 61 | 62 | float FieldOfView::bottom() 63 | { 64 | return _bottom; 65 | } 66 | 67 | void FieldOfView::setTop(float top) 68 | { 69 | _top = top; 70 | } 71 | 72 | float FieldOfView::top() 73 | { 74 | return _top; 75 | } 76 | 77 | GLKMatrix4 FieldOfView::toPerspectiveMatrix(float near, float far) 78 | { 79 | float left = -tanf(GLKMathDegreesToRadians(_left)) * near; 80 | float right = tanf(GLKMathDegreesToRadians(_right)) * near; 81 | float bottom = -tanf(GLKMathDegreesToRadians(_bottom)) * near; 82 | float top = tanf(GLKMathDegreesToRadians(_top)) * near; 83 | GLKMatrix4 frustrum = GLKMatrix4MakeFrustum(left, right, bottom, top, near, far); 84 | return frustrum; 85 | } 86 | 87 | bool FieldOfView::equals(FieldOfView *other) 88 | { 89 | if (other == nullptr) 90 | { 91 | return false; 92 | } 93 | else if (other == this) 94 | { 95 | return true; 96 | } 97 | return 98 | (_left == other->_left) 99 | && (_right == other->_right) 100 | && (_bottom == other->_bottom) 101 | && (_top == other->_top); 102 | } 103 | 104 | NSString *FieldOfView::toString() 105 | { 106 | return [NSString stringWithFormat:@"{left:%f right:%f bottom:%f top:%f}", _left, _right, _bottom, _top]; 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/GLStateBackup.h: -------------------------------------------------------------------------------- 1 | // 2 | // GLStateBackup.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__GLStateBackup__ 8 | #define __CardboardSDK_iOS__GLStateBackup__ 9 | 10 | #include 11 | 12 | #import 13 | 14 | 15 | namespace CardboardSDK 16 | { 17 | 18 | class GLStateBackup 19 | { 20 | public: 21 | GLStateBackup(); 22 | void addTrackedVertexAttribute(GLuint attributeId); 23 | void clearTrackedVertexAttributes(); 24 | void readFromGL(); 25 | void writeToGL(); 26 | 27 | private: 28 | class VertexAttributeState 29 | { 30 | public: 31 | VertexAttributeState(GLuint attributeId); 32 | 33 | void readFromGL(); 34 | void writeToGL(); 35 | 36 | private: 37 | GLuint _attributeId; 38 | GLint _enabled; 39 | }; 40 | 41 | 42 | GLint _viewport[4]; 43 | bool _cullFaceEnabled; 44 | bool _scissorTestEnabled; 45 | bool _depthTestEnabled; 46 | GLfloat _clearColor[4]; 47 | GLint _shaderProgram; 48 | GLint _scissorBox[4]; 49 | GLint _activeTexture; 50 | GLint _texture2DBinding; 51 | GLint _arrayBufferBinding; 52 | GLint _elementArrayBufferBinding; 53 | std::vector _vertexAttributes; 54 | }; 55 | 56 | } 57 | 58 | #endif /* defined(__CardboardSDK_iOS__GLStateBackup__) */ 59 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/GLStateBackup.mm: -------------------------------------------------------------------------------- 1 | // 2 | // GLStateBackup.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "GLStateBackup.h" 8 | 9 | 10 | namespace CardboardSDK 11 | { 12 | 13 | GLStateBackup::VertexAttributeState::VertexAttributeState(GLuint attributeId) : 14 | _attributeId(attributeId), 15 | _enabled(false) 16 | { 17 | } 18 | 19 | void GLStateBackup::VertexAttributeState::readFromGL() 20 | { 21 | glGetVertexAttribiv(_attributeId, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &_enabled); 22 | } 23 | 24 | void GLStateBackup::VertexAttributeState::writeToGL() { 25 | if (_enabled == false) 26 | { 27 | glDisableVertexAttribArray(_attributeId); 28 | } 29 | else 30 | { 31 | glEnableVertexAttribArray(_attributeId); 32 | } 33 | } 34 | 35 | 36 | GLStateBackup::GLStateBackup() : 37 | _cullFaceEnabled(false), 38 | _scissorTestEnabled(false), 39 | _depthTestEnabled(false), 40 | _shaderProgram(-1), 41 | _activeTexture(-1), 42 | _texture2DBinding(-1), 43 | _arrayBufferBinding(-1), 44 | _elementArrayBufferBinding(-1) 45 | { 46 | for (int i = 0; i < 4; i++) 47 | { 48 | _viewport[i] = 0; 49 | _clearColor[i] = 0; 50 | _scissorBox[i] = 0; 51 | } 52 | 53 | } 54 | 55 | void GLStateBackup::addTrackedVertexAttribute(GLuint attributeId) 56 | { 57 | _vertexAttributes.push_back(VertexAttributeState(attributeId)); 58 | } 59 | 60 | void GLStateBackup::clearTrackedVertexAttributes() 61 | { 62 | _vertexAttributes.clear(); 63 | } 64 | 65 | void GLStateBackup::readFromGL() { 66 | glGetIntegerv(GL_VIEWPORT, _viewport); 67 | _cullFaceEnabled = glIsEnabled(GL_CULL_FACE); 68 | _scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST); 69 | _depthTestEnabled = glIsEnabled(GL_DEPTH_TEST); 70 | glGetFloatv(GL_COLOR_CLEAR_VALUE, _clearColor); 71 | glGetIntegerv(GL_CURRENT_PROGRAM, &_shaderProgram); 72 | glGetIntegerv(GL_SCISSOR_BOX, _scissorBox); 73 | glGetIntegerv(GL_ACTIVE_TEXTURE, &_activeTexture); 74 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &_texture2DBinding); 75 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &_arrayBufferBinding); 76 | glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &_elementArrayBufferBinding); 77 | for (std::vector::iterator it = _vertexAttributes.begin(); 78 | it != _vertexAttributes.end(); 79 | ++it) 80 | { 81 | (*it).readFromGL(); 82 | } 83 | } 84 | 85 | void GLStateBackup::writeToGL() 86 | { 87 | for (std::vector::iterator it = _vertexAttributes.begin(); 88 | it != _vertexAttributes.end(); 89 | ++it) 90 | { 91 | (*it).writeToGL(); 92 | } 93 | glBindBuffer(GL_ARRAY_BUFFER, _arrayBufferBinding); 94 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _elementArrayBufferBinding); 95 | glBindTexture(GL_TEXTURE_2D, _texture2DBinding); 96 | glActiveTexture(_activeTexture); 97 | glScissor(_scissorBox[0], _scissorBox[1], _scissorBox[2], _scissorBox[3]); 98 | glUseProgram(_shaderProgram); 99 | glClearColor(_clearColor[0], _clearColor[1], _clearColor[2], _clearColor[3]); 100 | if (_cullFaceEnabled) 101 | { 102 | glEnable(GL_CULL_FACE); 103 | } 104 | else 105 | { 106 | glDisable(GL_CULL_FACE); 107 | } 108 | if (_scissorTestEnabled) 109 | { 110 | glEnable(GL_SCISSOR_TEST); 111 | } 112 | else 113 | { 114 | glDisable(GL_SCISSOR_TEST); 115 | } 116 | if (_depthTestEnabled) 117 | { 118 | glEnable(GL_DEPTH_TEST); 119 | } 120 | else { 121 | glDisable(GL_DEPTH_TEST); 122 | } 123 | glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]); 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/HeadMountedDisplay.h: -------------------------------------------------------------------------------- 1 | // 2 | // HeadMountedDisplay.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__HeadMountedDisplay__ 8 | #define __CardboardSDK_iOS__HeadMountedDisplay__ 9 | 10 | #import 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | class CardboardDeviceParams; 17 | class ScreenParams; 18 | 19 | 20 | class HeadMountedDisplay 21 | { 22 | public: 23 | HeadMountedDisplay(UIScreen *screen); 24 | HeadMountedDisplay(HeadMountedDisplay *hmd); 25 | ~HeadMountedDisplay(); 26 | 27 | void setScreen(ScreenParams* screen); 28 | ScreenParams *getScreen(); 29 | 30 | void setCardboard(CardboardDeviceParams *cardboard); 31 | CardboardDeviceParams *getCardboard(); 32 | 33 | bool equals(HeadMountedDisplay *other); 34 | 35 | private: 36 | ScreenParams *_screen; 37 | CardboardDeviceParams *_cardboard; 38 | }; 39 | 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/HeadMountedDisplay.mm: -------------------------------------------------------------------------------- 1 | // 2 | // HeadMountedDisplay.cpp 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "HeadMountedDisplay.h" 8 | 9 | #include "CardboardDeviceParams.h" 10 | #include "ScreenParams.h" 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | HeadMountedDisplay::HeadMountedDisplay(UIScreen *screen) 17 | { 18 | _screen = new ScreenParams(screen); 19 | _cardboard = new CardboardDeviceParams(); 20 | } 21 | 22 | HeadMountedDisplay::HeadMountedDisplay(HeadMountedDisplay *hmd) 23 | { 24 | _screen = new ScreenParams(hmd->getScreen()); 25 | _cardboard = new CardboardDeviceParams(hmd->getCardboard()); 26 | } 27 | 28 | HeadMountedDisplay::~HeadMountedDisplay() 29 | { 30 | if (_screen != nullptr) { delete _screen; } 31 | if (_cardboard != nullptr) { delete _cardboard; } 32 | } 33 | 34 | void HeadMountedDisplay::setScreen(ScreenParams* screen) 35 | { 36 | if (_screen != nullptr) 37 | { 38 | delete _screen; 39 | } 40 | _screen = new ScreenParams(screen); 41 | } 42 | 43 | ScreenParams* HeadMountedDisplay::getScreen() 44 | { 45 | return _screen; 46 | } 47 | 48 | void HeadMountedDisplay::setCardboard(CardboardDeviceParams *cardboard) 49 | { 50 | if (_cardboard != nullptr) 51 | { 52 | delete _cardboard; 53 | } 54 | _cardboard = new CardboardDeviceParams(cardboard); 55 | } 56 | 57 | CardboardDeviceParams* HeadMountedDisplay::getCardboard() 58 | { 59 | return _cardboard; 60 | } 61 | 62 | bool HeadMountedDisplay::equals(HeadMountedDisplay *other) 63 | { 64 | if (other == nullptr) 65 | { 66 | return false; 67 | } 68 | else if (other == this) 69 | { 70 | return true; 71 | } 72 | return _screen->equals(other->_screen) && _cardboard->equals(other->_cardboard); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/HeadTransform.h: -------------------------------------------------------------------------------- 1 | // 2 | // HeadTransform.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__HeadTransform__ 8 | #define __CardboardSDK_iOS__HeadTransform__ 9 | 10 | #import 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | class HeadTransform 17 | { 18 | public: 19 | HeadTransform(); 20 | 21 | void setHeadView(GLKMatrix4 headView); 22 | GLKMatrix4 headView(); 23 | 24 | GLKVector3 translation(); 25 | GLKVector3 forwardVector(); 26 | GLKVector3 upVector(); 27 | GLKVector3 rightVector(); 28 | GLKQuaternion quaternion(); 29 | GLKVector3 eulerAngles(); 30 | 31 | private: 32 | GLKMatrix4 _headView; 33 | }; 34 | 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/HeadTransform.mm: -------------------------------------------------------------------------------- 1 | // 2 | // HeadTransform.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "HeadTransform.h" 8 | 9 | 10 | namespace CardboardSDK 11 | { 12 | 13 | HeadTransform::HeadTransform() : 14 | _headView(GLKMatrix4Identity) 15 | { 16 | } 17 | 18 | void HeadTransform::setHeadView(GLKMatrix4 headview) 19 | { 20 | _headView = headview; 21 | } 22 | 23 | GLKMatrix4 HeadTransform::headView() 24 | { 25 | return _headView; 26 | } 27 | 28 | GLKVector3 HeadTransform::translation() 29 | { 30 | return GLKVector3Make(_headView.m[12], _headView.m[13], _headView.m[14]); 31 | } 32 | 33 | GLKVector3 HeadTransform::forwardVector() 34 | { 35 | return GLKVector3Make(-_headView.m[8], -_headView.m[9], -_headView.m[10]); 36 | } 37 | 38 | GLKVector3 HeadTransform::upVector() 39 | { 40 | return GLKVector3Make(_headView.m[4], _headView.m[5], _headView.m[6]); 41 | } 42 | 43 | GLKVector3 HeadTransform::rightVector() 44 | { 45 | return GLKVector3Make(_headView.m[0], _headView.m[1], _headView.m[2]); 46 | } 47 | 48 | GLKQuaternion HeadTransform::quaternion() 49 | { 50 | float t = _headView.m[0] + _headView.m[5] + _headView.m[10]; 51 | float s, w, x, y, z; 52 | if (t >= 0.0f) 53 | { 54 | s = sqrtf(t + 1.0f); 55 | w = 0.5f * s; 56 | s = 0.5f / s; 57 | x = (_headView.m[9] - _headView.m[6]) * s; 58 | y = (_headView.m[2] - _headView.m[8]) * s; 59 | z = (_headView.m[4] - _headView.m[1]) * s; 60 | } 61 | else if ((_headView.m[0] > _headView.m[5]) && (_headView.m[0] > _headView.m[10])) 62 | { 63 | s = sqrtf(1.0f + _headView.m[0] - _headView.m[5] - _headView.m[10]); 64 | x = s * 0.5f; 65 | s = 0.5f / s; 66 | y = (_headView.m[4] + _headView.m[1]) * s; 67 | z = (_headView.m[2] + _headView.m[8]) * s; 68 | w = (_headView.m[9] - _headView.m[6]) * s; 69 | } 70 | else if (_headView.m[5] > _headView.m[10]) 71 | { 72 | s = sqrtf(1.0f + _headView.m[5] - _headView.m[0] - _headView.m[10]); 73 | y = s * 0.5f; 74 | s = 0.5f / s; 75 | x = (_headView.m[4] + _headView.m[1]) * s; 76 | z = (_headView.m[9] + _headView.m[6]) * s; 77 | w = (_headView.m[2] - _headView.m[8]) * s; 78 | } 79 | else 80 | { 81 | s = sqrtf(1.0f + _headView.m[10] - _headView.m[0] - _headView.m[5]); 82 | z = s * 0.5f; 83 | s = 0.5f / s; 84 | x = (_headView.m[2] + _headView.m[8]) * s; 85 | y = (_headView.m[9] + _headView.m[6]) * s; 86 | w = (_headView.m[4] - _headView.m[1]) * s; 87 | } 88 | 89 | return GLKQuaternionMake(x, y, z, w); 90 | } 91 | 92 | GLKVector3 HeadTransform::eulerAngles() 93 | { 94 | float yaw = 0; 95 | float roll = 0; 96 | float pitch = asinf(_headView.m[6]); 97 | if (sqrtf(1.0f - _headView.m[6] * _headView.m[6]) >= 0.01f) 98 | { 99 | yaw = atan2f(-_headView.m[2], _headView.m[10]); 100 | roll = atan2f(-_headView.m[4], _headView.m[5]); 101 | } 102 | else 103 | { 104 | yaw = 0.0f; 105 | roll = atan2f(_headView.m[1], _headView.m[0]); 106 | } 107 | return GLKVector3Make(-pitch, -yaw, -roll); 108 | } 109 | 110 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/ScreenParams.h: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenParams.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__ScreenParams__ 8 | #define __CardboardSDK_iOS__ScreenParams__ 9 | 10 | #import 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | class ScreenParams 17 | { 18 | public: 19 | ScreenParams(UIScreen *screen); 20 | ScreenParams(ScreenParams *screenParams); 21 | 22 | int width(); 23 | int height(); 24 | 25 | float widthInMeters(); 26 | float heightInMeters(); 27 | 28 | void setBorderSizeInMeters(float screenBorderSize); 29 | float borderSizeInMeters(); 30 | 31 | bool equals(ScreenParams *other); 32 | 33 | private: 34 | UIScreen *_screen; 35 | CGFloat _scale; 36 | float _xMetersPerPixel; 37 | float _yMetersPerPixel; 38 | float _borderSizeMeters; 39 | 40 | float pixelsPerInch(UIScreen *screen); 41 | }; 42 | 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/ScreenParams.mm: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenParams.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "ScreenParams.h" 8 | 9 | #include 10 | 11 | 12 | // Enable to make the lens-distorted viewports slightly 13 | // smaller on iPhone 6/6+ and bigger on iPhone 5/5s 14 | #define SCREEN_PARAMS_CORRECT_IPHONE_VIEWPORTS 1 15 | 16 | 17 | #define CBScreenIsRetina() ([[UIScreen mainScreen] scale] == 2.0) 18 | #define CBScreenIsIpad() (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) 19 | #define CBScreenIsIphone() (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) 20 | #define CBScreenIsIphone4Width() (CBScreenIsIphone() && [UIScreen mainScreen].sizeFixedToPortrait.width == 320.0) 21 | #define CBScreenIsIphone4Height() (CBScreenIsIphone() && [UIScreen mainScreen].sizeFixedToPortrait.height == 480.0) 22 | #define CBScreenIsIphone5Width() (CBScreenIsIphone4Width()) 23 | #define CBScreenIsIphone5Height() (CBScreenIsIphone() && [UIScreen mainScreen].sizeFixedToPortrait.height == 568.0) 24 | #define CBScreenIsIphone6Width() (CBScreenIsIphone() && [UIScreen mainScreen].sizeFixedToPortrait.width == 375.0) 25 | #define CBScreenIsIphone6Height() (CBScreenIsIphone() && [UIScreen mainScreen].sizeFixedToPortrait.height == 667.0) 26 | #define CBScreenIsIphone6PlusWidth() (CBScreenIsIphone() && [[UIScreen mainScreen] scale] == 3.0f && [UIScreen mainScreen].sizeFixedToPortrait.width == 414.0) 27 | #define CBScreenIsIphone6PlusHeight() (CBScreenIsIphone() && [[UIScreen mainScreen] scale] == 3.0f && [UIScreen mainScreen].sizeFixedToPortrait.height == 736.0) 28 | 29 | 30 | @interface UIScreen (CBDOrientationAware) 31 | 32 | - (CGSize)orientationAwareSize; 33 | - (CGSize)sizeFixedToPortrait; 34 | 35 | @end 36 | 37 | @implementation UIScreen (CBDOrientationAware) 38 | 39 | - (CGSize)orientationAwareSize 40 | { 41 | // Starting on iOS 8 bounds are orientation dependepent 42 | CGSize screenSize = self.bounds.size; 43 | if ((NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) 44 | && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) 45 | { 46 | return CGSizeMake(screenSize.height, screenSize.width); 47 | } 48 | return screenSize; 49 | } 50 | 51 | - (CGSize)sizeFixedToPortrait 52 | { 53 | CGSize size = self.bounds.size; 54 | return CGSizeMake(MIN(size.width, size.height), MAX(size.width, size.height)); 55 | } 56 | 57 | @end 58 | 59 | 60 | namespace CardboardSDK 61 | { 62 | 63 | ScreenParams::ScreenParams(UIScreen *screen) 64 | { 65 | _screen = screen; 66 | if ([screen respondsToSelector:@selector(nativeScale)]) 67 | { 68 | _scale = screen.nativeScale; 69 | } 70 | else 71 | { 72 | _scale = screen.scale; 73 | } 74 | 75 | float screenPixelsPerInch = pixelsPerInch(screen); 76 | 77 | const float metersPerInch = 0.0254f; 78 | const float defaultBorderSizeMeters = 0.003f; 79 | _xMetersPerPixel = (metersPerInch / screenPixelsPerInch); 80 | _yMetersPerPixel = (metersPerInch / screenPixelsPerInch); 81 | 82 | _borderSizeMeters = defaultBorderSizeMeters; 83 | 84 | #if SCREEN_PARAMS_CORRECT_IPHONE_VIEWPORTS 85 | if (CBScreenIsIphone5Width()) 86 | { 87 | _borderSizeMeters = 0.006f; 88 | } 89 | else if (CBScreenIsIphone6Width() || CBScreenIsIphone6PlusWidth()) 90 | { 91 | _borderSizeMeters = 0.001f; 92 | } 93 | #endif 94 | } 95 | 96 | ScreenParams::ScreenParams(ScreenParams *screenParams) 97 | { 98 | _scale = screenParams->_scale; 99 | _xMetersPerPixel = screenParams->_xMetersPerPixel; 100 | _yMetersPerPixel = screenParams->_yMetersPerPixel; 101 | _borderSizeMeters = screenParams->_borderSizeMeters; 102 | } 103 | 104 | int ScreenParams::width() 105 | { 106 | return [_screen orientationAwareSize].width * _scale; 107 | } 108 | 109 | int ScreenParams::height() 110 | { 111 | return [_screen orientationAwareSize].height * _scale; 112 | } 113 | 114 | float ScreenParams::widthInMeters() 115 | { 116 | float meters = width() * _xMetersPerPixel; 117 | return meters; 118 | } 119 | 120 | float ScreenParams::heightInMeters() 121 | { 122 | float meters = height() * _yMetersPerPixel; 123 | return meters; 124 | } 125 | 126 | void ScreenParams::setBorderSizeInMeters(float screenBorderSize) 127 | { 128 | _borderSizeMeters = screenBorderSize; 129 | } 130 | 131 | float ScreenParams::borderSizeInMeters() 132 | { 133 | return _borderSizeMeters; 134 | } 135 | 136 | bool ScreenParams::equals(ScreenParams *other) 137 | { 138 | if (other == nullptr) 139 | { 140 | return false; 141 | } 142 | else if (other == this) 143 | { 144 | return true; 145 | } 146 | return 147 | (width() == other->width()) 148 | && (height() == other->height()) 149 | && (widthInMeters() == other->widthInMeters()) 150 | && (heightInMeters() == other->heightInMeters()) 151 | && (borderSizeInMeters() == other->borderSizeInMeters()); 152 | } 153 | 154 | float ScreenParams::pixelsPerInch(UIScreen *screen) 155 | { 156 | // Default iPhone retina pixels per inch 157 | float pixelsPerInch = 163.0f * 2; 158 | struct utsname sysinfo; 159 | if (uname(&sysinfo) == 0) 160 | { 161 | NSString *identifier = [NSString stringWithUTF8String:sysinfo.machine]; 162 | NSArray *deviceClassArray = 163 | @[ 164 | // iPads 165 | @{@"identifiers": 166 | @[@"iPad1,1", 167 | @"iPad2,1", @"iPad2,2", @"iPad2,3", @"iPad2,4", 168 | @"iPad3,1", @"iPad3,2", @"iPad3,3", @"iPad3,4", 169 | @"iPad3,5", @"iPad3,6", @"iPad4,1", @"iPad4,2"], 170 | @"pointsPerInch": @132.0f}, 171 | // iPhones, iPad Minis and simulators 172 | @{@"identifiers": 173 | @[@"iPod5,1", 174 | @"iPhone1,1", @"iPhone1,2", 175 | @"iPhone2,1", 176 | @"iPhone3,1", @"iPhone3,2", @"iPhone3,3", 177 | @"iPhone4,1", 178 | @"iPhone5,1", @"iPhone5,2", @"iPhone5,3", @"iPhone5,4", 179 | @"iPhone6,1", @"iPhone6,2", 180 | @"iPhone7,1", @"iPhone7,2", 181 | @"iPad2,5", @"iPad2,6", @"iPad2,7", 182 | @"iPad4,4", @"iPad4,5", 183 | @"i386", @"x86_64"], 184 | @"pointsPerInch": @163.0f } ]; 185 | for (NSDictionary *deviceClass in deviceClassArray) 186 | { 187 | for (NSString *deviceId in deviceClass[@"identifiers"]) 188 | { 189 | if ([identifier isEqualToString:deviceId]) 190 | { 191 | pixelsPerInch = [deviceClass[@"pointsPerInch"] floatValue] * _scale; 192 | break; 193 | } 194 | } 195 | } 196 | } 197 | return pixelsPerInch; 198 | } 199 | 200 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/HeadTracker.h: -------------------------------------------------------------------------------- 1 | // 2 | // HeadTracker.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #ifndef __CardboardSDK_iOS__HeadTracker__ 7 | #define __CardboardSDK_iOS__HeadTracker__ 8 | 9 | #include "OrientationEKF.h" 10 | 11 | #import 12 | #import 13 | 14 | 15 | namespace CardboardSDK 16 | { 17 | 18 | class HeadTracker 19 | { 20 | public: 21 | HeadTracker(); 22 | virtual ~HeadTracker(); 23 | 24 | void startTracking(UIInterfaceOrientation orientation); 25 | void stopTracking(); 26 | GLKMatrix4 lastHeadView(); 27 | 28 | void updateDeviceOrientation(UIInterfaceOrientation orientation); 29 | 30 | bool neckModelEnabled(); 31 | void setNeckModelEnabled(bool enabled); 32 | 33 | bool isReady(); 34 | 35 | CMAttitude *attitude ; 36 | 37 | private: 38 | CMMotionManager *_motionManager; 39 | size_t _sampleCount; 40 | OrientationEKF *_tracker; 41 | GLKMatrix4 _displayFromDevice; 42 | GLKMatrix4 _inertialReferenceFrameFromWorld; 43 | GLKMatrix4 _correctedInertialReferenceFrameFromWorld; 44 | GLKMatrix4 _lastHeadView; 45 | NSTimeInterval _lastGyroEventTimestamp; 46 | bool _headingCorrectionComputed; 47 | bool _neckModelEnabled; 48 | GLKMatrix4 _neckModelTranslation; 49 | float _orientationCorrectionAngle; 50 | 51 | const float _defaultNeckHorizontalOffset = 0.08f; 52 | const float _defaultNeckVerticalOffset = 0.075f; 53 | }; 54 | 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/HeadTracker.mm: -------------------------------------------------------------------------------- 1 | // 2 | // HeadTracker.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #include "HeadTracker.h" 7 | 8 | 9 | namespace CardboardSDK 10 | { 11 | 12 | #define HEAD_TRACKER_MODE_EKF 0 13 | #define HEAD_TRACKER_MODE_CORE_MOTION 1 14 | #define HEAD_TRACKER_MODE_CORE_MOTION_EKF 2 15 | 16 | #define HEAD_TRACKER_MODE HEAD_TRACKER_MODE_CORE_MOTION_EKF 17 | 18 | static const size_t kInitialSamplesToSkip = 10; 19 | 20 | namespace 21 | { 22 | 23 | GLKMatrix4 GetRotateEulerMatrix(float x, float y, float z) 24 | { 25 | x *= (float)(M_PI / 180.0f); 26 | y *= (float)(M_PI / 180.0f); 27 | z *= (float)(M_PI / 180.0f); 28 | float cx = (float) cos(x); 29 | float sx = (float) sin(x); 30 | float cy = (float) cos(y); 31 | float sy = (float) sin(y); 32 | float cz = (float) cos(z); 33 | float sz = (float) sin(z); 34 | float cxsy = cx * sy; 35 | float sxsy = sx * sy; 36 | GLKMatrix4 matrix; 37 | matrix.m[0] = cy * cz; 38 | matrix.m[1] = -cy * sz; 39 | matrix.m[2] = sy; 40 | matrix.m[3] = 0.0f; 41 | matrix.m[4] = cxsy * cz + cx * sz; 42 | matrix.m[5] = -cxsy * sz + cx * cz; 43 | matrix.m[6] = -sx * cy; 44 | matrix.m[7] = 0.0f; 45 | matrix.m[8] = -sxsy * cz + sx * sz; 46 | matrix.m[9] = sxsy * sz + sx * cz; 47 | matrix.m[10] = cx * cy; 48 | matrix.m[11] = 0.0f; 49 | matrix.m[12] = 0.0f; 50 | matrix.m[13] = 0.0f; 51 | matrix.m[14] = 0.0f; 52 | matrix.m[15] = 1.0f; 53 | return matrix; 54 | } 55 | 56 | #if HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_CORE_MOTION 57 | GLKMatrix4 GLMatrixFromRotationMatrix(CMRotationMatrix rotationMatrix) 58 | { 59 | GLKMatrix4 glRotationMatrix; 60 | 61 | glRotationMatrix.m00 = rotationMatrix.m11; 62 | glRotationMatrix.m01 = rotationMatrix.m12; 63 | glRotationMatrix.m02 = rotationMatrix.m13; 64 | glRotationMatrix.m03 = 0.0f; 65 | 66 | glRotationMatrix.m10 = rotationMatrix.m21; 67 | glRotationMatrix.m11 = rotationMatrix.m22; 68 | glRotationMatrix.m12 = rotationMatrix.m23; 69 | glRotationMatrix.m13 = 0.0f; 70 | 71 | glRotationMatrix.m20 = rotationMatrix.m31; 72 | glRotationMatrix.m21 = rotationMatrix.m32; 73 | glRotationMatrix.m22 = rotationMatrix.m33; 74 | glRotationMatrix.m23 = 0.0f; 75 | 76 | glRotationMatrix.m30 = 0.0f; 77 | glRotationMatrix.m31 = 0.0f; 78 | glRotationMatrix.m32 = 0.0f; 79 | glRotationMatrix.m33 = 1.0f; 80 | 81 | return glRotationMatrix; 82 | } 83 | #endif 84 | 85 | } // anonymous namespace 86 | 87 | HeadTracker::HeadTracker() : 88 | // this assumes the device is landscape with the home button on the right (UIInterfaceOrientationLandscapeRight) 89 | _displayFromDevice(GetRotateEulerMatrix(0.f, 0.f, -90.f)), 90 | // the inertial reference frame has z up and x forward, while the world has -z forward and x right 91 | _inertialReferenceFrameFromWorld(GetRotateEulerMatrix(-90.f, 0.f, 90.f)), 92 | _lastGyroEventTimestamp(0), 93 | _orientationCorrectionAngle(0), 94 | _neckModelEnabled(false) 95 | { 96 | _motionManager = [[CMMotionManager alloc] init]; 97 | _tracker = new OrientationEKF(); 98 | 99 | _correctedInertialReferenceFrameFromWorld = _inertialReferenceFrameFromWorld; 100 | _lastHeadView = GLKMatrix4Identity; 101 | _neckModelTranslation = GLKMatrix4Identity; 102 | _neckModelTranslation = GLKMatrix4Translate(_neckModelTranslation, 0, -_defaultNeckVerticalOffset, _defaultNeckHorizontalOffset); 103 | } 104 | 105 | HeadTracker::~HeadTracker() 106 | { 107 | delete _tracker; 108 | 109 | } 110 | 111 | void HeadTracker::startTracking(UIInterfaceOrientation orientation) 112 | { 113 | updateDeviceOrientation(orientation); 114 | 115 | _tracker->reset(); 116 | 117 | _headingCorrectionComputed = false; 118 | _sampleCount = 0; // used to skip bad data when core motion starts 119 | 120 | #if HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_EKF 121 | NSOperationQueue *accelerometerQueue = [[NSOperationQueue alloc] init]; 122 | NSOperationQueue *gyroQueue = [[NSOperationQueue alloc] init]; 123 | 124 | // Probably capped at less than 100Hz 125 | // (http://stackoverflow.com/questions/4790111/what-is-the-official-iphone-4-maximum-gyroscope-data-update-frequency) 126 | _motionManager.accelerometerUpdateInterval = 1.0/100.0; 127 | [_motionManager startAccelerometerUpdatesToQueue:accelerometerQueue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) 128 | { 129 | ++_sampleCount; 130 | if (_sampleCount <= kInitialSamplesToSkip) { return; } 131 | CMAcceleration acceleration = accelerometerData.acceleration; 132 | // note core motion uses units of G while the EKF uses ms^-2 133 | const float kG = 9.81f; 134 | _tracker->processAcceleration(GLKVector3Make(kG*acceleration.x, kG*acceleration.y, kG*acceleration.z), accelerometerData.timestamp); 135 | }]; 136 | 137 | _motionManager.gyroUpdateInterval = 1.0/100.0; 138 | [_motionManager startGyroUpdatesToQueue:gyroQueue withHandler:^(CMGyroData *gyroData, NSError *error) { 139 | if (_sampleCount <= kInitialSamplesToSkip) { return; } 140 | CMRotationRate rotationRate = gyroData.rotationRate; 141 | _tracker->processGyro(GLKVector3Make(rotationRate.x, rotationRate.y, rotationRate.z), gyroData.timestamp); 142 | _lastGyroEventTimestamp = gyroData.timestamp; 143 | }]; 144 | #elif HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_CORE_MOTION 145 | if (_motionManager.isDeviceMotionAvailable) 146 | { 147 | [_motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical]; 148 | _sampleCount = kInitialSamplesToSkip + 1; 149 | } 150 | #elif HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_CORE_MOTION_EKF 151 | NSOperationQueue *deviceMotionQueue = [[NSOperationQueue alloc] init]; 152 | _motionManager.deviceMotionUpdateInterval = 1.0/100.0; 153 | [_motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical toQueue:deviceMotionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) { 154 | ++_sampleCount; 155 | if (_sampleCount <= kInitialSamplesToSkip) { return; } 156 | CMAcceleration acceleration = motion.gravity; 157 | CMRotationRate rotationRate = motion.rotationRate; 158 | // note core motion uses units of G while the EKF uses ms^-2 159 | const float kG = 9.81f; 160 | _tracker->processAcceleration(GLKVector3Make(kG*acceleration.x, kG*acceleration.y, kG*acceleration.z), motion.timestamp); 161 | _tracker->processGyro(GLKVector3Make(rotationRate.x, rotationRate.y, rotationRate.z), motion.timestamp); 162 | _lastGyroEventTimestamp = motion.timestamp; 163 | }]; 164 | #endif 165 | 166 | } 167 | 168 | void HeadTracker::stopTracking() 169 | { 170 | #if HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_EKF 171 | [_motionManager stopAccelerometerUpdates]; 172 | [_motionManager stopGyroUpdates]; 173 | #elif HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_CORE_MOTION || HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_CORE_MOTION_EKF 174 | [_motionManager stopDeviceMotionUpdates]; 175 | #endif 176 | } 177 | 178 | bool HeadTracker::isReady() 179 | { 180 | bool isTrackerReady = (_sampleCount > kInitialSamplesToSkip); 181 | #if HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_EKF || HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_CORE_MOTION_EKF 182 | isTrackerReady = isTrackerReady && _tracker->isReady(); 183 | #endif 184 | return isTrackerReady; 185 | } 186 | 187 | GLKMatrix4 HeadTracker::lastHeadView() 188 | { 189 | 190 | attitude = _motionManager.deviceMotion.attitude; 191 | 192 | #if HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_EKF || HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_CORE_MOTION_EKF 193 | 194 | NSTimeInterval currentTimestamp = CACurrentMediaTime(); 195 | double secondsSinceLastGyroEvent = currentTimestamp - _lastGyroEventTimestamp; 196 | // 1/30 of a second prediction (shoud it be 1/60?) 197 | double secondsToPredictForward = secondsSinceLastGyroEvent + 1.0/30; 198 | GLKMatrix4 deviceFromInertialReferenceFrame = _tracker->getPredictedGLMatrix(secondsToPredictForward); 199 | 200 | #elif HEAD_TRACKER_MODE == HEAD_TRACKER_MODE_CORE_MOTION 201 | 202 | CMDeviceMotion *motion = _motionManager.deviceMotion; 203 | CMRotationMatrix rotationMatrix = motion.attitude.rotationMatrix; 204 | GLKMatrix4 deviceFromInertialReferenceFrame = GLKMatrix4Transpose(GLMatrixFromRotationMatrix(rotationMatrix)); // note the matrix inversion 205 | 206 | if (!motion) { return _lastHeadView; } 207 | 208 | #endif 209 | 210 | if (!isReady()) { return _lastHeadView; } 211 | 212 | if (!_headingCorrectionComputed) 213 | { 214 | // fix the heading by aligning world -z with the projection 215 | // of the device -z on the ground plane 216 | 217 | GLKMatrix4 deviceFromWorld = GLKMatrix4Multiply(deviceFromInertialReferenceFrame, _inertialReferenceFrameFromWorld); 218 | GLKMatrix4 worldFromDevice = GLKMatrix4Transpose(deviceFromWorld); 219 | 220 | GLKVector3 deviceForward = GLKVector3Make(0.f, 0.f, -1.f); 221 | GLKVector3 deviceForwardWorld = GLKMatrix4MultiplyVector3(worldFromDevice, deviceForward); 222 | 223 | if (fabsf(deviceForwardWorld.y) < 0.99f) 224 | { 225 | deviceForwardWorld.y = 0.f; // project onto ground plane 226 | 227 | deviceForwardWorld = GLKVector3Normalize(deviceForwardWorld); 228 | 229 | // want to find R such that 230 | // deviceForwardWorld = R * [0 0 -1]' 231 | // where R is a rotation matrix about y, i.e.: 232 | // [ c 0 s] 233 | // R = [ 0 1 0] 234 | // [-s 0 c] 235 | 236 | float c = -deviceForwardWorld.z; 237 | float s = -deviceForwardWorld.x; 238 | // note we actually want to use the inverse, so 239 | // transpose when building 240 | GLKMatrix4 Rt = GLKMatrix4Make( 241 | c, 0.f, -s, 0.f, 242 | 0.f, 1.f, 0.f, 0.f, 243 | s, 0.f, c, 0.f, 244 | 0.f, 0.f, 0.f, 1.f ); 245 | 246 | _correctedInertialReferenceFrameFromWorld = GLKMatrix4Multiply( 247 | _inertialReferenceFrameFromWorld, 248 | Rt); 249 | } 250 | _headingCorrectionComputed = true; 251 | } 252 | 253 | GLKMatrix4 deviceFromWorld = GLKMatrix4Multiply( 254 | deviceFromInertialReferenceFrame, 255 | _correctedInertialReferenceFrameFromWorld); 256 | GLKMatrix4 displayFromWorld = GLKMatrix4Multiply(_displayFromDevice, deviceFromWorld); 257 | 258 | if (_neckModelEnabled) 259 | { 260 | displayFromWorld = GLKMatrix4Multiply(_neckModelTranslation, displayFromWorld); 261 | displayFromWorld = GLKMatrix4Translate(displayFromWorld, 0.0f, _defaultNeckVerticalOffset, 0.0f); 262 | } 263 | 264 | _lastHeadView = displayFromWorld; 265 | 266 | return _lastHeadView; 267 | } 268 | 269 | void HeadTracker::updateDeviceOrientation(UIInterfaceOrientation orientation) 270 | { 271 | if (orientation == UIInterfaceOrientationLandscapeLeft) 272 | { 273 | _displayFromDevice = GetRotateEulerMatrix(0.f, 0.f, 90.f); 274 | } 275 | else if (orientation == UIInterfaceOrientationLandscapeRight) 276 | { 277 | _displayFromDevice = GetRotateEulerMatrix(0.f, 0.f, -90.f); 278 | } 279 | } 280 | 281 | bool HeadTracker::neckModelEnabled() 282 | { 283 | return _neckModelEnabled; 284 | } 285 | 286 | void HeadTracker::setNeckModelEnabled(bool enabled) 287 | { 288 | _neckModelEnabled = enabled; 289 | } 290 | 291 | } 292 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/Internal/Matrix3x3d.h: -------------------------------------------------------------------------------- 1 | // 2 | // Matrix3x3d.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #ifndef __CardboardSDK_iOS__Matrix3x3d__ 7 | #define __CardboardSDK_iOS__Matrix3x3d__ 8 | 9 | #include "Vector3d.h" 10 | 11 | #import 12 | 13 | 14 | namespace CardboardSDK 15 | { 16 | 17 | class Matrix3x3d 18 | { 19 | public: 20 | Matrix3x3d(); 21 | Matrix3x3d(double m00, double m01, double m02, 22 | double m10, double m11, double m12, 23 | double m20, double m21, double m22); 24 | Matrix3x3d(Matrix3x3d *o); 25 | 26 | void set(double m00, double m01, double m02, 27 | double m10, double m11, double m12, 28 | double m20, double m21, double m22); 29 | void set(Matrix3x3d *o); 30 | void setZero(); 31 | void setIdentity(); 32 | void setSameDiagonal(double d); 33 | 34 | double get(int row, int col); 35 | void set(int row, int col, double value); 36 | 37 | void getColumn(int col, Vector3d *v); 38 | void setColumn(int col, Vector3d *v); 39 | 40 | void scale(double s); 41 | 42 | void plusEquals(Matrix3x3d *b); 43 | void minusEquals(Matrix3x3d *b); 44 | 45 | void transpose(); 46 | void transpose(Matrix3x3d *result); 47 | 48 | static void add(Matrix3x3d *a, Matrix3x3d *b, Matrix3x3d *result); 49 | static void mult(Matrix3x3d *a, Matrix3x3d *b, Matrix3x3d *result); 50 | static void mult(Matrix3x3d *a, Vector3d *v, Vector3d *result); 51 | 52 | double determinant(); 53 | 54 | bool invert(Matrix3x3d *result); 55 | 56 | GLKMatrix3 getGLKMatrix(); 57 | 58 | 59 | private: 60 | double _m[9]; 61 | }; 62 | 63 | } 64 | 65 | #endif -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/Internal/Matrix3x3d.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Matrix3x3d.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #include "Matrix3x3d.h" 7 | 8 | 9 | namespace CardboardSDK 10 | { 11 | 12 | Matrix3x3d::Matrix3x3d() 13 | { 14 | setZero(); 15 | } 16 | 17 | Matrix3x3d::Matrix3x3d(double m00, double m01, double m02, 18 | double m10, double m11, double m12, 19 | double m20, double m21, double m22) 20 | { 21 | _m[0] = m00; 22 | _m[1] = m01; 23 | _m[2] = m02; 24 | _m[3] = m10; 25 | _m[4] = m11; 26 | _m[5] = m12; 27 | _m[6] = m20; 28 | _m[7] = m21; 29 | _m[8] = m22; 30 | } 31 | 32 | Matrix3x3d::Matrix3x3d(Matrix3x3d *o) 33 | { 34 | for (int i = 0; i < 9; i++) 35 | { 36 | _m[i] = o->_m[i]; 37 | } 38 | } 39 | 40 | void Matrix3x3d::set(double m00, double m01, double m02, 41 | double m10, double m11, double m12, 42 | double m20, double m21, double m22) 43 | { 44 | _m[0] = m00; 45 | _m[1] = m01; 46 | _m[2] = m02; 47 | _m[3] = m10; 48 | _m[4] = m11; 49 | _m[5] = m12; 50 | _m[6] = m20; 51 | _m[7] = m21; 52 | _m[8] = m22; 53 | } 54 | 55 | void Matrix3x3d::set(Matrix3x3d *o) 56 | { 57 | for (int i = 0; i < 9; i++) 58 | { 59 | _m[i] = o->_m[i]; 60 | } 61 | } 62 | 63 | void Matrix3x3d::setZero() 64 | { 65 | for (int i = 0; i < 9; i++) 66 | { 67 | _m[i] = 0; 68 | } 69 | } 70 | 71 | void Matrix3x3d::setIdentity() 72 | { 73 | _m[0] = 1; 74 | _m[1] = 0; 75 | _m[2] = 0; 76 | _m[3] = 0; 77 | _m[4] = 1; 78 | _m[5] = 0; 79 | _m[6] = 0; 80 | _m[7] = 0; 81 | _m[8] = 1; 82 | } 83 | 84 | void Matrix3x3d::setSameDiagonal(double d) 85 | { 86 | _m[0] = d; 87 | _m[4] = d; 88 | _m[8] = d; 89 | } 90 | 91 | double Matrix3x3d::get(int row, int col) 92 | { 93 | return _m[(3 * row + col)]; 94 | } 95 | 96 | void Matrix3x3d::set(int row, int col, double value) 97 | { 98 | _m[(3 * row + col)] = value; 99 | } 100 | 101 | void Matrix3x3d::getColumn(int col, Vector3d *v) 102 | { 103 | v->_x = _m[col]; 104 | v->_y = _m[col + 3]; 105 | v->_z = _m[col + 6]; 106 | } 107 | 108 | void Matrix3x3d::setColumn(int col, Vector3d *v) 109 | { 110 | _m[col] = v->_x; 111 | _m[col + 3] = v->_y; 112 | _m[col + 6] = v->_z; 113 | } 114 | 115 | void Matrix3x3d::scale(double s) 116 | { 117 | for (int i = 0; i < 9; i++) 118 | { 119 | _m[i] *= s; 120 | } 121 | } 122 | 123 | void Matrix3x3d::plusEquals(Matrix3x3d *b) 124 | { 125 | for (int i = 0; i < 9; i++) 126 | { 127 | _m[i] += b->_m[i]; 128 | } 129 | } 130 | 131 | void Matrix3x3d::minusEquals(Matrix3x3d *b) 132 | { 133 | for (int i = 0; i < 9; i++) 134 | { 135 | _m[i] -= b->_m[i]; 136 | } 137 | } 138 | 139 | void Matrix3x3d::transpose() 140 | { 141 | double tmp = _m[1]; 142 | _m[1] = _m[3]; 143 | _m[3] = tmp; 144 | tmp = _m[2]; 145 | _m[2] = _m[6]; 146 | _m[6] = tmp; 147 | tmp = _m[5]; 148 | _m[5] = _m[7]; 149 | _m[7] = tmp; 150 | } 151 | 152 | void Matrix3x3d::transpose(Matrix3x3d *result) 153 | { 154 | result->_m[0] = _m[0]; 155 | result->_m[1] = _m[3]; 156 | result->_m[2] = _m[6]; 157 | result->_m[3] = _m[1]; 158 | result->_m[4] = _m[4]; 159 | result->_m[5] = _m[7]; 160 | result->_m[6] = _m[2]; 161 | result->_m[7] = _m[5]; 162 | result->_m[8] = _m[8]; 163 | } 164 | 165 | void Matrix3x3d::add(Matrix3x3d *a, Matrix3x3d *b, Matrix3x3d *result) 166 | { 167 | for (int i = 0; i < 9; i++) 168 | { 169 | result->_m[i] = a->_m[i] + b->_m[i]; 170 | } 171 | } 172 | 173 | void Matrix3x3d::mult(Matrix3x3d *a, Matrix3x3d *b, Matrix3x3d *result) 174 | { 175 | result->set(a->_m[0] * b->_m[0] + a->_m[1] * b->_m[3] + a->_m[2] * b->_m[6], 176 | a->_m[0] * b->_m[1] + a->_m[1] * b->_m[4] + a->_m[2] * b->_m[7], 177 | a->_m[0] * b->_m[2] + a->_m[1] * b->_m[5] + a->_m[2] * b->_m[8], 178 | a->_m[3] * b->_m[0] + a->_m[4] * b->_m[3] + a->_m[5] * b->_m[6], 179 | a->_m[3] * b->_m[1] + a->_m[4] * b->_m[4] + a->_m[5] * b->_m[7], 180 | a->_m[3] * b->_m[2] + a->_m[4] * b->_m[5] + a->_m[5] * b->_m[8], 181 | a->_m[6] * b->_m[0] + a->_m[7] * b->_m[3] + a->_m[8] * b->_m[6], 182 | a->_m[6] * b->_m[1] + a->_m[7] * b->_m[4] + a->_m[8] * b->_m[7], 183 | a->_m[6] * b->_m[2] + a->_m[7] * b->_m[5] + a->_m[8] * b->_m[8]); 184 | } 185 | 186 | void Matrix3x3d::mult(Matrix3x3d *a, Vector3d *v, Vector3d *result) 187 | { 188 | result->set(a->_m[0] * v->_x + a->_m[1] * v->_y + a->_m[2] * v->_z, 189 | a->_m[3] * v->_x + a->_m[4] * v->_y + a->_m[5] * v->_z, 190 | a->_m[6] * v->_x + a->_m[7] * v->_y + a->_m[8] * v->_z); 191 | } 192 | 193 | double Matrix3x3d::determinant() 194 | { 195 | return get(0, 0) * (get(1, 1) * get(2, 2) - get(2, 1) * get(1, 2)) 196 | - get(0, 1) * (get(1, 0) * get(2, 2) - get(1, 2) * get(2, 0)) 197 | + get(0, 2) * (get(1, 0) * get(2, 1) - get(1, 1) * get(2, 0)); 198 | } 199 | 200 | bool Matrix3x3d::invert(Matrix3x3d *result) 201 | { 202 | double d = determinant(); 203 | if (d == 0.0) 204 | { 205 | return false; 206 | } 207 | double invdet = 1.0 / d; 208 | result->set( (_m[4] * _m[8] - _m[7] * _m[5]) * invdet, 209 | -(_m[1] * _m[8] - _m[2] * _m[7]) * invdet, 210 | (_m[1] * _m[5] - _m[2] * _m[4]) * invdet, 211 | -(_m[3] * _m[8] - _m[5] * _m[6]) * invdet, 212 | (_m[0] * _m[8] - _m[2] * _m[6]) * invdet, 213 | -(_m[0] * _m[5] - _m[3] * _m[2]) * invdet, 214 | (_m[3] * _m[7] - _m[6] * _m[4]) * invdet, 215 | -(_m[0] * _m[7] - _m[6] * _m[1]) * invdet, 216 | (_m[0] * _m[4] - _m[3] * _m[1]) * invdet); 217 | return true; 218 | } 219 | 220 | GLKMatrix3 Matrix3x3d::getGLKMatrix() 221 | { 222 | return GLKMatrix3Make(_m[0], _m[1], _m[2], 223 | _m[3], _m[4], _m[5], 224 | _m[6], _m[7], _m[8]); 225 | } 226 | 227 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/Internal/OrientationEKF.h: -------------------------------------------------------------------------------- 1 | // 2 | // OrientationEKF.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #ifndef __CardboardSDK_iOS__OrientationEKF__ 7 | #define __CardboardSDK_iOS__OrientationEKF__ 8 | 9 | #include "Vector3d.h" 10 | #include "Matrix3x3d.h" 11 | 12 | #import 13 | 14 | 15 | namespace CardboardSDK 16 | { 17 | 18 | class OrientationEKF 19 | { 20 | public: 21 | OrientationEKF(); 22 | virtual ~OrientationEKF(); 23 | 24 | void reset(); 25 | bool isReady(); 26 | 27 | void processGyro(GLKVector3 gyro, double sensorTimeStamp); 28 | void processAcceleration(GLKVector3 acc, double sensorTimeStamp); 29 | 30 | double getHeadingDegrees(); 31 | void setHeadingDegrees(double heading); 32 | 33 | GLKMatrix4 getGLMatrix(); 34 | GLKMatrix4 getPredictedGLMatrix(double secondsAfterLastGyroEvent); 35 | 36 | 37 | private: 38 | Matrix3x3d _so3SensorFromWorld; 39 | Matrix3x3d _so3LastMotion; 40 | Matrix3x3d _mP; 41 | Matrix3x3d _mQ; 42 | Matrix3x3d _mR; 43 | Matrix3x3d _mRAcceleration; 44 | Matrix3x3d _mS; 45 | Matrix3x3d _mH; 46 | Matrix3x3d _mK; 47 | Vector3d _vNu; 48 | Vector3d _vZ; 49 | Vector3d _vH; 50 | Vector3d _vU; 51 | Vector3d _vX; 52 | Vector3d _vDown; 53 | Vector3d _vNorth; 54 | double _sensorTimeStampGyro; 55 | GLKVector3 _lastGyro; 56 | double _previousAccelNorm; 57 | double _movingAverageAccelNormChange; 58 | double _filteredGyroTimestep; 59 | bool _timestepFilterInit; 60 | int _numGyroTimestepSamples; 61 | bool _gyroFilterValid; 62 | bool _alignedToGravity; 63 | bool _alignedToNorth; 64 | 65 | void filterGyroTimestep(double timestep); 66 | void updateCovariancesAfterMotion(); 67 | void updateAccelerationCovariance(double currentAccelNorm); 68 | void accelerationObservationFunctionForNumericalJacobian(Matrix3x3d *so3SensorFromWorldPred, Vector3d *result); 69 | }; 70 | 71 | } 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/Internal/OrientationEKF.mm: -------------------------------------------------------------------------------- 1 | // 2 | // OrientationEKF.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #include "OrientationEKF.h" 7 | 8 | #include "SO3Util.h" 9 | 10 | #include 11 | #include 12 | 13 | 14 | namespace CardboardSDK 15 | { 16 | 17 | static const double DEG_TO_RAD = M_PI / 180.0; 18 | static const double RAD_TO_DEG = 180.0 / M_PI; 19 | 20 | namespace 21 | { 22 | GLKMatrix4 glMatrixFromSo3(Matrix3x3d *so3) 23 | { 24 | GLKMatrix4 rotationMatrix; 25 | for (int r = 0; r < 3; r++) 26 | { 27 | for (int c = 0; c < 3; c++) 28 | { 29 | rotationMatrix.m[(4 * c + r)] = so3->get(r, c); 30 | } 31 | } 32 | rotationMatrix.m[3] = 0.0; 33 | rotationMatrix.m[7] = 0.0; 34 | rotationMatrix.m[11] = 0.0; 35 | rotationMatrix.m[12] = 0.0; 36 | rotationMatrix.m[13] = 0.0; 37 | rotationMatrix.m[14] = 0.0; 38 | rotationMatrix.m[15] = 1.0; 39 | return rotationMatrix; 40 | } 41 | 42 | } 43 | 44 | OrientationEKF::OrientationEKF() : 45 | _previousAccelNorm(0.0), 46 | _movingAverageAccelNormChange(0.0), 47 | _timestepFilterInit(false), 48 | _gyroFilterValid(true) 49 | 50 | { 51 | reset(); 52 | } 53 | 54 | OrientationEKF::~OrientationEKF() 55 | { 56 | } 57 | 58 | void OrientationEKF::reset() 59 | { 60 | _sensorTimeStampGyro = 0.0; 61 | _so3SensorFromWorld.setIdentity(); 62 | _so3LastMotion.setIdentity(); 63 | _mP.setZero(); 64 | _mP.setSameDiagonal(25.0); 65 | _mQ.setZero(); 66 | _mQ.setSameDiagonal(1.0); 67 | _mR.setZero(); 68 | _mR.setSameDiagonal(0.0625); 69 | _mRAcceleration.setZero(); 70 | _mRAcceleration.setSameDiagonal(0.5625); 71 | _mS.setZero(); 72 | _mH.setZero(); 73 | _mK.setZero(); 74 | _vNu.setZero(); 75 | _vZ.setZero(); 76 | _vH.setZero(); 77 | _vU.setZero(); 78 | _vX.setZero(); 79 | // Flipped from Android so it uses the same convention as CoreMotion 80 | // was: _vDown.set(0.0, 0.0, 9.81); 81 | _vDown.set(0.0, 0.0, -9.81); 82 | _vNorth.set(0.0, 1.0, 0.0); 83 | _alignedToGravity = false; 84 | _alignedToNorth = false; 85 | } 86 | 87 | bool OrientationEKF::isReady() 88 | { 89 | return _alignedToGravity; 90 | } 91 | 92 | double OrientationEKF::getHeadingDegrees() 93 | { 94 | double x = _so3SensorFromWorld.get(2, 0); 95 | double y = _so3SensorFromWorld.get(2, 1); 96 | double mag = sqrt(x * x + y * y); 97 | if (mag < 0.1) { 98 | return 0.0; 99 | } 100 | double heading = -90.0 - atan2(y, x) * RAD_TO_DEG; 101 | if (heading < 0.0) { 102 | heading += 360.0; 103 | } 104 | if (heading >= 360.0) { 105 | heading -= 360.0; 106 | } 107 | return heading; 108 | } 109 | 110 | void OrientationEKF::setHeadingDegrees(double heading) 111 | { 112 | double currentHeading = getHeadingDegrees(); 113 | double deltaHeading = heading - currentHeading; 114 | double s = sin(deltaHeading * DEG_TO_RAD); 115 | double c = cos(deltaHeading * DEG_TO_RAD); 116 | Matrix3x3d deltaHeadingRotationMatrix(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); 117 | Matrix3x3d::mult(&_so3SensorFromWorld, &deltaHeadingRotationMatrix, &_so3SensorFromWorld); 118 | } 119 | 120 | GLKMatrix4 OrientationEKF::getGLMatrix() 121 | { 122 | return glMatrixFromSo3(&_so3SensorFromWorld); 123 | } 124 | 125 | GLKMatrix4 OrientationEKF::getPredictedGLMatrix(double secondsAfterLastGyroEvent) 126 | { 127 | double dT = secondsAfterLastGyroEvent; 128 | Vector3d pmu(_lastGyro.x * -dT, _lastGyro.y * -dT, _lastGyro.z * -dT); 129 | Matrix3x3d so3PredictedMotion; 130 | SO3Util::so3FromMu(&pmu, &so3PredictedMotion); 131 | Matrix3x3d so3PredictedState; 132 | Matrix3x3d::mult(&so3PredictedMotion, &_so3SensorFromWorld, &so3PredictedState); 133 | return glMatrixFromSo3(&so3PredictedState); 134 | } 135 | 136 | void OrientationEKF::processGyro(GLKVector3 gyro, double sensorTimeStamp) 137 | { 138 | if (_sensorTimeStampGyro != 0.0) { 139 | 140 | double dT = sensorTimeStamp - _sensorTimeStampGyro; 141 | if (dT > 0.04f) { 142 | dT = _gyroFilterValid ? _filteredGyroTimestep : 0.01; 143 | } else { 144 | filterGyroTimestep(dT); 145 | } 146 | 147 | _vU.set(gyro.x * -dT, gyro.y * -dT, gyro.z * -dT); 148 | SO3Util::so3FromMu(&_vU, &_so3LastMotion); 149 | Matrix3x3d::mult(&_so3LastMotion, &_so3SensorFromWorld, &_so3SensorFromWorld); 150 | updateCovariancesAfterMotion(); 151 | Matrix3x3d temp; 152 | temp.set(&_mQ); 153 | temp.scale(dT * dT); 154 | _mP.plusEquals(&temp); 155 | 156 | } 157 | _sensorTimeStampGyro = sensorTimeStamp; 158 | _lastGyro = gyro; 159 | } 160 | 161 | void OrientationEKF::processAcceleration(GLKVector3 acc, double sensorTimeStamp) 162 | { 163 | _vZ.set(acc.x, acc.y, acc.z); 164 | updateAccelerationCovariance(_vZ.length()); 165 | if (_alignedToGravity) 166 | { 167 | accelerationObservationFunctionForNumericalJacobian(&_so3SensorFromWorld, &_vNu); 168 | const double eps = 1.0E-7; 169 | for (int dof = 0; dof < 3; dof++) 170 | { 171 | Vector3d delta; 172 | delta.setZero(); 173 | delta.setComponent(dof, eps); 174 | Matrix3x3d tempM; 175 | SO3Util::so3FromMu(&delta, &tempM); 176 | Matrix3x3d::mult(&tempM, &_so3SensorFromWorld, &tempM); 177 | Vector3d tempV; 178 | accelerationObservationFunctionForNumericalJacobian(&tempM, &tempV); 179 | Vector3d::sub(&_vNu, &tempV, &tempV); 180 | tempV.scale(1.0/eps); 181 | _mH.setColumn(dof, &tempV); 182 | } 183 | 184 | 185 | Matrix3x3d mHt; 186 | _mH.transpose(&mHt); 187 | Matrix3x3d temp; 188 | Matrix3x3d::mult(&_mP, &mHt, &temp); 189 | Matrix3x3d::mult(&_mH, &temp, &temp); 190 | Matrix3x3d::add(&temp, &_mRAcceleration, &_mS); 191 | _mS.invert(&temp); 192 | Matrix3x3d::mult(&mHt, &temp, &temp); 193 | Matrix3x3d::mult(&_mP, &temp, &_mK); 194 | Matrix3x3d::mult(&_mK, &_vNu, &_vX); 195 | Matrix3x3d::mult(&_mK, &_mH, &temp); 196 | Matrix3x3d temp2; 197 | temp2.setIdentity(); 198 | temp2.minusEquals(&temp); 199 | Matrix3x3d::mult(&temp2, &_mP, &_mP); 200 | SO3Util::so3FromMu(&_vX, &_so3LastMotion); 201 | Matrix3x3d::mult(&_so3LastMotion, &_so3SensorFromWorld, &_so3SensorFromWorld); 202 | updateCovariancesAfterMotion(); 203 | } 204 | else 205 | { 206 | SO3Util::so3FromTwoVecN(&_vDown, &_vZ, &_so3SensorFromWorld); 207 | _alignedToGravity = true; 208 | } 209 | } 210 | 211 | void OrientationEKF::filterGyroTimestep(double timestep) 212 | { 213 | const double kFilterCoeff = 0.95; 214 | if (!_timestepFilterInit) 215 | { 216 | _filteredGyroTimestep = timestep; 217 | _numGyroTimestepSamples = 1; 218 | _timestepFilterInit = true; 219 | } 220 | else 221 | { 222 | _filteredGyroTimestep = kFilterCoeff * _filteredGyroTimestep + (1.0-kFilterCoeff) * timestep; 223 | ++_numGyroTimestepSamples; 224 | _gyroFilterValid = (_numGyroTimestepSamples > 10); 225 | } 226 | } 227 | 228 | void OrientationEKF::updateCovariancesAfterMotion() 229 | { 230 | Matrix3x3d temp; 231 | _so3LastMotion.transpose(&temp); 232 | Matrix3x3d::mult(&_mP, &temp, &temp); 233 | Matrix3x3d::mult(&_so3LastMotion, &temp, &_mP); 234 | _so3LastMotion.setIdentity(); 235 | } 236 | 237 | void OrientationEKF::updateAccelerationCovariance(double currentAccelNorm) 238 | { 239 | double currentAccelNormChange = fabs(currentAccelNorm - _previousAccelNorm); 240 | _previousAccelNorm = currentAccelNorm; 241 | const double kSmoothingFactor = 0.5; 242 | _movingAverageAccelNormChange = kSmoothingFactor * _movingAverageAccelNormChange + (1.0-kSmoothingFactor) * currentAccelNormChange; 243 | const double kMaxAccelNormChange = 0.15; 244 | const double kMinAccelNoiseSigma = 0.75; 245 | const double kMaxAccelNoiseSigma = 7.0; 246 | double normChangeRatio = _movingAverageAccelNormChange / kMaxAccelNormChange; 247 | double accelNoiseSigma = std::min(kMaxAccelNoiseSigma, kMinAccelNoiseSigma + normChangeRatio * (kMaxAccelNoiseSigma-kMinAccelNoiseSigma)); 248 | _mRAcceleration.setSameDiagonal(accelNoiseSigma * accelNoiseSigma); 249 | } 250 | 251 | void OrientationEKF::accelerationObservationFunctionForNumericalJacobian(Matrix3x3d *so3SensorFromWorldPred, Vector3d *result) 252 | { 253 | Matrix3x3d::mult(so3SensorFromWorldPred, &_vDown, &_vH); 254 | Matrix3x3d temp; 255 | SO3Util::so3FromTwoVecN(&_vH, &_vZ, &temp); 256 | SO3Util::muFromSO3(&temp, result); 257 | } 258 | 259 | } 260 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/Internal/SO3Util.h: -------------------------------------------------------------------------------- 1 | // 2 | // SO3Util.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #ifndef __CardboardSDK_iOS__So3Util__ 7 | #define __CardboardSDK_iOS__So3Util__ 8 | 9 | #include "Vector3d.h" 10 | #include "Matrix3x3d.h" 11 | 12 | 13 | namespace CardboardSDK 14 | { 15 | 16 | class SO3Util 17 | { 18 | public: 19 | static void so3FromTwoVecN(Vector3d *a, Vector3d *b, Matrix3x3d *result); 20 | static void rotationPiAboutAxis(Vector3d *v, Matrix3x3d *result); 21 | static void so3FromMu(Vector3d *w, Matrix3x3d *result); 22 | static void muFromSO3(Matrix3x3d *so3, Vector3d *result); 23 | static void rodriguesSo3Exp(Vector3d *w, double kA, double kB, Matrix3x3d *result); 24 | static void generatorField(int i, Matrix3x3d *pos, Matrix3x3d *result); 25 | }; 26 | 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/Internal/SO3Util.mm: -------------------------------------------------------------------------------- 1 | // 2 | // SO3Util.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #include "SO3Util.h" 7 | 8 | 9 | namespace CardboardSDK 10 | { 11 | 12 | void SO3Util::so3FromTwoVecN(Vector3d *a, Vector3d *b, Matrix3x3d *result) 13 | { 14 | Vector3d so3FromTwoVecNN; 15 | Vector3d::cross(a, b, &so3FromTwoVecNN); 16 | 17 | if (so3FromTwoVecNN.length() == 0.0) 18 | { 19 | double dot = Vector3d::dot(a, b); 20 | if (dot >= 0.0) 21 | { 22 | result->setIdentity(); 23 | } 24 | else 25 | { 26 | Vector3d so3FromTwoVecNRotationAxis; 27 | Vector3d::ortho(a, &so3FromTwoVecNRotationAxis); 28 | SO3Util::rotationPiAboutAxis(&so3FromTwoVecNRotationAxis, result); 29 | } 30 | return; 31 | } 32 | 33 | Vector3d so3FromTwoVecNA(a); 34 | Vector3d so3FromTwoVecNB(b); 35 | so3FromTwoVecNN.normalize(); 36 | so3FromTwoVecNA.normalize(); 37 | so3FromTwoVecNB.normalize(); 38 | 39 | Vector3d tempVector; 40 | Matrix3x3d r1; 41 | r1.setColumn(0, &so3FromTwoVecNA); 42 | r1.setColumn(1, &so3FromTwoVecNN); 43 | Vector3d::cross(&so3FromTwoVecNN, &so3FromTwoVecNA, &tempVector); 44 | r1.setColumn(2, &tempVector); 45 | 46 | Matrix3x3d r2; 47 | r2.setColumn(0, &so3FromTwoVecNB); 48 | r2.setColumn(1, &so3FromTwoVecNN); 49 | Vector3d::cross(&so3FromTwoVecNN, &so3FromTwoVecNB, &tempVector); 50 | r2.setColumn(2, &tempVector); 51 | 52 | r1.transpose(); 53 | Matrix3x3d::mult(&r2, &r1, result); 54 | } 55 | 56 | void SO3Util::rotationPiAboutAxis(Vector3d *v, Matrix3x3d *result) 57 | { 58 | Vector3d rotationPiAboutAxisTemp(v); 59 | rotationPiAboutAxisTemp.scale(M_PI / rotationPiAboutAxisTemp.length()); 60 | const double kA = 0.0; 61 | const double kB = 0.20264236728467558; 62 | SO3Util::rodriguesSo3Exp(&rotationPiAboutAxisTemp, kA, kB, result); 63 | } 64 | 65 | void SO3Util::so3FromMu(Vector3d *w, Matrix3x3d *result) 66 | { 67 | const double thetaSq = Vector3d::dot(w, w); 68 | const double theta = sqrt(thetaSq); 69 | double kA, kB; 70 | if (thetaSq < 1.0E-08) 71 | { 72 | kA = 1.0 - 0.16666667163372 * thetaSq; 73 | kB = 0.5; 74 | } 75 | else 76 | { 77 | if (thetaSq < 1.0E-06) 78 | { 79 | kB = 0.5 - 0.0416666679084301 * thetaSq; 80 | kA = 1.0 - thetaSq * 0.16666667163372 * (1.0 - 0.16666667163372 * thetaSq); 81 | } 82 | else 83 | { 84 | const double invTheta = 1.0 / theta; 85 | kA = sin(theta) * invTheta; 86 | kB = (1.0 - cos(theta)) * (invTheta * invTheta); 87 | } 88 | } 89 | SO3Util::rodriguesSo3Exp(w, kA, kB, result); 90 | } 91 | 92 | void SO3Util::muFromSO3(Matrix3x3d *so3, Vector3d *result) 93 | { 94 | const double cosAngle = (so3->get(0, 0) + so3->get(1, 1) + so3->get(2, 2) - 1.0) * 0.5; 95 | result->set((so3->get(2, 1) - so3->get(1, 2)) / 2.0, 96 | (so3->get(0, 2) - so3->get(2, 0)) / 2.0, 97 | (so3->get(1, 0) - so3->get(0, 1)) / 2.0); 98 | 99 | double sinAngleAbs = result->length(); 100 | if (cosAngle > 0.7071067811865476) 101 | { 102 | if (sinAngleAbs > 0.0) 103 | { 104 | result->scale(asin(sinAngleAbs) / sinAngleAbs); 105 | } 106 | } 107 | else if (cosAngle > -0.7071067811865476) 108 | { 109 | const double angle = acos(cosAngle); 110 | result->scale(angle / sinAngleAbs); 111 | } 112 | else 113 | { 114 | double angle = M_PI - asin(sinAngleAbs); 115 | double d0 = so3->get(0, 0) - cosAngle; 116 | double d1 = so3->get(1, 1) - cosAngle; 117 | double d2 = so3->get(2, 2) - cosAngle; 118 | 119 | Vector3d r2; 120 | if ((d0 * d0 > d1 * d1) && (d0 * d0 > d2 * d2)) 121 | { 122 | r2.set(d0, 123 | (so3->get(1, 0) + so3->get(0, 1)) / 2.0, 124 | (so3->get(0, 2) + so3->get(2, 0)) / 2.0); 125 | } 126 | else if (d1 * d1 > d2 * d2) 127 | { 128 | r2.set((so3->get(1, 0) + so3->get(0, 1)) / 2.0, 129 | d1, 130 | (so3->get(2, 1) + so3->get(1, 2)) / 2.0); 131 | } 132 | else 133 | { 134 | r2.set((so3->get(0, 2) + so3->get(2, 0)) / 2.0, 135 | (so3->get(2, 1) + so3->get(1, 2)) / 2.0, 136 | d2); 137 | } 138 | 139 | if (Vector3d::dot(&r2, result) < 0.0) 140 | { 141 | r2.scale(-1.0); 142 | } 143 | 144 | r2.normalize(); 145 | r2.scale(angle); 146 | result->set(&r2); 147 | } 148 | } 149 | 150 | void SO3Util::rodriguesSo3Exp(Vector3d *w, double kA, double kB, Matrix3x3d *result) 151 | { 152 | const double wx2 = w->_x * w->_x; 153 | const double wy2 = w->_y * w->_y; 154 | const double wz2 = w->_z * w->_z; 155 | result->set(0.0, 0.0, 1.0 - kB * (wy2 + wz2)); 156 | result->set(1.0, 1.0, 1.0 - kB * (wx2 + wz2)); 157 | result->set(2.0, 2.0, 1.0 - kB * (wx2 + wy2)); 158 | 159 | double a = kA * w->_z; 160 | double b = kB * (w->_x * w->_y); 161 | result->set(0.0, 1.0, b - a); 162 | result->set(1.0, 0.0, b + a); 163 | 164 | a = kA * w->_y; 165 | b = kB * (w->_x * w->_z); 166 | result->set(0.0, 2.0, b + a); 167 | result->set(2.0, 0.0, b - a); 168 | 169 | a = kA * w->_x; 170 | b = kB * (w->_y * w->_z); 171 | result->set(1.0, 2.0, b - a); 172 | result->set(2.0, 1.0, b + a); 173 | } 174 | 175 | void SO3Util::generatorField(int i, Matrix3x3d *pos, Matrix3x3d *result) 176 | { 177 | result->set(i, 0.0, 0.0); 178 | result->set((i + 1) % 3, 179 | 0, 180 | -pos->get((i + 2) % 3, 0)); 181 | 182 | result->set((i + 2) % 3, 183 | 0, 184 | pos->get((i + 1) % 3, 0)); 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/Internal/Vector3d.h: -------------------------------------------------------------------------------- 1 | // 2 | // Vector3d.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #ifndef __CardboardSDK_iOS__Vector3d__ 7 | #define __CardboardSDK_iOS__Vector3d__ 8 | 9 | 10 | namespace CardboardSDK 11 | { 12 | 13 | class Vector3d 14 | { 15 | friend class Matrix3x3d; 16 | friend class SO3Util; 17 | 18 | public: 19 | Vector3d(); 20 | Vector3d(double x, double y, double z); 21 | Vector3d(Vector3d *other); 22 | void set(double x, double y, double z); 23 | void setComponent(int i, double val); 24 | void setZero(); 25 | void set(Vector3d *other); 26 | void scale(double s); 27 | void normalize(); 28 | static double dot(Vector3d *a, Vector3d *b); 29 | double length(); 30 | bool sameValues(Vector3d *other); 31 | static void sub(Vector3d *a, Vector3d *b, Vector3d *result); 32 | static void cross(Vector3d *a, Vector3d *b, Vector3d *result); 33 | static void ortho(Vector3d *v, Vector3d *result); 34 | static int largestAbsComponent(Vector3d *v); 35 | 36 | inline double x() const { return _x; } 37 | inline double y() const { return _y; } 38 | inline double z() const { return _z; } 39 | 40 | private: 41 | double _x; 42 | double _y; 43 | double _z; 44 | }; 45 | 46 | } 47 | 48 | #endif -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/Internal/Vector3d.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Vector3d.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #include "Vector3d.h" 7 | #include 8 | 9 | 10 | namespace CardboardSDK 11 | { 12 | 13 | Vector3d::Vector3d() 14 | { 15 | setZero(); 16 | } 17 | 18 | Vector3d::Vector3d(Vector3d *v) 19 | { 20 | set(v->_x, v->_y, v->_z); 21 | } 22 | 23 | Vector3d::Vector3d(double x, double y, double z) 24 | { 25 | set(x, y, z); 26 | } 27 | 28 | void Vector3d::set(double x, double y, double z) 29 | { 30 | _x = x; 31 | _y = y; 32 | _z = z; 33 | } 34 | 35 | void Vector3d::setComponent(int i, double val) 36 | { 37 | if (i == 0) 38 | { 39 | _x = val; 40 | } 41 | else if (i == 1) 42 | { 43 | _y = val; 44 | } 45 | else 46 | { 47 | _z = val; 48 | } 49 | } 50 | 51 | void Vector3d::setZero() 52 | { 53 | set(0, 0, 0); 54 | } 55 | 56 | void Vector3d::set(Vector3d *other) 57 | { 58 | set(other->_x, other->_y, other->_z); 59 | } 60 | 61 | void Vector3d::scale(double s) 62 | { 63 | _x *= s; 64 | _y *= s; 65 | _z *= s; 66 | } 67 | 68 | void Vector3d::normalize() 69 | { 70 | double d = length(); 71 | if (d != 0.0) 72 | { 73 | scale(1.0 / d); 74 | } 75 | } 76 | 77 | double Vector3d::dot(Vector3d *a, Vector3d *b) 78 | { 79 | return a->_x * b->_x + a->_y * b->_y + a->_z * b->_z; 80 | } 81 | 82 | double Vector3d::length() 83 | { 84 | return sqrt(_x * _x + _y * _y + _z * _z); 85 | } 86 | 87 | bool Vector3d::sameValues(Vector3d *other) 88 | { 89 | return (_x == other->_x) && (_y == other->_y) && (_z == other->_z); 90 | } 91 | 92 | void Vector3d::sub(Vector3d *a, Vector3d *b, Vector3d *result) 93 | { 94 | result->set(a->_x - b->_x, 95 | a->_y - b->_y, 96 | a->_z - b->_z); 97 | } 98 | 99 | void Vector3d::cross(Vector3d *a, Vector3d *b, Vector3d *result) 100 | { 101 | result->set(a->_y * b->_z - a->_z * b->_y, 102 | a->_z * b->_x - a->_x * b->_z, 103 | a->_x * b->_y - a->_y * b->_x); 104 | } 105 | 106 | void Vector3d::ortho(Vector3d *v, Vector3d *result) 107 | { 108 | int k = largestAbsComponent(v) - 1; 109 | if (k < 0) 110 | { 111 | k = 2; 112 | } 113 | result->setZero(); 114 | result->setComponent(k, 1.0); 115 | cross(v, result, result); 116 | result->normalize(); 117 | } 118 | 119 | int Vector3d::largestAbsComponent(Vector3d *v) 120 | { 121 | double xAbs = fabs(v->_x); 122 | double yAbs = fabs(v->_y); 123 | double zAbs = fabs(v->_z); 124 | if (xAbs > yAbs) 125 | { 126 | if (xAbs > zAbs) 127 | { 128 | return 0; 129 | } 130 | return 2; 131 | } 132 | if (yAbs > zAbs) 133 | { 134 | return 1; 135 | } 136 | return 2; 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/MagnetSensor.h: -------------------------------------------------------------------------------- 1 | // 2 | // MagnetSensor.h 3 | // CardboardSDK-iOS 4 | // 5 | // 6 | 7 | #ifndef __CardboardSDK_iOS__MagnetSensor__ 8 | #define __CardboardSDK_iOS__MagnetSensor__ 9 | 10 | #import 11 | #import 12 | 13 | #include 14 | 15 | 16 | namespace CardboardSDK 17 | { 18 | 19 | NSString *const CBDTriggerPressedNotification = @"CBTriggerPressedNotification"; 20 | 21 | class MagnetSensor 22 | { 23 | public: 24 | MagnetSensor(); 25 | virtual ~MagnetSensor() {} 26 | void start(); 27 | void stop(); 28 | 29 | private: 30 | CMMotionManager *_manager; 31 | size_t _sampleIndex; 32 | GLKVector3 _baseline; 33 | std::vector _sensorData; 34 | std::vector _offsets; 35 | 36 | 37 | void addData(GLKVector3 value); 38 | void evaluateModel(); 39 | void computeOffsets(int start, GLKVector3 baseline); 40 | 41 | static const size_t numberOfSamples = 20; 42 | }; 43 | 44 | } 45 | 46 | #endif -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Sensors/MagnetSensor.mm: -------------------------------------------------------------------------------- 1 | // 2 | // MagnetSensor.mm 3 | // CardboardSDK-iOS 4 | // 5 | // 6 | 7 | #include "MagnetSensor.h" 8 | 9 | #include 10 | 11 | 12 | namespace CardboardSDK 13 | { 14 | 15 | MagnetSensor::MagnetSensor() : 16 | _sampleIndex(0), 17 | _sensorData(2 * numberOfSamples), 18 | _offsets(numberOfSamples) 19 | { 20 | _manager = [[CMMotionManager alloc] init]; 21 | } 22 | 23 | void MagnetSensor::start() 24 | { 25 | if (_manager.isMagnetometerAvailable && !_manager.isMagnetometerActive) 26 | { 27 | _manager.magnetometerUpdateInterval = 1.0f / 100.0f; 28 | NSOperationQueue *magnetometerQueue = [[NSOperationQueue alloc] init]; 29 | [_manager startMagnetometerUpdatesToQueue:magnetometerQueue 30 | withHandler:^(CMMagnetometerData *magnetometerData, NSError *error) 31 | { 32 | addData(GLKVector3 { 33 | (float) magnetometerData.magneticField.x, 34 | (float) magnetometerData.magneticField.y, 35 | (float) magnetometerData.magneticField.z}); 36 | }]; 37 | } 38 | } 39 | 40 | void MagnetSensor::stop() 41 | { 42 | [_manager stopMagnetometerUpdates]; 43 | } 44 | 45 | void MagnetSensor::addData(GLKVector3 value) 46 | { 47 | _sensorData[_sampleIndex % (2 * numberOfSamples)] = value; 48 | _baseline = value; 49 | ++_sampleIndex; 50 | evaluateModel(); 51 | } 52 | 53 | void MagnetSensor::evaluateModel() 54 | { 55 | if (_sampleIndex < (2 * numberOfSamples)) 56 | { 57 | return; 58 | } 59 | float minimums[2]; 60 | float maximums[2]; 61 | for (int i = 0; i < 2; i++) 62 | { 63 | computeOffsets(i * numberOfSamples, _baseline); 64 | minimums[i] = *std::min_element(_offsets.begin(), _offsets.end()); 65 | maximums[i] = *std::max_element(_offsets.begin(), _offsets.end()); 66 | } 67 | 68 | if (minimums[0] < 30.0f && maximums[1] > 130.0f) 69 | { 70 | _sampleIndex = 0; 71 | [[NSNotificationCenter defaultCenter] postNotificationName:CBDTriggerPressedNotification object:nil]; 72 | } 73 | } 74 | 75 | void MagnetSensor::computeOffsets(int start, GLKVector3 baseline) 76 | { 77 | size_t frontIndex = _sampleIndex % (2 * numberOfSamples); // currently the oldest sample 78 | for (int i = 0; i < numberOfSamples; i++) 79 | { 80 | GLKVector3 point = _sensorData[(frontIndex + start + i) % (2 * numberOfSamples)]; 81 | float o[] = {point.x - baseline.x, point.y - baseline.y, point.z - baseline.z}; 82 | float magnitude = (float)sqrt(o[0] * o[0] + o[1] * o[1] + o[2] * o[2]); 83 | _offsets[i] = magnitude; 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Viewport.h: -------------------------------------------------------------------------------- 1 | // 2 | // Viewport.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #ifndef __CardboardSDK_iOS__Viewport__ 8 | #define __CardboardSDK_iOS__Viewport__ 9 | 10 | #import 11 | #import 12 | 13 | 14 | namespace CardboardSDK 15 | { 16 | 17 | struct Viewport 18 | { 19 | public: 20 | int x; 21 | int y; 22 | int width; 23 | int height; 24 | 25 | void setViewport(int x, int y, int width, int height); 26 | 27 | void setGLViewport(); 28 | void setGLScissor(); 29 | 30 | CGRect toCGRect(); 31 | NSString *toString(); 32 | }; 33 | 34 | } 35 | 36 | #endif -------------------------------------------------------------------------------- /CardboardSDK/Cardboard/Viewport.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Viewport.mm 3 | // CardboardSDK-iOS 4 | // 5 | 6 | 7 | #include "Viewport.h" 8 | 9 | #import 10 | 11 | 12 | namespace CardboardSDK 13 | { 14 | 15 | void Viewport::setViewport(int x, int y, int width, int height) 16 | { 17 | this->x = x; 18 | this->y = y; 19 | this->width = width; 20 | this->height = height; 21 | } 22 | 23 | void Viewport::setGLViewport() 24 | { 25 | glViewport(x, y, width, height); 26 | } 27 | 28 | void Viewport::setGLScissor() 29 | { 30 | glScissor(x, y, width, height); 31 | } 32 | 33 | CGRect Viewport::toCGRect() 34 | { 35 | return CGRectMake(x, y, width, height); 36 | } 37 | 38 | NSString *Viewport::toString() 39 | { 40 | return [NSString stringWithFormat:@"{x:%d y:%d width:%d height:%d}", this->x, this->y, this->width, this->height]; 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /CardboardSDK/CardboardSDK.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardboardSDK.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #include "DebugUtils.h" 7 | 8 | #include "GLHelpers.h" 9 | #import "CBDViewController.h" 10 | #import "CBDStereoGLView.h" -------------------------------------------------------------------------------- /CardboardSDK/Misc/DebugUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // DebugUtils.h 3 | // 4 | // Created by Ricardo Sánchez-Sáez on 11/03/13. 5 | // 6 | // 7 | // Adapted from: http://iphoneprogrammingfordummies.blogspot.ie/2010/09/nslog-tricks-log-only-in-debug-mode-add.html 8 | // 9 | 10 | #ifndef _DebugUtils_h 11 | #define _DebugUtils_h 12 | 13 | // Lean and clean NSLog 14 | #ifdef DEBUG 15 | #define NSLog(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]); 16 | #endif 17 | 18 | // Print only in Debug mode 19 | #ifdef DEBUG 20 | #define DBLog(fmt, ...) NSLog(@"%@", [NSString stringWithFormat:(fmt), ##__VA_ARGS__]); 21 | #else 22 | #define DBLog(...) 23 | #endif 24 | 25 | // DBLog but only print out if assert is true 26 | #ifdef DEBUG 27 | #define DBALog( assert , fmt , ... ) if ( assert ) { DBLog(fmt, ##__VA_ARGS__); } 28 | #else 29 | #define DBALog(...) 30 | #endif 31 | 32 | // Print function name only in Debug 33 | #ifdef DEBUG 34 | #define DLog(fmt, ...) NSLog((@"%p %s " fmt), self, __PRETTY_FUNCTION__, ##__VA_ARGS__); 35 | #else 36 | #define DLog(...) 37 | #endif 38 | 39 | // Print function name only in Debug if DDLOG_DEBUG_SYMBOL defined to a non-zero value 40 | #ifdef DEBUG 41 | #define DDLog(DDLOG_DEBUG_SYMBOL, fmt, ...) \ 42 | if (DDLOG_DEBUG_SYMBOL) \ 43 | { NSLog((@"%p %s " fmt), self, __PRETTY_FUNCTION__, ##__VA_ARGS__); } 44 | #else 45 | #define DDLog(...) 46 | #endif 47 | 48 | // Print function name only in Debug if DDLOG_DEBUG_SYMBOL defined to a non-zero value 49 | #ifdef DEBUG 50 | #define DCLog(DDLOG_DEBUG_SYMBOL, fmt, ...) \ 51 | if (DDLOG_DEBUG_SYMBOL) \ 52 | { NSLog((@"%s " fmt), __PRETTY_FUNCTION__, ##__VA_ARGS__); } 53 | #else 54 | #define DCLog(...) 55 | #endif 56 | 57 | 58 | // DLog but only print out if assert is true 59 | #ifdef DEBUG 60 | #define DALog( assert , fmt , ... ) if( assert ) { DLog(fmt,##__VA_ARGS__); } 61 | #else 62 | #define DALog(...) 63 | #endif 64 | 65 | // Print function name in Debug and release 66 | #define ALog(fmt, ...) NSLog((@"%s " fmt), __PRETTY_FUNCTION__, ##__VA_ARGS__); 67 | 68 | #endif -------------------------------------------------------------------------------- /CardboardSDK/Misc/GLHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // GLHelpers.h 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #ifndef _GLHelpers_h 7 | #define _GLHelpers_h 8 | 9 | 10 | #import 11 | #import 12 | 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | 19 | #if defined(DEBUG) 20 | void GLCheckForError(); 21 | #else 22 | #define GLCheckForError() 23 | #endif 24 | 25 | #define BUFFER_OFFSET(i) ((char *)NULL + (i)) 26 | 27 | BOOL GLCompileShader(GLuint *shader, GLenum type, const GLchar *source); 28 | BOOL GLCompileShaderFromFile(GLuint *shader, GLenum type, NSString *file); 29 | BOOL GLLinkProgram(GLuint program); 30 | BOOL GLValidateProgram(GLuint program); 31 | 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /CardboardSDK/Misc/GLHelpers.m: -------------------------------------------------------------------------------- 1 | // 2 | // GLHelpers.m 3 | // CardboardSDK-iOS 4 | // 5 | 6 | #import "GLHelpers.h" 7 | 8 | 9 | #if defined(DEBUG) 10 | void GLCheckForError() 11 | { 12 | GLenum err = glGetError(); 13 | if (err != GL_NO_ERROR) 14 | { 15 | NSLog(@"glError: %d", err); 16 | 17 | 18 | } 19 | } 20 | #endif 21 | 22 | BOOL GLCompileShader(GLuint *shader, GLenum type, const GLchar *source) 23 | { 24 | if (!source) { 25 | NSLog(@"Failed to read shader source"); 26 | return NO; 27 | } 28 | 29 | *shader = glCreateShader(type); 30 | glShaderSource(*shader, 1, &source, NULL); 31 | glCompileShader(*shader); 32 | 33 | #if defined(DEBUG) 34 | GLint logLength = 0; 35 | glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); 36 | if (logLength > 0) { 37 | GLchar *log = (GLchar *)malloc(logLength); 38 | glGetShaderInfoLog(*shader, logLength, &logLength, log); 39 | NSLog(@"Shader compile log:\n%s", log); 40 | free(log); 41 | } 42 | #endif 43 | 44 | GLint status = 0; 45 | glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); 46 | if (status == 0) { 47 | glDeleteShader(*shader); 48 | return NO; 49 | } 50 | 51 | return YES; 52 | } 53 | 54 | BOOL GLCompileShaderFromFile(GLuint *shader, GLenum type, NSString *file) 55 | { 56 | const GLchar *source = (GLchar *)[[NSString stringWithContentsOfFile:file 57 | encoding:NSUTF8StringEncoding 58 | error:nil] UTF8String]; 59 | return GLCompileShader(shader, type, source); 60 | } 61 | 62 | BOOL GLLinkProgram(GLuint program) 63 | { 64 | GLint status; 65 | glLinkProgram(program); 66 | 67 | #if defined(DEBUG) 68 | GLint logLength; 69 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); 70 | if (logLength > 0) { 71 | GLchar *log = (GLchar *)malloc(logLength); 72 | glGetProgramInfoLog(program, logLength, &logLength, log); 73 | NSLog(@"Program link log:\n%s", log); 74 | free(log); 75 | } 76 | #endif 77 | 78 | glGetProgramiv(program, GL_LINK_STATUS, &status); 79 | if (status == 0) 80 | { 81 | return NO; 82 | } 83 | 84 | return YES; 85 | } 86 | 87 | BOOL GLValidateProgram(GLuint program) 88 | { 89 | GLint logLength, status; 90 | 91 | glValidateProgram(program); 92 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); 93 | if (logLength > 0) { 94 | GLchar *log = (GLchar *)malloc(logLength); 95 | glGetProgramInfoLog(program, logLength, &logLength, log); 96 | NSLog(@"Program validate log:\n%s", log); 97 | free(log); 98 | } 99 | 100 | glGetProgramiv(program, GL_VALIDATE_STATUS, &status); 101 | if (status == 0) 102 | { 103 | return NO; 104 | } 105 | 106 | return YES; 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #SFOpenGLESPanorama 2 | 3 | 4 | This project illustrates how to implement a parorama viewer on iOS 5 | 6 | ![ScreenShot](https://raw.github.com/JagieChen/SFOpenGLESPanorama/master/s1.png) 7 | 8 | Bellow is the output for Google Cardboard: 9 | 10 | ![ScreenShot](https://raw.github.com/JagieChen/SFOpenGLESPanorama/master/s2.png) 11 | 12 | ## Features 13 | 14 | * Based on OpenGLES 2.0,without GLKBaseEffect; 15 | * The camera's viewpoint can be controlled by pan gestures or CMDeviceMotion's attitudes; 16 | * Support output for Google Cardboard benefitting from "CardboardSDK-iOS" (https://github.com/rsanchezsaez/CardboardSDK-iOS); 17 | * I keep the code as concise as I can. 18 | 19 | ## License 20 | 21 | This code is distributed under the terms and conditions of the [MIT license](LICENSE). 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama.xcodeproj/project.xcworkspace/xcuserdata/Jagie.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/SFOpenGLESPanorama.xcodeproj/project.xcworkspace/xcuserdata/Jagie.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SFOpenGLESPanorama.xcodeproj/xcuserdata/Jagie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 56 | 68 | 69 | 70 | 72 | 84 | 85 | 86 | 88 | 100 | 101 | 102 | 104 | 116 | 117 | 118 | 120 | 132 | 133 | 134 | 136 | 148 | 149 | 150 | 152 | 164 | 165 | 166 | 168 | 180 | 181 | 182 | 184 | 196 | 197 | 198 | 200 | 212 | 213 | 214 | 216 | 228 | 229 | 230 | 232 | 244 | 245 | 246 | 248 | 260 | 261 | 262 | 264 | 276 | 277 | 278 | 279 | 280 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama.xcodeproj/xcuserdata/Jagie.xcuserdatad/xcschemes/SFOpenGLESPanorama.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama.xcodeproj/xcuserdata/Jagie.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SFOpenGLESPanorama.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 557B0E601B8A025D0062A4B7 16 | 17 | primary 18 | 19 | 20 | 557B0E7D1B8A025D0062A4B7 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/GameViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.h 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface GameViewController : GLKViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/GameViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.m 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | #import "GameViewController.h" 10 | #import 11 | @import CoreMotion; 12 | 13 | 14 | 15 | @interface GameViewController () { 16 | GLuint _program; 17 | 18 | GLuint _vertexBufferID; 19 | GLuint _indexBufferID; 20 | GLuint _vertexArrayID; 21 | 22 | 23 | 24 | GLint _u_mvpMatrix; 25 | GLint _u_cubemap; 26 | GLuint _textureID; 27 | GLint _a_position; 28 | 29 | 30 | 31 | CGFloat _rotateX; 32 | CGFloat _rotateY; 33 | } 34 | @property (strong, nonatomic) EAGLContext *context; 35 | 36 | @property (nonatomic, strong) CMMotionManager * motionManager; 37 | 38 | @property(nonatomic,strong) CMAttitude *lastAttitude; 39 | 40 | @end 41 | 42 | @implementation GameViewController 43 | 44 | - (void)viewDidLoad 45 | { 46 | [super viewDidLoad]; 47 | 48 | self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 49 | 50 | if (!self.context) { 51 | NSLog(@"Failed to create ES context"); 52 | } 53 | 54 | GLKView *view = (GLKView *)self.view; 55 | view.context = self.context; 56 | view.drawableDepthFormat = GLKViewDrawableDepthFormat24; 57 | 58 | [self setupGL]; 59 | 60 | UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(rotatePanGestureRecognizer:)]; 61 | [self.view addGestureRecognizer:panGestureRecognizer]; 62 | 63 | 64 | 65 | // 2.1 Create a CMMotionManager instance and store it in the property "motionManager" 66 | self.motionManager = [[CMMotionManager alloc] init]; 67 | 68 | // 2.1 Set the motion update interval to 1/60 69 | self.motionManager.deviceMotionUpdateInterval = 1.0 / self.preferredFramesPerSecond; 70 | 71 | // 2.1 Start updating the motion using the reference frame CMAttitudeReferenceFrameXArbitraryCorrectedZVertical 72 | [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical]; 73 | } 74 | 75 | - (IBAction)onClose:(id)sender { 76 | [self dismissViewControllerAnimated:YES completion:nil]; 77 | } 78 | 79 | 80 | - (void)rotatePanGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer 81 | { 82 | 83 | CGPoint p = [panGestureRecognizer translationInView:self.view]; 84 | 85 | _rotateX += -p.y * 0.01; 86 | _rotateY += -p.x * 0.01; 87 | 88 | 89 | [panGestureRecognizer setTranslation:CGPointZero inView:self.view]; 90 | } 91 | 92 | - (void)dealloc 93 | { 94 | [self tearDownGL]; 95 | [self.motionManager stopDeviceMotionUpdates]; 96 | 97 | if ([EAGLContext currentContext] == self.context) { 98 | [EAGLContext setCurrentContext:nil]; 99 | } 100 | } 101 | 102 | - (void)didReceiveMemoryWarning 103 | { 104 | [super didReceiveMemoryWarning]; 105 | 106 | if ([self isViewLoaded] && ([[self view] window] == nil)) { 107 | self.view = nil; 108 | 109 | [self tearDownGL]; 110 | 111 | if ([EAGLContext currentContext] == self.context) { 112 | [EAGLContext setCurrentContext:nil]; 113 | } 114 | self.context = nil; 115 | } 116 | 117 | // Dispose of any resources that can be recreated. 118 | } 119 | 120 | 121 | 122 | 123 | 124 | 125 | - (void)setupGL 126 | { 127 | [EAGLContext setCurrentContext:self.context]; 128 | 129 | [self loadShaders]; 130 | 131 | 132 | 133 | // The 8 corners of a cube 134 | const float vertices[24] = { 135 | -0.5, -0.5, 0.5, 136 | 0.5, -0.5, 0.5, 137 | -0.5, 0.5, 0.5, 138 | 0.5, 0.5, 0.5, 139 | -0.5, -0.5, -0.5, 140 | 0.5, -0.5, -0.5, 141 | -0.5, 0.5, -0.5, 142 | 0.5, 0.5, -0.5, 143 | }; 144 | 145 | glGenBuffers(1, &_vertexBufferID); 146 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID); 147 | glBufferData(GL_ARRAY_BUFFER, 148 | sizeof(vertices), 149 | vertices, 150 | GL_STATIC_DRAW); 151 | 152 | 153 | const GLubyte indices[36] = { 154 | 0,2,1, //front 155 | 2,3,1, 156 | 157 | 1,3,7,//right 158 | 1,7,5, 159 | 160 | 0,6,2,//left 161 | 0,4,6, 162 | 163 | 0,1,4,//bottom 164 | 1,5,4, 165 | 166 | 4,5,6,//back 167 | 5,7,6, 168 | 169 | 3,6,7,//top 170 | 3,2,6 171 | }; 172 | glGenBuffers(1, &_indexBufferID); 173 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferID); 174 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, 175 | sizeof(indices), 176 | indices, 177 | GL_STATIC_DRAW); 178 | 179 | 180 | glGenVertexArraysOES(1, &_vertexArrayID); 181 | glBindVertexArrayOES(_vertexArrayID); 182 | 183 | glEnableVertexAttribArray(_a_position); 184 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID); 185 | glVertexAttribPointer(_a_position, 186 | 3, 187 | GL_FLOAT, 188 | GL_FALSE, 189 | 0, 190 | NULL); 191 | glBindVertexArrayOES(0); 192 | 193 | 194 | //texture 195 | 196 | NSMutableArray *pics = [NSMutableArray array]; 197 | // faces.push_back("right.jpg"); 198 | // faces.push_back("left.jpg"); 199 | // faces.push_back("top.jpg"); 200 | // faces.push_back("bottom.jpg"); 201 | // faces.push_back("back.jpg"); 202 | // faces.push_back("front.jpg"); 203 | 204 | NSArray *names = @[@"right",@"left",@"top",@"bottom",@"back",@"front"]; 205 | 206 | for (NSString *n in names) { 207 | NSString *path = [[NSBundle mainBundle] 208 | pathForResource:n ofType:@"jpeg"]; 209 | [pics addObject:path]; 210 | } 211 | 212 | 213 | NSError *error = nil; 214 | GLKTextureInfo * textInfo = [GLKTextureLoader 215 | cubeMapWithContentsOfFiles:pics 216 | options:nil 217 | error:&error]; 218 | 219 | _textureID = textInfo.name; 220 | 221 | glEnable(GL_CULL_FACE); 222 | glEnable(GL_DEPTH_TEST); 223 | } 224 | 225 | - (void)tearDownGL 226 | { 227 | [EAGLContext setCurrentContext:self.context]; 228 | 229 | glDeleteBuffers(1, &_vertexBufferID); 230 | glDeleteVertexArraysOES(1, &_vertexArrayID); 231 | glDeleteTextures(1, &_textureID); 232 | 233 | if (_program) { 234 | glDeleteProgram(_program); 235 | _program = 0; 236 | } 237 | } 238 | 239 | #pragma mark - GLKView and GLKViewController delegate methods 240 | 241 | - (void)update 242 | { 243 | CMDeviceMotion *deviceMotion = self.motionManager.deviceMotion; 244 | 245 | 246 | CMAttitude *attitude = deviceMotion.attitude; 247 | //Pitch: A pitch is a rotation around a lateral (X) axis that passes through the device from side to side 248 | //Roll: A roll is a rotation around a longitudinal (Y) axis that passes through the device from its top to bottom 249 | //Yaw: A yaw is a rotation around an axis (Z) that runs vertically through the device. It is perpendicular to the body of the device, with its origin at the center of gravity and directed toward the bottom of the device 250 | 251 | if (self.lastAttitude != nil) { 252 | 253 | float roll = attitude.roll ; 254 | //float pitch = attitude.pitch; 255 | float yaw = attitude.yaw; 256 | 257 | //NSLog(@"%f,%f,%f",GLKMathRadiansToDegrees(roll), GLKMathRadiansToDegrees(pitch),GLKMathRadiansToDegrees(yaw)); 258 | 259 | 260 | _rotateX -= roll - self.lastAttitude.roll; 261 | _rotateY -= yaw - self.lastAttitude.yaw; 262 | 263 | 264 | } 265 | 266 | self.lastAttitude = attitude; 267 | 268 | } 269 | 270 | - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect 271 | { 272 | 273 | glClearColor(1.0, 1.0f, 1.0f, 1.0f); 274 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 275 | 276 | 277 | 278 | const GLfloat aspectRatio = 279 | (GLfloat)view.drawableWidth / (GLfloat)view.drawableHeight; 280 | 281 | GLKMatrix4 projectionMatrix = 282 | GLKMatrix4MakePerspective( 283 | GLKMathDegreesToRadians(50),// a suitable value 284 | aspectRatio, 285 | 0.1f, // Don't make near plane too close 286 | 20.0f); // Far arbitrarily far enough to contain scene 287 | 288 | GLKMatrix4 modelViewMatrix = GLKMatrix4MakeRotation(_rotateX, 1.0f, 0.0f, 0.0f); 289 | 290 | modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix,_rotateY, 0.0f, 1.0f, 0.0f); 291 | 292 | 293 | 294 | 295 | GLKMatrix4 skyboxModelView = modelViewMatrix; 296 | 297 | GLKMatrix4 modelViewProjectionMatrix = GLKMatrix4Multiply( projectionMatrix, skyboxModelView); 298 | 299 | glUseProgram(_program); // Step 1 300 | glUniformMatrix4fv(_u_mvpMatrix, 1, 0, 301 | modelViewProjectionMatrix.m); 302 | 303 | 304 | glUniform1i(_u_cubemap, 0); 305 | glBindVertexArrayOES(_vertexArrayID); 306 | 307 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferID); 308 | glBindTexture(GL_TEXTURE_CUBE_MAP, 309 | _textureID); 310 | 311 | 312 | glDrawElements(GL_TRIANGLES, 313 | 36, 314 | GL_UNSIGNED_BYTE, 315 | NULL); 316 | 317 | 318 | 319 | 320 | 321 | } 322 | 323 | #pragma mark - OpenGL ES 2 shader compilation 324 | 325 | - (BOOL)loadShaders 326 | { 327 | GLuint vertShader, fragShader; 328 | NSString *vertShaderPathname, *fragShaderPathname; 329 | 330 | // Create shader program. 331 | _program = glCreateProgram(); 332 | 333 | // Create and compile vertex shader. 334 | vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"]; 335 | if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) { 336 | NSLog(@"Failed to compile vertex shader"); 337 | return NO; 338 | } 339 | 340 | // Create and compile fragment shader. 341 | fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"]; 342 | if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) { 343 | NSLog(@"Failed to compile fragment shader"); 344 | return NO; 345 | } 346 | 347 | // Attach vertex shader to program. 348 | glAttachShader(_program, vertShader); 349 | 350 | // Attach fragment shader to program. 351 | glAttachShader(_program, fragShader); 352 | 353 | 354 | 355 | // Link program. 356 | if (![self linkProgram:_program]) { 357 | NSLog(@"Failed to link program: %d", _program); 358 | 359 | if (vertShader) { 360 | glDeleteShader(vertShader); 361 | vertShader = 0; 362 | } 363 | if (fragShader) { 364 | glDeleteShader(fragShader); 365 | fragShader = 0; 366 | } 367 | if (_program) { 368 | glDeleteProgram(_program); 369 | _program = 0; 370 | } 371 | 372 | return NO; 373 | } 374 | 375 | // Get uniform locations. 376 | _u_mvpMatrix = glGetUniformLocation(_program, "u_mvpMatrix"); 377 | _u_cubemap = glGetUniformLocation(_program,"u_cubemap"); 378 | _a_position = glGetAttribLocation(_program,"a_position"); 379 | 380 | // Release vertex and fragment shaders. 381 | if (vertShader) { 382 | glDetachShader(_program, vertShader); 383 | glDeleteShader(vertShader); 384 | } 385 | if (fragShader) { 386 | glDetachShader(_program, fragShader); 387 | glDeleteShader(fragShader); 388 | } 389 | 390 | return YES; 391 | } 392 | 393 | - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file 394 | { 395 | GLint status; 396 | const GLchar *source; 397 | 398 | source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String]; 399 | if (!source) { 400 | NSLog(@"Failed to load vertex shader"); 401 | return NO; 402 | } 403 | 404 | *shader = glCreateShader(type); 405 | glShaderSource(*shader, 1, &source, NULL); 406 | glCompileShader(*shader); 407 | 408 | #if defined(DEBUG) 409 | GLint logLength; 410 | glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); 411 | if (logLength > 0) { 412 | GLchar *log = (GLchar *)malloc(logLength); 413 | glGetShaderInfoLog(*shader, logLength, &logLength, log); 414 | NSLog(@"Shader compile log:\n%s", log); 415 | free(log); 416 | } 417 | #endif 418 | 419 | glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); 420 | if (status == 0) { 421 | glDeleteShader(*shader); 422 | return NO; 423 | } 424 | 425 | return YES; 426 | } 427 | 428 | - (BOOL)linkProgram:(GLuint)prog 429 | { 430 | GLint status; 431 | glLinkProgram(prog); 432 | 433 | #if defined(DEBUG) 434 | GLint logLength; 435 | glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); 436 | if (logLength > 0) { 437 | GLchar *log = (GLchar *)malloc(logLength); 438 | glGetProgramInfoLog(prog, logLength, &logLength, log); 439 | NSLog(@"Program link log:\n%s", log); 440 | free(log); 441 | } 442 | #endif 443 | 444 | glGetProgramiv(prog, GL_LINK_STATUS, &status); 445 | if (status == 0) { 446 | return NO; 447 | } 448 | 449 | return YES; 450 | } 451 | 452 | - (BOOL)validateProgram:(GLuint)prog 453 | { 454 | GLint logLength, status; 455 | 456 | glValidateProgram(prog); 457 | glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); 458 | if (logLength > 0) { 459 | GLchar *log = (GLchar *)malloc(logLength); 460 | glGetProgramInfoLog(prog, logLength, &logLength, log); 461 | NSLog(@"Program validate log:\n%s", log); 462 | free(log); 463 | } 464 | 465 | glGetProgramiv(prog, GL_VALIDATE_STATUS, &status); 466 | if (status == 0) { 467 | return NO; 468 | } 469 | 470 | return YES; 471 | } 472 | 473 | 474 | @end 475 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/GameViewControllerForCardBoard.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewControllerForCardBoard.h 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/26/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | #import "CBDViewController.h" 10 | 11 | @interface GameViewControllerForCardBoard : CBDViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/GameViewControllerForCardBoard.mm: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.m 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | #import "GameViewControllerForCardBoard.h" 10 | #import 11 | #import "GLHelpers.h" 12 | 13 | @interface GameViewControllerForCardBoard () { 14 | GLuint _program; 15 | 16 | GLuint _vertexBufferID; 17 | GLuint _indexBufferID; 18 | GLuint _vertexArrayID; 19 | 20 | 21 | 22 | GLint _u_mvpMatrix; 23 | GLint _u_cubemap; 24 | GLuint _textureID; 25 | GLint _a_position; 26 | 27 | 28 | CGFloat _rotateX; 29 | CGFloat _rotateY; 30 | 31 | } 32 | 33 | @property(nonatomic,strong) CMAttitude *lastAttitude; 34 | 35 | @end 36 | 37 | @implementation GameViewControllerForCardBoard 38 | 39 | -(instancetype)initWithCoder:(NSCoder *)aDecoder{ 40 | self = [super initWithCoder:aDecoder]; 41 | if (!self) {return nil; } 42 | 43 | self.stereoRendererDelegate = self; 44 | 45 | return self; 46 | } 47 | 48 | 49 | - (IBAction)onClose:(id)sender { 50 | [self dismissViewControllerAnimated:YES completion:nil]; 51 | } 52 | 53 | #pragma mark - CBDStereoRendererDelegate delegate methods 54 | - (void)setupRendererWithView:(GLKView *)glView{ 55 | [EAGLContext setCurrentContext:glView.context]; 56 | 57 | [self loadShaders]; 58 | 59 | 60 | 61 | // The 8 corners of a cube 62 | const float vertices[24] = { 63 | -0.5, -0.5, 0.5, 64 | 0.5, -0.5, 0.5, 65 | -0.5, 0.5, 0.5, 66 | 0.5, 0.5, 0.5, 67 | -0.5, -0.5, -0.5, 68 | 0.5, -0.5, -0.5, 69 | -0.5, 0.5, -0.5, 70 | 0.5, 0.5, -0.5, 71 | }; 72 | 73 | glGenBuffers(1, &_vertexBufferID); 74 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID); 75 | glBufferData(GL_ARRAY_BUFFER, 76 | sizeof(vertices), 77 | vertices, 78 | GL_STATIC_DRAW); 79 | 80 | 81 | const GLubyte indices[36] = { 82 | 0,2,1, //front 83 | 2,3,1, 84 | 85 | 1,3,7,//right 86 | 1,7,5, 87 | 88 | 0,6,2,//left 89 | 0,4,6, 90 | 91 | 0,1,4,//bottom 92 | 1,5,4, 93 | 94 | 4,5,6,//back 95 | 5,7,6, 96 | 97 | 3,6,7,//top 98 | 3,2,6 99 | }; 100 | glGenBuffers(1, &_indexBufferID); 101 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferID); 102 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, 103 | sizeof(indices), 104 | indices, 105 | GL_STATIC_DRAW); 106 | 107 | 108 | glGenVertexArraysOES(1, &_vertexArrayID); 109 | glBindVertexArrayOES(_vertexArrayID); 110 | 111 | glEnableVertexAttribArray(_a_position); 112 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID); 113 | glVertexAttribPointer(_a_position, 114 | 3, 115 | GL_FLOAT, 116 | GL_FALSE, 117 | 0, 118 | NULL); 119 | glBindVertexArrayOES(0); 120 | 121 | 122 | //texture 123 | 124 | NSMutableArray *pics = [NSMutableArray array]; 125 | // faces.push_back("right.jpg"); 126 | // faces.push_back("left.jpg"); 127 | // faces.push_back("top.jpg"); 128 | // faces.push_back("bottom.jpg"); 129 | // faces.push_back("back.jpg"); 130 | // faces.push_back("front.jpg"); 131 | 132 | NSArray *names = @[@"right",@"left",@"top",@"bottom",@"back",@"front"]; 133 | 134 | for (NSString *n in names) { 135 | NSString *path = [[NSBundle mainBundle] 136 | pathForResource:n ofType:@"jpeg"]; 137 | [pics addObject:path]; 138 | } 139 | 140 | 141 | NSError *error = nil; 142 | GLKTextureInfo * textInfo = [GLKTextureLoader 143 | cubeMapWithContentsOfFiles:pics 144 | options:nil 145 | error:&error]; 146 | 147 | _textureID = textInfo.name; 148 | 149 | 150 | glEnable(GL_DEPTH_TEST); 151 | glClearColor(0.2f, 0.2f, 0.2f, 0.5f); // Dark background so text shows up well. 152 | GLCheckForError(); 153 | 154 | } 155 | - (void)shutdownRendererWithView:(GLKView *)glView{ 156 | [EAGLContext setCurrentContext:glView.context]; 157 | 158 | glDeleteBuffers(1, &_vertexBufferID); 159 | glDeleteVertexArraysOES(1, &_vertexArrayID); 160 | glDeleteTextures(1, &_textureID); 161 | 162 | if (_program) { 163 | glDeleteProgram(_program); 164 | _program = 0; 165 | } 166 | 167 | } 168 | - (void)renderViewDidChangeSize:(CGSize)size{ 169 | 170 | } 171 | 172 | - (void)prepareNewFrameWithHeadViewMatrix:(GLKMatrix4)headViewMatrix{ 173 | 174 | if (self.lastAttitude != nil) { 175 | 176 | float roll = self.attitude.roll ; 177 | //float pitch = self.attitude.pitch; 178 | float yaw = self.attitude.yaw; 179 | 180 | //NSLog(@"%f,%f,%f",GLKMathRadiansToDegrees(roll), GLKMathRadiansToDegrees(pitch),GLKMathRadiansToDegrees(yaw)); 181 | 182 | 183 | _rotateX -= roll - self.lastAttitude.roll; 184 | _rotateY -= yaw - self.lastAttitude.yaw; 185 | 186 | 187 | } 188 | 189 | self.lastAttitude = self.attitude; 190 | 191 | } 192 | 193 | - (void)drawEyeWithEye:(CBDEye *)eye{ 194 | [EAGLContext setCurrentContext:self.view.context]; 195 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 196 | 197 | 198 | 199 | const float zNear = 0.1f; 200 | const float zFar = 100.0f; 201 | 202 | GLKMatrix4 _camera = GLKMatrix4MakeLookAt(0, 0, 0.01, 203 | 0, 0, 0, 204 | 0, 1.0f, 0); 205 | 206 | GLKMatrix4 eyeView = GLKMatrix4Multiply([eye eyeViewMatrix], _camera); 207 | 208 | GLKMatrix4 eyePerspective = [eye perspectiveMatrixWithZNear:zNear zFar:zFar]; 209 | 210 | 211 | GLKMatrix4 modelViewMatrix = GLKMatrix4MakeRotation(_rotateX, 1.0f, 0.0f, 0.0f); 212 | modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix,_rotateY, 0.0f, 1.0f, 0.0f); 213 | 214 | 215 | GLKMatrix4 modelViewProjectionMatrix =GLKMatrix4Multiply(eyePerspective, GLKMatrix4Multiply( eyeView, modelViewMatrix)); 216 | 217 | 218 | glUseProgram(_program); // Step 1 219 | glUniformMatrix4fv(_u_mvpMatrix, 1, 0, 220 | modelViewProjectionMatrix.m); 221 | 222 | 223 | glUniform1i(_u_cubemap, 0); 224 | glBindVertexArrayOES(_vertexArrayID); 225 | 226 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferID); 227 | glBindTexture(GL_TEXTURE_CUBE_MAP, 228 | _textureID); 229 | 230 | 231 | glDrawElements(GL_TRIANGLES, 232 | 36, 233 | GL_UNSIGNED_BYTE, 234 | NULL); 235 | GLCheckForError(); 236 | glBindVertexArrayOES(0); 237 | 238 | 239 | } 240 | - (void)finishFrameWithViewportRect:(CGRect)viewPort{ 241 | 242 | } 243 | 244 | 245 | 246 | 247 | 248 | #pragma mark - OpenGL ES 2 shader compilation 249 | 250 | - (BOOL)loadShaders 251 | { 252 | GLuint vertShader, fragShader; 253 | NSString *vertShaderPathname, *fragShaderPathname; 254 | 255 | // Create shader program. 256 | _program = glCreateProgram(); 257 | 258 | // Create and compile vertex shader. 259 | vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"]; 260 | if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) { 261 | NSLog(@"Failed to compile vertex shader"); 262 | return NO; 263 | } 264 | 265 | // Create and compile fragment shader. 266 | fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"]; 267 | if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) { 268 | NSLog(@"Failed to compile fragment shader"); 269 | return NO; 270 | } 271 | 272 | // Attach vertex shader to program. 273 | glAttachShader(_program, vertShader); 274 | 275 | // Attach fragment shader to program. 276 | glAttachShader(_program, fragShader); 277 | 278 | 279 | 280 | // Link program. 281 | if (![self linkProgram:_program]) { 282 | NSLog(@"Failed to link program: %d", _program); 283 | 284 | if (vertShader) { 285 | glDeleteShader(vertShader); 286 | vertShader = 0; 287 | } 288 | if (fragShader) { 289 | glDeleteShader(fragShader); 290 | fragShader = 0; 291 | } 292 | if (_program) { 293 | glDeleteProgram(_program); 294 | _program = 0; 295 | } 296 | 297 | return NO; 298 | } 299 | 300 | // Get uniform locations. 301 | _u_mvpMatrix = glGetUniformLocation(_program, "u_mvpMatrix"); 302 | _u_cubemap = glGetUniformLocation(_program,"u_cubemap"); 303 | _a_position = glGetAttribLocation(_program,"a_position"); 304 | 305 | // Release vertex and fragment shaders. 306 | if (vertShader) { 307 | glDetachShader(_program, vertShader); 308 | glDeleteShader(vertShader); 309 | } 310 | if (fragShader) { 311 | glDetachShader(_program, fragShader); 312 | glDeleteShader(fragShader); 313 | } 314 | 315 | return YES; 316 | } 317 | 318 | - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file 319 | { 320 | GLint status; 321 | const GLchar *source; 322 | 323 | source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String]; 324 | if (!source) { 325 | NSLog(@"Failed to load vertex shader"); 326 | return NO; 327 | } 328 | 329 | *shader = glCreateShader(type); 330 | glShaderSource(*shader, 1, &source, NULL); 331 | glCompileShader(*shader); 332 | 333 | #if defined(DEBUG) 334 | GLint logLength; 335 | glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); 336 | if (logLength > 0) { 337 | GLchar *log = (GLchar *)malloc(logLength); 338 | glGetShaderInfoLog(*shader, logLength, &logLength, log); 339 | NSLog(@"Shader compile log:\n%s", log); 340 | free(log); 341 | } 342 | #endif 343 | 344 | glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); 345 | if (status == 0) { 346 | glDeleteShader(*shader); 347 | return NO; 348 | } 349 | 350 | return YES; 351 | } 352 | 353 | - (BOOL)linkProgram:(GLuint)prog 354 | { 355 | GLint status; 356 | glLinkProgram(prog); 357 | 358 | #if defined(DEBUG) 359 | GLint logLength; 360 | glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); 361 | if (logLength > 0) { 362 | GLchar *log = (GLchar *)malloc(logLength); 363 | glGetProgramInfoLog(prog, logLength, &logLength, log); 364 | NSLog(@"Program link log:\n%s", log); 365 | free(log); 366 | } 367 | #endif 368 | 369 | glGetProgramiv(prog, GL_LINK_STATUS, &status); 370 | if (status == 0) { 371 | return NO; 372 | } 373 | 374 | return YES; 375 | } 376 | 377 | - (BOOL)validateProgram:(GLuint)prog 378 | { 379 | GLint logLength, status; 380 | 381 | glValidateProgram(prog); 382 | glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); 383 | if (logLength > 0) { 384 | GLchar *log = (GLchar *)malloc(logLength); 385 | glGetProgramInfoLog(prog, logLength, &logLength, log); 386 | NSLog(@"Program validate log:\n%s", log); 387 | free(log); 388 | } 389 | 390 | glGetProgramiv(prog, GL_VALIDATE_STATUS, &status); 391 | if (status == 0) { 392 | return NO; 393 | } 394 | 395 | return YES; 396 | } 397 | 398 | 399 | @end 400 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /SFOpenGLESPanorama/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.swordfishsoft.ios.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarHidden 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationLandscapeLeft 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/Shaders/Shader.fsh: -------------------------------------------------------------------------------- 1 | // 2 | // Shader.fsh 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | // 10 | // SkyboxShader.fsh 11 | // 12 | // 13 | 14 | 15 | 16 | uniform samplerCube u_cubemap; 17 | 18 | ///////////////////////////////////////////////////////////////// 19 | // Varyings 20 | ///////////////////////////////////////////////////////////////// 21 | varying lowp vec3 textureDir; 22 | 23 | 24 | void main() 25 | { 26 | lowp vec3 newVar = textureDir; 27 | gl_FragColor = textureCube(u_cubemap,textureDir); 28 | } 29 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/Shaders/Shader.vsh: -------------------------------------------------------------------------------- 1 | // 2 | // Shader.vsh 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | 10 | ///////////////////////////////////////////////////////////////// 11 | // VERTEX ATTRIBUTES 12 | ///////////////////////////////////////////////////////////////// 13 | attribute vec3 a_position; 14 | 15 | ///////////////////////////////////////////////////////////////// 16 | // UNIFORMS 17 | ///////////////////////////////////////////////////////////////// 18 | uniform highp mat4 u_mvpMatrix; 19 | 20 | 21 | ///////////////////////////////////////////////////////////////// 22 | // Varyings 23 | ///////////////////////////////////////////////////////////////// 24 | varying lowp vec3 textureDir; 25 | 26 | 27 | void main() 28 | { 29 | 30 | gl_Position = u_mvpMatrix * vec4(a_position, 1.0); 31 | 32 | textureDir = a_position; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/back.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/SFOpenGLESPanorama/back.jpeg -------------------------------------------------------------------------------- /SFOpenGLESPanorama/bottom.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/SFOpenGLESPanorama/bottom.jpeg -------------------------------------------------------------------------------- /SFOpenGLESPanorama/front.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/SFOpenGLESPanorama/front.jpeg -------------------------------------------------------------------------------- /SFOpenGLESPanorama/left.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/SFOpenGLESPanorama/left.jpeg -------------------------------------------------------------------------------- /SFOpenGLESPanorama/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SFOpenGLESPanorama 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SFOpenGLESPanorama/right.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/SFOpenGLESPanorama/right.jpeg -------------------------------------------------------------------------------- /SFOpenGLESPanorama/top.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/SFOpenGLESPanorama/top.jpeg -------------------------------------------------------------------------------- /SFOpenGLESPanoramaTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.swordfishsoft.ios.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SFOpenGLESPanoramaTests/SFOpenGLESPanoramaTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SFOpenGLESPanoramaTests.m 3 | // SFOpenGLESPanoramaTests 4 | // 5 | // Created by Jagie on 8/23/15. 6 | // Copyright (c) 2015 Jagie. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SFOpenGLESPanoramaTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation SFOpenGLESPanoramaTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /s1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/s1.png -------------------------------------------------------------------------------- /s2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heilum/SFOpenGLESPanorama/c846c633754b3d60b3c1173f901d1d0ed27a9e25/s2.png --------------------------------------------------------------------------------