diff --git a/src/frag.glsl b/src/frag.glsl
index b1fee38751e553cf9ebfb25c2dd0de82160e6b2a..e3685cd4620e76f9dcd48a6db3aebe29adaf18a0 100644
--- a/src/frag.glsl
+++ b/src/frag.glsl
@@ -1,10 +1,21 @@
 #version 450
 
-layout(location=0)in vec3 fragColor;
+layout(location=0)in vec3 normal;
 layout(location=0)out vec4 f_color;
 
-const vec3 light=normalize(vec3(4,6,8));
+layout(set=0,binding=0)uniform Data{
+    vec4[32]pos;
+    vec4[32]col;
+    uint light_count;
+}uniforms;
 
 void main(){
-    f_color=vec4(vec3(dot(fragColor,light))*.5+.5,1.);
+    vec3 accum=vec3(0.,0.,0.);
+    
+    for(int i=0;i<uniforms.light_count;i++)
+    {
+        accum+=uniforms.col[i].xyz*((dot(normalize(normal),uniforms.pos[i].xyz)*.5)+.5);
+    }
+    
+    f_color=vec4(accum,1.);
 }
\ No newline at end of file
diff --git a/src/gui.rs b/src/gui.rs
index 8cb66044361ae46bb56c6c92d066e9e462bb2af6..841d0fa3976ffffb135f1fee95a4757b10cc61d6 100644
--- a/src/gui.rs
+++ b/src/gui.rs
@@ -1,6 +1,11 @@
-use egui::{Color32, Frame, Id, ScrollArea, TextEdit, TextStyle};
+use egui::{
+    plot::{Line, Plot, PlotPoints},
+    Color32, Frame, Id, ScrollArea, TextEdit, TextStyle,
+};
 use egui_winit_vulkano::Gui;
 
+use crate::objects::{Light, Mesh};
+
 fn sized_text(ui: &mut egui::Ui, text: impl Into<String>, size: f32) {
     ui.label(egui::RichText::new(text).size(size));
 }
@@ -17,6 +22,11 @@ Vulkan(o) is hard, that I know...
 pub struct GState {
     pub cursor_sensitivity: f32,
     pub move_speed: f32,
+
+    pub meshes: Vec<Mesh>,
+    pub lights: Vec<Light>,
+
+    pub fps: [f64; 128],
 }
 
 impl Default for GState {
@@ -24,6 +34,11 @@ impl Default for GState {
         Self {
             cursor_sensitivity: 1.0,
             move_speed: 1.0,
+
+            meshes: vec![],
+            lights: vec![],
+
+            fps: [0.0; 128],
         }
     }
 }
@@ -40,11 +55,37 @@ pub fn gui_up(gui: &mut Gui, state: &mut GState) {
                     sized_text(ui, "Settings", 32.0);
                 });
                 ui.separator();
-                ui.vertical_centered(|ui| {
-                    //ui.heading("Camera Control");
-                    ui.add(egui::Slider::new(&mut state.cursor_sensitivity, 0.0..=2.0).text("Mouse Sensitivity"));
-                    ui.add(egui::Slider::new(&mut state.move_speed, 0.0..=2.0).text("Movement Speed"));
-                });   
+                egui::ScrollArea::vertical().show(ui, |ui| {
+                    ui.vertical_centered(|ui| {
+                        ui.heading("Camera Control");
+                        ui.add(egui::Slider::new(&mut state.cursor_sensitivity, 0.0..=2.0).text("Mouse Sensitivity"));
+                        ui.add(egui::Slider::new(&mut state.move_speed, 0.0..=2.0).text("Movement Speed"));
+                        ui.heading("Meshes");
+                        for mesh in &mut state.meshes {
+                            ui.label(mesh.name.clone());
+                            ui.add(egui::Slider::new(&mut mesh.pos.x, -100.0..=100.0).text("Position.x"));
+                            ui.add(egui::Slider::new(&mut mesh.pos.y, -100.0..=100.0).text("Position.y"));
+                            ui.add(egui::Slider::new(&mut mesh.pos.z, -100.0..=100.0).text("Position.z"));
+                            ui.add(egui::Slider::new(&mut mesh.rot.x.0, 0.0..=360.0).text("Rotation.x"));
+                            ui.add(egui::Slider::new(&mut mesh.rot.y.0, 0.0..=360.0).text("Rotation.y"));
+                            ui.add(egui::Slider::new(&mut mesh.rot.z.0, 0.0..=360.0).text("Rotation.z"));
+                        }
+                        ui.heading("Lights");
+                        for light in &mut state.lights {
+                            ui.label("Light");
+                            ui.add(egui::Slider::new(&mut light.pos.x, -100.0..=100.0).text("Position.x"));
+                            ui.add(egui::Slider::new(&mut light.pos.y, -100.0..=100.0).text("Position.y"));
+                            ui.add(egui::Slider::new(&mut light.pos.z, -100.0..=100.0).text("Position.z"));
+                            ui.add(egui::Slider::new(&mut light.colour.x, 0.0..=1.0).text("Colour.r"));
+                            ui.add(egui::Slider::new(&mut light.colour.y, 0.0..=1.0).text("Colour.g"));
+                            ui.add(egui::Slider::new(&mut light.colour.z, 0.0..=1.0).text("Colour.b"));
+                        }
+                        let fps: PlotPoints = state.fps.iter().enumerate().map(|(x,y)| [x as f64,*y]).collect::<Vec<_>>().into();
+                        let line = Line::new(fps);
+                        ui.heading("FPS");
+                        Plot::new("fps").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line));
+                    });
+                });
             });
     });
 }
