kinect-viewer / KVRenderer.m

/*
 * 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 "KVRenderer.h"
#import "utility.h"

const NSString * const KVSurfaceMode = @"KVSurfaceMode";
const NSString * const KVPlainSurfaceMode = @"KVPlainSurfaceMode";
const NSString * const KVTexturedSurfaceMode = @"KVTexturedSurfaceMode";

@interface KVRenderer ()
- (void) buildPointField;
- (GLuint) loadShaderOfType: (GLenum) type fromFiles: (NSArray *) names;

@property(assign, nonatomic) double rayCastingTotal;
@property(assign, nonatomic) NSUInteger rayCastingCount;

@property(assign, nonatomic) double vertexPlacementTotal;
@property(assign, nonatomic) NSUInteger vertexPlacementCount;

@property GLuint depthTexture;
@property GLuint colorTexture;
@property GLuint program;
@property GLuint pointFieldVbo;
@end

@implementation KVRenderer

@synthesize rayCastingCount, rayCastingTotal, vertexPlacementCount, vertexPlacementTotal;
@synthesize depthTexture, colorTexture;
@synthesize program;
@synthesize pointFieldVbo;

- init {
  if ((self = [super init])) {
    [self buildPointField];
  }
  return self;
}

#pragma mark --- Implementation of KVSpaceModel

- (void) initGL {
  GLint vertexShader = [self loadShaderOfType: GL_VERTEX_SHADER
                                    fromFiles: [NSArray arrayWithObject: @"main"]];
  
  GLint fragmentShader = [self loadShaderOfType: GL_FRAGMENT_SHADER
                                      fromFiles: [NSArray arrayWithObjects: @"lighting", @"main", nil]];
  
  program = glCreateProgram();
  glAttachShader(program, vertexShader);
  glAttachShader(program, fragmentShader);
  glLinkProgram(program);
  glUseProgram(program);
  
  GLint depthUniform = glGetUniformLocation(program, "depthTex");
  glUniform1i(depthUniform, 0);
  
  GLint colorUniform = glGetUniformLocation(program, "colorTex");
  glUniform1i(colorUniform, 1);  
  
  glGenTextures(1, &depthTexture);
  glBindTexture(GL_TEXTURE_2D, depthTexture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

  glGenTextures(1, &colorTexture);
  glBindTexture(GL_TEXTURE_2D, colorTexture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  
  glEnable(GL_POINT_SPRITE);
  glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
  
  glGenBuffers(1, &pointFieldVbo);
  glBindBuffer(GL_ARRAY_BUFFER, pointFieldVbo);
  glBufferData(GL_ARRAY_BUFFER, 640 * 480 * 2 * sizeof(GLfloat), points, GL_STATIC_DRAW);
  
  glEnableClientState(GL_VERTEX_ARRAY);
  glVertexPointer(2, GL_FLOAT, 0, 0);
}

- (void) setDepthSamples: (NSData *)data {
  NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
  
  const uint16_t *sample = [data bytes];
  memcpy(&depthImage, sample, sizeof depthImage);
  
  NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
  rayCastingCount++;
  rayCastingTotal += end - start;
  
  if ((rayCastingCount & 0xF) == 0) {
    NSLog(@"Raycasting: latest %.03fµs mean %.03fµs",
          (end - start) * 1000000.,
          (rayCastingTotal / rayCastingCount) * 1000000.);
  }
}

- (void) setColorSamples: (NSData *)data {
  memcpy(&colorImage, [data bytes], sizeof colorImage);
}

- (void) drawInCurrentOpenGLContextWithOptions: (NSDictionary *) options {
  BOOL textureFromCamera = [[options objectForKey: KVSurfaceMode] boolValue];
  
  NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];

  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, depthTexture);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE16, 640, 480, 0, GL_LUMINANCE, GL_UNSIGNED_SHORT, &depthImage);

  // TODO set texturing control uniform bool
  if (textureFromCamera) {
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, colorTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, 640, 480, 0, GL_RGB, GL_UNSIGNED_BYTE, &colorImage);
  }
  
  glColor3f(1, 1, 1);
  glDrawArrays(GL_POINTS, 0, 640 * 480);
  
  NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
  vertexPlacementCount++;
  vertexPlacementTotal += end - start;
  
  if ((vertexPlacementCount & 0xF) == 0) {
    NSLog(@"Vertex placement: latest %.03fµs mean %.03fµs",
          (end - start) * 1000000.,
          (vertexPlacementTotal / vertexPlacementCount) * 1000000.);
  }
  
}

#pragma mark --- Internals

// We pre-fill the X and Y components with distance from the top-left
// corner of the image.  These values never make it to the rasterizer:
// they are merely input to the vertex shader that performs perspective
// correction.
- (void) buildPointField {
  for (int y = 0; y < 480; y++) {
    for (int x = 0; x < 640; x++) {
      points[y][x].x = (x / 640.f);
      points[y][x].y = (y / 480.f);
    }
  }
}

- (GLuint) loadShaderOfType: (GLenum) type fromFiles: (NSArray *) names {
  NSString *extension;
  if (type == GL_VERTEX_SHADER) {
    extension = @"vs";
  } else if (type == GL_FRAGMENT_SHADER) {
    extension = @"fs";
  } else if (type == GL_GEOMETRY_SHADER_EXT) {
    extension = @"gs";
  } else {
    @throw [NSException exceptionWithName: @"KVContractError" reason: @"Bad value for shader type" userInfo: nil];
  }
  
  const char *sources[[names count]];
  GLint lengths[[names count]];
  int i = 0;
  for (NSString *name in names) {
    NSData *source = [NSData dataWithContentsOfFile: [[NSBundle mainBundle] pathForResource: name ofType: extension]];
    sources[i] = [source bytes];
    lengths[i] = (GLint) [source length];
    i++;
  }
  
  GLuint shader = glCreateShader(type);
  glShaderSource(shader, i, &sources[0], &lengths[0]);
  glCompileShader(shader);
  
  return shader;
}

@end
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.