Commits

Cliff Biffle committed 52b0d57

Completely refactored the model/renderer relationship in preparation for voxel rendering and SLAM stuff. There are no longer DepthMaps or Renderers, because the rendering method needs intimate awareness of the depth model. Instead, a new KVSpaceModel type combines both.

We lose some features with this commit. Some, like quad rendering, are unlikely to return. Others I plan to reimplement immediately: foreground isolation, camera texture mapping. Finally, background data averaging needs more thought, and will probably be replaced by a more flexible sensor noise model.

Comments (0)

Files changed (25)

English.lproj/MainMenu.xib

 		</object>
 		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
 			<bool key="EncodedWithXMLCoder">YES</bool>
+			<integer value="533"/>
 			<integer value="296"/>
-			<integer value="533"/>
 		</object>
 		<object class="NSArray" key="IBDocument.PluginDependencies">
 			<bool key="EncodedWithXMLCoder">YES</bool>
 									<reference key="NSOnImage" ref="35465992"/>
 									<reference key="NSMixedImage" ref="502551668"/>
 								</object>
-								<object class="NSMenuItem" id="858242009">
-									<reference key="NSMenu" ref="466310130"/>
-									<string key="NSTitle">Overlay Wireframe</string>
-									<string key="NSKeyEquiv">f</string>
-									<int key="NSKeyEquivModMask">1048576</int>
-									<int key="NSMnemonicLoc">2147483647</int>
-									<reference key="NSOnImage" ref="35465992"/>
-									<reference key="NSMixedImage" ref="502551668"/>
-								</object>
-								<object class="NSMenuItem" id="851891320">
-									<reference key="NSMenu" ref="466310130"/>
-									<bool key="NSIsDisabled">YES</bool>
-									<bool key="NSIsSeparator">YES</bool>
-									<string key="NSTitle"/>
-									<string key="NSKeyEquiv"/>
-									<int key="NSMnemonicLoc">2147483647</int>
-									<reference key="NSOnImage" ref="35465992"/>
-									<reference key="NSMixedImage" ref="502551668"/>
-								</object>
-								<object class="NSMenuItem" id="138643579">
-									<reference key="NSMenu" ref="466310130"/>
-									<string key="NSTitle">No Depth History</string>
-									<string key="NSKeyEquiv">1</string>
-									<int key="NSKeyEquivModMask">1572864</int>
-									<int key="NSMnemonicLoc">2147483647</int>
-									<reference key="NSOnImage" ref="35465992"/>
-									<reference key="NSMixedImage" ref="502551668"/>
-								</object>
-								<object class="NSMenuItem" id="63974516">
-									<reference key="NSMenu" ref="466310130"/>
-									<string key="NSTitle">Average Depth Over Time</string>
-									<string key="NSKeyEquiv">2</string>
-									<int key="NSKeyEquivModMask">1572864</int>
-									<int key="NSMnemonicLoc">2147483647</int>
-									<reference key="NSOnImage" ref="35465992"/>
-									<reference key="NSMixedImage" ref="502551668"/>
-								</object>
-								<object class="NSMenuItem" id="1038079737">
-									<reference key="NSMenu" ref="466310130"/>
-									<string key="NSTitle">Occlusion-Aware Depth</string>
-									<string key="NSKeyEquiv">3</string>
-									<int key="NSKeyEquivModMask">1572864</int>
-									<int key="NSMnemonicLoc">2147483647</int>
-									<reference key="NSOnImage" ref="35465992"/>
-									<reference key="NSMixedImage" ref="502551668"/>
-								</object>
 								<object class="NSMenuItem" id="264846174">
 									<reference key="NSMenu" ref="466310130"/>
 									<bool key="NSIsDisabled">YES</bool>
 								</object>
 								<object class="NSMenuItem" id="477715379">
 									<reference key="NSMenu" ref="466310130"/>
-									<string key="NSTitle">Filled Quads</string>
+									<string key="NSTitle">Mesh</string>
 									<string key="NSKeyEquiv">2</string>
 									<int key="NSKeyEquivModMask">1048576</int>
 									<int key="NSMnemonicLoc">2147483647</int>
 								</object>
 								<object class="NSMenuItem" id="578250298">
 									<reference key="NSMenu" ref="466310130"/>
-									<string key="NSTitle">Filled Tris</string>
+									<string key="NSTitle">Filled</string>
 									<string key="NSKeyEquiv">3</string>
 									<int key="NSKeyEquivModMask">1048576</int>
 									<int key="NSMnemonicLoc">2147483647</int>
 				</object>
 				<object class="IBConnectionRecord">
 					<object class="IBActionConnection" key="connection">
-						<string key="label">selectFilledQuadRenderer:</string>
-						<reference key="source" ref="1014"/>
-						<reference key="destination" ref="477715379"/>
-					</object>
-					<int key="connectionID">546</int>
-				</object>
-				<object class="IBConnectionRecord">
-					<object class="IBActionConnection" key="connection">
-						<string key="label">selectFilledTriRenderer:</string>
-						<reference key="source" ref="1014"/>
-						<reference key="destination" ref="578250298"/>
-					</object>
-					<int key="connectionID">548</int>
-				</object>
-				<object class="IBConnectionRecord">
-					<object class="IBActionConnection" key="connection">
 						<string key="label">toggleDepthFieldUpdates:</string>
 						<reference key="source" ref="1014"/>
 						<reference key="destination" ref="406940919"/>
 				</object>
 				<object class="IBConnectionRecord">
 					<object class="IBActionConnection" key="connection">
-						<string key="label">selectStaticDepthMap:</string>
-						<reference key="source" ref="1014"/>
-						<reference key="destination" ref="138643579"/>
-					</object>
-					<int key="connectionID">565</int>
-				</object>
-				<object class="IBConnectionRecord">
-					<object class="IBActionConnection" key="connection">
-						<string key="label">selectAveragingDepthMap:</string>
-						<reference key="source" ref="1014"/>
-						<reference key="destination" ref="63974516"/>
-					</object>
-					<int key="connectionID">566</int>
-				</object>
-				<object class="IBConnectionRecord">
-					<object class="IBActionConnection" key="connection">
 						<string key="label">toggleTextures:</string>
 						<reference key="source" ref="1014"/>
 						<reference key="destination" ref="1032511002"/>
 				</object>
 				<object class="IBConnectionRecord">
 					<object class="IBActionConnection" key="connection">
-						<string key="label">toggleWireframe:</string>
+						<string key="label">selectMeshRenderer:</string>
 						<reference key="source" ref="1014"/>
-						<reference key="destination" ref="858242009"/>
+						<reference key="destination" ref="477715379"/>
 					</object>
-					<int key="connectionID">570</int>
+					<int key="connectionID">573</int>
 				</object>
 				<object class="IBConnectionRecord">
 					<object class="IBActionConnection" key="connection">
-						<string key="label">selectMultiDepthMap:</string>
+						<string key="label">selectSolidRenderer:</string>
 						<reference key="source" ref="1014"/>
-						<reference key="destination" ref="1038079737"/>
+						<reference key="destination" ref="578250298"/>
 					</object>
-					<int key="connectionID">572</int>
+					<int key="connectionID">574</int>
 				</object>
 			</object>
 			<object class="IBMutableOrderedSet" key="objectRecords">
 							<bool key="EncodedWithXMLCoder">YES</bool>
 							<reference ref="102151532"/>
 							<reference ref="237841660"/>
-							<reference ref="851891320"/>
 							<reference ref="719261243"/>
 							<reference ref="477715379"/>
 							<reference ref="578250298"/>
 							<reference ref="433488773"/>
 							<reference ref="26014547"/>
 							<reference ref="264846174"/>
-							<reference ref="138643579"/>
-							<reference ref="63974516"/>
 							<reference ref="1032511002"/>
-							<reference ref="858242009"/>
-							<reference ref="1038079737"/>
 						</object>
 						<reference key="parent" ref="586577488"/>
 					</object>
 						<reference key="parent" ref="439893737"/>
 					</object>
 					<object class="IBObjectRecord">
-						<int key="objectID">535</int>
-						<reference key="object" ref="851891320"/>
-						<reference key="parent" ref="466310130"/>
-					</object>
-					<object class="IBObjectRecord">
 						<int key="objectID">536</int>
 						<reference key="object" ref="719261243"/>
 						<reference key="parent" ref="466310130"/>
 						<reference key="parent" ref="466310130"/>
 					</object>
 					<object class="IBObjectRecord">
-						<int key="objectID">563</int>
-						<reference key="object" ref="138643579"/>
-						<reference key="parent" ref="466310130"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">564</int>
-						<reference key="object" ref="63974516"/>
-						<reference key="parent" ref="466310130"/>
-					</object>
-					<object class="IBObjectRecord">
 						<int key="objectID">567</int>
 						<reference key="object" ref="1032511002"/>
 						<reference key="parent" ref="466310130"/>
 					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">569</int>
