Source

kinect-viewer / KVDepthView.m

Full commit
Cliff Biffle 38960df 




Cliff Biffle 788984a 

Cliff Biffle 0351b52 
Cliff Biffle 01e6772 
Cliff Biffle 788984a 
Cliff Biffle 425b76a 
Cliff Biffle 788984a 
Cliff Biffle ef6df26 
Cliff Biffle 788984a 

Cliff Biffle 8148390 

Cliff Biffle 82cb6cd 
Cliff Biffle 52b0d57 




Cliff Biffle 788984a 


Cliff Biffle faea47e 

Cliff Biffle 52b0d57 
Cliff Biffle 9dd4e79 

Cliff Biffle 615e63c 
Cliff Biffle 9bd3aec 
Cliff Biffle faea47e 
Cliff Biffle c76e5b7 
Cliff Biffle 81340fa 
Cliff Biffle 788984a 
Cliff Biffle 78b0573 
Cliff Biffle 788984a 












Cliff Biffle 78b0573 

Cliff Biffle 9cf6763 
Cliff Biffle c76e5b7 
Cliff Biffle 339dea4 
Cliff Biffle 615e63c 
Cliff Biffle 9cf6763 
Cliff Biffle 788984a 
Cliff Biffle 78b0573 
Cliff Biffle 788984a 
Cliff Biffle 22fef44 
Cliff Biffle 788984a 







Cliff Biffle d1d5126 
Cliff Biffle 8a7ded8 

Cliff Biffle c54dc0d 
Cliff Biffle 2892936 

Cliff Biffle c54dc0d 
Cliff Biffle 780397a 

Cliff Biffle 2892936 

Cliff Biffle 780397a 
Cliff Biffle c54dc0d 
Cliff Biffle 780397a 


Cliff Biffle 2892936 
Cliff Biffle d6c486b 
Cliff Biffle 2892936 
Cliff Biffle 780397a 
Cliff Biffle 788984a 
Cliff Biffle d6c486b 
Cliff Biffle e529285 
Cliff Biffle 788984a 
Cliff Biffle 615e63c 
Cliff Biffle 788984a 

Cliff Biffle 78b0573 



Cliff Biffle 8608896 

Cliff Biffle 78b0573 
















Cliff Biffle 8148390 
Cliff Biffle 78b0573 




Cliff Biffle 8148390 

Cliff Biffle 78b0573 




Cliff Biffle a7ce3c5 

Cliff Biffle 78b0573 

Cliff Biffle a7ce3c5 










Cliff Biffle 8148390 






Cliff Biffle 78b0573 

Cliff Biffle 8148390 
Cliff Biffle 78b0573 




Cliff Biffle 8148390 

Cliff Biffle 78b0573 







Cliff Biffle 82cb6cd 






































Cliff Biffle 6a699fe 
Cliff Biffle 788984a 

Cliff Biffle 82cb6cd 
Cliff Biffle afbe1ef 
Cliff Biffle 788984a 

Cliff Biffle c76e5b7 
Cliff Biffle 8148390 

Cliff Biffle c76e5b7 
Cliff Biffle 8148390 


Cliff Biffle c76e5b7 


Cliff Biffle 9aeccac 
Cliff Biffle 788984a 
Cliff Biffle c76e5b7 



Cliff Biffle 8148390 

Cliff Biffle c76e5b7 
Cliff Biffle 9aeccac 
Cliff Biffle 9bd3aec 
Cliff Biffle c76e5b7 


Cliff Biffle 788984a 
Cliff Biffle 52b0d57 













Cliff Biffle 788984a 

Cliff Biffle 78b0573 

Cliff Biffle 788984a 
























Cliff Biffle 1c419a3 

Cliff Biffle 9cf6763 
Cliff Biffle bce82f8 
Cliff Biffle 9cf6763 

Cliff Biffle 1c419a3 






Cliff Biffle 9cf6763 


Cliff Biffle bce82f8 
Cliff Biffle 9cf6763 
Cliff Biffle 1c419a3 

Cliff Biffle 9cf6763 







Cliff Biffle 8c25cb9 

Cliff Biffle bd8b773 

Cliff Biffle 8c25cb9 


Cliff Biffle 23ce9a9 






Cliff Biffle 44d0f46 
Cliff Biffle 23ce9a9 


Cliff Biffle 44d0f46 








Cliff Biffle 23ce9a9 

