diff --git a/shaders/lab0.vert b/shaders/basic.vert
similarity index 100%
rename from shaders/lab0.vert
rename to shaders/basic.vert
diff --git a/shaders/lab0.frag b/shaders/ground.frag
similarity index 100%
rename from shaders/lab0.frag
rename to shaders/ground.frag
diff --git a/shaders/surface.frag b/shaders/surface.frag
new file mode 100644
index 0000000000000000000000000000000000000000..7d92382799e0b4e4370ba9fc4c86ebcb5e8c3c01
--- /dev/null
+++ b/shaders/surface.frag
@@ -0,0 +1,11 @@
+#version 150
+
+in float shade;
+
+out vec4 out_Color;
+
+void main(void)
+{
+	out_Color=vec4(shade,shade,shade,1.0);
+}
+
diff --git a/shaders/surface.vert b/shaders/surface.vert
new file mode 100644
index 0000000000000000000000000000000000000000..a3adbe90a7a42e871e52704cbed2f275d47f6f1e
--- /dev/null
+++ b/shaders/surface.vert
@@ -0,0 +1,17 @@
+#version 150
+
+in  vec3  in_Position;
+in  vec3  in_Normal;
+in  vec2  in_TexCoord;
+
+uniform mat4 projectionMatrix;
+uniform mat4 modelToWorldToView;
+
+out float shade;
+
+void main(void)
+{
+	shade = (mat3(modelToWorldToView)*in_Normal).z; // Fake shading
+	gl_Position=projectionMatrix*modelToWorldToView*vec4(in_Position, 1.0);
+}
+
diff --git a/shaders/waterfall.frag b/shaders/waterfall.frag
new file mode 100644
index 0000000000000000000000000000000000000000..7d92382799e0b4e4370ba9fc4c86ebcb5e8c3c01
--- /dev/null
+++ b/shaders/waterfall.frag
@@ -0,0 +1,11 @@
+#version 150
+
+in float shade;
+
+out vec4 out_Color;
+
+void main(void)
+{
+	out_Color=vec4(shade,shade,shade,1.0);
+}
+
diff --git a/src/main.cpp b/src/main.cpp
index dfd379a6893aec6633db0d216082c3b0cb008ad4..ae8883a13c31341825c64dc0ac78000ed1027f5e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,60 +1,123 @@
 // Revised 2019 with a bit better variable names.
 // Experimental C++ version 2022. Almost no code changes.
+#include <iostream>
 
 #include <GL/gl.h>
 #define MAIN
-#include "MicroGlut.h"
 #include "GL_utilities.h"
-#include "VectorUtils4.h"
 #include "LittleOBJLoader.h"
+#include "MicroGlut.h"
+#include "VectorUtils4.h"
 // uses framework OpenGL
 // uses framework Cocoa
 
-GLuint program;
+struct Object {
+    Model* model;
+    GLuint program;
+
+    Object() = default;
+
+    template <typename Func>
+    void draw(Func func) const
+    {
+        glUseProgram(program);
+        func(program);
+        DrawModel(model, program, "in_Position", "in_Normal", "in_TexCoord");
+    }
+};
+
+struct Scene {
+    Object ground;
+    Object surface;
+    Object waterfall;
+    mat4 proj_matrix = perspective(70.0, 1.0, 0.2, 20.0);
+    mat4 view_matrix = lookAtv(vec3(-5, 4, -5), vec3(0, 0, 0), vec3(0, 1, 0));
+
+
+    void init()
+    {
+        Model* ground_model = LoadModel("models/ground.obj");
+        Model* surface_model = LoadModel("models/surface.obj");
+        Model* waterfall_model = LoadModel("models/waterfall.obj");
+
+        // This is not a ground logic program
+        GLuint ground_program = loadShaders("shaders/basic.vert", "shaders/ground.frag");
+        GLuint surface_program = loadShaders("shaders/surface.vert", "shaders/surface.frag");
+        GLuint waterfall_program = loadShaders("shaders/basic.vert", "shaders/waterfall.frag");
+        printError("compiled shaders");
+        std::cout << "ground prog: " << ground_program << "\n";
+        std::cout << "surf prog: " << surface_program << "\n";
+        std::cout << "wfall prog: " << waterfall_program << "\n";
+
+        ground = Object { ground_model, ground_program };
+        surface = Object { surface_model, surface_program };
+        waterfall = Object { waterfall_model, waterfall_program };
+    }
+
+    void draw()
+    {
+        ground.draw([&](GLuint program) {
+            // Add uniforms, texture calls etc. here
+            glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, GL_TRUE, proj_matrix.m);
+            glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, GL_TRUE, view_matrix.m);
+        });
+        surface.draw([&](GLuint program) {
+            // Add uniforms, texture calls etc. here
+            glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, GL_TRUE, proj_matrix.m);
+            glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, GL_TRUE, view_matrix.m);
+        });
+        waterfall.draw([&](GLuint program) {
+            // Add uniforms, texture calls etc. here
+            glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, GL_TRUE, proj_matrix.m);
+            glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, GL_TRUE, view_matrix.m);
+        });
+    }
+};
+
+Scene scene{};
 
 void init(void)
 {
-	dumpInfo();
-
-	// GL inits
-	glClearColor(0.2,0.2,0.5,0);
-	glEnable(GL_DEPTH_TEST);
-	glEnable(GL_CULL_FACE);
-	glCullFace(GL_BACK);
-	printError("GL inits"); // This is merely a vague indication of where something might be wrong
-
-	// Load and compile shader
-	program = loadShaders("shaders/lab0.vert", "shaders/lab0.frag");
-	printError("init shader");
-}
+    dumpInfo();
 
+    // GL inits
+    glClearColor(0.2, 0.2, 0.5, 0);
+    glEnable(GL_DEPTH_TEST);
+    glEnable(GL_CULL_FACE);
+    glCullFace(GL_BACK);
+    printError("GL inits"); // This is merely a vague indication of where something might be wrong
+                            //
+    scene.init();
+
+    // Load and compile shader
+    printError("init shader");
+}
 
 void display(void)
 {
-	printError("pre display");
+    printError("pre display");
+
+    // clear the screen
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
-	// clear the screen
-	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+    // activate the program, and set its variables
+    scene.draw();
 
-	//activate the program, and set its variables
-	glUseProgram(program);
-	
-	printError("display");
-	
-	glutSwapBuffers();
+    printError("display");
+
+    glutSwapBuffers();
 }
 
-int main(int argc, char *argv[])
+int main(int argc, char* argv[])
 {
-	glutInit(&argc, argv);
-	glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);
-	glutInitContextVersion(3, 2);
-	glutInitWindowSize(800, 800);
-	glutCreateWindow ("TSBK03 Project");
-	glutDisplayFunc(display); 
-	glutRepeatingTimer(20);
-	init ();
-	glutMainLoop();
-	exit(0);
+    glutInit(&argc, argv);
+    glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
+    glutInitContextVersion(3, 2);
+    glutInitWindowSize(800, 800);
+    glutCreateWindow("TSBK03 Project");
+    glutDisplayFunc(display);
+    glutRepeatingTimer(20);
+    init();
+    glutMainLoop();
+    exit(0);
 }
-