-						<reference key="object" ref="858242009"/>
-						<reference key="parent" ref="466310130"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">571</int>
-						<reference key="object" ref="1038079737"/>
-						<reference key="parent" ref="466310130"/>
-					</object>
 				</object>
 			</object>
 			<object class="NSMutableDictionary" key="flattenedProperties">
 					<string>5.ImportedFromIB2</string>
 					<string>533.CustomClassName</string>
 					<string>533.IBPluginDependency</string>
-					<string>535.IBPluginDependency</string>
 					<string>536.IBPluginDependency</string>
 					<string>539.IBPluginDependency</string>
 					<string>541.IBPluginDependency</string>
 					<string>56.ImportedFromIB2</string>
 					<string>560.IBPluginDependency</string>
 					<string>562.IBPluginDependency</string>
-					<string>563.IBPluginDependency</string>
-					<string>564.IBPluginDependency</string>
 					<string>567.IBPluginDependency</string>
-					<string>569.IBPluginDependency</string>
 					<string>57.IBEditorWindowLastContentRect</string>
 					<string>57.IBPluginDependency</string>
 					<string>57.ImportedFromIB2</string>
 					<string>57.editorWindowContentRectSynchronizationRect</string>
-					<string>571.IBPluginDependency</string>
 					<string>58.IBPluginDependency</string>
 					<string>58.ImportedFromIB2</string>
 					<string>72.IBPluginDependency</string>
 					<string>{74, 862}</string>
 					<string>{{6, 978}, {478, 20}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>{{547, 553}, {264, 283}}</string>
+					<string>{{547, 643}, {238, 193}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>{{475, 832}, {234, 43}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<integer value="1"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>{{392, 653}, {223, 183}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<integer value="1"/>
 					<string>{{23, 794}, {245, 183}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<integer value="1"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<integer value="1"/>
 				</object>
 			</object>
 			<nil key="sourceID"/>
-			<int key="maxID">572</int>
+			<int key="maxID">574</int>
 		</object>
 		<object class="IBClassDescriber" key="IBDocument.Classes">
 			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
 						<object class="NSArray" key="dict.sortedKeys">
 							<bool key="EncodedWithXMLCoder">YES</bool>
 							<string>selectAveragingDepthMap:</string>
-							<string>selectFilledQuadRenderer:</string>
-							<string>selectFilledTriRenderer:</string>
+							<string>selectMeshRenderer:</string>
 							<string>selectMultiDepthMap:</string>
 							<string>selectPointCloudRenderer:</string>
+							<string>selectSolidRenderer:</string>
 							<string>selectStaticDepthMap:</string>
 							<string>toggleAnaglyph:</string>
 							<string>toggleDepthFieldUpdates:</string>
 						<object class="NSArray" key="dict.sortedKeys">
 							<bool key="EncodedWithXMLCoder">YES</bool>
 							<string>selectAveragingDepthMap:</string>
-							<string>selectFilledQuadRenderer:</string>
-							<string>selectFilledTriRenderer:</string>
+							<string>selectMeshRenderer:</string>
 							<string>selectMultiDepthMap:</string>
 							<string>selectPointCloudRenderer:</string>
+							<string>selectSolidRenderer:</string>
 							<string>selectStaticDepthMap:</string>
 							<string>toggleAnaglyph:</string>
 							<string>toggleDepthFieldUpdates:</string>
 								<string key="candidateClassName">id</string>
 							</object>
 							<object class="IBActionInfo">
-								<string key="name">selectFilledQuadRenderer:</string>
-								<string key="candidateClassName">id</string>
-							</object>
-							<object class="IBActionInfo">
-								<string key="name">selectFilledTriRenderer:</string>
+								<string key="name">selectMeshRenderer:</string>
 								<string key="candidateClassName">id</string>
 							</object>
 							<object class="IBActionInfo">
 								<string key="candidateClassName">id</string>
 							</object>
 							<object class="IBActionInfo">
+								<string key="name">selectSolidRenderer:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
 								<string key="name">selectStaticDepthMap:</string>
 								<string key="candidateClassName">id</string>
 							</object>

KVAbstractRenderer.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "types.h"
-#import "KVRenderer.h"
-
-// Abstract base class for KVRenderers.  Factors out a wee
-// bit of boilerplate code.
-@interface KVAbstractRenderer : NSObject <KVRenderer> {
-  float yRays[240];
-  float xRays[320];
-}
-
-// These properties are synthesized for the benefit of
-// subclasses.
-@property(assign, nonatomic) BOOL lit;
-@property(assign, nonatomic) BOOL textured;
-@property(retain, nonatomic) NSColor *color;
-@property(assign, nonatomic) projective_transform_t colorTransform;
-
-// Sets the properties to sensible defaults.
-- init;
-
-// Subclasses must implement -[drawInCurrentOpenGLContext:colors:].
-
-- (vec3f_t) colorAsRGBVector;
-
-- (void) projectInto: (vec3f_t *) coord x: (int) x y: (int) y z: (float) z;
-
-@end
-
-typedef vec3f_t (*KVAbstractRendererProjector)(id, SEL, vec3f_t *, int, int, float);

KVAbstractRenderer.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 "KVAbstractRenderer.h"
-#import "utility.h"
-
-@interface KVAbstractRenderer ()
-- (void) buildRayField;
-@end
-
-@implementation KVAbstractRenderer
-#pragma mark --- NSObject Overrides
-
-- init {
-  if ((self = [super init])) {
-    self.color = [NSColor colorWithDeviceRed: 1.F green: 1.F blue: 1.F alpha: 1.F];
-    [self buildRayField];
-  }
-  return self;
-}
-
-#pragma mark --- KVRenderer implementation (partial)
-
-@synthesize lit;
-@synthesize color;
-@synthesize colorTransform;
-@synthesize textured;
-
-- (void) drawInCurrentOpenGLContext: (const linear_depth_t *)zs colors: (const colors_t *)colors {
-  [self doesNotRecognizeSelector: _cmd];  // subclass responsibility
-}
-
-- (vec3f_t) colorAsRGBVector {
-  return (vec3f_t) {
-    (float) [color redComponent],
-    (float) [color greenComponent],
-    (float) [color blueComponent]
-  };
-}
-
-- (void) projectInto: (vec3f_t *)coord x: (int) x y: (int) y z: (float) z {
-  int cx = x - 320, cy = y - 240;
-  int xRay, yRay;
-  float xSign = 1, ySign = 1;
-  if (cx < 0) {
-    xRay = -cx - 1;
-  } else {
-    xRay = cx;
-    xSign = -1.F;
-  }
-  if (cy < 0) {
-    yRay = -cy - 1;
-  } else {
-    yRay = cy;
-    ySign = -1.F;
-  }
-  *coord = (vec3f_t) {
-    xRays[xRay] * z * xSign,
-    yRays[yRay] * z * ySign,
-    z
-  };
-}
-
-// Pre-computes the projection of each camera X/Y coordinate into OpenGL's
-// X/Y space.  Essentially, this is undoing the perspective effect of the
-// camera's wide-angle lens, by projecting rays.
-// It's interesting that the Kinect's depth samples are *not* the length of
-// the ray, which is what I naïvely expected, but rather distance from the
-// XY plane containing the camera.  The depth can thus be natively used as
-// OpenGL's Z coordinate, and the Z of every projected ray will be 1.
-// Thus, we do not store Z.
-- (void) buildRayField {
-  // We assume square pixels, and derive the Y FOV from the X.
-  static const float fovXDegrees = 57.F;  // Pretty close to actual?
-  
-  const float fovX = fovXDegrees / 180.F * (float) M_PI;
-  for (int y = 0; y < 240; y++) {
-    float theta = (y / 320.F) * fovX/2.F;
-    yRays[y] = sinf(theta);
-  }
-  for (int x = 0; x < 320; x++) {
-    float psi = (x / 320.F) * fovX/2.F;
-    xRays[x] = sinf(psi);
-  }
-}
-
-@end

KVAveragingDepthMap.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "types.h"
-#import "KVDepthMap.h"
-
-@interface KVAveragingDepthMap : NSObject <KVDepthMap> {
- @private
-  GLfloat linearizationTable[2048];
-  linear_depth_t zs;
-  uint32_t sampleCount[480][640];
-  
-  colors_t colors;
-}
-
-@end

KVAveragingDepthMap.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 "KVAveragingDepthMap.h"
-#import "KVRenderer.h"
-
-@interface KVAveragingDepthMap ()
-- (void) buildLinearizationTable;
-@end
-
-@implementation KVAveragingDepthMap
-
-- init {
-  if ((self = [super init])) {
-    [self buildLinearizationTable];
-  }
-  return self;
-}
-
-#pragma mark --- KVDepthMap implementation
-
-- (void) updateDepth: (const depth_t *)rawDepthSamples {
-  linear_depth_t *out = &zs;  // Avoid ivar indirection during loop.
-  
-  for (int y = 0; y < 480; y++) {
-    for (int x = 0; x < 640; x++) {
-      float linear = linearizationTable[rawDepthSamples->samples[y][x]];
-      uint32_t n = sampleCount[y][x]++;
-      if (linear == 0.F) continue;
-      
-      float prev = out->z[y][x];
-      float delta = fabsf(linear - out->z[y][x]);
-      if (prev == 0.F || delta > 0.05F * linear) {
-        // If we've never gotten data for this point,
-        // or if it's changed substantially,
-        // we take what we can get!
-        out->z[y][x] = linear;
-        sampleCount[y][x] = 1;
-      } else {
-        // Add to the presumed normal distribution.
-        float nf = (float) n;
-        out->z[y][x] = (nf * out->z[y][x] + linear) / (nf + 1.F);
-      }
-    }
-  }
-}
-
-- (void) updateColor: (const colors_t *)pixels {
-  memcpy(&colors, pixels, sizeof(colors_t));
-}
-
-- (void) drawInCurrentOpenGLContextWithRenderer: (id <KVRenderer>) renderer {
-  [renderer drawInCurrentOpenGLContext: &zs colors: &colors];
-}
-
-#pragma mark --- Internals
-
-// The Kinect produces 11-bit depth samples.  Z-resolution decreases
-// with distance, following a curve that looks suspiciously like the
-// one used for perspective projection.  The ROS folks at CCNY derived
-// the equation below, for converting Kinect depth samples to meters.
-// This method memoizes the function over the entire 2048-element
-// domain.  This implies replacing a couple of floating-point constant
-// loads, an addition, and a division with an indirection to very hot
-// memory.  On my machine this eliminates 60% of time spent calculating
-// linear depths.
-- (void) buildLinearizationTable {
-  for (uint16_t depth = 0; depth < 2048; depth++) {
-    linearizationTable[depth] = -325.616F / ((GLfloat) depth + -1084.61F);
-    
-    // Simplification: when the Kinect reports "no data" for a point
-    // (typically because the IR projector is occluded) the result is
-    // a "very close" reading of nearly zero meters.  All my renderers
-    // filter out such readings, so we coerce it to exactly 0.F here
-    // to simplify that.
-    if (linearizationTable[depth] < 0.1F) {
-      linearizationTable[depth] = 0.F;
-    }
-  }
-}
-
-@end

KVDepthMap.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "types.h"
-
-@protocol KVRenderer;
-
-//
-// Transforms depth samples to an internal representation,
-// and provides them to renderers on request.
-@protocol KVDepthMap
-
-// Updates the internal state with a new set of depth
-// samples.
-- (void) updateDepth: (const depth_t *)rawDepthSamples;
-
-// Updates the internal state with a new set of color
-// samples.
-- (void) updateColor: (const colors_t *)pixels;
-
-// Provides a linearized floating-point depth map to the
-// given renderer -- possibly multiple times.
-// Implementations may freely reconfigure the renderer.
-- (void) drawInCurrentOpenGLContextWithRenderer: (id <KVRenderer>) renderer;
-
-@end
 
 #import "types.h"
 #import "KFKinect.h"
-#import "KVRenderer.h"
-#import "KVDepthMap.h"
+
+@protocol KVSpaceModel;
 
 typedef struct {
   vec3f_t pos;
   NSColor *deviceWhite;  
 }
 
-@property(retain) NSObject <KVDepthMap> *depthMap;
-@property(retain) NSObject <KVRenderer> *renderer;
-@property(retain) NSObject <KVRenderer> *wireRenderer;
+@property(retain) id <KVSpaceModel> spaceModel;
 
 @property(assign) BOOL frozen;
 @property(assign, nonatomic) BOOL anaglyph;
 @property(assign, nonatomic) BOOL lightsOn;
 @property(assign, nonatomic) BOOL textured;
-@property(assign, nonatomic) BOOL wireframe;
 @property(nonatomic) BOOL recording;
 @property(assign, nonatomic) projective_transform_t colorTransform;
+@property(assign, nonatomic) NSString *polygonMode;
 
 - (void) depthFieldUpdated: (const depth_t *)buffer from: (KFKinect *)kinect;
 - (void) colorFieldUpdated: (const colors_t *)buffer from: (KFKinect *)kinect;
 
 - (IBAction) selectPointCloudRenderer: sender;
-- (IBAction) selectFilledQuadRenderer: sender;
-- (IBAction) selectFilledTriRenderer: sender;
-
-- (IBAction) selectStaticDepthMap: sender;
-- (IBAction) selectAveragingDepthMap: sender;
-- (IBAction) selectMultiDepthMap: sender;
+- (IBAction) selectMeshRenderer: sender;
+- (IBAction) selectSolidRenderer: sender;
 
 - (IBAction) toggleDepthFieldUpdates: sender;
 - (IBAction) toggleLighting: sender;
 - (IBAction) toggleRecording: sender;
 - (IBAction) toggleAnaglyph: sender;
 - (IBAction) toggleTextures: sender;
-- (IBAction) toggleWireframe: sender;
 @end
  */
 
 #import "KVDepthView.h"
-#import "KVPointCloudRenderer.h"
-#import "KVQuadRenderer.h"
-#import "KVWireRenderer.h"
-#import "KVTriStripRenderer.h"
-#import "KVDoubleBufferDepthMap.h"
-#import "KVAveragingDepthMap.h"
-#import "KVMultiDepthMap.h"
+#import "KVSpaceModel.h"
+#import "KVRayFieldSpaceModel.h"
 #import "trackball.h"
 #import "utility.h"
 
 - (void) updateProjection;
 - (void) updateModelView;
 - (void) updateColorTransform;
+
+@property(assign, nonatomic) BOOL updateQueued;
+- (void) requestUpdateFromBackgroundThread;
+- (void) markAsDirty;
+
 @end
 
 @implementation KVDepthView
 #pragma mark --- Properties ---
 
+@synthesize updateQueued;
+
 @synthesize lightsOn;
 
 - (void) setLightsOn: (BOOL)flag {
 }
 
 @synthesize textured;
-@synthesize depthMap;
-@synthesize wireframe;
-
-@synthesize wireRenderer;
-- (void) setWireRenderer: (NSObject <KVRenderer>*)newRenderer {
-  NSObject <KVRenderer> *currentRenderer = wireRenderer;
-  wireRenderer = [newRenderer retain];
-  [currentRenderer release];
-  
-  newRenderer.color = [NSColor colorWithDeviceRed: 0.0 green: 1.0 blue: 0.0 alpha: 1.0];
-}
+@synthesize spaceModel;
 
 @synthesize colorTransform;
 - (void) setColorTransform: (projective_transform_t)newTransform {
   self.colorTransform = colorTransform;
 }
 
-@synthesize renderer;
-- (void) setRenderer: (NSObject <KVRenderer> *)newRenderer {
-  NSObject <KVRenderer> *currentRenderer = renderer;
-  renderer = [newRenderer retain];
-  
-  [currentRenderer unbind: @"lit"];
-  [currentRenderer unbind: @"textured"];
-  [currentRenderer unbind: @"colorTransform"];
-  [currentRenderer release];
-
-  [renderer bind: @"lit" toObject: self withKeyPath: @"lightsOn" options: nil];
-  [renderer bind: @"textured" toObject: self withKeyPath: @"textured" options: nil];
-  [renderer bind: @"colorTransform" toObject: self withKeyPath: @"colorTransform" options: nil];
-  
-  [self setNeedsDisplay: YES];
-}
-
 @synthesize frozen;
 @synthesize anaglyph;
 @synthesize recording;
+@synthesize polygonMode;
 
 #pragma mark --- Trivial NSView Overrides ---
 
     .scale = { 0.92F, 0.92F },
   };
   
-  self.wireRenderer = [[[KVWireRenderer alloc] init] autorelease];
-  
-  [self selectPointCloudRenderer: nil];
-  [self selectStaticDepthMap: nil];
+  self.spaceModel = [[[KVRayFieldSpaceModel alloc] init] autorelease];
+  self.polygonMode = KVFilledPolygonMode;
 }
 
 #pragma mark --- GL Stuff ---
   glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
 }
 
-#define INTEROCULAR_DISTANCE (0.0635F)
+#define INTEROCULAR_DISTANCE (0.0635F/1)
 - (void) drawRect: (NSRect)dirtyRect {
   [self resizeGL];
     
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   
-  renderer.color = deviceWhite;
+  NSDictionary *options = [NSDictionary dictionaryWithObject: self.polygonMode forKey: KVPolygonMode];
+  
   if (self.anaglyph) {
     camera.pos.x -= INTEROCULAR_DISTANCE / 2;
 
   
   // Draw right eye or center.
   [self updateModelView];
-  [depthMap drawInCurrentOpenGLContextWithRenderer: renderer];
-  if (wireframe) {
-    [depthMap drawInCurrentOpenGLContextWithRenderer: wireRenderer];
-  }
+  [spaceModel drawInCurrentOpenGLContextWithOptions: options];
   
   if (self.anaglyph) {
     // Draw left eye.
     
     glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
     [self updateModelView];
-    [depthMap drawInCurrentOpenGLContextWithRenderer: renderer];
-    if (wireframe) {
-      [depthMap drawInCurrentOpenGLContextWithRenderer: wireRenderer];
-    }
+    [spaceModel drawInCurrentOpenGLContextWithOptions: options];
     
     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 ---
 #pragma mark --- Actions ---
 
 - (IBAction) selectPointCloudRenderer: sender {
-  self.renderer = [[[KVPointCloudRenderer alloc] init] autorelease];
+  self.polygonMode = KVDottedPolygonMode;
+  [self setNeedsDisplay: YES];
 }
 
-- (IBAction) selectFilledQuadRenderer: sender {
-  self.renderer = [[[KVQuadRenderer alloc] init] autorelease];
+- (IBAction) selectMeshRenderer: sender {
+  self.polygonMode = KVWireframePolygonMode;
+  [self setNeedsDisplay: YES];
 }
 
-- (IBAction) selectFilledTriRenderer: sender {
-  self.renderer = [[[KVTriStripRenderer alloc] init] autorelease];
-}
-
-- (IBAction) selectStaticDepthMap: sender {
-  self.depthMap = [[[KVDoubleBufferDepthMap alloc] init] autorelease];
-}
-
-- (IBAction) selectAveragingDepthMap: sender {
-  self.depthMap = [[[KVAveragingDepthMap alloc] init] autorelease];
-}
-
-- (IBAction) selectMultiDepthMap: sender {
-  self.depthMap = [[[KVMultiDepthMap alloc] init] autorelease];
+- (IBAction) selectSolidRenderer: sender {
+  self.polygonMode = KVFilledPolygonMode;
+  [self setNeedsDisplay: YES];
 }
 
 - (IBAction) toggleDepthFieldUpdates: sender {
   [sender setState: self.frozen? NSOnState : NSOffState];
 }
 
-- (IBAction) toggleWireframe: sender {
-  self.wireframe = !self.wireframe;
-  [sender setState: self.wireframe? NSOnState : NSOffState];
-}
-
 - (IBAction) toggleLighting: sender {
   self.lightsOn = !lightsOn;
   [sender setState: lightsOn? NSOnState : NSOffState];
   }
       
   if (!self.frozen) {
-    [depthMap updateDepth: buffer];
+    [spaceModel setDepthSamples: [NSData dataWithBytesNoCopy: (void *) buffer length: sizeof *buffer freeWhenDone: NO]];
     [self setNeedsDisplay: YES];
   }
 }
 
 - (void) colorFieldUpdated: (const colors_t *)buffer from: (KFKinect *)kinect {
   if (!self.frozen) {
-    [depthMap updateColor: buffer];
+    [spaceModel setColorSamples: [NSData dataWithBytesNoCopy: (void *) buffer length: sizeof *buffer freeWhenDone: NO]];
     [self setNeedsDisplay: YES];
   }
 }

KVDoubleBufferDepthMap.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "types.h"
-#import "KVDepthMap.h"
-
-@interface KVDoubleBufferDepthMap : NSObject <KVDepthMap> {
- @private
-  GLfloat linearizationTable[2048];
-  
-  linear_depth_t zs[2];
-  colors_t colors[2];
-}
-
-@end

KVDoubleBufferDepthMap.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 "KVDoubleBufferDepthMap.h"
-#import "KVRenderer.h"
-
-@interface KVDoubleBufferDepthMap ()
-
-@property(assign, nonatomic) unsigned int backColorBuffer;
-@property(assign, nonatomic) unsigned int backDepthBuffer;
-
-@property(readonly) unsigned int frontColorBuffer;
-@property(readonly) unsigned int frontDepthBuffer;
-
-@property(assign, nonatomic) unsigned int lastColorBufferRendered;
-@property(assign, nonatomic) unsigned int lastDepthBufferRendered;
-
-- (void) flipBuffers;
-
-- (void) buildLinearizationTable;
-@end
-
-@implementation KVDoubleBufferDepthMap
-
-# pragma mark --- Initialization and properties
-
-- init {
-  if ((self = [super init])) {
-    [self buildLinearizationTable];
-  }
-  return self;
-}
-
-@synthesize backColorBuffer;
-
-- (unsigned int) frontColorBuffer {
-  return backColorBuffer ^ 1;
-}
-
-@synthesize backDepthBuffer;
-
-- (unsigned int) frontDepthBuffer {
-  return backDepthBuffer ^ 1;
-}
-
-@synthesize lastDepthBufferRendered, lastColorBufferRendered;
-
-#pragma mark --- KVDepthMap implementation
-
-- (void) updateDepth: (const depth_t *)rawDepthSamples {
-  unsigned int buffer = self.backDepthBuffer;
-  linear_depth_t *out = &zs[buffer];  // Avoid ivar indirection during loop.
-  
-  for (int y = 0; y < 480; y++) {
-    for (int x = 0; x < 640; x++) {
-      out->z[y][x] = linearizationTable[rawDepthSamples->samples[y][x]];
-    }
-  }
-  
-  self.lastDepthBufferRendered = buffer;
-}
-
-- (void) updateColor: (const colors_t *)pixels {
-  unsigned int buffer = self.backColorBuffer;
-  
-  memcpy(&colors[buffer], pixels, sizeof(colors_t));
-  
-  self.lastColorBufferRendered = buffer;
-}
-
-- (void) drawInCurrentOpenGLContextWithRenderer: (id <KVRenderer>) renderer {
-  NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
-  [renderer drawInCurrentOpenGLContext: &zs[self.frontDepthBuffer] colors: &colors[self.frontColorBuffer]];
-  [self flipBuffers];
-  NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
-
-  if (0) {
-    NSLog(@"Render (%@, lights=%d, texture=%d) took %.03fµs",
-          [[renderer class] description],
-          renderer.lit,
-          renderer.textured,
-          (end - start) * 1000000.);
-  }
-}
-
-#pragma mark --- Internals
-
-- (void) flipBuffers {
-  if (self.lastColorBufferRendered == self.backColorBuffer) {
-    self.backColorBuffer = self.frontColorBuffer;
-  }
-  if (self.lastDepthBufferRendered == self.backDepthBuffer) {
-    self.backDepthBuffer = self.frontDepthBuffer;
-  }
-}
-
-// The Kinect produces 11-bit depth samples.  Z-resolution decreases
-// with distance, following a curve that looks suspiciously like the
-// one used for perspective projection.  The ROS folks at CCNY derived
-// the equation below, for converting Kinect depth samples to meters.
-// This method memoizes the function over the entire 2048-element
-// domain.  This implies replacing a couple of floating-point constant
-// loads, an addition, and a division with an indirection to very hot
-// memory.  On my machine this eliminates 60% of time spent calculating
-// linear depths.
-- (void) buildLinearizationTable {
-  for (uint16_t depth = 0; depth < 2048; depth++) {
-    linearizationTable[depth] = -325.616F / ((GLfloat) depth + -1084.61F);
-    
-    // Simplification: when the Kinect reports "no data" for a point
-    // (typically because the IR projector is occluded) the result is
-    // a "very close" reading of nearly zero meters.  All my renderers
-    // filter out such readings, so we coerce it to exactly 0.F here
-    // to simplify that.
-    if (linearizationTable[depth] < 0.1F) {
-      linearizationTable[depth] = 0.F;
-    }
-  }
-}
-
-@end

KVMultiDepthMap.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "types.h"
-#import "KVDepthMap.h"
-
-// Maintains two planes of depth map, so that foreground and
-// background can be separated.
-@interface KVMultiDepthMap : NSObject <KVDepthMap> {
- @private
-  depth_t depthCopy;
-  colors_t colorCopy;
-}
-
-- init;
-
-@end

KVMultiDepthMap.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 "KVMultiDepthMap.h"
-#import "KVRenderer.h"
-
-@interface KVDepthPlane : NSObject {
-  GLfloat linearizationTable[2048];
-  depth_t cachedDepth;
-  colors_t cachedColors;
-  linear_depth_t zs;
-}
-
-@property(retain, nonatomic) NSColor *color;
-
-- (void) updateAndTransformDepth: (depth_t *) depth colors: (const colors_t *)colors finalPlane: (BOOL)finalPlane;
-- (void) drawInCurrentOpenGLContextWithRenderer: (id <KVRenderer>)renderer;
-- (void) buildLinearizationTable;
-@end
-
-@interface KVMultiDepthMap ()
-
-@property(retain, nonatomic) NSMutableArray *planes;
-
-@end
-
-@implementation KVMultiDepthMap
-
-# pragma mark --- Initialization and properties
-
-@synthesize planes;
-
-- init {
-  if ((self = [super init])) {
-    static const int colorCount = 2;
-    NSColor *color[2] = {
-      [NSColor colorWithDeviceRed: 1. green: 1. blue: 1. alpha: 1.],
-      [NSColor colorWithDeviceRed: 1. green: 0. blue: 0. alpha: 1.],
-    };
-    self.planes = [NSMutableArray array];
-    for (NSUInteger i = 0; i < 2; i++) {
-      [self.planes addObject: [[[KVDepthPlane alloc] initWithColor: color[i % colorCount]] autorelease]];
-    }
-  }
-  return self;
-}
-
-#pragma mark --- KVDepthMap implementation
-
-- (void) updateDepth: (const depth_t *)rawDepthSamples {
-  memcpy(&depthCopy, rawDepthSamples, sizeof(depth_t));
-  
-  NSUInteger planeIndex = 0;
-  for (KVDepthPlane *plane in planes) {
-    [plane updateAndTransformDepth: &depthCopy colors: &colorCopy finalPlane: (planeIndex == 1)];
-    planeIndex++;
-  }
-}
-
-- (void) updateColor: (const colors_t *)pixels {
-  memcpy(&colorCopy, pixels, sizeof(colors_t));
-}
-
-- (void) drawInCurrentOpenGLContextWithRenderer: (id <KVRenderer>) renderer {
-  NSUInteger x = 0;
-  for (KVDepthPlane *plane in planes) {
-    [plane drawInCurrentOpenGLContextWithRenderer: renderer];
-    x++;
-  }
-}
-
-@end
-
-@implementation KVDepthPlane
-
-@synthesize color;
-
-#define NO_DEPTH (2047)
-#define ALL_CLEAR (2045)
-#define JUMP (3)
-
-- initWithColor: (NSColor *)newColor {
-  if ((self = [super init])) {
-    self.color = newColor;
-    [self buildLinearizationTable];
-  }
-  return self;
-}
-
-- (void) drawInCurrentOpenGLContextWithRenderer: (id <KVRenderer>)renderer {
-  renderer.color = color;
-  [renderer drawInCurrentOpenGLContext: &zs colors: &cachedColors];
-}
-
-- (void) updateAndTransformDepth: (depth_t *)depth colors: (const colors_t *)newColors finalPlane: (BOOL) finalPlane {
-  // Remember: we're working with raw camera depth samples here.  Smaller is
-  // closer.
-  for (int y = 0; y < 480; y++) {
-    for (int x = 0; x < 640; x++) {
-      // Ignore all holes in the depth data.
-      if (depth->samples[y][x] == NO_DEPTH) {
-        cachedColors.pixels[y][x] = newColors->pixels[y][x];
-        continue;
-      }
-      
-      if (depth->samples[y][x] == ALL_CLEAR) {
-        // This ray is not occluded!  I CAN SEE FOREVER
-        cachedDepth.samples[y][x] = 0;
-      } else {
-        if (!finalPlane && depth->samples[y][x] < cachedDepth.samples[y][x] - JUMP) {
-          // We are occluded.  Do not change the cache.
-          // Leave it for a closer plane.
-        } else {
-          // Update us.
-          cachedDepth.samples[y][x] = depth->samples[y][x];
-          cachedColors.pixels[y][x] = newColors->pixels[y][x];
-          // Knock a hole for later planes.
-          depth->samples[y][x] = ALL_CLEAR;
-        }
-      }
-      zs.z[y][x] = linearizationTable[cachedDepth.samples[y][x]];
-    }
-  }
-}
-
-// The Kinect produces 11-bit depth samples.  Z-resolution decreases
-// with distance, following a curve that looks suspiciously like the
-// one used for perspective projection.  The ROS folks at CCNY derived
-// the equation below, for converting Kinect depth samples to meters.
-// This method memoizes the function over the entire 2048-element
-// domain.  This implies replacing a couple of floating-point constant
-// loads, an addition, and a division with an indirection to very hot
-// memory.  On my machine this eliminates 60% of time spent calculating
-// linear depths.
-- (void) buildLinearizationTable {
-  for (uint16_t depth = 0; depth < 2048; depth++) {
-    linearizationTable[depth] = -325.616F / ((GLfloat) depth + -1084.61F);
-    
-    // Simplification: when the Kinect reports "no data" for a point
-    // (typically because the IR projector is occluded) the result is
-    // a "very close" reading of nearly zero meters.  All my renderers
-    // filter out such readings, so we coerce it to exactly 0.F here
-    // to simplify that.
-    if (linearizationTable[depth] < 0.1F) {
-      linearizationTable[depth] = 0.F;
-    }
-  }
-  linearizationTable[0] = 0.F;
-}
-
-@end

KVPointCloudRenderer.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "KVAbstractRenderer.h"
-
-// Renders depth samples into a cloud of zero-dimensional
-// points in 3D space.
-@interface KVPointCloudRenderer : KVAbstractRenderer {
-}
-
-@end

KVPointCloudRenderer.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 "KVPointCloudRenderer.h"
-#import "types.h"
-#import "utility.h"
-
-@implementation KVPointCloudRenderer
-
-- (void) drawInCurrentOpenGLContext: (const linear_depth_t *)zs
-                             colors: (const colors_t *)colors {
-  // Avoid the property message send for a significant perf win.
-  // (Sending a message doubles our render time.)
-  BOOL textured = self.textured;
-  BOOL lit = self.lit;
-  const projective_transform_t xform = self.colorTransform;
-  KVAbstractRendererProjector projector =
-      (KVAbstractRendererProjector) [self methodForSelector: @selector(projectInto:x:y:z:)];
-  
-  vec3f_t baseColor = [self colorAsRGBVector];
-  glColor3fv(&baseColor.x);
-  
-  // 0D points can still be lit!
-  glNormal3f(0, 0, -1);
-
-  glBegin(GL_POINTS);
-  
-  vec3f_t coords;
-  for (int y = 0; y < 480; y++) {
-    for (int x = 0; x < 640; x++) {
-      GLfloat depth = zs->z[y][x];
-      if (depth == 0.F) continue;
-      
-      if (textured) {
-        int colorX = (int) (x * xform.scale.x + xform.offset.x);
-        int colorY = (int) (y * xform.scale.y + xform.offset.y);
-        const rgb_t *pixelColor = &colors->pixels[colorY][colorX];
-        
-        glColor3ubv(&pixelColor->r);
-      } else if (!lit) {
-        vec3f_t falseColor = vector_scale(baseColor, 1.F - depth/10.F);
-        glColor3fv(&falseColor.x);
-      }
-      
-      projector(self, @selector(projectInto:x:y:z:), &coords, x, y, depth);
-      
-      glVertex3fv(&coords.x);
-    }
-  }
-  glEnd();
-}
-
-@end

KVQuadRenderer.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "KVAbstractRenderer.h"
-
-// Renders depth samples by joining them to their
-// neighbors using quads.  Correctly computes surface
-// normals, etc.
-@interface KVQuadRenderer : KVAbstractRenderer {
-}
-
-@end

KVQuadRenderer.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 "KVQuadRenderer.h"
-#import "types.h"
-#import "utility.h"
-
-@implementation KVQuadRenderer
-
-- (void) drawInCurrentOpenGLContext: (const linear_depth_t *)zs
-                             colors: (const colors_t *)colors {
-  // Avoid message send for large performance win.
-  BOOL textured = self.textured;
-  BOOL lit = self.lit;
-  projective_transform_t xform = self.colorTransform;
-  KVAbstractRendererProjector projector =
-      (KVAbstractRendererProjector) [self methodForSelector: @selector(projectInto:x:y:z:)];
-
-  vec3f_t normal;
-  
-  glBegin(GL_QUADS);
-  vec3f_t baseColor = [self colorAsRGBVector];
-  glColor3fv(&baseColor.x);
-  
-  for (int y = 0; y < 479; y++) {
-    for (int x = 0; x < 639; x++) {
-      // A CCW traversal of the neighboring points:
-      int xs[4] = { x, x, x + 1, x + 1 };
-      int ys[4] = { y, y + 1, y + 1, y };
-      
-      BOOL skip = NO;
-      vec3f_t coords[4];
-      GLfloat vcolors[4];
-      GLfloat minDepth = 1000.F, maxDepth = -1000.F;
-      for (int i = 0; i < 4; i++) {
-        GLfloat d = zs->z[ys[i]][xs[i]];
-        
-        if (d == 0.F) skip = YES;
-        
-        minDepth = fminf(minDepth, d);
-        maxDepth = fmaxf(maxDepth, d);
-        
-        projector(self, @selector(projectInto:x:y:z:), &coords[i], xs[i], ys[i], d);
-        
-        vcolors[i] = 1.F - (d / 9.F);
-      }
-      
-      if (fabsf(minDepth - maxDepth) > 0.4F) skip = YES;
-      
-      if (skip) continue;
-      
-      if (lit) {
-        // Note: the normal is correct for the upper-left half only.
-        normal = compute_normal(coords[0], coords[1], coords[2]);
-        glNormal3fv(&normal.x);
-      }
-      
-      for (int i = 0; i < 4; i++) {
-        if (textured) {
-          int colorX = (int) (xs[i] * xform.scale.x + xform.offset.x);
-          int colorY = (int) (ys[i] * xform.scale.y + xform.offset.y);
-          const rgb_t *pixelColor = &colors->pixels[colorY][colorX];
-          
-          glColor3ubv(&pixelColor->r);
-        } else if (!lit) {
-          vec3f_t falseColor = vector_scale(baseColor, vcolors[i]);
-          glColor3fv(&falseColor.x);
-        }
-        glVertex3fv(&coords[i].x);
-      }
-    }
-  }
-  glEnd();
-}
-
-@end

KVRayFieldSpaceModel.h

+//
+//  KVSimpleSpaceModel.h
+//  KinectViewer
+//
+//  Created by Cliff L. Biffle on 2010-11-29.
+//  Copyright 2010 Cliff L. Biffle. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+#import "KVSpaceModel.h"
+#import "types.h"
+
+// Projects depth and color data into 3D space, starting from
+// scratch at each frame and describing only the nearest visible
+// surfaces.
+@interface KVRayFieldSpaceModel : NSObject <KVSpaceModel> {
+  // Memoized linearization function for 12-bit depth samples.
+  float linearizationTable[2048];
+  // The X coordinate of each unit ray.
+  float unitX[640];
+  // The Y coordinate of each unit ray.
+  float unitY[480];
+    
+  // The actual scene model.  Alarmingly, gcc generates suboptimal
+  // code if we use [480][640].
+  vec3f_t rays[480 * 640];
+  
+  // The raw RGB samples of the last color image.  These do not
+  // map one-to-one onto rays because the cameras are offset.
+  rgb_t colorImage[480][640];
+}
+
+@end
+
+extern NSString * const KVPolygonMode;
+extern NSString * const KVFilledPolygonMode;
+extern NSString * const KVWireframePolygonMode;
+extern NSString * const KVDottedPolygonMode;

KVRayFieldSpaceModel.m

+//
+//  KVSimpleSpaceModel.m
+//  KinectViewer
+//
+//  Created by Cliff L. Biffle on 2010-11-29.
+//  Copyright 2010 Cliff L. Biffle. All rights reserved.
+//
+
+#import "KVRayFieldSpaceModel.h"
+
+NSString * const KVPolygonMode = @"KVPolygonMode";
+NSString * const KVFilledPolygonMode = @"KVFilledPolygonMode";
+NSString * const KVWireframePolygonMode = @"KVWireframePolygonMode";
+NSString * const KVDottedPolygonMode = @"KVDottedPolygonMode";
+
+@interface KVRayFieldSpaceModel ()
+- (void) buildLinearizationTable;
+- (void) buildUnitRays;
+
+@property(assign, nonatomic) double rayCastingTotal;
+@property(assign, nonatomic) NSUInteger rayCastingCount;
+
+@property(assign, nonatomic) double vertexPlacementTotal;
+@property(assign, nonatomic) NSUInteger vertexPlacementCount;
+
+- (void) renderAsPointsWithOptions: (NSDictionary *) options;
+- (void) renderAsMeshWithOptions: (NSDictionary *) options;
+- (void) renderAsSolidWithOptions: (NSDictionary *) options;
+@end
+
+@implementation KVRayFieldSpaceModel
+
+@synthesize rayCastingCount, rayCastingTotal, vertexPlacementCount, vertexPlacementTotal;
+
+- init {
+  if ((self = [super init])) {
+    [self buildUnitRays];
+    [self buildLinearizationTable];
+  }
+  return self;
+}
+
+#pragma mark --- Implementation of KVSpaceModel
+
+- (void) setDepthSamples: (NSData *)data {
+  NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
+  
+  const uint16_t *sample = [data bytes];
+  
+  int vidx = 0;
+  for (int y = 0; y < 480; y++) {
+    for (int x = 0; x < 640; x++, vidx++) {
+      uint16_t s = sample[vidx];
+      float z;
+      if (s < 2048) {
+        z = linearizationTable[s];
+      } else {
+        z = 0;
+      }
+      
+      rays[vidx] = (vec3f_t) {
+        .x = unitX[x] * z,
+        .y = unitY[y] * z,
+        .z = z,
+      };
+    }
+  }
+  
+  NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
+  rayCastingCount++;
+  rayCastingTotal += end - start;
+  NSLog(@"setDepthSamples: mean %.03fµs", (rayCastingTotal / rayCastingCount) * 1000000.);
+}
+
+- (void) setColorSamples: (NSData *)data {
+  memcpy(&colorImage, [data bytes], sizeof colorImage);
+}
+
+- (void) drawInCurrentOpenGLContextWithOptions: (NSDictionary *) options {
+  NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
+
+  id modeOption = [options valueForKey: KVPolygonMode];
+  if (modeOption == KVWireframePolygonMode) {
+    [self renderAsMeshWithOptions: options];
+  } else if (modeOption == KVDottedPolygonMode) {
+    [self renderAsPointsWithOptions: options];
+  } else {
+    [self renderAsSolidWithOptions: options];
+  }
+  
+  NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
+  vertexPlacementCount++;
+  vertexPlacementTotal += end - start;
+  NSLog(@"drawInCurrentOpenGLContextWithOptions: current %.03fµs mean %.03fµs",
+        (end - start) * 1000000.,
+        (vertexPlacementTotal / vertexPlacementCount) * 1000000.);
+}
+
+#pragma mark --- Internals
+
+- (void) renderAsPointsWithOptions: (NSDictionary *) options {
+  glBegin(GL_POINTS);
+  glColor3f(1, 1, 1);
+  for (int i = 0; i < 480 * 640; i++) {
+    glVertex3fv(&rays[i].x);    
+  }
+  glEnd();
+}
+
+- (void) renderAsSolidWithOptions: (NSDictionary *) options {
+  float jump = 0.1f;
+  
+  glColor3f(1, 1, 1);
+  int vidx = 0;
+  for (int y = 0; y < 480 - 1; y++) {
+    BOOL inStrip = NO;
+    float lastDepth = rays[vidx].z;
+    for (int x = 0; x < 640; x++, vidx++) {
+      for (int step = 0; step < 640 * 2; step += 640) {
+        float z = rays[vidx + step].z;
+        float delta = z - lastDepth;
+        lastDepth = z;
+        
+        if (delta < -jump || delta > jump || z == 0.f) {
+          if (inStrip) {
+            glEnd();
+            inStrip = NO;
+          }
+        } else {
+          if (!inStrip) {
+            glBegin(GL_TRIANGLE_STRIP);
+            inStrip = YES;
+          }
+          glVertex3fv(&rays[vidx + step].x);
+        }
+        
+        if (!inStrip) break;
+      }
+    }
+    if (inStrip) glEnd();
+  }
+}
+
+- (void) renderAsMeshWithOptions: (NSDictionary *) options {
+  float jump = 0.1f;
+  
+  glBegin(GL_LINES);
+  glColor3f(1, 1, 1);
+  int vidx = 0;
+  for (int y = 0; y < 480 - 1; y++) {
+    vidx = y * 640;
+    for (int x = 0; x < 640 - 1; x++, vidx++) {
+      float cornerZ = rays[vidx].z;
+      if (cornerZ == 0.f) continue;
+      
+      float delta;
+      
+      delta = cornerZ - rays[vidx + 640].z;
+      if (delta > -jump && delta < jump && delta != cornerZ) {
+        glVertex3fv(&rays[vidx].x);
+        glVertex3fv(&rays[vidx + 640].x);
+      } 
+      
+      delta = cornerZ - rays[vidx + 1].z;
+      if (delta > -jump && delta < jump && delta != cornerZ) {
+        glVertex3fv(&rays[vidx].x);
+        glVertex3fv(&rays[vidx + 1].x);
+      } 
+    }
+  }
+  glEnd();
+}
+
+// The Kinect produces 11-bit depth samples.  Z-resolution decreases
+// with distance, following a curve that looks suspiciously like the
+// one used for perspective projection.  The ROS folks at CCNY derived
+// the equation below, for converting Kinect depth samples to meters.
+// This method memoizes the function over the entire 2048-element
+// domain.  This implies replacing a couple of floating-point constant
+// loads, an addition, and a division with an indirection to very hot
+// memory.  On my machine this eliminates 60% of time spent calculating
+// linear depths.
+- (void) buildLinearizationTable {
+  for (uint16_t depth = 0; depth < 2048; depth++) {
+    linearizationTable[depth] = -325.616F / ((GLfloat) depth + -1084.61F);
+  }
+  
+  // Simplification: when the Kinect reports "no data" for a point
+  // (typically because the IR projector is occluded) the result is
+  // a "very close" reading of nearly zero meters.  All my renderers
+  // filter out such readings, so we coerce it to exactly 0.F here
+  // to simplify that.
+  linearizationTable[0] = 0.F;
+  linearizationTable[2047] = 0.F;
+}
+
+- (void) buildUnitRays {
+  // We assume square pixels, and derive the Y FOV from the X.
+  static const float fovXDegrees = 57.F;  // Pretty close to actual?
+  
+  const float fovX = fovXDegrees / 180.F * (float) M_PI;
+  for (int y = 0; y < 240; y++) {
+    float theta = ((y + 0.5F) / 320.F) * fovX/2.F;
+    unitY[y + 240] = -sinf(theta);
+    unitY[239 - y] = sinf(theta);
+  }
+  for (int x = 0; x < 320; x++) {
+    float psi = ((x + 0.5F) / 320.F) * fovX/2.F;
+    unitX[x + 320] = -sinf(psi);
+    unitX[319 - x] = sinf(psi);
+  }
+}
+
+@end

KVRenderer.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "types.h"
-
-//
-// A strategy for transforming depth samples into OpenGL
-// data.
-@protocol KVRenderer
-
-// Whether the renderer assumes lighting is enabled.
-// If lighting is disabled, the renderer may apply false
-// colors to emphasize depth.
-// This won't take effect until the next call to
-// -[drawInCurrentOpenGLContext].
-@property(assign, nonatomic) BOOL lit;
-
-// Whether the renderer maps color data from the camera
-// onto the 3D scene.
-@property(assign, nonatomic) BOOL textured;
-
-// Selects the base color for the rendering.  Note that
-// if lighting is disabled, the renderer may transform
-// or even ignore this color.
-@property(retain, nonatomic) NSColor *color;
-
-// Sets the projection for color data onto depth data.
-@property(assign, nonatomic) projective_transform_t colorTransform;
-
-// Applies the renderer's internal state to the currently
-// active OpenGL context.  This method may be called many
-// times for a single call to -[update:].
-- (void) drawInCurrentOpenGLContext: (const linear_depth_t *)zs colors: (const colors_t *)colors;
-
-@end
+/*
+ * 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 <Cocoa/Cocoa.h>
+
+// Transforms raw camera depth and color information into a model
+// of the scene, and translates that model into something visible
+// through OpenGL.
+@protocol KVSpaceModel <NSObject>
+
+// Most recent frame of depth samples, in the format generated by
+// KVDataSource.  Note that the setter will either make an internal
+// copy of this or simply use it to update an internal model.
+- (void) setDepthSamples: (NSData *) depthSamples;
+
+// Most recent frame of depth samples, in the format generated by
+// KVDataSource.  Note that the setter will either make an internal
+// copy of this or simply use it to update an internal model.
+- (void) setColorSamples: (NSData *) colorSamples;
+
+- (void) drawInCurrentOpenGLContextWithOptions: (NSDictionary *) options;
+
+@end

KVTriStripRenderer.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "KVAbstractRenderer.h"
-
-// Renders depth samples by joining adjacent samples
-// into triangle strips.  Computes almost-correct
-// vertex normals for smooth shading.
-@interface KVTriStripRenderer : KVAbstractRenderer {
-  vec3f_t vertices[480][640];
-  vec3f_t normals[480][640];
-}
-
-@end

KVTriStripRenderer.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 "KVTriStripRenderer.h"
-#import "types.h"
-#import "utility.h"
-
-@implementation KVTriStripRenderer
-
-- (void) drawInCurrentOpenGLContext: (const linear_depth_t *)zs
-                             colors: (const colors_t *)colors {
-  // Theory: each horizontal raster row of the image is converted to a
-  // triangle strip, using the simplest possible method:
-  //
-  // 0 2 4 6
-  // |/|/|/
-  // 1 3 5   ...and so on.
-  
-  // Avoid message send for a large performance win.
-  BOOL textured = self.textured;
-  BOOL lit = self.lit;
-  const projective_transform_t xform = self.colorTransform;
-
-  KVAbstractRendererProjector projector =
-      (KVAbstractRendererProjector) [self methodForSelector: @selector(projectInto:x:y:z:)];
-
-  vec3f_t baseColor = [self colorAsRGBVector];
-  glColor3fv(&baseColor.x);
-  
-  for (int y = 0; y < 480; y++) {
-    for (int x = 0; x < 640; x++) {
-      GLfloat d = zs->z[y][x];
-      projector(self, @selector(projectInto:x:y:z:), &vertices[y][x], x, y, d);
-    }
-  }
-  
-  if (lit) {
-    // The right and bottom edges won't have correct normals.
-    // So be it.
-    for (int y = 0; y < 479; y++) {
-      for (int x = 0; x < 639; x++) {
-        normals[y][x] = compute_normal(vertices[y][x], vertices[y + 1][x], vertices[y + 1][x + 1]);
-      }
-    }
-  }
-  
-  for (int y = 0; y < 479; y++) {
-    glBegin(GL_TRIANGLE_STRIP);
-    
-    // Whether we have called glBegin
-    BOOL inStrip = YES;
-    // Z of last point rendered, for slope test.
-    GLfloat lastDepth = vertices[y][0].z;
-    
-    for (int x = 0; x < 640; x++) {
-      
-      for (int step = 0; step < 2; step++) {
-        GLfloat d = vertices[y + step][x].z;
-        GLfloat deltaZ = fabsf(d - lastDepth);
-        lastDepth = d;
-        
-        if (d == 0.F || deltaZ > 0.05F * d) {
-          // Hole in the data or depth discontinuity.
-          if (inStrip) {
-            glEnd();
-            inStrip = NO;
-          }
-          // Break out of step loop to ensure that we always start
-          // each vertical edge at the *top*.  Otherwise the winding
-          // gets hosed.  This may cause us to skip a single vertex.
-          break;
-        } else {
-          // Valid data.
-          if (!inStrip) {
-            glBegin(GL_TRIANGLE_STRIP);
-            inStrip = YES;
-          }
-        }
-        
-        if (lit) {
-          glNormal3fv(&normals[y + step][x].x);
-        }
-
-        if (textured) {
-          int colorX = (int) (x * xform.scale.x + xform.offset.x);
-          int colorY = (int) ((y + step) * xform.scale.y + xform.offset.y);
-          const rgb_t *pixelColor = &colors->pixels[colorY][colorX];
-          
-          glColor3ubv(&pixelColor->r);
-        } else if (!lit) {
-          vec3f_t falseColor = vector_scale(baseColor, 1.F - d/10.F);
-          glColor3fv(&falseColor.x);
-        }
-        glVertex3fv(&vertices[y + step][x].x);
-      }
-    }
-    if (inStrip) glEnd();
-  }
-}
-
-@end

KVWireRenderer.h

-/*
- * 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 <Cocoa/Cocoa.h>
-#import "KVAbstractRenderer.h"
-
-// Renders depth samples using a decimated wireframe
-// mesh.
-@interface KVWireRenderer : KVAbstractRenderer {
-}
-
-@end

KVWireRenderer.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 "KVWireRenderer.h"
-#import "types.h"
-#import "utility.h"
-
-#define STRIDE (5)
-
-@implementation KVWireRenderer
-
-- (void) drawInCurrentOpenGLContext: (const linear_depth_t *)zs
-                             colors: (const colors_t *)colors {
-  KVAbstractRendererProjector projector =
-      (KVAbstractRendererProjector) [self methodForSelector: @selector(projectInto:x:y:z:)];
-  
-  glBegin(GL_LINES);
-  vec3f_t baseColor = [self colorAsRGBVector];
-  glColor3fv(&baseColor.x);
-  
-  for (int y = 0; y < 480 - STRIDE; y += STRIDE) {
-    for (int x = 0; x < 640 - STRIDE; x += STRIDE) {
-      // A CCW traversal of the neighboring points:
-      int xs[4] = { x, x, x + STRIDE, x + STRIDE };
-      int ys[4] = { y, y + STRIDE, y + STRIDE, y };
-      
-      BOOL skip = NO;
-      vec3f_t coords[4];
-      GLfloat minDepth = 1000.F, maxDepth = -1000.F;
-      for (int i = 0; i < 4; i++) {
-        GLfloat d = zs->z[ys[i]][xs[i]] - 0.02F;
-        
-        if (d == 0.F) skip = YES;
-        
-        minDepth = fminf(minDepth, d);
-        maxDepth = fmaxf(maxDepth, d);
-        
-        projector(self, @selector(projectInto:x:y:z:), &coords[i], xs[i], ys[i], d);
-      }
-      
-      if (fabsf(minDepth - maxDepth) > 0.4F) skip = YES;
-      
-      if (skip) continue;
-      
-      for (int i = 0; i < 4; i++) {
-        int nextI = (i + 1) % 4;
-        glVertex3fv(&coords[i].x);
-        glVertex3fv(&coords[nextI].x);
-      }
-    }
-  }
-  glEnd();
-}
-
-@end

KinectViewer.xcodeproj/project.pbxproj

 		8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
 		8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
 		8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
-		C708F46412A03E8500656901 /* KVAbstractRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = C708F46112A03E8500656901 /* KVAbstractRenderer.m */; };
-		C708F46512A03E8500656901 /* KVTriStripRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = C708F46312A03E8500656901 /* KVTriStripRenderer.m */; };
-		C708F46A12A03E8F00656901 /* KVPointCloudRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = C708F46712A03E8F00656901 /* KVPointCloudRenderer.m */; };
-		C708F46B12A03E8F00656901 /* KVQuadRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = C708F46912A03E8F00656901 /* KVQuadRenderer.m */; };
-		C708F55912A0728800656901 /* KVDoubleBufferDepthMap.m in Sources */ = {isa = PBXBuildFile; fileRef = C708F55812A0728800656901 /* KVDoubleBufferDepthMap.m */; };
-		C708F5E412A0779500656901 /* KVAveragingDepthMap.m in Sources */ = {isa = PBXBuildFile; fileRef = C708F5E312A0779500656901 /* KVAveragingDepthMap.m */; };
 		C70E62AF129C5055004A44B3 /* KVDepthView.m in Sources */ = {isa = PBXBuildFile; fileRef = C70E62AE129C5055004A44B3 /* KVDepthView.m */; };
 		C70E62FB129C572E004A44B3 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C70E62FA129C572E004A44B3 /* OpenGL.framework */; };
 		C70E635F129C63BE004A44B3 /* trackball.c in Sources */ = {isa = PBXBuildFile; fileRef = C70E635D129C63BE004A44B3 /* trackball.c */; };
 		C70E66AA129C9024004A44B3 /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = C70E6690129C8FD5004A44B3 /* libusb.h */; };
 		C70E66BB129C906C004A44B3 /* libdriver.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C70E669F129C9005004A44B3 /* libdriver.a */; };
 		C70E66CA129C9121004A44B3 /* libusb-1.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C70E66C9129C9121004A44B3 /* libusb-1.0.dylib */; };