Cliff Biffle 339dea4 
Cliff Biffle 23ce9a9 



Cliff Biffle 76d6883 

Cliff Biffle 14c5dbb 



Cliff Biffle 23ce9a9 
Cliff Biffle c76e5b7 




Cliff Biffle 339dea4 
Cliff Biffle 9dd4e79 
Cliff Biffle 339dea4 
Cliff Biffle 9dd4e79 
Cliff Biffle 339dea4 

Cliff Biffle 78b0573 
Cliff Biffle 788984a 
Cliff Biffle 6f88052 
Cliff Biffle 78b0573 
Cliff Biffle 615e63c 
Cliff Biffle 78b0573 
Cliff Biffle 788984a 


Cliff Biffle 6f88052 




Cliff Biffle 78b0573 
Cliff Biffle 615e63c 
Cliff Biffle 78b0573 

Cliff Biffle 788984a 

Cliff Biffle 6f88052 



Cliff Biffle 788984a 
/*
 * Copyright 2010 Cliff L. Biffle.  All Rights Reserved.
 * Use of this source code is governed by the Apache License 2.0,
 * which can be found in the LICENSE file.
 */

#import "KVDepthView.h"
#import "KVRenderer.h"
#import "KVDepthRecorder.h"
#import "trackball.h"
#import "utility.h"

@interface KVDepthView ()
- (void) resetCamera;
- (void) resizeGL;
- (void) updateProjectionWithOffset: (float) offset;
- (void) updateModelViewWithOffset: (float) offset;
- (void) updateTextureMatrix;

@property(assign, nonatomic) BOOL updateQueued;
- (void) requestUpdateFromBackgroundThread;
- (void) markAsDirty;

@end

@implementation KVDepthView
#pragma mark --- Properties ---

@synthesize updateQueued;

@synthesize textured;
@synthesize renderer;

@synthesize frozen;
@synthesize anaglyph;
@synthesize recording;

#pragma mark --- Trivial NSView Overrides ---

- (BOOL) acceptsFirstResponder {
  return YES;
}

- (BOOL) becomeFirstResponder {
  return YES;
}

- (BOOL) resignFirstResponder {
  return YES;
}

#pragma mark --- Initialization ---

- (void) awakeFromNib {
  deviceWhite = [[NSColor colorWithDeviceRed: 1.F green: 1.F blue: 1.F alpha: 1.F] retain];
  
  self.renderer = [[[KVRenderer alloc] init] autorelease];
}

#pragma mark --- GL Stuff ---

- (void) prepareOpenGL {
  // Sync to vertical blank.
  GLint swapInterval = 1;
  [[self openGLContext] setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
  
  // GL state initialization
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glFrontFace(GL_CCW);
  glShadeModel(GL_SMOOTH);
  glEnable(GL_POINT_SMOOTH);

  GLfloat diffuseColor[4] = { 1.f, 1.f, 1.f, 1.0f };
  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseColor);

  GLfloat ambientColor[4] = { 0.2f, 0.2f, 0.2f, 1 };
  glLightfv(GL_LIGHT0, GL_AMBIENT, ambientColor);
  
  GLfloat specularColor[4] = { 1, 1, 1, 1 };
  glLightfv(GL_LIGHT0, GL_SPECULAR, specularColor);
  
  GLfloat globalAmbient[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, globalAmbient);
  
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  
  GLfloat specular[4] = { 0.0f, 0.0f, 0.0f, 1 };
  glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
  glEnable(GL_COLOR_MATERIAL);
  
  glClearColor(0.3f, 0, 0.5f, 0);

  [self resetCamera];
  [renderer initGL];
}

- (void) resetCamera {
  camera = (camera_t) {
    .fov = 57,
    .pivot = { 0, 0, 0 },
    .pos = { 0, 0, 0 },
    .target = { 0, 0, 3.f },
    .up = { 0, 1, 0 },
  };
}

- (void) resizeGL {
  NSRect bounds = [self bounds];
  
  // Note: this always happens on the first drawing call.
  if (camera.viewHeight != bounds.size.height
      || camera.viewWidth != bounds.size.width) {
    camera.viewHeight = (float) bounds.size.height;
    camera.viewWidth = (float) bounds.size.width;
    
    glViewport(0, 0, camera.viewWidth, camera.viewHeight);
  }
}

