diff --git a/demo/main.cpp b/demo/main.cpp
index a1f1eacb83bc1c0e69091f2d7ce523d1b4b26a60..677e31b0a12557c49753896223570d98a77478a8 100644
--- a/demo/main.cpp
+++ b/demo/main.cpp
@@ -82,54 +82,33 @@ int main(int argc, char* argv[]) {
 	ecs.registerComponent<fggl::gfx::MeshToken>();
 	ecs.registerComponent<fggl::math::Transform>();
 
-	int nCubes = 3;
-	int nSlopes = 3;
-
-	for (int i=0; i<nCubes; i++) {
-		// create an entity
-		auto entity = ecs.createEntity();
-
-		// set the position
-		auto result = ecs.addComponent<fggl::math::Transform>(entity);
-		result->origin( glm::vec3( 0.0f, 0.0f, i  * -1.0f) );
-
-		// in a supprise to no one it's a triangle
-		auto mesh = fggl::data::make_cube();
-		auto token = meshRenderer.upload(mesh);
-		token.pipeline = shaderNormals;
-		ecs.addComponent<fggl::gfx::MeshToken>(entity, token);
-	}
+	int nCubes = 1;
+	int nSections = 3;
 
 	constexpr float HALF_PI = M_PI / 2.0f;
-	for (int i=0; i<nSlopes; i++) {
-		// create an entity
+
+	for ( int i=0; i<nCubes; i++ ) {
 		auto entity = ecs.createEntity();
 
 		// set the position
 		auto result = ecs.addComponent<fggl::math::Transform>(entity);
-		result->origin( glm::vec3( 1.0f, 0.0f, i * -1.0f) );
-		result->euler( glm::vec3( 0.0f, HALF_PI, 0.0f) );
+		result->origin( glm::vec3( 0.0f, 0.0f, i * -1.0f) );
 
-		// in a supprise to no one it's a triangle
-		auto mesh = fggl::data::make_slope();
-		auto token = meshRenderer.upload(mesh);
-		token.pipeline = shaderNormals;
-		ecs.addComponent<fggl::gfx::MeshToken>(entity, token);
-	}
+		fggl::data::Mesh mesh;
+		for (int i=-(nSections/2); i<=nSections/2; i++) {
+			const auto shapeOffset = glm::vec3( 0.0f, 0.0f, i * 1.0f );
 
-	for (int i=0; i<nSlopes; i++) {
-		// create an entity
-		auto entity = ecs.createEntity();
+			const auto cubeMat = glm::translate( fggl::math::mat4( 1.0f ) , shapeOffset );
+			const auto leftSlope = fggl::math::modelMatrix( glm::vec3(-1.0f, 0.0f, 0.0f) + shapeOffset, glm::vec3( 0.0f, -HALF_PI, 0.0f) );
+			const auto rightSlope = fggl::math::modelMatrix( glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset, glm::vec3( 0.0f, HALF_PI, 0.0f) );
 
-		// set the position
-		auto result = ecs.addComponent<fggl::math::Transform>(entity);
-		result->origin( glm::vec3( -1.0f, 0.0f, i * -1.0f) );
-		result->euler( glm::vec3( 0.0f, -HALF_PI, 0.0f) );
+			fggl::data::make_cube( mesh, cubeMat );
+			fggl::data::make_slope( mesh, leftSlope );
+			fggl::data::make_slope( mesh, rightSlope );
+		}
 
-		// in a supprise to no one it's a triangle
-		auto mesh = fggl::data::make_slope();
-		auto token = meshRenderer.upload(mesh);
-		token.pipeline = shaderNormals;
+		auto token = meshRenderer.upload( mesh );
+		token.pipeline = shaderPhong;
 		ecs.addComponent<fggl::gfx::MeshToken>(entity, token);
 	}
 
@@ -143,12 +122,12 @@ int main(int argc, char* argv[]) {
 		// update step
 		time += dt;
 
-/*		float amount = glm::radians( time / 2048.0f * 360.0f );
+		float amount = glm::radians( time / 2048.0f * 360.0f );
 		auto spinners = ecs.getEntityWith<fggl::math::Transform>();
 		for ( auto entity : spinners ) {
 			auto transform = ecs.getComponent<fggl::math::Transform>(entity);
 			transform->euler(glm::vec3(0.0f, amount, 0.0f));
-		}*/
+		}
 
 		// render step
 		ogl.clear();
diff --git a/fggl/data/procedural.cpp b/fggl/data/procedural.cpp
index 0c832137143bb24e58055aa17d8869f53cb06f57..0f65c60ef702e0140f33916ef6056643a61fef67 100644
--- a/fggl/data/procedural.cpp
+++ b/fggl/data/procedural.cpp
@@ -2,7 +2,10 @@
 #include "procedural.hpp"
 #include "model.hpp"
 
+#include <glm/ext/matrix_transform.hpp>
+#include <glm/gtc/quaternion.hpp>
 #include <iostream>
+
 #include <glm/geometric.hpp>
 
 using namespace fggl::data;
@@ -14,15 +17,17 @@ static glm::vec3 calcSurfaceNormal(glm::vec3 a, glm::vec3 b, glm::vec3 c) {
 	return glm::normalize( glm::cross( u, v ) );
 }
 
-static void computeNormals( fggl::data::Mesh& mesh, const int* idx, const int* colIdx, int points) {
+static void computeNormals( fggl::data::Mesh& mesh, const int* idx, const int* colIdx, int nPoints) {
 
 	// we're assuming all the normals are zero...
-	for (auto& vertex : mesh.vertexList() ) {
+	// FIXME this will touch any vertex that appears in idx more than once mutliple times...
+	for ( int i=0; i<nPoints; i++ ) {
+		auto& vertex = mesh.vertex( colIdx[ idx[i] ] );
 		vertex.normal = glm::vec3(0.0f);
 	}
 
 	// each vertex normal should be the sum of the triangles it's part of.
-	for (int i=0; i<points; i += 3) {
+	for (int i=0; i<nPoints; i += 3) {
 		auto& v1 = mesh.vertex( colIdx[ idx[i] ] );
 		auto& v2 = mesh.vertex( colIdx[ idx[i + 1] ] );
 		auto& v3 = mesh.vertex( colIdx[ idx[i + 2] ] );
@@ -30,9 +35,9 @@ static void computeNormals( fggl::data::Mesh& mesh, const int* idx, const int* c
 		const glm::vec3 normal = calcSurfaceNormal( v1.posititon, v2.posititon, v3.posititon );
 //		v2.normal += calcSurfaceNormal( v2.posititon, v3.posititon, v1.posititon );
 //		v3.normal += calcSurfaceNormal( v3.posititon, v1.posititon, v2.posititon );
-		v1.normal = normal;
-		v2.normal = normal;
-		v3.normal = normal;
+		v1.normal += normal;
+		v2.normal += normal;
+		v3.normal += normal;
 
 	}
 
@@ -43,30 +48,25 @@ static void computeNormals( fggl::data::Mesh& mesh, const int* idx, const int* c
 	}
 }
 
-static void computeNormalsDirect( fggl::data::Mesh& mesh, const int* colIdx, int points) {
+static void computeNormalsDirect( fggl::data::Mesh& mesh, const int* colIdx, int nPoints) {
 
 	// we're assuming all the normals are zero...
-	for (auto& vertex : mesh.vertexList() ) {
+	for ( int i=0; i<nPoints; i++ ) {
+		auto& vertex = mesh.vertex( colIdx[i] );
 		vertex.normal = glm::vec3(0.0f);
 	}
 
-	// each vertex normal should be the sum of the triangles it's part of.
-	for (int i=0; i<points; i += 3) {
+	// We're assuming each vertex appears only once (because we're not indexed)
+	for (int i=0; i<nPoints; i += 3) {
 		auto& v1 = mesh.vertex( colIdx[ i ] );
 		auto& v2 = mesh.vertex( colIdx[ i + 1 ] );
 		auto& v3 = mesh.vertex( colIdx[ i + 2 ] );
 
-		const glm::vec3 normal = calcSurfaceNormal( v1.posititon, v2.posititon, v3.posititon );
+		const glm::vec3 normal = glm::normalize( calcSurfaceNormal( v1.posititon, v2.posititon, v3.posititon ) );
 		v1.normal = normal;
 		v2.normal = normal;
 		v3.normal = normal;
 	}
-
-	// each vertex should currently be the sum of every triangle it's part of
-	// so we need to normalize them
-	for (auto& vertex : mesh.vertexList() ) {
-		vertex.normal = glm::normalize( vertex.normal );
-	}
 }
 
 fggl::data::Mesh fggl::data::make_triangle() {
@@ -153,7 +153,28 @@ fggl::data::Mesh fggl::data::make_quad_xz() {
 }
 
 
-fggl::data::Mesh fggl::data::make_cube() {
+constexpr float HALF_PI = M_PI / 2.0f;
+static void populateMesh(fggl::data::Mesh& mesh, const fggl::math::mat4 transform, 
+		          int nIdx, const fggl::math::vec3* pos, const int* idx ) {
+	int colIdx[nIdx];
+
+	// generate mesh
+	for (int i=0; i<nIdx; i++) {
+		glm::vec3 rawPos = transform * glm::vec4( pos[ idx[i] ], 1.0 );
+
+		Vertex vert{};
+		vert.posititon = rawPos;
+		vert.normal = glm::vec3(0.0f, 0.0f, 0.0f);
+		vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f);
+		colIdx[ i ] = mesh.pushVertex( vert );
+		mesh.pushIndex( colIdx[i] );
+	}
+
+	computeNormalsDirect( mesh, colIdx, nIdx );
+}
+
+
+fggl::data::Mesh fggl::data::make_cube(fggl::data::Mesh& mesh, const fggl::math::mat4& transform) {
 
 	// done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7
 	constexpr fggl::math::vec3 pos[] {
@@ -183,25 +204,11 @@ fggl::data::Mesh fggl::data::make_cube() {
 	};
 
 	int nIdx = sizeof(idx) / sizeof(int);
-	int colIdx[nIdx];
-
-	// generate mesh
-	fggl::data::Mesh mesh;
-	for (int i=0; i<nIdx; i++) {
-		Vertex vert{};
-		vert.posititon = pos[ idx[i] ];
-		vert.normal = glm::vec3(0.0f, 0.0f, 0.0f);
-		vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f);
-		colIdx[ i ] = mesh.pushVertex( vert );
-		mesh.pushIndex( colIdx[i] );
-	}
-
-	computeNormalsDirect( mesh, colIdx, nIdx );
-
+	populateMesh(mesh, transform, nIdx, pos, idx);
 	return mesh;
 }
 
-fggl::data::Mesh fggl::data::make_slope() {
+fggl::data::Mesh fggl::data::make_slope(fggl::data::Mesh& mesh, const fggl::math::mat4& transform) {
 
 	// done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7
 	// FIXME remove 2 and 3 and renumber the index list accordingly
@@ -228,24 +235,11 @@ fggl::data::Mesh fggl::data::make_slope() {
 	};
 
 	int nIdx = sizeof(idx) / sizeof(int);
-	int colIdx[nIdx];
-
-	// generate mesh
-	fggl::data::Mesh mesh;
-	for (int i=0; i<nIdx; i++) {
-		Vertex vert{};
-		vert.posititon = pos[ idx[i] ];
-		vert.normal = glm::vec3(0.0f, 0.0f, 0.0f);
-		vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f);
-		colIdx[ i ] = mesh.pushVertex( vert );
-		mesh.pushIndex( colIdx[i] );
-	}
-
-	computeNormalsDirect( mesh, colIdx, nIdx );
+	populateMesh(mesh, transform, nIdx, pos, idx);
 	return mesh;
 }
 
-fggl::data::Mesh fggl::data::make_ditch() {
+fggl::data::Mesh fggl::data::make_ditch(fggl::data::Mesh& mesh, const fggl::math::mat4& transform) {
 
 	// done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7
 	// FIXME remove 2 and renumber the index list accordingly
@@ -274,24 +268,11 @@ fggl::data::Mesh fggl::data::make_ditch() {
 	};
 
 	int nIdx = sizeof(idx) / sizeof(int);
-	int colIdx[nIdx];
-
-	// generate mesh
-	fggl::data::Mesh mesh;
-	for (int i=0; i<nIdx; i++) {
-		Vertex vert{};
-		vert.posititon = pos[ idx[i] ];
-		vert.normal = glm::vec3(0.0f, 0.0f, 0.0f);
-		vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f);
-		colIdx[ i ] = mesh.pushVertex( vert );
-		mesh.pushIndex( colIdx[i] );
-	}
-
-	computeNormalsDirect( mesh, colIdx, nIdx );
+	populateMesh(mesh, transform, nIdx, pos, idx);
 	return mesh;
 }
 
-fggl::data::Mesh fggl::data::make_point() {
+fggl::data::Mesh fggl::data::make_point(fggl::data::Mesh& mesh, const fggl::math::mat4& transform) {
 
 	// done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7
 	constexpr fggl::math::vec3 pos[] {
@@ -315,20 +296,7 @@ fggl::data::Mesh fggl::data::make_point() {
 	};
 
 	int nIdx = sizeof(idx) / sizeof(int);
-	int colIdx[nIdx];
-
-	// generate mesh
-	fggl::data::Mesh mesh;
-	for (int i=0; i<nIdx; i++) {
-		Vertex vert{};
-		vert.posititon = pos[ idx[i] ];
-		vert.normal = glm::vec3(0.0f, 0.0f, 0.0f);
-		vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f);
-		colIdx[ i ] = mesh.pushVertex( vert );
-		mesh.pushIndex( colIdx[i] );
-	}
-
-	computeNormalsDirect( mesh, colIdx, nIdx );
+	populateMesh(mesh, transform, nIdx, pos, idx);
 	return mesh;
 }
 
diff --git a/fggl/data/procedural.hpp b/fggl/data/procedural.hpp
index 1e39d9437038a8f7b10c9d36f02e44c93e90dacb..35a21800e2650ffb08f7fce9806ec24560eebdea 100644
--- a/fggl/data/procedural.hpp
+++ b/fggl/data/procedural.hpp
@@ -3,6 +3,8 @@
 
 namespace fggl::data {
 
+	constexpr math::mat4 OFFSET_NONE(1.0f);
+
 	Mesh make_triangle();
 
 	// quads
@@ -10,10 +12,24 @@ namespace fggl::data {
 	Mesh make_quad_xz();
 
 	// simple shapes
-	Mesh make_cube();
+	Mesh make_cube(Mesh& mesh, const math::mat4& offset);
+	inline Mesh make_cube(Mesh& mesh) {
+		return make_cube(mesh, OFFSET_NONE);
+	}
 
 	// blockout shapes
-	Mesh make_slope();
-	Mesh make_ditch();
-	Mesh make_point();
+	Mesh make_slope(Mesh& mesh, const math::mat4& offset);
+	inline Mesh make_slope(Mesh& mesh) {
+		return make_slope(mesh, OFFSET_NONE);
+	}
+
+	Mesh make_ditch(Mesh& mesh, const math::mat4& offset);
+	inline Mesh make_ditch(Mesh& mesh) {
+		return make_ditch(mesh, OFFSET_NONE);
+	}
+
+	Mesh make_point(Mesh& mesh, const math::mat4& offset);
+	inline Mesh make_point(Mesh& mesh) {
+		return make_point(mesh, OFFSET_NONE);
+	}
 }
diff --git a/fggl/math/types.hpp b/fggl/math/types.hpp
index 77373e6edb29610c97739fe26f9f46907eb06de7..32d45d308031d4d1b22fb0d4eeca3d68f0d382f3 100644
--- a/fggl/math/types.hpp
+++ b/fggl/math/types.hpp
@@ -20,6 +20,14 @@ namespace fggl::math {
 	constexpr vec3 FORWARD { 1.0f, 0.0f, 0.0f };
 	constexpr vec3 RIGHT { 0.0f, 0.0f, 1.0f };
 
+	inline glm::mat4 modelMatrix( const vec3 offset, const quat rotation ) {
+		return glm::translate( glm::mat4(1.0f), offset ) * glm::toMat4( rotation );
+	}
+
+	inline glm::mat4 modelMatrix( glm::vec3 offset, glm::vec3 eulerAngles ) {
+		return modelMatrix( offset, glm::quat( eulerAngles ) );
+	}
+
 	struct Transform {
 
 		Transform() : m_local(1.0f), m_origin(0.0f), m_model(1.0f), m_rotation() {