diff --git a/applications/reconstruct/CMakeLists.txt b/applications/reconstruct/CMakeLists.txt
index 57b0e38dbdec2cc93cea9894599ecfd9fd05d8c3..f91274efb605bac4e75bba6177af09e05b2cc16a 100644
--- a/applications/reconstruct/CMakeLists.txt
+++ b/applications/reconstruct/CMakeLists.txt
@@ -5,9 +5,7 @@
 set(REPSRC
 	src/main.cpp
 	src/voxel_scene.cpp
-	src/scene_rep_hash_sdf.cu
 	#src/ray_cast_sdf.cu
-	src/voxel_render.cu
 	src/camera_util.cu
 	#src/ray_cast_sdf.cpp
 	src/registration.cpp
diff --git a/applications/reconstruct/include/ftl/scene.hpp b/applications/reconstruct/include/ftl/scene.hpp
deleted file mode 100644
index 451d469f7da0721afa8f77ea20de8229b77371ee..0000000000000000000000000000000000000000
--- a/applications/reconstruct/include/ftl/scene.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _FTL_RECONSTRUCT_SCENE_HPP_
-#define _FTL_RECONSTRUCT_SCENE_HPP_
-
-namespace ftl {
-
-class Scene {
-    public:
-    Scene();
-    ~Scene();
-
-    void getFrame(eigen::Matrix4f &pose, ftl::rgbd::Frame &);
-    ftl::rgbd::Frame getFrame(eigen::Matrix4f &pose);
-
-    void getFrameSet(ftl::rgbd::FrameSet &);
-    ftl::rgbd::FrameSet &getFrameSet();
-};
-
-}
-
-#endif  // _FTL_RECONSTRUCT_SCENE_HPP_
diff --git a/applications/reconstruct/src/ilw.cpp b/applications/reconstruct/src/ilw.cpp
index 1ca6312afb269bdd7a22b99a56c587c7b6b9c548..bef1f806a94bd3ec0b63dd06216b0ccf4571af1e 100644
--- a/applications/reconstruct/src/ilw.cpp
+++ b/applications/reconstruct/src/ilw.cpp
@@ -19,6 +19,8 @@ bool ILW::process(ftl::rgbd::FrameSet &fs) {
         for (int j=0; j<3; ++j) {
             _phase2(fs);
         }
+
+		// TODO: Break if no time left
     }
 
     return true;
@@ -26,10 +28,14 @@ bool ILW::process(ftl::rgbd::FrameSet &fs) {
 
 bool ILW::_phase0(ftl::rgbd::FrameSet &fs) {
     // Clear points channel...
+
+	// Upload camera data?
 }
 
 bool ILW::_phase1(ftl::rgbd::FrameSet &fs) {
     // Run correspondence kernel to find points
+
+	// For each camera combination
 }
 
 bool ILW::_phase2(ftl::rgbd::FrameSet &fs) {
diff --git a/applications/reconstruct/src/ilw.hpp b/applications/reconstruct/src/ilw.hpp
index 0914905e5a500580c17ee4fe8e7bf108a33e7611..ec0965af3c4f30b14530fd594aed84a5c539c85d 100644
--- a/applications/reconstruct/src/ilw.hpp
+++ b/applications/reconstruct/src/ilw.hpp
@@ -13,6 +13,12 @@ struct ILWData{
     ftl::cuda::TextureObject<float4> correspondence;
 
     ftl::cuda::TextureObject<float4> points;
+
+	// Residual potential energy
+	ftl::cuda::TextureObject<float> residual;
+
+	// Flow magnitude
+	ftl::cuda::TextureObject<float> flow;
 };
 }
 
diff --git a/applications/reconstruct/src/voxel_render.cu b/applications/reconstruct/src/voxel_render.cu
deleted file mode 100644
index 46eb6f57c031b0541add1dcd982a9c6d1e690140..0000000000000000000000000000000000000000
--- a/applications/reconstruct/src/voxel_render.cu
+++ /dev/null
@@ -1,261 +0,0 @@
-#include "splat_render_cuda.hpp"
-#include <cuda_runtime.h>
-
-#include <ftl/cuda_matrix_util.hpp>
-
-#include "splat_params.hpp"
-
-#define T_PER_BLOCK 8
-#define NUM_GROUPS_X 1024
-
-#define NUM_CUDA_BLOCKS  10000
-
-using ftl::cuda::TextureObject;
-using ftl::render::SplatParams;
-
-__global__ void clearDepthKernel(ftl::voxhash::HashData hashData, TextureObject<int> depth) {
-	const unsigned int x = blockIdx.x*blockDim.x + threadIdx.x;
-	const unsigned int y = blockIdx.y*blockDim.y + threadIdx.y;
-
-	if (x < depth.width() && y < depth.height()) {
-		depth(x,y) = 0x7f800000; //PINF;
-		//colour(x,y) = make_uchar4(76,76,82,0);
-	}
-}
-
-#define SDF_BLOCK_SIZE_PAD 8
-#define SDF_BLOCK_BUFFER 512  // > 8x8x8
-#define SDF_DX 1
-#define SDF_DY SDF_BLOCK_SIZE_PAD
-#define SDF_DZ (SDF_BLOCK_SIZE_PAD*SDF_BLOCK_SIZE_PAD)
-
-#define LOCKED 0x7FFFFFFF
-
-//! computes the (local) virtual voxel pos of an index; idx in [0;511]
-__device__ 
-int3 pdelinVoxelIndex(uint idx)	{
-	int x = idx % SDF_BLOCK_SIZE_PAD;
-	int y = (idx % (SDF_BLOCK_SIZE_PAD * SDF_BLOCK_SIZE_PAD)) / SDF_BLOCK_SIZE_PAD;
-	int z = idx / (SDF_BLOCK_SIZE_PAD * SDF_BLOCK_SIZE_PAD);	
-	return make_int3(x,y,z);
-}
-
-//! computes the linearized index of a local virtual voxel pos; pos in [0;7]^3
-__device__ 
-uint plinVoxelPos(const int3& virtualVoxelPos) {
-	return  
-		virtualVoxelPos.z * SDF_BLOCK_SIZE_PAD * SDF_BLOCK_SIZE_PAD +
-		virtualVoxelPos.y * SDF_BLOCK_SIZE_PAD +
-		virtualVoxelPos.x;
-}
-
-//! computes the linearized index of a local virtual voxel pos; pos in [0;7]^3
-__device__ 
-uint plinVoxelPos(int x, int y, int z) {
-	return  
-		z * SDF_BLOCK_SIZE_PAD * SDF_BLOCK_SIZE_PAD +
-		y * SDF_BLOCK_SIZE_PAD + x;
-}
-
-__device__  
-void deleteVoxel(ftl::voxhash::Voxel& v) {
-	v.color = make_uchar3(0,0,0);
-	v.weight = 0;
-	v.sdf = PINF;
-}
-
-__device__ inline int3 blockDelinear(const int3 &base, uint i) {
-	return make_int3(base.x + (i & 0x1), base.y + (i & 0x2), base.z + (i & 0x4));
-}
-
-__device__ inline uint blockLinear(int x, int y, int z) {
-	return x + (y << 1) + (z << 2);
-}
-
-__device__ inline bool getVoxel(uint *voxels, int ix) {
-	return voxels[ix/32] & (0x1 << (ix % 32));
-}
-
-__global__ void occupied_image_kernel(ftl::voxhash::HashData hashData, TextureObject<int> depth, SplatParams params) {
-	__shared__ uint voxels[16];
-	__shared__ ftl::voxhash::HashEntryHead block;
-
-	// Stride over all allocated blocks
-	for (int bi=blockIdx.x; bi<*hashData.d_hashCompactifiedCounter; bi+=NUM_CUDA_BLOCKS) {
-	__syncthreads();
-
-	const uint i = threadIdx.x;	//inside of an SDF block
-
-	if (i == 0) block = hashData.d_hashCompactified[bi]->head;
-	if (i < 16) {
-		voxels[i] = hashData.d_hashCompactified[bi]->voxels[i];
-		//valid[i] = hashData.d_hashCompactified[bi]->validity[i];
-	}
-
-	// Make sure all hash entries are cached
-	__syncthreads();
-
-	const int3 pi_base = hashData.SDFBlockToVirtualVoxelPos(make_int3(block.posXYZ));
-	const int3 vp = make_int3(hashData.delinearizeVoxelIndex(i));
-	const int3 pi = pi_base + vp;
-	const float3 worldPos = hashData.virtualVoxelPosToWorld(pi);
-
-	const bool v = getVoxel(voxels, i);
-
-	uchar4 color = make_uchar4(255,0,0,255);
-	bool is_surface = v; //((params.m_flags & ftl::render::kShowBlockBorders) && edgeX + edgeY + edgeZ >= 2);
-
-
-	// Only for surface voxels, work out screen coordinates
-	if (!is_surface) continue;
-
-	// TODO: For each original camera, render a new depth map
-
-	const float3 camPos = params.m_viewMatrix * worldPos;
-	const float2 screenPosf = params.camera.cameraToKinectScreenFloat(camPos);
-	const uint2 screenPos = make_uint2(make_int2(screenPosf)); //  + make_float2(0.5f, 0.5f)
-
-	//printf("Worldpos: %f,%f,%f\n", camPos.x, camPos.y, camPos.z);
-
-	if (camPos.z < params.camera.m_sensorDepthWorldMin) continue;
-
-	const unsigned int x = screenPos.x;
-	const unsigned int y = screenPos.y;
-	const int idepth = static_cast<int>(camPos.z * 1000.0f);
-
-	// See: Gunther et al. 2013. A GPGPU-based Pipeline for Accelerated Rendering of Point Clouds
-	if (x < depth.width() && y < depth.height()) {
-		atomicMin(&depth(x,y), idepth);
-	}
-
-	}  // Stride
-}
-
-__global__ void isosurface_image_kernel(ftl::voxhash::HashData hashData, TextureObject<int> depth, SplatParams params) {
-	// TODO:(Nick) Reduce bank conflicts by aligning these
-	__shared__ uint voxels[16];
-	//__shared__ uint valid[16];
-	__shared__ ftl::voxhash::HashEntryHead block;
-
-	// Stride over all allocated blocks
-	for (int bi=blockIdx.x; bi<*hashData.d_hashCompactifiedCounter; bi+=NUM_CUDA_BLOCKS) {
-	__syncthreads();
-
-	const uint i = threadIdx.x;	//inside of an SDF block
-
-	if (i == 0) block = hashData.d_hashCompactified[bi]->head;
-	if (i < 16) {
-		voxels[i] = hashData.d_hashCompactified[bi]->voxels[i];
-		//valid[i] = hashData.d_hashCompactified[bi]->validity[i];
-	}
-
-	// Make sure all hash entries are cached
-	__syncthreads();
-
-	const int3 pi_base = hashData.SDFBlockToVirtualVoxelPos(make_int3(block.posXYZ));
-	const int3 vp = make_int3(hashData.delinearizeVoxelIndex(i));
-	const int3 pi = pi_base + vp;
-	//const uint j = plinVoxelPos(vp);  // Padded linear index
-	const float3 worldPos = hashData.virtualVoxelPosToWorld(pi);
-
-	// Load distances and colours into shared memory + padding
-	//const ftl::voxhash::Voxel &v = hashData.d_SDFBlocks[block.ptr + i];
-	//voxels[j] = v;
-	const bool v = getVoxel(voxels, i);
-
-	//__syncthreads();
-
-	//if (voxels[j].weight == 0) continue;
-	if (vp.x == 7 || vp.y == 7 || vp.z == 7) continue;
-
-
-	int edgeX = (vp.x == 0 ) ? 1 : 0;
-	int edgeY = (vp.y == 0 ) ? 1 : 0;
-	int edgeZ = (vp.z == 0 ) ? 1 : 0;
-
-	uchar4 color = make_uchar4(255,0,0,255);
-	bool is_surface = v; //((params.m_flags & ftl::render::kShowBlockBorders) && edgeX + edgeY + edgeZ >= 2);
-	//if (is_surface) color = make_uchar4(255,(vp.x == 0 && vp.y == 0 && vp.z == 0) ? 255 : 0,0,255);
-
-	if (v) continue;  // !getVoxel(valid, i)
-
-	//if (vp.z == 7) voxels[j].color = make_uchar3(0,255,(voxels[j].sdf < 0.0f) ? 255 : 0);
-
-	// Identify surfaces through sign change. Since we only check in one direction
-	// it is fine to check for any sign change?
-
-
-#pragma unroll
-	for (int u=0; u<=1; u++) {
-		for (int v=0; v<=1; v++) {
-			for (int w=0; w<=1; w++) {
-				const int3 uvi = make_int3(vp.x+u,vp.y+v,vp.z+w);
-
-				// Skip these cases since we didn't load voxels properly
-				//if (uvi.x == 8 || uvi.z == 8 || uvi.y == 8) continue;
-
-				const bool vox = getVoxel(voxels, hashData.linearizeVoxelPos(uvi));
-				if (vox) { //getVoxel(valid, hashData.linearizeVoxelPos(uvi))) {
-					is_surface = true;
-					// Should break but is slower?
-				}
-			}
-		}
-	}
-
-	// Only for surface voxels, work out screen coordinates
-	if (!is_surface) continue;
-
-	// TODO: For each original camera, render a new depth map
-
-	const float3 camPos = params.m_viewMatrix * worldPos;
-	const float2 screenPosf = params.camera.cameraToKinectScreenFloat(camPos);
-	const uint2 screenPos = make_uint2(make_int2(screenPosf)); //  + make_float2(0.5f, 0.5f)
-
-	//printf("Worldpos: %f,%f,%f\n", camPos.x, camPos.y, camPos.z);
-
-	if (camPos.z < params.camera.m_sensorDepthWorldMin) continue;
-
-	// For this voxel in hash, get its screen position and check it is on screen
-	// Convert depth map to int by x1000 and use atomicMin
-	//const int pixsize = static_cast<int>((c_hashParams.m_virtualVoxelSize*params.camera.fx/(camPos.z*0.8f)))+1;  // Magic number increase voxel to ensure coverage
-
-	const unsigned int x = screenPos.x;
-	const unsigned int y = screenPos.y;
-	const int idepth = static_cast<int>(camPos.z * 1000.0f);
-
-	// See: Gunther et al. 2013. A GPGPU-based Pipeline for Accelerated Rendering of Point Clouds
-	if (x < depth.width() && y < depth.height()) {
-		atomicMin(&depth(x,y), idepth);
-	}
-
-	}  // Stride
-}
-
-void ftl::cuda::isosurface_point_image(const ftl::voxhash::HashData& hashData,
-			const TextureObject<int> &depth,
-			const SplatParams &params, cudaStream_t stream) {
-
-	const dim3 clear_gridSize((depth.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (depth.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
-	const dim3 clear_blockSize(T_PER_BLOCK, T_PER_BLOCK);
-
-	clearDepthKernel<<<clear_gridSize, clear_blockSize, 0, stream>>>(hashData, depth);
-
-#ifdef _DEBUG
-	cudaSafeCall(cudaDeviceSynchronize());
-#endif
-
-	const unsigned int threadsPerBlock = SDF_BLOCK_SIZE*SDF_BLOCK_SIZE*SDF_BLOCK_SIZE;
-	const dim3 gridSize(NUM_CUDA_BLOCKS, 1);
-	const dim3 blockSize(threadsPerBlock, 1);
-
-	occupied_image_kernel<<<gridSize, blockSize, 0, stream>>>(hashData, depth, params);
-
-	cudaSafeCall( cudaGetLastError() );
-
-#ifdef _DEBUG
-	cudaSafeCall(cudaDeviceSynchronize());
-#endif
-}
-
-
diff --git a/applications/reconstruct/src/voxel_scene.cpp b/applications/reconstruct/src/voxel_scene.cpp
index ab8b2c1dd25b530e1ffb4fcf4a29c42bd86dfbb6..63844fcc8c5dc6cf188166e5d17dbdaadd4cb8ae 100644
--- a/applications/reconstruct/src/voxel_scene.cpp
+++ b/applications/reconstruct/src/voxel_scene.cpp
@@ -1,7 +1,4 @@
 #include <ftl/voxel_scene.hpp>
-#include "compactors.hpp"
-#include "garbage.hpp"
-#include "integrators.hpp"
 #include "depth_camera_cuda.hpp"
 
 #include <opencv2/core/cuda_stream_accessor.hpp>
@@ -16,9 +13,9 @@ using std::vector;
 
 #define 	SAFE_DELETE_ARRAY(a)   { delete [] (a); (a) = NULL; }
 
-extern "C" void resetCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams);
-extern "C" void resetHashBucketMutexCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams, cudaStream_t);
-extern "C" void allocCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams, int camid, const DepthCameraParams &depthCameraParams, cudaStream_t);
+//extern "C" void resetCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams);
+//extern "C" void resetHashBucketMutexCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams, cudaStream_t);
+//extern "C" void allocCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams, int camid, const DepthCameraParams &depthCameraParams, cudaStream_t);
 //extern "C" void fillDecisionArrayCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams, const DepthCameraData& depthCameraData);
 //extern "C" void compactifyHashCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams);
 //extern "C" unsigned int compactifyHashAllInOneCUDA(ftl::voxhash::HashData& hashData, const ftl::voxhash::HashParams& hashParams);
@@ -184,7 +181,7 @@ int SceneRep::upload() {
 		//if (i > 0) cudaSafeCall(cudaStreamSynchronize(cv::cuda::StreamAccessor::getStream(cameras_[i-1].stream)));
 
 		//allocate all hash blocks which are corresponding to depth map entries
-		if (value("voxels", false)) _alloc(i, cv::cuda::StreamAccessor::getStream(cam.stream));
+		//if (value("voxels", false)) _alloc(i, cv::cuda::StreamAccessor::getStream(cam.stream));
 
 		// Calculate normals
 	}
@@ -264,7 +261,7 @@ int SceneRep::upload(ftl::rgbd::FrameSet &fs) {
 		//if (i > 0) cudaSafeCall(cudaStreamSynchronize(cv::cuda::StreamAccessor::getStream(cameras_[i-1].stream)));
 
 		//allocate all hash blocks which are corresponding to depth map entries
-		if (value("voxels", false)) _alloc(i, cv::cuda::StreamAccessor::getStream(cam.stream));
+		//if (value("voxels", false)) _alloc(i, cv::cuda::StreamAccessor::getStream(cam.stream));
 
 		// Calculate normals
 	}
@@ -299,7 +296,7 @@ void SceneRep::integrate() {
 
 void SceneRep::garbage() {
 	//_compactifyAllocated();
-	if (value("voxels", false)) _garbageCollect();
+	//if (value("voxels", false)) _garbageCollect();
 
 	//cudaSafeCall(cudaStreamSynchronize(integ_stream_));
 }
@@ -419,19 +416,19 @@ void SceneRep::_alloc(int camid, cudaStream_t stream) {
 	}
 	else {*/
 		//this version is faster, but it doesn't guarantee that all blocks are allocated (staggers alloc to the next frame)
-		resetHashBucketMutexCUDA(m_hashData, m_hashParams, stream);
-		allocCUDA(m_hashData, m_hashParams, camid, cameras_[camid].params, stream);
+		//resetHashBucketMutexCUDA(m_hashData, m_hashParams, stream);
+		//allocCUDA(m_hashData, m_hashParams, camid, cameras_[camid].params, stream);
 	//}
 }
 
 
 void SceneRep::_compactifyVisible(const DepthCameraParams &camera) { //const DepthCameraData& depthCameraData) {
-	ftl::cuda::compactifyOccupied(m_hashData, m_hashParams, integ_stream_);		//this version uses atomics over prefix sums, which has a much better performance
+	//ftl::cuda::compactifyOccupied(m_hashData, m_hashParams, integ_stream_);		//this version uses atomics over prefix sums, which has a much better performance
 	//m_hashData.updateParams(m_hashParams);	//make sure numOccupiedBlocks is updated on the GPU
 }
 
 void SceneRep::_compactifyAllocated() {
-	ftl::cuda::compactifyAllocated(m_hashData, m_hashParams, integ_stream_);		//this version uses atomics over prefix sums, which has a much better performance
+	//ftl::cuda::compactifyAllocated(m_hashData, m_hashParams, integ_stream_);		//this version uses atomics over prefix sums, which has a much better performance
 	//std::cout << "Occ blocks = " << m_hashParams.m_numOccupiedBlocks << std::endl;
 	//m_hashData.updateParams(m_hashParams);	//make sure numOccupiedBlocks is updated on the GPU
 }
@@ -441,7 +438,7 @@ void SceneRep::_compactifyAllocated() {
 	else ftl::cuda::integrateRegistration(m_hashData, m_hashParams, depthCameraData, depthCameraParams, integ_stream_);
 }*/
 
-extern "C" void bilateralFilterFloatMap(float* d_output, float* d_input, float sigmaD, float sigmaR, unsigned int width, unsigned int height);
+//extern "C" void bilateralFilterFloatMap(float* d_output, float* d_input, float sigmaD, float sigmaR, unsigned int width, unsigned int height);
 
 void SceneRep::_integrateDepthMaps() {
 	//cudaSafeCall(cudaDeviceSynchronize());
@@ -456,11 +453,11 @@ void SceneRep::_integrateDepthMaps() {
 		//ftl::cuda::hole_fill(*(cameras_[i].gpu.depth2_tex_), *(cameras_[i].gpu.depth_tex_), cameras_[i].params, integ_stream_);
 		//bilateralFilterFloatMap(cameras_[i].gpu.depth_tex_->devicePtr(), cameras_[i].gpu.depth3_tex_->devicePtr(), 3, 7, cameras_[i].gpu.depth_tex_->width(), cameras_[i].gpu.depth_tex_->height());
 	}
-	if (value("voxels", false)) ftl::cuda::integrateDepthMaps(m_hashData, m_hashParams, cameras_.size(), integ_stream_);
+	//if (value("voxels", false)) ftl::cuda::integrateDepthMaps(m_hashData, m_hashParams, cameras_.size(), integ_stream_);
 }
 
 void SceneRep::_garbageCollect() {
 	//ftl::cuda::garbageCollectIdentify(m_hashData, m_hashParams, integ_stream_);
-	resetHashBucketMutexCUDA(m_hashData, m_hashParams, integ_stream_);	//needed if linked lists are enabled -> for memeory deletion
-	ftl::cuda::garbageCollectFree(m_hashData, m_hashParams, integ_stream_);
+	//resetHashBucketMutexCUDA(m_hashData, m_hashParams, integ_stream_);	//needed if linked lists are enabled -> for memeory deletion
+	//ftl::cuda::garbageCollectFree(m_hashData, m_hashParams, integ_stream_);
 }
diff --git a/applications/reconstruct/include/ftl/cuda_operators.hpp b/components/common/cpp/include/ftl/cuda_operators.hpp
similarity index 100%
rename from applications/reconstruct/include/ftl/cuda_operators.hpp
rename to components/common/cpp/include/ftl/cuda_operators.hpp
diff --git a/applications/reconstruct/include/ftl/cuda_util.hpp b/components/common/cpp/include/ftl/cuda_util.hpp
similarity index 100%
rename from applications/reconstruct/include/ftl/cuda_util.hpp
rename to components/common/cpp/include/ftl/cuda_util.hpp
diff --git a/components/rgbd-sources/include/ftl/rgbd/camera.hpp b/components/rgbd-sources/include/ftl/rgbd/camera.hpp
index 8acad9c41d6c4950398cde7d9f44ab8ae0bc08b6..5e410ab5ed563688796f2c14d4649e86ee8b37f5 100644
--- a/components/rgbd-sources/include/ftl/rgbd/camera.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/camera.hpp
@@ -2,23 +2,69 @@
 #ifndef _FTL_RGBD_CAMERA_PARAMS_HPP_
 #define _FTL_RGBD_CAMERA_PARAMS_HPP_
 
+#include <vector_types.h>
+#include <cuda_runtime.h>
+#include <ftl/cuda_util.hpp>
+
 namespace ftl{
 namespace rgbd {
 
-struct Camera {
-	double fx;
-	double fy;
-	double cx;
-	double cy;
-	unsigned int width;
-	unsigned int height;
-	double minDepth;
-	double maxDepth;
-	double baseline;
-	double doffs;
+/**
+ * All properties associated with cameras. This structure is designed to
+ * operate on CPU and GPU.
+ */
+struct __align__(16) Camera {
+	double fx;				// Focal length X
+	double fy;				// Focal length Y (usually same as fx)
+	double cx;				// Principle point Y
+	double cy;				// Principle point Y
+	unsigned int width;		// Pixel width
+	unsigned int height;	// Pixel height
+	double minDepth;		// Near clip in meters
+	double maxDepth;		// Far clip in meters
+	double baseline;		// For stereo pair
+	double doffs;			// Disparity offset
+
+	/**
+	 * Convert camera coordinates into screen coordinates.
+	 */
+	template <typename T> __device__ T camToScreen(const float3 &pos) const;
+
+	/**
+	 * Convert screen plus depth into camera coordinates.
+	 */
+	__device__ float3 screenToCam(uint ux, uint uy, float depth) const; 
 };
 
 };
 };
 
+// ---- IMPLEMENTATIONS --------------------------------------------------------
+
+template <> __device__
+inline float2 ftl::rgbd::Camera::camToScreen<float2>(const float3 &pos) const {
+	return make_float2(
+			pos.x*fx/pos.z + cx,			
+			pos.y*fy/pos.z + cy);
+}
+
+template <> __device__
+inline int2 ftl::rgbd::Camera::camToScreen<int2>(const float3 &pos) const {
+	float2 pImage = camToScreen<float2>(pos);
+	return make_int2(pImage + make_float2(0.5f, 0.5f));
+}
+
+template <> __device__
+inline uint2 ftl::rgbd::Camera::camToScreen<uint2>(const float3 &pos) const {
+	int2 p = camToScreen<int2>(pos);
+	return make_uint2(p.x, p.y);
+}
+
+__device__
+inline float3 ftl::rgbd::Camera::screenToCam(uint ux, uint uy, float depth) const {
+	const float x = ((float)ux-cx) / fx;
+	const float y = ((float)uy-cy) / fy;
+	return make_float3(depth*x, depth*y, depth);
+}
+
 #endif
diff --git a/components/scene-sources/include/ftl/scene/framescene.hpp b/components/scene-sources/include/ftl/scene/framescene.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0235a60331899ca125514e0905c845bafd1e466c
--- /dev/null
+++ b/components/scene-sources/include/ftl/scene/framescene.hpp
@@ -0,0 +1,28 @@
+#ifndef _FTL_SCENE_FRAMESCENE_HPP_
+#define _FTL_SCENE_FRAMESCENE_HPP_
+
+#include <ftl/scene/scene.hpp>
+
+namespace ftl {
+namespace scene {
+
+/**
+ * A scene represented internally as a set of image frames that together
+ * define a point cloud.
+ */
+class FrameScene : public ftl::scene::Scene {
+	public:
+	FrameScene();
+	~FrameScene();
+
+	bool update(ftl::rgbd::FrameSet &);
+
+	bool render(ftl::rgbd::Source *, ftl::rgbd::Frame &);
+	bool encode(std::vector<uint8_t> &);
+	bool decode(const std::vector<uint8_t> &);
+};
+
+}
+}
+
+#endif  // _FTL_SCENE_FRAMESCENE_HPP_
diff --git a/components/scene-sources/include/ftl/scene/scene.hpp b/components/scene-sources/include/ftl/scene/scene.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..856819cf403982d54ea3fd12a3cd195b9d437919
--- /dev/null
+++ b/components/scene-sources/include/ftl/scene/scene.hpp
@@ -0,0 +1,21 @@
+#ifndef _FTL_RECONSTRUCT_SCENE_HPP_
+#define _FTL_RECONSTRUCT_SCENE_HPP_
+
+namespace ftl {
+namespace scene {
+
+class Scene {
+    public:
+    Scene();
+    virtual ~Scene();
+
+    virtual bool render(ftl::rgbd::Source *, ftl::rgbd::Frame &)=0;
+
+	virtual bool encode(std::vector<uint8_t> &)=0;
+	virtual bool decode(const std::vector<uint8_t> &)=0;
+};
+
+}  // scene
+}  // ftl
+
+#endif  // _FTL_RECONSTRUCT_SCENE_HPP_