-		C758017712A3522700391C33 /* KVWireRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = C758017612A3522700391C33 /* KVWireRenderer.m */; };
-		C75801FB12A3590000391C33 /* KVMultiDepthMap.m in Sources */ = {isa = PBXBuildFile; fileRef = C75801FA12A3590000391C33 /* KVMultiDepthMap.m */; };
+		C758083412A4C46700391C33 /* KVRayFieldSpaceModel.m in Sources */ = {isa = PBXBuildFile; fileRef = C758083312A4C46700391C33 /* KVRayFieldSpaceModel.m */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
 		8D1107320486CEB800E47090 /* KinectViewer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KinectViewer.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		C708F28C129F95C100656901 /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = "<group>"; };
 		C708F3D8129FAE4300656901 /* utility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utility.h; sourceTree = "<group>"; };
-		C708F45F12A03E6500656901 /* KVRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVRenderer.h; sourceTree = "<group>"; };
-		C708F46012A03E8500656901 /* KVAbstractRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVAbstractRenderer.h; sourceTree = "<group>"; };
-		C708F46112A03E8500656901 /* KVAbstractRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVAbstractRenderer.m; sourceTree = "<group>"; };
-		C708F46212A03E8500656901 /* KVTriStripRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVTriStripRenderer.h; sourceTree = "<group>"; };
-		C708F46312A03E8500656901 /* KVTriStripRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVTriStripRenderer.m; sourceTree = "<group>"; };
-		C708F46612A03E8F00656901 /* KVPointCloudRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVPointCloudRenderer.h; sourceTree = "<group>"; };
-		C708F46712A03E8F00656901 /* KVPointCloudRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVPointCloudRenderer.m; sourceTree = "<group>"; };
-		C708F46812A03E8F00656901 /* KVQuadRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVQuadRenderer.h; sourceTree = "<group>"; };
-		C708F46912A03E8F00656901 /* KVQuadRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVQuadRenderer.m; sourceTree = "<group>"; };
-		C708F55412A0726800656901 /* KVDepthMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVDepthMap.h; sourceTree = "<group>"; };
-		C708F55712A0728800656901 /* KVDoubleBufferDepthMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVDoubleBufferDepthMap.h; sourceTree = "<group>"; };
-		C708F55812A0728800656901 /* KVDoubleBufferDepthMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVDoubleBufferDepthMap.m; sourceTree = "<group>"; };
-		C708F5E212A0779500656901 /* KVAveragingDepthMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVAveragingDepthMap.h; sourceTree = "<group>"; };
-		C708F5E312A0779500656901 /* KVAveragingDepthMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVAveragingDepthMap.m; sourceTree = "<group>"; };
 		C70E62AD129C5055004A44B3 /* KVDepthView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVDepthView.h; sourceTree = "<group>"; };
 		C70E62AE129C5055004A44B3 /* KVDepthView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVDepthView.m; sourceTree = "<group>"; };
 		C70E62FA129C572E004A44B3 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
 		C70E6690129C8FD5004A44B3 /* libusb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libusb.h; sourceTree = "<group>"; };
 		C70E669F129C9005004A44B3 /* libdriver.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdriver.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		C70E66C9129C9121004A44B3 /* libusb-1.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libusb-1.0.dylib"; path = "usr/local/lib/libusb-1.0.dylib"; sourceTree = SDKROOT; };
-		C758017512A3522700391C33 /* KVWireRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVWireRenderer.h; sourceTree = "<group>"; };
-		C758017612A3522700391C33 /* KVWireRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVWireRenderer.m; sourceTree = "<group>"; };
-		C75801F912A3590000391C33 /* KVMultiDepthMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVMultiDepthMap.h; sourceTree = "<group>"; };
-		C75801FA12A3590000391C33 /* KVMultiDepthMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVMultiDepthMap.m; sourceTree = "<group>"; };
+		C758081A12A4C22400391C33 /* KVSpaceModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVSpaceModel.h; sourceTree = "<group>"; };
+		C758083212A4C46700391C33 /* KVRayFieldSpaceModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVRayFieldSpaceModel.h; sourceTree = "<group>"; };
+		C758083312A4C46700391C33 /* KVRayFieldSpaceModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVRayFieldSpaceModel.m; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
 			children = (
 				C70E6683129C8FD5004A44B3 /* driver */,
 				C7F36B8812A19EBD00EBC3AB /* Model */,
-				C7F36B8A12A19ED300EBC3AB /* Renderers */,
 				080E96DDFE201D6D7F000001 /* App */,
 				29B97315FDCFA39411CA2CEA /* Other Sources */,
 				29B97317FDCFA39411CA2CEA /* Resources */,
 		C7F36B8812A19EBD00EBC3AB /* Model */ = {
 			isa = PBXGroup;
 			children = (
-				C708F55412A0726800656901 /* KVDepthMap.h */,
-				C708F55712A0728800656901 /* KVDoubleBufferDepthMap.h */,
-				C708F55812A0728800656901 /* KVDoubleBufferDepthMap.m */,
-				C708F5E212A0779500656901 /* KVAveragingDepthMap.h */,
-				C708F5E312A0779500656901 /* KVAveragingDepthMap.m */,
-				C75801F912A3590000391C33 /* KVMultiDepthMap.h */,
-				C75801FA12A3590000391C33 /* KVMultiDepthMap.m */,
+				C758081A12A4C22400391C33 /* KVSpaceModel.h */,
+				C758083212A4C46700391C33 /* KVRayFieldSpaceModel.h */,
+				C758083312A4C46700391C33 /* KVRayFieldSpaceModel.m */,
 			);
 			name = Model;
 			sourceTree = "<group>";
 		};
-		C7F36B8A12A19ED300EBC3AB /* Renderers */ = {
-			isa = PBXGroup;
-			children = (
-				C708F45F12A03E6500656901 /* KVRenderer.h */,
-				C708F46012A03E8500656901 /* KVAbstractRenderer.h */,
-				C708F46112A03E8500656901 /* KVAbstractRenderer.m */,
-				C708F46612A03E8F00656901 /* KVPointCloudRenderer.h */,
-				C708F46712A03E8F00656901 /* KVPointCloudRenderer.m */,
-				C708F46812A03E8F00656901 /* KVQuadRenderer.h */,
-				C708F46912A03E8F00656901 /* KVQuadRenderer.m */,
-				C708F46212A03E8500656901 /* KVTriStripRenderer.h */,
-				C708F46312A03E8500656901 /* KVTriStripRenderer.m */,
-				C758017512A3522700391C33 /* KVWireRenderer.h */,
-				C758017612A3522700391C33 /* KVWireRenderer.m */,
-			);
-			name = Renderers;
-			sourceTree = "<group>";
-		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
 				C70E62AF129C5055004A44B3 /* KVDepthView.m in Sources */,
 				C70E635F129C63BE004A44B3 /* trackball.c in Sources */,
 				C70E6691129C8FD5004A44B3 /* KFKinect.m in Sources */,
-				C708F46412A03E8500656901 /* KVAbstractRenderer.m in Sources */,
-				C708F46512A03E8500656901 /* KVTriStripRenderer.m in Sources */,
-				C708F46A12A03E8F00656901 /* KVPointCloudRenderer.m in Sources */,
-				C708F46B12A03E8F00656901 /* KVQuadRenderer.m in Sources */,
-				C708F55912A0728800656901 /* KVDoubleBufferDepthMap.m in Sources */,
-				C708F5E412A0779500656901 /* KVAveragingDepthMap.m in Sources */,
-				C758017712A3522700391C33 /* KVWireRenderer.m in Sources */,
-				C75801FB12A3590000391C33 /* KVMultiDepthMap.m in Sources */,
+				C758083412A4C46700391C33 /* KVRayFieldSpaceModel.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
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.