diff --git a/src/main.rs b/src/main.rs
index fe1b48adec799cb91d3e70b120de7d5b8b3b374e..09e3ceca9ee776e7c04288ad87ae94e8bf0d5fe5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,13 +24,19 @@ use obj::{LoadConfig, ObjData};
 use rodio::{source::Source, Decoder, OutputStream};
 use std::io::Cursor;
 use std::{sync::Arc, time::Instant};
+use vulkano::buffer::CpuBufferPool;
 use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
+use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
+use vulkano::descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet};
+use vulkano::device::DeviceOwned;
 use vulkano::format::Format;
 use vulkano::image::AttachmentImage;
-use vulkano::memory::allocator::StandardMemoryAllocator;
+use vulkano::memory::allocator::{MemoryUsage, StandardMemoryAllocator};
 use vulkano::pipeline::graphics::depth_stencil::DepthStencilState;
 use vulkano::pipeline::graphics::rasterization::CullMode;
 use vulkano::pipeline::graphics::rasterization::FrontFace::Clockwise;
+use vulkano::pipeline::PipelineBindPoint;
+use vulkano::shader::ShaderModule;
 use vulkano::swapchain::{PresentMode, SwapchainPresentInfo};
 use vulkano::VulkanLibrary;
 use winit::event::{DeviceEvent, DeviceId, ElementState, MouseButton, VirtualKeyCode};
@@ -70,8 +76,12 @@ use winit::{
     window::{Window, WindowBuilder},
 };
 
-use crate::gui::*;
 mod gui;