- (void) updateProjectionWithOffset: (float) offset {
  [[self openGLContext] makeCurrentContext];
  
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  
  GLdouble near = -camera.pos.z + 0.5;  
  GLdouble far = -camera.pos.z + 8.0;
  
  // I do not fully understand this.
  if (near < 0.0001) near = 0.0001;
  if (far < 1.0) far = 1.0;
  
  GLdouble fovRadians = camera.fov / 180. * M_PI;
  GLdouble fovYRadians = fovRadians / 4. * 3.;
  GLdouble aspectRatio = camera.viewWidth / (GLdouble) camera.viewHeight;
  
  GLdouble left, top;
  if (aspectRatio > (4./3.)) {  // landscape
    GLdouble halfHeight = near * tan(fovYRadians / 2);
    left = -aspectRatio * halfHeight;
    top = halfHeight;
  } else {  // portrait
    GLdouble halfWidth = near * tan(fovRadians / 2);
    left = -halfWidth;
    top = halfWidth / aspectRatio;
  }
  
  double zeroParallax = 1;
  glFrustum(left - offset * near / zeroParallax,
            -left - offset * near / zeroParallax,
            -top,
            top,
            near,
            far);
}

- (void) updateModelViewWithOffset: (float) offset {
  [[self openGLContext] makeCurrentContext];
  
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  gluLookAt(camera.pos.x - offset, camera.pos.y, camera.pos.z,
            camera.target.x - offset, camera.target.y, camera.target.z,
            camera.up.x, camera.up.y, camera.up.z);
  
  glRotatef(trackballRotation[0], trackballRotation[1], trackballRotation[2], trackballRotation[3]);
  glRotatef(worldRotation[0], worldRotation[1], worldRotation[2], worldRotation[3]);
  glRotatef(spinY, 0, 1, 0);
  glRotatef(spinX, 1, 0, 0);
}

- (void) updateTextureMatrix {
  [[self openGLContext] makeCurrentContext];
  
  glMatrixMode(GL_TEXTURE);
  
  // Since we pass the depth field to the card in Texture 0,
  // why not pass the depth inverse-projection matrix in
  // the corresponding matrix?
  glActiveTexture(GL_TEXTURE0);
  
  GLdouble fovDegrees = 57.;
  GLdouble halfFovRadians = fovDegrees / 2. / 180. * M_PI;
  // The width of the projected depth field at Z=1
  GLdouble widthAt1 = tan(halfFovRadians);
  // Ratio of screen height : width.
  GLdouble aspectRatio = 3. / 4.;
  
  // Linearization constants per CCNY ROS folks:
  //  Z = ca / (depth + cb)
  GLdouble ca = -325.616, cb = -1084.61;

  // This matrix is the product of the following pipeline:
  // - Convert texel-space (aka clip-space, 0 ≤ x,y ≤ 1)
  //   into perspective-space (-1 ≤ x,y ≤ 1).
  // - Convert camera's depth samples (converted to floating
  //   point by OpenGL) into 1/Z.
  // - Convert perspective-space into view-space by
  //   multiplying xy by tan(fov/2).
  // - Correct aspect ratio in Y.
  // - Exchange Z and W to correct inversion of Z.
  GLdouble matrix[16] = {
    -2 * widthAt1, 0, 0, 0,
    0, -2 * widthAt1 * aspectRatio, 0, 0,
    0, 0, 0, 65535. / ca,
    widthAt1, widthAt1 * aspectRatio, 1, cb / ca,
  };
  glLoadMatrixd(matrix);
}

#define INTEROCULAR_DISTANCE (0.0635F/2)
- (void) drawRect: (NSRect)dirtyRect {
  [self resizeGL];
  [self updateTextureMatrix];
  
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  if (self.anaglyph) {
    [self updateProjectionWithOffset: INTEROCULAR_DISTANCE / 2];
    [self updateModelViewWithOffset: INTEROCULAR_DISTANCE / 2];
    glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
  } else {
    [self updateProjectionWithOffset: 0];
    [self updateModelViewWithOffset: 0];
  }
  
  // Draw right eye or center.
  [renderer drawInCurrentOpenGLContextWithOptions: nil];
  
  if (self.anaglyph) {
    // Draw left eye.
    glClear(GL_DEPTH_BUFFER_BIT);
    
    [self updateProjectionWithOffset: -INTEROCULAR_DISTANCE / 2];
    [self updateModelViewWithOffset: -INTEROCULAR_DISTANCE / 2];
    glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
    [renderer drawInCurrentOpenGLContextWithOptions: nil];
    
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  }
  
  [[self openGLContext] flushBuffer];
  self.updateQueued = NO;
}