+use crate::gui::*;
+mod objects;
+use crate::objects::*;
+
+pub type MemoryAllocator = StandardMemoryAllocator;
 
 fn main() {
     // The first step of any Vulkan program is to create an instance.
@@ -81,7 +91,7 @@ fn main() {
     // All the window-drawing functionalities are part of non-core extensions that we need
     // to enable manually. To do so, we ask the `vulkano_win` crate for the list of extensions
     // required to draw to a window.
-    let library = VulkanLibrary::new().unwrap();
+    let library = VulkanLibrary::new().expect("Vulkan is not installed???");
     let required_extensions = vulkano_win::required_extensions(&library);
 
     // Now creating the instance.
@@ -284,50 +294,6 @@ fn main() {
         .unwrap()
     };
 
-    const OBJ: &[u8] = include_bytes!("bunny.obj");
-
-    let buny = ObjData::load_buf_with_config(OBJ, LoadConfig::default()).unwrap();
-
-    let polys = &buny.objects[0].groups[0].polys;
-
-    let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
-
-    // We now create a buffer that will store the shape of our triangle.
-    // We use #[repr(C)] here to force rustc to not do anything funky with our data, although for this
-    // particular example, it doesn't actually change the in-memory representation.
-    #[repr(C)]
-    #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
-    struct Vertex {
-        position: [f32; 3],
-        normal: [f32; 3],
-    }
-    impl_vertex!(Vertex, position, normal);
-
-    let vertices = polys
-        .iter()
-        .flat_map(|p| {
-            p.0.iter()
-                .map(|v| Vertex {
-                    position: buny.position[v.0],
-                    normal: v
-                        .2
-                        .and_then(|vt| Some(buny.normal[vt]))
-                        .unwrap_or([0.0, 0.0, 0.0]),
-                })
-                .collect::<Vec<Vertex>>()
-        })
-        .collect::<Vec<Vertex>>();
-    let vertex_buffer = CpuAccessibleBuffer::from_iter(
-        &memory_allocator,
-        BufferUsage {
-            vertex_buffer: true,
-            ..BufferUsage::empty()
-        },
-        false,
-        vertices,
-    )
-    .unwrap();
-
     // The next step is to create the shaders.
     //
     // The raw shader creation API provided by the vulkano library is unsafe for various
@@ -344,7 +310,7 @@ fn main() {
     //
     // A more detailed overview of what the `shader!` macro generates can be found in the
     // `vulkano-shaders` crate docs. You can view them at https://docs.rs/vulkano-shaders/
-    mod vs {
+    mod mesh_vs {
         vulkano_shaders::shader! {
             ty: "vertex",
             src: "
@@ -363,7 +329,7 @@ fn main() {
                 
                 void main() {
                     mat4 worldview = pc.view * pc.world;
-                    v_normal = normalize(transpose(inverse(mat3(worldview))) * normal);
+                    v_normal = normal; //normalize(transpose(inverse(mat3(worldview))) * normal);
                     gl_Position = pc.proj * worldview * vec4(position*1000.0, 1.0);
                 }
 			",
@@ -375,19 +341,26 @@ fn main() {
         }
     }
 
-    mod fs {
+    mod mesh_fs {
         vulkano_shaders::shader! {
             ty: "fragment",
-            path: "src/frag.glsl"
+            path: "src/frag.glsl",
+            types_meta: {
+                use bytemuck::{Pod, Zeroable};
+
+                #[derive(Clone, Copy, Zeroable, Pod, Debug)]
+            },
         }
     }
 
-    let vs = vs::load(device.clone()).unwrap();
-    let fs = fs::load(device.clone()).unwrap();
+    let mesh_vs = mesh_vs::load(device.clone()).unwrap();
+    let mesh_fs = mesh_fs::load(device.clone()).unwrap();
 
     /*let uniform_buffer =
     CpuBufferPool::<vs::ty::PushConstantData>::uniform_buffer(memory_allocator);*/
 
+    let memory_allocator = Arc::new(MemoryAllocator::new_default(device.clone()));
+
     // At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL
     // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this
     // manually.
@@ -439,33 +412,6 @@ fn main() {
     )
     .unwrap();
 
-    // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
-    // program, but much more specific.
-    let pipeline = GraphicsPipeline::start()
-        // We have to indicate which subpass of which render pass this pipeline is going to be used
-        // in. The pipeline will only be usable from this particular subpass.
-        .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
-        // We need to indicate the layout of the vertices.
-        .vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
-        // The content of the vertex buffer describes a list of triangles.
-        .input_assembly_state(InputAssemblyState::new())
-        // A Vulkan shader can in theory contain multiple entry points, so we have to specify
-        // which one.
-        .vertex_shader(vs.entry_point("main").unwrap(), ())
-        // Use a resizable viewport set to draw over the entire window
-        .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
-        // See `vertex_shader`.
-        .fragment_shader(fs.entry_point("main").unwrap(), ())
-        .depth_stencil_state(DepthStencilState::simple_depth_test())
-        .rasterization_state(RasterizationState {
-            front_face: Fixed(Clockwise),
-            cull_mode: Fixed(CullMode::Back),
-            ..RasterizationState::default()
-        })
-        // Now that our builder is filled, we call `build()` to obtain an actual pipeline.
-        .build(device.clone())
-        .unwrap();
-
     // Dynamic viewports allow us to recreate just the viewport when the window is resized
     // Otherwise we would have to recreate the whole pipeline.
     let mut viewport = Viewport {
@@ -479,8 +425,10 @@ fn main() {
     //
     // Since we need to draw to multiple images, we are going to create a different framebuffer for
     // each image.
-    let mut framebuffers = window_size_dependent_setup(
+    let ([mut mesh_pipeline], mut framebuffers) = window_size_dependent_setup(
         &memory_allocator,
+        &mesh_vs,
+        &mesh_fs,
         &images,
         render_pass.clone(),
         &mut viewport,
@@ -524,9 +472,18 @@ fn main() {
     stream_handle.play_raw(source.convert_samples()).unwrap();
     */
 
-    let rotation_start = Instant::now();
+    let mut render_start = Instant::now();
+
+    let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
 
-    //let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
+    let uniform_buffer = CpuBufferPool::<mesh_fs::ty::Data>::new(
+        memory_allocator.clone(),
+        BufferUsage {
+            uniform_buffer: true,
+            ..BufferUsage::empty()
+        },
+        MemoryUsage::Upload,
+    );
 
     // Create an egui GUI
     let mut gui = Gui::new_with_subpass(
@@ -561,6 +518,19 @@ fn main() {
         d: false,
     };
 
+    gstate.meshes.push(load_obj(
+        &memory_allocator,
+        &mut Cursor::new(PLATONIC_SOLIDS[0].1),
+        PLATONIC_SOLIDS[0].0.to_string(),
+    ));
+
+    gstate
+        .lights
+        .push(Light::new([4., 6., 8.], [1., 1., 8.], 0.01));
+    gstate
+        .lights
+        .push(Light::new([-4., 6., -8.], [8., 4., 1.], 0.01));
+
     event_loop.run(move |event, _, control_flow| {
         if let Event::WindowEvent { event: we, .. } = &event {
             if !gui.update(we) {
@@ -613,12 +583,23 @@ fn main() {
                 ..
             } => {
                 if looking {
-                    camforward.x -= Deg(delta.1 as f32) * gstate.cursor_sensitivity;
-                    camforward.y += Deg(delta.0 as f32) * gstate.cursor_sensitivity;
+                    camforward.x -= Deg(delta.1 as f32) * gstate.cursor_sensitivity * 0.3;
+                    camforward.y += Deg(delta.0 as f32) * gstate.cursor_sensitivity * 0.3;
+                    camforward.x = camforward.x + Deg(360f32) % Deg(360f32);
+                    camforward.y = camforward.y + Deg(360f32) % Deg(360f32);
                 }
                 //println!("AXISM {:?}", delta);
             }
             Event::RedrawEventsCleared => {
+                for i in 1..gstate.fps.len() {
+                    gstate.fps[i - 1] = gstate.fps[i];
+                }
+
+                gstate.fps[gstate.fps.len() - 1] =
+                    1.0 / (Instant::now() - render_start).as_secs_f64();
+
+                render_start = Instant::now();
+
                 // Do not draw frame when screen dimensions are zero.
                 // On Windows, this can occur from minimizing the application.
                 let window = surface.object().unwrap().downcast_ref::<Window>().unwrap();
@@ -653,8 +634,10 @@ fn main() {
                     swapchain = new_swapchain;
                     // Because framebuffers contains an Arc on the old swapchain, we need to
                     // recreate framebuffers as well.
-                    framebuffers = window_size_dependent_setup(
+                    ([mesh_pipeline], framebuffers) = window_size_dependent_setup(
                         &memory_allocator,
+                        &mesh_vs,
+                        &mesh_fs,
                         &new_images,
                         render_pass.clone(),
                         &mut viewport,
@@ -664,7 +647,7 @@ fn main() {
 
                 //println!("{:?}", right);
 
-                let uniform_data = {
+                let mut push_constants = {
                     if looking {
                         if keys.w {
                             campos -= Matrix3::from_angle_y(camforward.y)
@@ -718,7 +701,7 @@ fn main() {
                         * Matrix4::from_scale(scale);
                     //*Matrix4::from_angle_z(Deg(180f32));
 
-                    let pc = vs::ty::PushConstantData {
+                    let pc = mesh_vs::ty::PushConstantData {
                         world: Matrix4::identity().into(),
                         view: view.into(),
                         proj: proj.into(),
@@ -735,13 +718,35 @@ fn main() {
                     pc
                 };
 
-                //let layout = pipeline.layout().set_layouts().get(0).unwrap();
-                /*let set = PersistentDescriptorSet::new(
-                    &memory_allocator,
+                let uniform_buffer_subbuffer = {
+                    let mut pos = [[0f32; 4]; 32];
+                    let mut col = [[0f32; 4]; 32];
+
+                    for (i, light) in gstate.lights.iter().enumerate() {
+                        pos[i][0] = light.pos.x;
+                        pos[i][1] = light.pos.y;
+                        pos[i][2] = light.pos.z;
+                        col[i][0] = light.colour.x;
+                        col[i][1] = light.colour.y;
+                        col[i][2] = light.colour.z;
+                    }
+
+                    let uniform_data = mesh_fs::ty::Data {
+                        pos,
+                        col,
+                        light_count: gstate.lights.len() as u32,
+                    };
+
+                    uniform_buffer.from_data(uniform_data).unwrap()
+                };
+
+                let layout = mesh_pipeline.layout().set_layouts().get(0).unwrap();
+                let set = PersistentDescriptorSet::new(
+                    &descriptor_set_allocator,
                     layout.clone(),
                     [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)],
                 )
-                .unwrap();*/
+                .unwrap();
 
                 // Before we can draw on the output, we have to *acquire* an image from the swapchain. If
                 // no image is available (which happens if you submit draw commands too quickly), then the
@@ -816,19 +821,31 @@ fn main() {
                     // The last two parameters contain the list of resources to pass to the shaders.
                     // Since we used an `EmptyPipeline` object, the objects have to be `()`.
                     .set_viewport(0, [viewport.clone()])
-                    .bind_pipeline_graphics(pipeline.clone())
-                    /*.bind_descriptor_sets(
+                    .bind_pipeline_graphics(mesh_pipeline.clone())
+                    .bind_descriptor_sets(
                         PipelineBindPoint::Graphics,
-                        pipeline.layout().clone(),
+                        mesh_pipeline.layout().clone(),
                         0,
                         set,
-                    )*/
-                    .bind_vertex_buffers(0, vertex_buffer.clone())
-                    .push_constants(pipeline.layout().clone(), 0, uniform_data)
-                    .draw(vertex_buffer.len() as u32, 1, 0, 0)
-                    .unwrap()
-                    // We leave the render pass. Note that if we had multiple
-                    // subpasses we could have called `next_subpass` to jump to the next subpass.
+                    );
+
+                for object in &gstate.meshes {
+                    push_constants.world =
+                        (Matrix4::from_translation(object.pos - Point3::origin())
+                            * Matrix4::from(object.rot)
+                            * object.scale)
+                            .into();
+                    builder
+                        .bind_vertex_buffers(0, object.vertices.clone())
+                        .bind_index_buffer(object.indices.clone())
+                        .push_constants(mesh_pipeline.layout().clone(), 0, push_constants)
+                        .draw_indexed(object.indices.len() as u32, 1, 0, 0, 0)
+                        .unwrap();
+                }
+
+                // We leave the render pass. Note that if we had multiple
+                // subpasses we could have called `next_subpass` to jump to the next subpass.
+                builder
                     .next_subpass(SubpassContents::SecondaryCommandBuffers)
                     .unwrap()
                     .execute_commands(cb)
@@ -879,10 +896,12 @@ fn main() {
 /// This method is called once during initialization, then again whenever the window is resized
 fn window_size_dependent_setup(
     allocator: &StandardMemoryAllocator,
+    mesh_vs: &ShaderModule,
+    mesh_fs: &ShaderModule,
     images: &[Arc<SwapchainImage>],
     render_pass: Arc<RenderPass>,
     viewport: &mut Viewport,
-) -> Vec<Arc<Framebuffer>> {
+) -> ([Arc<GraphicsPipeline>; 1], Vec<Arc<Framebuffer>>) {
     let dimensions = images[0].dimensions().width_height();
     viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
 
@@ -891,7 +910,7 @@ fn window_size_dependent_setup(
     )
     .unwrap();
 
-    images
+    let framebuffers = images
         .iter()
         .map(|image| {
             let view = ImageView::new_default(image.clone()).unwrap();
@@ -904,5 +923,39 @@ fn window_size_dependent_setup(
             )
             .unwrap()
         })
-        .collect::<Vec<_>>()
+        .collect::<Vec<_>>();
+
+    // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
+    // program, but much more specific.
+    let mesh_pipeline = GraphicsPipeline::start()
+        // We have to indicate which subpass of which render pass this pipeline is going to be used
+        // in. The pipeline will only be usable from this particular subpass.
+        .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
+        // We need to indicate the layout of the vertices.
+        .vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
+        // The content of the vertex buffer describes a list of triangles.
+        .input_assembly_state(InputAssemblyState::new())
+        // A Vulkan shader can in theory contain multiple entry points, so we have to specify
+        // which one.
+        .vertex_shader(mesh_vs.entry_point("main").unwrap(), ())
+        .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([
+            Viewport {
+                origin: [0.0, 0.0],
+                dimensions: [dimensions[0] as f32, dimensions[1] as f32],
+                depth_range: 0.0..1.0,
+            },
+        ]))
+        // See `vertex_shader`.
+        .fragment_shader(mesh_fs.entry_point("main").unwrap(), ())
+        .depth_stencil_state(DepthStencilState::simple_depth_test())
+        .rasterization_state(RasterizationState {
+            front_face: Fixed(Clockwise),
+            cull_mode: Fixed(CullMode::Back),
+            ..RasterizationState::default()
+        })
+        // Now that our builder is filled, we call `build()` to obtain an actual pipeline.
+        .build(allocator.device().clone())
+        .unwrap();
+
+    ([mesh_pipeline], framebuffers)
 }
diff --git a/src/objects.rs b/src/objects.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b5ed89f6171e678a0c7ea73380e22442e8107473
--- /dev/null
+++ b/src/objects.rs
@@ -0,0 +1,119 @@
+use std::{collections::HashMap, io::Read, sync::Arc};
+
+use bytemuck::{Pod, Zeroable};
+use cgmath::{Deg, Euler, Matrix3, Point3, SquareMatrix, Vector3};
+use obj::{LoadConfig, ObjData};
+use vulkano::{
+    buffer::{BufferUsage, CpuAccessibleBuffer},
+    impl_vertex,
+};
+
+use crate::MemoryAllocator;
+
+pub const PLATONIC_SOLIDS: [(&str, &[u8]); 1] = [("Buny", include_bytes!("bunny.obj"))];
+
+// We now create a buffer that will store the shape of our triangle.
+// We use #[repr(C)] here to force rustc to not do anything funky with our data, although for this
+// particular example, it doesn't actually change the in-memory representation.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
+pub struct Vertex {
+    position: [f32; 3],
+    normal: [f32; 3],
+}
+impl_vertex!(Vertex, position, normal);
+
+#[derive(Debug)]
+pub struct Mesh {
+    pub name: String,
+    pub vertices: Arc<CpuAccessibleBuffer<[Vertex]>>,
+    pub indices: Arc<CpuAccessibleBuffer<[u32]>>,
+    pub pos: Point3<f32>,
+    pub rot: Euler<Deg<f32>>,
+    pub scale: f32,
+}
+
+pub fn load_obj(memory_allocator: &MemoryAllocator, input: &mut dyn Read, name: String) -> Mesh {
+    let object = ObjData::load_buf_with_config(input, LoadConfig::default()).unwrap();
+
+    let mut vertices = vec![];
+
+    let mut indices = vec![];
+
+    let mut temp_hash_map = HashMap::<(u32, u32), u32>::new();
+
+    // We're gonna have to remap all the indices that OBJ uses. Annoying.
+    // Get each pair of vertex position to vertex normal and assign it a new index. This might duplicate
+    // vertices or normals but each *pair* needs a unique index
+    // Uses the hash map to check that we're not duplicating unnecessarily
+    for g in &object.objects[0].groups {
+        for p in &g.polys {
+            for v in &p.0 {
+                //println!("{:?}", v);
+                let mapping = ((v.0) as u32, (v.2.unwrap_or(0)) as u32);
+                if let Some(&exist) = &temp_hash_map.get(&mapping) {
+                    //println!("{:?}", exist);
+                    indices.push(exist);
+                } else {
+                    vertices.push(Vertex {
+                        position: object.position[mapping.0 as usize],
+                        normal: object.normal[mapping.1 as usize],
+                    });
+                    temp_hash_map.insert(mapping, (vertices.len() - 1) as u32);
+                    indices.push((vertices.len() - 1) as u32);
+                }
+            }
+        }
+    }
+
+    let vertex_buffer = CpuAccessibleBuffer::from_iter(
+        memory_allocator,
+        BufferUsage {
+            vertex_buffer: true,
+            ..BufferUsage::empty()
+        },
+        false,
+        vertices,
+    )
+    .unwrap();
+
+    let index_buffer = CpuAccessibleBuffer::from_iter(
+        memory_allocator,
+        BufferUsage {
+            index_buffer: true,
+            ..BufferUsage::empty()
+        },
+        false,
+        indices,
+    )
+    .unwrap();
+
+    Mesh {
+        vertices: vertex_buffer,
+        indices: index_buffer,
+        pos: Point3 {
+            x: 0.,
+            y: 0.,
+            z: 0.,
+        },
+        rot: Euler::new(Deg(0.), Deg(0.), Deg(0.)),
+        scale: 1.,
+        name,
+    }
+}
+
+#[derive(Debug)]
+pub struct Light {
+    pub pos: Point3<f32>,
+    pub colour: Vector3<f32>,
+}
+
+impl Light {
+    pub fn new(pos: [f32; 3], colour: [f32; 3], intensity: f32) -> Light {
+        let c: Vector3<f32> = colour.into();
+        Light {
+            pos: pos.into(),
+            colour: c * intensity,
+        }
+    }
+}