- (void) requestUpdateFromBackgroundThread {
  @synchronized (self) {
    if (!self.updateQueued) {
      self.updateQueued = YES;
      [self performSelectorOnMainThread: @selector(markAsDirty) withObject: nil waitUntilDone: NO];
    }
  }
}

- (void) markAsDirty {
  [self setNeedsDisplay: YES];
}

#pragma mark --- User Interaction ---

- (void) mouseDown: (NSEvent *)theEvent {
  NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil];
  location.y = camera.viewHeight - location.y;  // Flip Y axis
  usingTrackball = YES;
  startTrackball(location.x, location.y, 0, 0, camera.viewWidth, camera.viewHeight);
}

- (void) mouseUp: (NSEvent *)theEvent {
  if (usingTrackball) {
    usingTrackball = NO;
    addToRotationTrackball(trackballRotation, worldRotation);
    for (int i = 0; i < 4; i++) trackballRotation[i] = 0;
  }
}

- (void) mouseDragged: (NSEvent *)theEvent {
  NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil];
  location.y = camera.viewHeight - location.y;  // Flip Y axis

  if (usingTrackball) {
    rollToTrackball(location.x, location.y, trackballRotation);
    [self setNeedsDisplay: YES];
  }
}

#define SCROLL_SPEED (50.F)

- (void) scrollWheel: (NSEvent *)theEvent {
  float deltaV = (float) [theEvent deltaY];
  BOOL changes = NO;
  if (deltaV) {
    if ([theEvent modifierFlags] & NSCommandKeyMask) {
      camera.pos.z -= deltaV / SCROLL_SPEED;
      camera.target.z -= deltaV / SCROLL_SPEED;
    } else {
      camera.pos.y -= deltaV / SCROLL_SPEED;
      camera.target.y -= deltaV / SCROLL_SPEED;
    }
    changes = YES;
  }
  
  float deltaH = (float) [theEvent deltaX];
  if (deltaH) {
    camera.pos.x -= deltaH / SCROLL_SPEED;
    camera.target.x -= deltaH / SCROLL_SPEED;
    changes = YES;
  }
  
  if (changes) {
    [self setNeedsDisplay: YES];
  }
}

- (void) magnifyWithEvent: (NSEvent *)theEvent {
  float mag = (float) [theEvent magnification];
  camera.fov -= mag * 10.f;
  if (camera.fov < 5.f) camera.fov = 5.f;
  [self setNeedsDisplay: YES];
}

-(void)keyDown: (NSEvent *)theEvent
{
  NSString *characters = [theEvent characters];
  if ([characters length]) {
    unichar character = [characters characterAtIndex:0];
		switch (character) {
			case 'a':
				spinY += 10;
				[self setNeedsDisplay: YES];
				break;
			case 'd':
				spinY -= 10;
				[self setNeedsDisplay: YES];
				break;
			case 'w':
				spinX += 10;
				[self setNeedsDisplay: YES];
				break;
			case 's':
				spinX -= 10;
				[self setNeedsDisplay: YES];
				break;
        
		}
	}
}

#pragma mark --- Actions ---

- (IBAction) toggleDepthFieldUpdates: sender {
  self.frozen = !self.frozen;
  [sender setState: self.frozen? NSOnState : NSOffState];
}

- (IBAction) toggleAnaglyph: sender {
  self.anaglyph = !self.anaglyph;
  [self setNeedsDisplay: YES];
}

- (IBAction) toggleTextures: sender {
  self.textured = !textured;
  [self setNeedsDisplay: YES];
  [sender setState: textured? NSOnState : NSOffState];
}

#pragma mark --- KFKinect Delegate Implementation ---

- (void) setLatestDepthSamples: (NSData *)data {
  if (!self.frozen) {
    [renderer setDepthSamples: data];
    [self setNeedsDisplay: YES];
  }
}

- (NSData *)latestDepthSamples {
  return nil;
}

- (void) setLatestColorSamples: (NSData *)data {
  if (!self.frozen) {
    [renderer setColorSamples: data];
    [self setNeedsDisplay: YES];
  }
}

- (NSData *)latestColorSamples {
  return nil;
}

@end