diff --git a/.gitattributes b/.gitattributes
index c4006bd844467ceaea6a49b1266fbd07ce1c6aa6..44b7e7df6e16968f484ac85f7825c490e95212e7 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -14,7 +14,11 @@ README.md text
 *.cpp text
 *.h text
 *.hpp text
-*.png filter=lfs diff=lfs merge=lfs -text
+*.png filter=lfs diff=lfs merge=lfs 
 *.obj filter=lfs diff=lfs merge=lfs -text
-*.jpg filter=lfs diff=lfs merge=lfs -text
+*.jpg filter=lfs diff=lfs merge=lfs 
 *.mtl filter=lfs diff=lfs merge=lfs -text
+*.PNG filter=lfs diff=lfs merge=lfs 
+*.OBJ filter=lfs diff=lfs merge=lfs -text
+*.JPG filter=lfs diff=lfs merge=lfs 
+*.MTL filter=lfs diff=lfs merge=lfs -text
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18d8d4d79582bb69b27018a4040a1a3ec14892fd..2e91b48732b0221406d8057aa61229c86dd2ee02 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
@@ -55,6 +55,7 @@ conan_or_find_package(SDL2 REQUIRED)
 conan_or_find_package(spdlog REQUIRED)
 conan_or_find_package(freeimage REQUIRED)
 conan_or_find_package(openvr REQUIRED)
+conan_or_find_package(jsonformoderncpp REQUIRED)
 
 
 add_subdirectory(library)
diff --git a/LICENSE b/LICENSE
index 6b9b89a931166a76b2a209a0b026d3bc72e5a857..44a184c18c328385d7aba4d554b25cb9035d8db1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
 --------------------------------------------------------------------------------
  Project Phoenix
 
- Copyright (c) 2017 RWTH Aachen University, Germany,
+ Copyright (c) 2017-2018 RWTH Aachen University, Germany,
  Virtual Reality & Immersive Visualization Group.
 --------------------------------------------------------------------------------
  Contact Information
diff --git a/README.md b/README.md
index c331a333d0dc73820dbc3812fdf209ee1fe85a71..6e740b179a115ceca887a1edf8de4c64ab8561fd 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
  
 **Develop:** [![build status](https://devhub.vr.rwth-aachen.de/VR-Group/Project_Phoenix/badges/develop/build.svg)](https://devhub.vr.rwth-aachen.de/VR-Group/Project_Phoenix/commits/develop)
 
-*Project Phoenix* is Copyright (c) 2017 RWTH Aachen University, Germany,
+*Project Phoenix* is Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 Virtual Reality & Immersive Visualization Group.
 
 
diff --git a/cmake/ConanHelpers.cmake b/cmake/ConanHelpers.cmake
index d807a0a9db07249cf7b19550c2059b26642014e4..e5166a7fc4b22bb0ae4345f54795ca3d229c949c 100644
--- a/cmake/ConanHelpers.cmake
+++ b/cmake/ConanHelpers.cmake
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/cmake/ConfigureFiles.cmake b/cmake/ConfigureFiles.cmake
index 04481a4ec4c048bcd19de6bfb35308144c0eea0e..e405996934ace59d47dee16fcbc2ffd7eb9812db 100644
--- a/cmake/ConfigureFiles.cmake
+++ b/cmake/ConfigureFiles.cmake
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/cmake/Testing.cmake b/cmake/Testing.cmake
index f862a912882dd6cf943e983dd2ac80b00bb74b6d..36cf0f171ae76dfa442e590a7ef9c2c4beb462b2 100644
--- a/cmake/Testing.cmake
+++ b/cmake/Testing.cmake
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/cmake/WarningLevels.cmake b/cmake/WarningLevels.cmake
index e2e48aa512065151cdc0fa7bbed5f150d71ef308..2fbf048bdc3ae797c38ea9bccc836279d9e3dd13 100644
--- a/cmake/WarningLevels.cmake
+++ b/cmake/WarningLevels.cmake
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/cmake/cppcheck.cmake b/cmake/cppcheck.cmake
index 9e8a289654e9bee0be45e416d52f399e5f27b6ce..523502ddf8860098ff3f361b0db90503390144ce 100644
--- a/cmake/cppcheck.cmake
+++ b/cmake/cppcheck.cmake
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/cmake/cpplint.cmake b/cmake/cpplint.cmake
index fb15cefb7a6ebfd3298e04ee6c946a32369eac13..18d881e45ba1ac2152c31b7a2b8579fe323ab712 100644
--- a/cmake/cpplint.cmake
+++ b/cmake/cpplint.cmake
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/conanfile.py b/conanfile.py
index ba91df218a0aa70e333c637e26206c2f64d3eea6..550e2e251f56223e7aa58ef3dd65019070df498f 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
@@ -24,7 +24,7 @@ from conans import ConanFile, CMake
 
 class ProjectPhoenix(ConanFile):
     name = "phx"
-    version = "17.12.0"
+    version = "18.01.0"
     license = "3-Clause BSD License"
     description = """Project Phoenix"""
     settings = "os", "compiler", "build_type", "arch"
@@ -38,6 +38,7 @@ class ProjectPhoenix(ConanFile):
                 ("gl/1.0.0@RWTH-VR/thirdparty"),
                 ("glew/2.1.0_1@RWTH-VR/thirdparty"),
                 ("glm/0.9.8@RWTH-VR/thirdparty"),
+				("jsonformoderncpp/3.0.1@vthiery/stable"),
 				("openvr/1.0.10@RWTH-VR/thirdparty"),
                 ("SDL2/2.0.5@RWTH-VR/thirdparty"),
                 ("spdlog/0.14.0@RWTH-VR/thirdparty"),
diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt
index 374c36caf2fb0cc78e86f708b53b8f0dd3cf3775..ea9e625480443f4a735aee5a0754c18485e22390 100644
--- a/demos/CMakeLists.txt
+++ b/demos/CMakeLists.txt
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/demos/viewer/CMakeLists.txt b/demos/viewer/CMakeLists.txt
index d3bfb58406c1afb51c3d20c61c288886943704ca..cea35965784e4da544e2913c81a668c383b133bb 100644
--- a/demos/viewer/CMakeLists.txt
+++ b/demos/viewer/CMakeLists.txt
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/demos/viewer/src/controller_behavior.cpp b/demos/viewer/src/controller_behavior.cpp
index 64b9209784326b33b0957a99791558b265b089fd..f4fc9a45a6bcdc867ff836d6a039a9039a924f09 100644
--- a/demos/viewer/src/controller_behavior.cpp
+++ b/demos/viewer/src/controller_behavior.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -23,20 +23,25 @@
 #include "controller_behavior.hpp"
 
 #include "phx/entity.hpp"
+#include "phx/runtime_component.hpp"
 #include "phx/scene.hpp"
 #include "phx/transform.hpp"
 
 void ControllerBehavior::OnUpdate() {
   phx::Scene* scene = GetEntity()->GetScene();
   phx::Entity* runtime_entity = nullptr;
-  for (auto entity : scene->GetEntities()) {
-    if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_CONTROLLER_LEFT &&
-        side_ == Side::LEFT) {
-      runtime_entity = entity;
+  if (side_ == Side::LEFT) {
+    auto runtime_entities = scene->GetEntitiesWithComponents<
+        phx::RuntimeComponent<phx::LEFT_CONTROLLER>>();
+    if (!runtime_entities.empty()) {
+      runtime_entity = runtime_entities[0];
     }
-    if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_CONTROLLER_RIGHT &&
-        side_ == Side::RIGHT) {
-      runtime_entity = entity;
+  }
+  if (side_ == Side::RIGHT) {
+    auto runtime_entities = scene->GetEntitiesWithComponents<
+        phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>();
+    if (!runtime_entities.empty()) {
+      runtime_entity = runtime_entities[0];
     }
   }
   if (runtime_entity &&
diff --git a/demos/viewer/src/controller_behavior.hpp b/demos/viewer/src/controller_behavior.hpp
index add4476a3cc1948e898ce13e0dcb66d74557be0b..5f6855c2736df123f19220e24df2f6ccbd571a1a 100644
--- a/demos/viewer/src/controller_behavior.hpp
+++ b/demos/viewer/src/controller_behavior.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/demos/viewer/src/navigation_behavior.cpp b/demos/viewer/src/navigation_behavior.cpp
index 9d30d276bb478dde38dedbb608ad548d899caa70..71d6a2e2b04a717693d9a5f77e4068480e1ea39e 100644
--- a/demos/viewer/src/navigation_behavior.cpp
+++ b/demos/viewer/src/navigation_behavior.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/demos/viewer/src/navigation_behavior.hpp b/demos/viewer/src/navigation_behavior.hpp
index 240dd2079dd58479c5d26233bd55422264c49a1e..0209b0264c9786635732b0df1939ba9b33891a71 100644
--- a/demos/viewer/src/navigation_behavior.hpp
+++ b/demos/viewer/src/navigation_behavior.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/demos/viewer/src/rotation_behavior.cpp b/demos/viewer/src/rotation_behavior.cpp
index 61128cdbbb820adaf063dd8fb761564fabff9b3c..f9d889e567450afe5d271bee24eccf8d4eb807ff 100644
--- a/demos/viewer/src/rotation_behavior.cpp
+++ b/demos/viewer/src/rotation_behavior.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/demos/viewer/src/rotation_behavior.hpp b/demos/viewer/src/rotation_behavior.hpp
index 56b41ee5cfd6e01e0e7cf8400cb5436efbe92d9f..aa9b81e74b4568ff00d81ba27eb31318678dbd44 100644
--- a/demos/viewer/src/rotation_behavior.hpp
+++ b/demos/viewer/src/rotation_behavior.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/demos/viewer/src/viewer.cpp b/demos/viewer/src/viewer.cpp
index 57a9a4aa2b75f7d26550698641fd77a81d7a0e7a..3992fcfa5c26a70c3a7b140ff2d333e5823236a0 100644
--- a/demos/viewer/src/viewer.cpp
+++ b/demos/viewer/src/viewer.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -37,18 +37,18 @@
 #include "phx/material_handle.hpp"
 #include "phx/mesh.hpp"
 #include "phx/mesh_handle.hpp"
-#include "phx/openvr_mesh_loader.hpp"
+#include "phx/openvr_resource_loader.hpp"
 #include "phx/phoenix.hpp"
 #include "phx/rendering_system.hpp"
 #include "phx/resource_declaration.hpp"
 #include "phx/resource_manager.hpp"
 #include "phx/resource_proxy.hpp"
+#include "phx/runtime_component.hpp"
 #include "phx/scene.hpp"
 #include "phx/scene_loader.hpp"
 #include "phx/setup.hpp"
 #include "phx/splash_screen.hpp"
 #include "phx/transform.hpp"
-#include "phx/virtual_platform.hpp"
 #include "phx/window.hpp"
 
 #include "controller_behavior.hpp"
@@ -68,36 +68,36 @@ void AddControllerEntity(const std::shared_ptr<phx::Scene>& scene,
   controller->AddComponent<ControllerBehavior>()->SetSide(side);
 }
 
-void SetupOpenVRController(const std::shared_ptr<phx::Scene>& scene,
-                           phx::HMD* hmd) {
+void AddController(const std::shared_ptr<phx::Scene>& scene,
+                   ControllerBehavior::Side side) {
   auto& resource_manager = phx::ResourceManager::instance();
-  auto openvr_mesh_loader = std::make_unique<phx::OpenvrMeshLoader>(hmd);
-  resource_manager.RegisterResourceType(".openvr",
-                                        std::move(openvr_mesh_loader));
 
-  phx::ResourceDeclaration material_declaration{"material.openvr"};
+  phx::ResourceDeclaration mesh_declaration{
+      {"TYPE", "openVR"},
+      {"OpenVR_type", "mesh"},
+      {"side", (side == ControllerBehavior::RIGHT ? "right" : "left")}};
+  auto mesh_proxy =
+      phx::ResourceManager::instance().DeclareResource(mesh_declaration);
+  mesh_proxy->Load();
+
+  phx::ResourceDeclaration material_declaration{{"TYPE", "openVR"},
+                                                {"OpenVR_type", "material"}};
   auto material_proxy = resource_manager.DeclareResource(material_declaration);
   material_proxy->Load();
 
-  phx::ResourceDeclaration right_controller_declaration{
-      "controller_right.openvr"};
-  auto right_controller_proxy =
-      resource_manager.DeclareResource(right_controller_declaration);
-  right_controller_proxy->Load();
-  if (right_controller_proxy->GetAs<phx::Mesh>() != nullptr) {
-    AddControllerEntity(scene, right_controller_proxy, material_proxy,
-                        ControllerBehavior::RIGHT);
+  if (mesh_proxy->GetAs<phx::Mesh>() != nullptr) {
+    AddControllerEntity(scene, mesh_proxy, material_proxy, side);
   }
+}
 
-  phx::ResourceDeclaration left_controller_declaration{
-      "controller_left.openvr"};
-  auto left_controller_proxy =
-      resource_manager.DeclareResource(left_controller_declaration);
-  left_controller_proxy->Load();
-  if (left_controller_proxy->GetAs<phx::Mesh>() != nullptr) {
-    AddControllerEntity(scene, left_controller_proxy, material_proxy,
-                        ControllerBehavior::LEFT);
-  }
+void SetupOpenVRController(const std::shared_ptr<phx::Scene>& scene,
+                           phx::HMD* hmd) {
+  auto& resource_manager = phx::ResourceManager::instance();
+  auto openvr_loader = std::make_unique<phx::OpenVRResourceLoader>(hmd);
+  resource_manager.RegisterResourceType("openVR", std::move(openvr_loader));
+
+  AddController(scene, ControllerBehavior::RIGHT);
+  AddController(scene, ControllerBehavior::LEFT);
 }
 
 int main(int, char**) {
@@ -112,8 +112,6 @@ int main(int, char**) {
     splash.SetLoadProgress(progress);
     splash.Draw();
   });
-  phx::ResourceManager::instance().RegisterResourceType(
-      ".obj", std::move(assimp_loader));
 
   phx::InputSystem* input_system = engine->GetSystem<phx::InputSystem>();
   ViewerSystem* viewer_system = engine->CreateSystem<ViewerSystem>();
@@ -154,7 +152,9 @@ int main(int, char**) {
   }
 
   auto virtual_platform_transform =
-      scene->GetEntitiesWithComponents<phx::VirtualPlatform>()[0]
+      scene
+          ->GetEntitiesWithComponents<
+              phx::RuntimeComponent<phx::USER_PLATFORM>>()[0]
           ->GetFirstComponent<phx::Transform>();
 
   virtual_platform_transform->SetLocalTranslation(glm::vec3(0, 1.0, 5.0));
@@ -167,8 +167,8 @@ int main(int, char**) {
                                     1000.0f);
   camera_transform->SetLocalTranslation(glm::vec3(0, 0, 0));
   camera_transform->SetParent(virtual_platform_transform, false);
-  auto virtual_platform =
-      scene->GetEntitiesWithComponents<phx::VirtualPlatform>()[0];
+  auto virtual_platform = scene->GetEntitiesWithComponents<
+      phx::RuntimeComponent<phx::USER_PLATFORM>>()[0];
   virtual_platform->AddComponent<NavigationBehavior>();
 
   engine->Run();
diff --git a/demos/viewer/src/viewer_system.cpp b/demos/viewer/src/viewer_system.cpp
index 8d5e5238c48b0a250cc98df45d1eebf4fe4bb630..5354fab236b4a6c122722af754aca2697255c6df 100644
--- a/demos/viewer/src/viewer_system.cpp
+++ b/demos/viewer/src/viewer_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/demos/viewer/src/viewer_system.hpp b/demos/viewer/src/viewer_system.hpp
index 535190122303b22c033d77f79ffc8dcbbbed8f0c..f449c431543a0528a31c3217c491451e42965810 100644
--- a/demos/viewer/src/viewer_system.hpp
+++ b/demos/viewer/src/viewer_system.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 22a80bda4d5f3b6a18a96a623e20c6257e1ec2c1..9dc9bc1052dd1720d8f247b10c786894e5490faf 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
@@ -50,6 +50,7 @@ target_link_libraries(phoenix
   ${OPENGL_LIBRARIES}
   ${CONAN_OR_CMAKE_freeimage}
   ${CONAN_OR_CMAKE_openvr}
+  ${CONAN_OR_CMAKE_jsonformoderncpp}
   )
 
 generate_export_header(phoenix
diff --git a/library/phx/assimp_model_loader.cpp b/library/phx/assimp_model_loader.cpp
index e779865774fb39e4bb9bfeae7c6fd4eae44e2c04..74b525a957e21d6fe4b506c98c4381d90d0a0284 100644
--- a/library/phx/assimp_model_loader.cpp
+++ b/library/phx/assimp_model_loader.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -35,6 +35,7 @@
 #include "phx/model.hpp"
 #include "phx/resource_declaration.hpp"
 #include "phx/resource_manager.hpp"
+#include "phx/resource_utils.hpp"
 #include "phx/resources_path.hpp"
 
 namespace phx {
@@ -46,33 +47,15 @@ AssimpModelLoader::AssimpModelLoader() {
 
 std::unique_ptr<phx::Resource> AssimpModelLoader::Load(
     const ResourceDeclaration &declaration) {
-  std::string file_name = declaration;
-  auto index_divider_pos = declaration.find(";");
-  auto material_indicator_pos = std::string::npos;
-  unsigned int single_mesh_index = 0;
-  unsigned int single_material_index = 0;
-  if (index_divider_pos != std::string::npos) {
-    // we only want to load a single mesh or material
-    // format for a mesh:
-    //   id;file_name (e.g., 0;bunny.obj)
-    // format for a material:
-    //   material:id;file_name (e.g., material:0;bunny.obj)
-
-    // check whether a material is requested first
-    material_indicator_pos = declaration.find("material:");
-    if (material_indicator_pos != std::string::npos) {
-      // ok, it's a material
-      single_material_index =
-          std::stoi(declaration.substr(9, index_divider_pos - 9));
-    } else {
-      // ok, it's a mesh
-      single_mesh_index = std::stoi(declaration.substr(0, index_divider_pos));
-    }
-    file_name = declaration.substr(index_divider_pos + 1);
+  if (declaration.find("file_name") == declaration.end() ||
+      !declaration["file_name"].is_string()) {
+    warn(
+        "declaration {} cannot be parsed by AssimpModelLoader, it does not "
+        "contain a file_name key",
+        declaration.dump());
+    return nullptr;
   }
-
-  auto path = file_name.substr(0, file_name.find_last_of("/\\"));
-
+  std::string file_name = declaration["file_name"];
   if (last_loaded_scene_ != file_name || importer_.GetScene() == nullptr) {
     if (!LoadFile(file_name)) {
       return nullptr;
@@ -81,27 +64,27 @@ std::unique_ptr<phx::Resource> AssimpModelLoader::Load(
 
   const aiScene *scene = importer_.GetScene();
 
-  if (index_divider_pos != std::string::npos) {
-    if (material_indicator_pos != std::string::npos) {
-      return LoadSingleMaterial(scene->mMaterials[single_material_index], path);
-    } else {
-      return LoadSingleMesh(scene->mMeshes[single_mesh_index]);
-    }
+  if (declaration.find("material_index") != declaration.end()) {
+    std::size_t single_material_index = declaration["material_index"];
+    auto path = file_name.substr(0, file_name.find_last_of("/\\"));
+    return LoadSingleMaterial(scene->mMaterials[single_material_index], path);
+  }
+  if (declaration.find("mesh_index") != declaration.end()) {
+    std::size_t single_mesh_index = declaration["mesh_index"];
+    return LoadSingleMesh(scene->mMeshes[single_mesh_index]);
   }
 
   auto resource = std::make_unique<phx::Model>();
 
   for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
-    auto material_proxy = phx::ResourceManager::instance().DeclareResource(
-        "material:" + std::to_string(i) + ";" + file_name);
-    material_proxy->Load();
+    auto material_proxy = phx::ResourceUtils::LoadResourceFromFile(
+        file_name, {{"material_index", i}}, true);
     resource->AddMaterial(material_proxy);
   }
 
   for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
-    auto mesh_proxy = phx::ResourceManager::instance().DeclareResource(
-        std::to_string(i) + ";" + file_name);
-    mesh_proxy->Load();
+    auto mesh_proxy = phx::ResourceUtils::LoadResourceFromFile(
+        file_name, {{"mesh_index", i}}, true);
     resource->AddMesh(mesh_proxy);
     if (last_material_index_ != -1) {
       resource->SetMaterialForMesh(
@@ -164,23 +147,20 @@ std::unique_ptr<phx::Material> AssimpModelLoader::LoadSingleMaterial(
   aiString relative_path;
   if (AI_SUCCESS ==
       material->GetTexture(aiTextureType_AMBIENT, 0, &relative_path)) {
-    auto image_proxy = ResourceManager::instance().DeclareResource(
-        filepath + "/" + std::string(relative_path.C_Str()));
-    image_proxy->Load();
+    auto image_proxy = ResourceUtils::LoadResourceFromFile(
+        filepath + "/" + std::string(relative_path.C_Str()), {}, true);
     resource->SetAmbientImage(image_proxy);
   }
   if (AI_SUCCESS ==
       material->GetTexture(aiTextureType_DIFFUSE, 0, &relative_path)) {
-    auto image_proxy = ResourceManager::instance().DeclareResource(
-        filepath + "/" + std::string(relative_path.C_Str()));
-    image_proxy->Load();
+    auto image_proxy = ResourceUtils::LoadResourceFromFile(
+        filepath + "/" + std::string(relative_path.C_Str()), {}, true);
     resource->SetDiffuseImage(image_proxy);
   }
   if (AI_SUCCESS ==
       material->GetTexture(aiTextureType_SPECULAR, 0, &relative_path)) {
-    auto image_proxy = ResourceManager::instance().DeclareResource(
-        filepath + "/" + std::string(relative_path.C_Str()));
-    image_proxy->Load();
+    auto image_proxy = ResourceUtils::LoadResourceFromFile(
+        filepath + "/" + std::string(relative_path.C_Str()), {}, true);
     resource->SetSpecularImage(image_proxy);
   }
 
@@ -248,16 +228,13 @@ std::vector<glm::vec3> AssimpModelLoader::LoadNormals(const aiMesh *mesh) {
 }
 
 bool AssimpModelLoader::LoadFile(const std::string &filename) {
-  std::string complete_filename = phx::resources_root + filename;
-
   info("Load Model: {}", filename);
   const aiScene *scene = importer_.ReadFile(
-      complete_filename, aiProcess_Triangulate | aiProcess_GenSmoothNormals |
-                             aiProcess_JoinIdenticalVertices |
-                             aiProcess_SortByPType);
+      filename, aiProcess_Triangulate | aiProcess_GenSmoothNormals |
+                    aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
   std::cout << std::endl;
   if (!scene) {
-    phx::warn("Error loading model file \"{}\".", complete_filename);
+    phx::warn("Error loading model file \"{}\".", filename);
     return false;
   }
   info("Finished Loading Model");
diff --git a/library/phx/assimp_model_loader.hpp b/library/phx/assimp_model_loader.hpp
index 0981d61066063a798cc245b20d33e93cd4d1c3df..e128fe7477e4ed59cc6420d3b514a9c6b02ff5e9 100644
--- a/library/phx/assimp_model_loader.hpp
+++ b/library/phx/assimp_model_loader.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -54,7 +54,8 @@ class PHOENIX_EXPORT AssimpModelLoader final : public ResourceLoadStrategy {
   AssimpModelLoader& operator=(const AssimpModelLoader&) = delete;
   AssimpModelLoader& operator=(AssimpModelLoader&&) = delete;
 
-  std::unique_ptr<Resource> Load(const ResourceDeclaration& file_name) override;
+  std::unique_ptr<Resource> Load(
+      const ResourceDeclaration& declaration) override;
   void SetProgressUpdateCallback(const std::function<void(float)>& callback);
 
  protected:
diff --git a/library/phx/behavior.cpp b/library/phx/behavior.cpp
index 5c1480956d0980531b6b9c440b84f2014d0b1708..c5a6e2d4c079e5aaa67e5484f89daf878163ee1c 100644
--- a/library/phx/behavior.cpp
+++ b/library/phx/behavior.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/behavior.hpp b/library/phx/behavior.hpp
index 2cb411782b6f46c433b5d07f1f9862f909568116..6de30dc82b4f860f979f65f876cde355fc36b3cb 100644
--- a/library/phx/behavior.hpp
+++ b/library/phx/behavior.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/behavior_system.cpp b/library/phx/behavior_system.cpp
index 130e0cba6baad75ef427ce1950a0e0cbad3f349d..de26fa1794bfeeead8d2dce7e86ebe2bed663759 100644
--- a/library/phx/behavior_system.cpp
+++ b/library/phx/behavior_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/behavior_system.hpp b/library/phx/behavior_system.hpp
index ffb67f57d5ff87655b9312919ffdf87535d5ab6f..f8dfd0986bfe7d590cce5a06848484985d22bdbb 100644
--- a/library/phx/behavior_system.hpp
+++ b/library/phx/behavior_system.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/blit_pass.cpp b/library/phx/blit_pass.cpp
index 61d513bb5cb593fd3b5c9a1c9e6fd9611ef817e7..edc96534e6e531d9ca7ee2e28380339d5ee45177 100644
--- a/library/phx/blit_pass.cpp
+++ b/library/phx/blit_pass.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/blit_pass.hpp b/library/phx/blit_pass.hpp
index c0e4c4244fb3718a0acf5fd7510e9695af21144a..8b62607003da4462e6bacbfcd93af9e24616e619 100644
--- a/library/phx/blit_pass.hpp
+++ b/library/phx/blit_pass.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/clear_pass.cpp b/library/phx/clear_pass.cpp
index 6f95bf290a32142c91133e38ae16805e0927f646..6f5c166c60f40a676e390b5f4a3a497d302e87da 100644
--- a/library/phx/clear_pass.cpp
+++ b/library/phx/clear_pass.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/clear_pass.hpp b/library/phx/clear_pass.hpp
index 415d7e9373f6c753c161ff01c280e72c7fdb05d1..af2bd0d3af6ea666cb3f173d425d1334fad3e47e 100644
--- a/library/phx/clear_pass.hpp
+++ b/library/phx/clear_pass.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/component.cpp b/library/phx/component.cpp
index a3abeec58ef73c75b60760229c14fd86675f0367..7f34e170ca14df0bad295115a194950213a9e18f 100644
--- a/library/phx/component.cpp
+++ b/library/phx/component.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/component.hpp b/library/phx/component.hpp
index 38c98b3c11e2721391b3b84b9c7d7ba932bda149..e47c01d7e4a963200db8780b6e2fdf8c84498280 100644
--- a/library/phx/component.hpp
+++ b/library/phx/component.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/component_check.hpp b/library/phx/component_check.hpp
index c83f655463081bc8e9113426bcd3dc00f53d0447..d0cbcc88d3367b2b401a00258194282a661776c8 100644
--- a/library/phx/component_check.hpp
+++ b/library/phx/component_check.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/display_system.cpp b/library/phx/display_system.cpp
index 6d70d10bf70d83a7b64afbb87a656de81218aa28..80e7a9a440e1c3f81bed4cc6c627909598397af1 100644
--- a/library/phx/display_system.cpp
+++ b/library/phx/display_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/display_system.hpp b/library/phx/display_system.hpp
index 8a14d3c4c769acd65efc0591cdb688c1a8bbe1ed..ffcc68492dda98db366bf20f8bf9cd684590e1e8 100644
--- a/library/phx/display_system.hpp
+++ b/library/phx/display_system.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -26,6 +26,7 @@
 #include <memory>
 #include <vector>
 
+#include "phx/engine.hpp"
 #include "phx/export.hpp"
 #include "phx/hmd.hpp"
 #include "phx/system.hpp"
@@ -37,7 +38,6 @@ namespace phx {
 
 class PHOENIX_EXPORT DisplaySystem : public System {
  public:
-  explicit DisplaySystem(Engine* engine);
   DisplaySystem(const DisplaySystem&) = delete;
   DisplaySystem(DisplaySystem&&) = default;
   ~DisplaySystem();
@@ -57,6 +57,9 @@ class PHOENIX_EXPORT DisplaySystem : public System {
   void Update(const FrameTimer::TimeInfo&) override;
 
  protected:
+  explicit DisplaySystem(Engine* engine);
+  friend DisplaySystem* Engine::CreateSystem<DisplaySystem>();
+
   std::unique_ptr<Window> window_;
   std::unique_ptr<HMD> hmd_;
 };
diff --git a/library/phx/engine.cpp b/library/phx/engine.cpp
index 8af00cb0e73359fa087aff6b521338a2c1a27647..315ac6ae015c0562bb1c78c3a0461c275943469b 100644
--- a/library/phx/engine.cpp
+++ b/library/phx/engine.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -57,27 +57,19 @@ void Engine::Run() {
 
   frame_timer_.Start();
 
-  InitializeSystems();
   is_running_ = true;
   while (is_running_) UpdateSystems();
-  TerminateSystems();
 }
 
 void Engine::Stop() { is_running_ = false; }
 
 bool Engine::IsRunning() const { return is_running_; }
 
-void Engine::InitializeSystems() {
-  for (auto& system : systems_) system->Initialize();
-}
 void Engine::UpdateSystems() {
   // before updating all systems, update the frame timer
   frame_timer_.Tick();
   for (auto& system : systems_) system->Update(frame_timer_.GetTimeInfo());
 }
-void Engine::TerminateSystems() {
-  for (auto& system : systems_) system->Terminate();
-}
 
 std::string Engine::ToString() const {
   return "(Engine, #Systems: " + std::to_string(systems_.size()) +
diff --git a/library/phx/engine.hpp b/library/phx/engine.hpp
index 21863e82b142a71894d4784f20ca5c1338f4bc56..481afbe06eac5f947da28688362e5256427d1ad5 100644
--- a/library/phx/engine.hpp
+++ b/library/phx/engine.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -108,9 +108,7 @@ class PHOENIX_EXPORT Engine final : public Loggable {
     return typeid(SystemType) == typeid(system);
   }
 
-  void InitializeSystems();
   void UpdateSystems();
-  void TerminateSystems();
 
   std::vector<std::unique_ptr<System>> systems_;
   bool is_running_ = false;
diff --git a/library/phx/entity.cpp b/library/phx/entity.cpp
index c6183c6d1cfa5cb23cd770af036763251431da9e..38a70397762a9b64b0faef3e266db4da1a7fa833 100644
--- a/library/phx/entity.cpp
+++ b/library/phx/entity.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -26,14 +26,6 @@
 
 namespace phx {
 
-const char RUNTIME_ENTITY_NAME_HMD[] = "RuntimeEntityHMD";
-const char RUNTIME_ENTITY_NAME_EYE_LEFT[] = "RuntimeEntityEyeLeft";
-const char RUNTIME_ENTITY_NAME_EYE_RIGHT[] = "RuntimeEntityEyeRight";
-const char RUNTIME_ENTITY_NAME_CONTROLLER_LEFT[] =
-    "RuntimeEntityControllerLeft";
-const char RUNTIME_ENTITY_NAME_CONTROLLER_RIGHT[] =
-    "RuntimeEntityControllerRight";
-
 std::string Entity::ToString() const {
   return GetName() +
          " (Entity, #Components: " + std::to_string(GetNumberOfComponents()) +
diff --git a/library/phx/entity.hpp b/library/phx/entity.hpp
index 93f19645401f432add2facf1103897e3f529d81e..5cfb7e533d5d953c71c8091b4f21a2cac8611194 100644
--- a/library/phx/entity.hpp
+++ b/library/phx/entity.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -39,12 +39,6 @@ namespace phx {
 
 class Scene;  // necessary forward declaration to avoid circular #include
 
-extern const char PHOENIX_EXPORT RUNTIME_ENTITY_NAME_HMD[];
-extern const char PHOENIX_EXPORT RUNTIME_ENTITY_NAME_EYE_LEFT[];
-extern const char PHOENIX_EXPORT RUNTIME_ENTITY_NAME_EYE_RIGHT[];
-extern const char PHOENIX_EXPORT RUNTIME_ENTITY_NAME_CONTROLLER_LEFT[];
-extern const char PHOENIX_EXPORT RUNTIME_ENTITY_NAME_CONTROLLER_RIGHT[];
-
 class PHOENIX_EXPORT Entity : public Nameable, public Loggable {
   friend class Scene;
 
diff --git a/library/phx/frame_graph.cpp b/library/phx/frame_graph.cpp
index 567fc6f218bb716961e904049b79fc9c4fc3b38e..c672239f7ec7a5e17ecc6163b3731273ce057b89 100644
--- a/library/phx/frame_graph.cpp
+++ b/library/phx/frame_graph.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/frame_graph.hpp b/library/phx/frame_graph.hpp
index 0df4ba69c996767fcca631314846c4e47d84ea4f..c8d83a8051172fa58c06d15a8007674787927977 100644
--- a/library/phx/frame_graph.hpp
+++ b/library/phx/frame_graph.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/frame_timer.hpp b/library/phx/frame_timer.hpp
index 7b924df1aeae8d5d415ecb8d0e58003ca0a9b90e..82b967c3bad4cb5640299c9d2b04c61430531009 100644
--- a/library/phx/frame_timer.hpp
+++ b/library/phx/frame_timer.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/geometry_pass.cpp b/library/phx/geometry_pass.cpp
index abd442c588c134a4944784f6a3ed455a80a8c66e..01fd54e7d25011d18f4b237252237df445adcdf3 100644
--- a/library/phx/geometry_pass.cpp
+++ b/library/phx/geometry_pass.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -36,6 +36,7 @@
 #include "phx/logger.hpp"
 #include "phx/mesh.hpp"
 #include "phx/resource_proxy.hpp"
+#include "phx/resource_utils.hpp"
 #include "phx/transform.hpp"
 
 namespace phx {
@@ -139,11 +140,9 @@ void GeometryPass::Initialize() {
 
 void GeometryPass::SetUpShaders() {
   auto vertex_shader_proxy =
-      phx::ResourceManager::instance().DeclareResource("shader/phong.vert");
-  vertex_shader_proxy->Load();
+      ResourceUtils::LoadResourceFromFile("shader/phong.vert");
   auto fragment_shader_proxy =
-      phx::ResourceManager::instance().DeclareResource("shader/phong.frag");
-  fragment_shader_proxy->Load();
+      ResourceUtils::LoadResourceFromFile("shader/phong.frag");
 
   shader_program_ = std::make_unique<ShaderProgram>();
   shader_program_->SetShaderProxy(ShaderProgram::VERTEX, vertex_shader_proxy);
diff --git a/library/phx/geometry_pass.hpp b/library/phx/geometry_pass.hpp
index db4007006b820b56af8dfcc81a8a34ab327c7ee6..9d969edca251c110d96b2bf52e76dae54954614e 100644
--- a/library/phx/geometry_pass.hpp
+++ b/library/phx/geometry_pass.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/hierarchical.hpp b/library/phx/hierarchical.hpp
index 18af1b150a7fe5547e18c521fb2e15cd757f9f26..f12ecfc5a80770bbed87f5e02a12bca6926b0168 100644
--- a/library/phx/hierarchical.hpp
+++ b/library/phx/hierarchical.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/hmd.cpp b/library/phx/hmd.cpp
index 4ca0cedc90882daa7f38b8ae2d01dfef5351c256..847f58f078f0aa03293d97fbd52729f2e387b6ba 100644
--- a/library/phx/hmd.cpp
+++ b/library/phx/hmd.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -197,7 +197,11 @@ std::unique_ptr<Material> HMD::GetControllerMaterial(Controller controller) {
   material->SetSpecularColor(glm::vec3(0.3, 0.3, 0.3));
 
   auto texture_proxy = ResourceManager::instance().DeclareResource(
-      "texture:" + std::to_string(model->diffuseTextureId) + ".openvr");
+      {{"TYPE", "openVR"},
+       {"OpenVR_type", "texture"},
+       {"texture_id", model->diffuseTextureId},
+       {"side",
+        controller == HMD::Controller::LEFT_CONTROLLER ? "left" : "right"}});
   texture_proxy->Load();
   material->SetDiffuseImage(texture_proxy);
 
@@ -219,8 +223,10 @@ std::unique_ptr<Image> HMD::GetControllerTexture(int id) {
   std::copy(texture_map->rubTextureMapData,
             texture_map->rubTextureMapData + image_data.size(),
             image_data.begin());
-  auto image = std::make_unique<phx::ImageTyped<phx::ImageFormatRGBA>>(
-      texture_map->unWidth, texture_map->unHeight, image_data);
+  auto image = std::make_unique<phx::Image>(
+      image_data, std::array<std::size_t, 2>{
+                      {static_cast<std::size_t>(texture_map->unWidth),
+                       static_cast<std::size_t>(texture_map->unHeight)}});
 
   return image;
 }
diff --git a/library/phx/hmd.hpp b/library/phx/hmd.hpp
index 5bdab90b619b1141327f0e9b368b743999881479..a3e0b7cfb165fc91f4e9622a6261cf803889fd59 100644
--- a/library/phx/hmd.hpp
+++ b/library/phx/hmd.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -41,6 +41,8 @@ SUPPRESS_WARNINGS_END
 
 namespace phx {
 
+class OpenVRResourceLoader;
+
 class PHOENIX_EXPORT HMD {
  public:
   HMD();
@@ -71,10 +73,6 @@ class PHOENIX_EXPORT HMD {
   glm::mat4 GetLeftControllerTransformation();
   glm::mat4 GetRightControllerTransformation();
 
-  std::unique_ptr<Mesh> GetControllerMesh(Controller controller);
-  std::unique_ptr<Material> GetControllerMaterial(Controller controller);
-  std::unique_ptr<Image> GetControllerTexture(int id);
-
   static bool IsHMDPresent();
 
   void Submit(Side side, gl::texture_2d* texture);
@@ -87,7 +85,11 @@ class PHOENIX_EXPORT HMD {
   glm::mat4 GetProjectionMatrixFromOpenVR(const vr::Hmd_Eye eye);
   glm::mat4 GetEyeToHeadMatrixFromOpenVR(const vr::Hmd_Eye eye);
 
+  friend OpenVRResourceLoader;
   vr::RenderModel_t* GetControllerModel(Controller controller);
+  std::unique_ptr<Mesh> GetControllerMesh(Controller controller);
+  std::unique_ptr<Material> GetControllerMaterial(Controller controller);
+  std::unique_ptr<Image> GetControllerTexture(int id);
 
   static glm::mat4 TransformToGlmMatrix(const vr::HmdMatrix34_t& mat);
   static glm::mat4 TransformToGlmMatrix(const vr::HmdMatrix44_t& mat);
diff --git a/library/phx/image.cpp b/library/phx/image.cpp
index e752a2e15abd53aaf8d00e7d9d6a22b4400826c0..29619e6ad34ba2e1a2f0da6c8610bfaea19e85d5 100644
--- a/library/phx/image.cpp
+++ b/library/phx/image.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -22,447 +22,160 @@
 
 #include "image.hpp"
 
-#include <algorithm>
-#include <cassert>
-#include <cmath>
-#include <memory>
 #include <string>
-#include <vector>
-
-#include "phx/logger.hpp"
-
-namespace {
-// local class that takes care of initializing freeimage only once,
-// and deinitializing it at the end of the application
-class FreeImageInit {
- public:
-  static void InitFreeImage() {
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wexit-time-destructors"
-#endif
-    static FreeImageInit instance;
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-  }
-
- private:
-  FreeImageInit() { FreeImage_Initialise(); }
-  ~FreeImageInit() { FreeImage_DeInitialise(); }
-
-  FreeImageInit(const FreeImageInit&) = delete;
-  void operator=(const FreeImageInit&) = delete;
-};
-
-}  // namespace
+#include <utility>
 
 namespace phx {
-
-Image::Image(std::size_t width, std::size_t height)
-    : width_(width), height_(height) {}
-
-Image::Image(std::size_t width, std::size_t height,
-             const std::vector<unsigned char>& buffer)
-    : width_(width), height_(height), buffer_(buffer) {}
-
-std::unique_ptr<Image> Image::CreateEmptyImage(std::size_t width,
-                                               std::size_t height,
-                                               std::size_t bytes_per_pixel) {
-  assert(bytes_per_pixel == 1 || bytes_per_pixel == 3 || bytes_per_pixel == 4);
-  switch (bytes_per_pixel) {
-    case 1:
-      return std::make_unique<ImageTyped<ImageFormat8bit>>(width, height);
-    case 3:
-      return std::make_unique<ImageTyped<ImageFormatRGB>>(width, height);
-    case 4:
-      return std::make_unique<ImageTyped<ImageFormatRGBA>>(width, height);
-    default:
-      return nullptr;
-  }
-}
-
-FREE_IMAGE_FORMAT GetFreeImageFileType(const std::string& filename);
-FREE_IMAGE_FORMAT GetFreeImageFileType(const std::string& filename) {
-  // find out format - needs to have extension
-  auto extension_start = filename.find_first_of('.');
-  if (extension_start == std::string::npos) {
-    // failure
-    return FIF_UNKNOWN;
-  }
-  std::string extension = filename.substr(extension_start + 1);
-
-  std::transform(extension.begin(), extension.end(), extension.begin(),
-                 ::tolower);
-
-  // the most important file types first
-  if (extension == "png") return FIF_PNG;
-  if (extension == "jpg" || extension == "jpeg" || extension == "jif" ||
-      extension == "jpe")
-    return FIF_JPEG;
-  if (extension == "tga" || extension == "targa") return FIF_TARGA;
-  if (extension == "bmp") return FIF_BMP;
-  if (extension == "gif") return FIF_GIF;
-  if (extension == "tif" || extension == "tiff") return FIF_TIFF;
-
-  if (extension == "psd") return FIF_PSD;
-
-  // then all the rest
-  if (extension == "j2k" || extension == "j2c") return FIF_J2K;
-  if (extension == "jp2") return FIF_JP2;
-  if (extension == "jng") return FIF_JNG;
-  if (extension == "ico") return FIF_ICO;
-  if (extension == "koa") return FIF_KOALA;
-  if (extension == "lbm") return FIF_LBM;
-  if (extension == "iff") return FIF_IFF;
-  if (extension == "mng") return FIF_MNG;
-  if (extension == "pbm") return FIF_PBMRAW;
-  if (extension == "pcd") return FIF_PCD;
-  if (extension == "pcx") return FIF_PCX;
-  if (extension == "pgm") return FIF_PGMRAW;
-  if (extension == "ppm") return FIF_PPMRAW;
-  if (extension == "ras") return FIF_RAS;
-  if (extension == "wbmp") return FIF_WBMP;
-  if (extension == "cut") return FIF_CUT;
-  if (extension == "xbm") return FIF_XBM;
-  if (extension == "xpm") return FIF_XPM;
-  if (extension == "dds") return FIF_DDS;
-  if (extension == "hdr") return FIF_HDR;
-  if (extension == "g3") return FIF_FAXG3;
-  if (extension == "sgi") return FIF_SGI;
-  if (extension == "exr") return FIF_EXR;
-  if (extension == "pfm") return FIF_PFM;
-  if (extension == "pict") return FIF_PICT;
-  if (extension == "raw") return FIF_RAW;
-  if (extension == "webp") return FIF_WEBP;
-  if (extension == "jxr") return FIF_JXR;
-
-  return FIF_UNKNOWN;
+std::once_flag Image::once_flag_;
+
+Image::Image(const std::array<std::size_t, 2>& dimensions,
+             const std::size_t bits_per_pixel) {
+  Initialize();
+  native_ =
+      FreeImage_AllocateT(FIT_BITMAP, static_cast<std::int32_t>(dimensions[0]),
+                          static_cast<std::int32_t>(dimensions[1]),
+                          static_cast<std::int32_t>(bits_per_pixel), 0, 0, 0);
+  if (!native_) {
+    throw std::runtime_error("FreeImage_AllocateT failed.");
+  }
+}
+Image::Image(const std::string& filepath, const std::int32_t native_flags) {
+  Initialize();
+  format_ = FreeImage_GetFileType(filepath.c_str(), 0);
+  if (format_ == FIF_UNKNOWN)
+    format_ = FreeImage_GetFIFFromFilename(filepath.c_str());
+
+  native_ = FreeImage_Load(format_, filepath.c_str(), native_flags);
+  if (!native_) throw std::runtime_error("FreeImage_Load failed.");
+  SwapRedBlue(native_);
+}
+Image::Image(const Image& that)
+    : Resource(that),
+      Loggable(that),
+      native_(FreeImage_Clone(that.native_)),
+      format_(that.format_) {
+  if (!native_) throw std::runtime_error("FreeImage_Clone failed.");
+  FreeImage_CloneMetadata(native_, that.native_);
+}
+Image::Image(Image&& temp) noexcept
+    : Resource(temp),
+      Loggable(temp),
+      native_(std::move(temp.native_)),
+      format_(std::move(temp.format_)) {
+  temp.native_ = nullptr;
+}
+Image::~Image() {
+  if (native_) FreeImage_Unload(native_);
+}
+Image& Image::operator=(const Image& that) {
+  native_ = FreeImage_Clone(that.native_);
+  format_ = that.format_;
+  if (!native_) throw std::runtime_error("FreeImage_Clone failed.");
+  FreeImage_CloneMetadata(native_, that.native_);
+  return *this;
+}
+Image& Image::operator=(Image&& temp) noexcept {
+  if (this != &temp) {
+    native_ = std::move(temp.native_);
+    format_ = std::move(temp.format_);
+    temp.native_ = nullptr;
+  }
+  return *this;
+}
+
+bool Image::Save(const std::string& filepath, const std::int32_t native_flags) {
+  SwapRedBlue(native_);
+  const auto result =
+      FreeImage_Save(FreeImage_GetFIFFromFilename(filepath.c_str()), native_,
+                     filepath.c_str(), native_flags) != 0;
+  SwapRedBlue(native_);
+  return result;
+}
+
+std::array<std::size_t, 2> Image::GetDimensions() const {
+  return {{static_cast<std::size_t>(FreeImage_GetWidth(native_)),
+           static_cast<std::size_t>(FreeImage_GetHeight(native_))}};
+}
+std::size_t Image::GetPitch() const {
+  return static_cast<std::size_t>(FreeImage_GetPitch(native_));
+}
+std::size_t Image::GetBitsPerPixel() const {
+  return static_cast<std::size_t>(FreeImage_GetBPP(native_));
+}
+
+bool Image::IsEmpty() const { return FreeImage_HasPixels(native_) == 0; }
+
+void Image::SetPixelColor(const std::array<std::size_t, 2>& position,
+                          const std::array<std::uint8_t, 4>& color) {
+  FreeImage_SetPixelColor(
+      native_, static_cast<std::uint32_t>(position[0]),
+      static_cast<std::uint32_t>(position[1]),
+      reinterpret_cast<RGBQUAD*>(const_cast<std::uint8_t*>(color.data())));
+}
+std::array<std::uint8_t, 4> Image::GetPixelColor(
+    const std::array<std::size_t, 2>& position) const {
+  std::array<std::uint8_t, 4> color;
+  FreeImage_GetPixelColor(native_, static_cast<std::uint32_t>(position[0]),
+                          static_cast<std::uint32_t>(position[1]),
+                          reinterpret_cast<RGBQUAD*>(color.data()));
+  return color;
+}
+
+void Image::To4Bits() {
+  SwapRedBlue(native_);
+  Replace(FreeImage_ConvertTo4Bits(native_));
+}
+void Image::To8Bits() {
+  SwapRedBlue(native_);
+  Replace(FreeImage_ConvertToGreyscale(native_));
+}
+void Image::To16Bits() {
+  SwapRedBlue(native_);
+  Replace(FreeImage_ConvertTo16Bits555(native_));
 }
-
-std::unique_ptr<Image> Image::Load(const std::string& filename,
-                                   ImageLoadFlags flags) {
-  ::FreeImageInit::InitFreeImage();
-  // get file type
-  FREE_IMAGE_FORMAT format = GetFreeImageFileType(filename);
-  // unknown? fail
-  if (format == FIF_UNKNOWN) return nullptr;
-  // load as FIBITMAP
-  FIBITMAP* bitmap = FreeImage_Load(format, filename.c_str(), flags);
-  bitmap = FreeImage_ConvertTo32Bits(bitmap);
-  if (bitmap == nullptr) {
-    phx::warn("Failed to load bitmap from {}!", filename);
-    return nullptr;
-  }
-  // get dimensions
-  std::size_t width = static_cast<std::size_t>(FreeImage_GetWidth(bitmap));
-  std::size_t height = static_cast<std::size_t>(FreeImage_GetHeight(bitmap));
-  // get bpp
-  unsigned int bpp = FreeImage_GetBPP(bitmap);
-
-  // create typed image and load from FIBITMAP
-  std::unique_ptr<Image> image;
-  switch (bpp) {
-    case 8:
-      image = std::make_unique<ImageTyped<ImageFormat8bit>>(width, height);
-      break;
-    case 24:
-      image = std::make_unique<ImageTyped<ImageFormatRGB>>(width, height);
-      break;
-    case 32:
-      image = std::make_unique<ImageTyped<ImageFormatRGBA>>(width, height);
-      break;
-    default:
-      // we only support 8, 24, or 32 bpp!
-      return nullptr;
-  }
-
-  // load from FIBTIMAP
-  image->SetFromFreeImageRepresentation(bitmap);
-
-  // clean up
-  FreeImage_Unload(bitmap);
-
-  return image;
-}
-
-bool Image::Save(const std::string& filename, ImageSaveFlags flags) const {
-  FREE_IMAGE_FORMAT format = GetFreeImageFileType(filename);
-  if (format == FIF_UNKNOWN) {
-    // failure
-    return false;
-  }
-
-  // create a FreeImage version of this
-  ::FreeImageInit::InitFreeImage();
-  FIBITMAP* bitmap = CreateFreeImageRepresentation();
-
-  // save
-  bool success = FreeImage_Save(format, bitmap, filename.c_str(), flags);
-
-  // free bitmap
-  FreeImage_Unload(bitmap);
-
-  // success!?
-  return success;
+void Image::To24Bits() {
+  SwapRedBlue(native_);
+  Replace(FreeImage_ConvertTo24Bits(native_));
 }
-
-bool Image::GetIsEqual(const Image* other) const {
-  if (other == nullptr) return false;
-  return (other->width_ == width_ && other->height_ == height_ &&
-          other->buffer_ == buffer_);
+void Image::To32Bits() {
+  SwapRedBlue(native_);
+  Replace(FreeImage_ConvertTo32Bits(native_));
 }
 
 std::string Image::ToString() const {
-  return "Image: " + std::to_string(width_) + "x" + std::to_string(height_);
+  return "Image (dimensions: " + std::to_string(GetDimensions()[0]) + " " +
+         std::to_string(GetDimensions()[1]) +
+         ", bits per pixel: " + std::to_string(GetBitsPerPixel());
 }
 
-template <>
-FIBITMAP* phx::ImageTyped<phx::ImageFormatRGB>::CreateFreeImageRepresentation()
-    const {
-  FIBITMAP* bitmap = FreeImage_Allocate(static_cast<int>(width_),
-                                        static_cast<int>(height_), 24);
-  auto bits = FreeImage_GetBits(bitmap);
-  std::size_t max_index = this->width_ * this->height_ * 3;
-  for (std::size_t index = 0; index < max_index; index += 3) {
-    // just switch r and b bytes, copy g byte
-    bits[index + 0] = this->buffer_[index + 2];
-    bits[index + 1] = this->buffer_[index + 1];
-    bits[index + 2] = this->buffer_[index + 0];
-  }
-  return bitmap;
+void Image::Initialize() {
+  std::call_once(once_flag_, []() { FreeImage_Initialise(); });
 }
 
-template <>
-FIBITMAP* phx::ImageTyped<phx::ImageFormatRGBA>::CreateFreeImageRepresentation()
-    const {
-  FIBITMAP* bitmap = FreeImage_Allocate(static_cast<int>(width_),
-                                        static_cast<int>(height_), 32);
-  auto bits = FreeImage_GetBits(bitmap);
-  std::size_t max_index = this->width_ * this->height_ * 4;
-  for (std::size_t index = 0; index < max_index; index += 4) {
-    // just switch r and b bytes, copy g byte, copy a byte
-    bits[index + 0] = this->buffer_[index + 2];
-    bits[index + 1] = this->buffer_[index + 1];
-    bits[index + 2] = this->buffer_[index + 0];
-    bits[index + 3] = this->buffer_[index + 3];
-  }
-  return bitmap;
+void Image::Replace(FIBITMAP* native) {
+  if (native_) FreeImage_Unload(native_);
+  native_ = native;
+  SwapRedBlue(native_);
+  if (!native_) throw std::runtime_error("Replace failed.");
 }
 
-template <>
-FIBITMAP* phx::ImageTyped<phx::ImageFormat8bit>::CreateFreeImageRepresentation()
-    const {
-  FIBITMAP* bitmap = FreeImage_Allocate(static_cast<int>(width_),
-                                        static_cast<int>(height_), 8);
-  auto bits = FreeImage_GetBits(bitmap);
-  // just copy bytes
-  std::size_t max_index = this->width_ * this->height_ * 1;
-  for (std::size_t i = 0; i < max_index; ++i) {
-    bits[i] = this->buffer_[i];
-  }
-  return bitmap;
-}
-
-template <>
-void phx::ImageTyped<phx::ImageFormatRGB>::SetFromFreeImageRepresentation(
-    FIBITMAP* bitmap) {
-  std::size_t bpp = this->GetBytesPerPixel();
-  assert(bpp == 3);
-  auto bits = FreeImage_GetBits(bitmap);
-  std::size_t max_index = this->width_ * this->height_ * bpp;
-  for (std::size_t index = 0; index < max_index; index += bpp) {
-    // just switch r and b bytes, copy g byte
-    this->buffer_[index + 0] = bits[index + 2];
-    this->buffer_[index + 1] = bits[index + 1];
-    this->buffer_[index + 2] = bits[index + 0];
-  }
-}
-
-template <>
-void phx::ImageTyped<phx::ImageFormatRGBA>::SetFromFreeImageRepresentation(
-    FIBITMAP* bitmap) {
-  std::size_t bpp = this->GetBytesPerPixel();
-  assert(bpp == 4);
-  auto bits = FreeImage_GetBits(bitmap);
-  std::size_t max_index = this->width_ * this->height_ * bpp;
-  for (std::size_t index = 0; index < max_index; index += bpp) {
-    // just switch r and b bytes, copy g byte, copy a byte
-    this->buffer_[index + 0] = bits[index + 2];
-    this->buffer_[index + 1] = bits[index + 1];
-    this->buffer_[index + 2] = bits[index + 0];
-    this->buffer_[index + 3] = bits[index + 3];
-  }
-}
-
-template <>
-void phx::ImageTyped<phx::ImageFormat8bit>::SetFromFreeImageRepresentation(
-    FIBITMAP* bitmap) {
-  std::size_t bpp = this->GetBytesPerPixel();
-  assert(bpp == 1);
-  auto bits = FreeImage_GetBits(bitmap);
-  // just copy bytes
-  std::size_t max_index = this->width_ * this->height_ * bpp;
-  for (std::size_t i = 0; i < max_index; ++i) {
-    this->buffer_[i] = bits[i];
-  }
-}
+bool Image::SwapRedBlue(FIBITMAP* dib) {
+  if (FreeImage_GetImageType(dib) != FIT_BITMAP) return false;
+  const auto bytes_per_pixel = FreeImage_GetBPP(dib) / 8;
+  if (bytes_per_pixel > 4 || bytes_per_pixel < 3) return false;
 
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormat8bit>::Convert<phx::ImageFormat8bit>() const {
-  // just copy the image
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormat8bit));
-  std::size_t max_index =
-      this->width_ * this->height_ * sizeof(phx::ImageFormat8bit);
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_index; ++i) {
-    image_pointer[i] = this->buffer_[i];
+  auto height = FreeImage_GetHeight(dib);
+  auto pitch = FreeImage_GetPitch(dib);
+  auto line_size = FreeImage_GetLine(dib);
+  auto line = FreeImage_GetBits(dib);
+  for (unsigned y = 0; y < height; ++y, line += pitch) {
+    for (auto pixel = line; pixel < line + line_size;
+         pixel += bytes_per_pixel) {
+      pixel[0] ^= pixel[2];
+      pixel[2] ^= pixel[0];
+      pixel[0] ^= pixel[2];
+    }
   }
-  return image;
+  return true;
 }
-
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormatRGB>::Convert<phx::ImageFormatRGB>() const {
-  // just copy the image
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormatRGB));
-  std::size_t max_index =
-      this->width_ * this->height_ * sizeof(phx::ImageFormatRGB);
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_index; ++i) {
-    image_pointer[i] = this->buffer_[i];
-  }
-  return image;
-}
-
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormatRGBA>::Convert<phx::ImageFormatRGBA>() const {
-  // just copy the image
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormatRGBA));
-  std::size_t max_index =
-      this->width_ * this->height_ * sizeof(phx::ImageFormatRGBA);
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_index; ++i) {
-    image_pointer[i] = this->buffer_[i];
-  }
-  return image;
-}
-
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormatRGB>::Convert<phx::ImageFormatRGBA>() const {
-  // just copy the image and add alpha components (255)
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormatRGBA));
-  std::size_t max_entry = this->width_ * this->height_;
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_entry; ++i) {
-    image_pointer[i * 4 + 0] = this->buffer_[i * 3 + 0];
-    image_pointer[i * 4 + 1] = this->buffer_[i * 3 + 1];
-    image_pointer[i * 4 + 2] = this->buffer_[i * 3 + 2];
-    image_pointer[i * 4 + 3] = 255;
-  }
-  return image;
-}
-
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormatRGB>::Convert<phx::ImageFormat8bit>() const {
-  // new components are averages of old ones (grayscale conversion)
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormat8bit));
-  std::size_t max_entry = this->width_ * this->height_;
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_entry; ++i) {
-    float sum = std::round(static_cast<float>(this->buffer_[i * 3 + 0] +
-                                              this->buffer_[i * 3 + 1] +
-                                              this->buffer_[i * 3 + 2]) /
-                           3.f);
-    image_pointer[i] = static_cast<unsigned char>(sum);
-  }
-  return image;
-}
-
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormatRGBA>::Convert<phx::ImageFormat8bit>() const {
-  // new components are averages of old ones (grayscale conversion)
-  // alpha is ignored
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormat8bit));
-  std::size_t max_entry = this->width_ * this->height_;
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_entry; ++i) {
-    float sum = std::round(static_cast<float>(this->buffer_[i * 4 + 0] +
-                                              this->buffer_[i * 4 + 1] +
-                                              this->buffer_[i * 4 + 2]) /
-                           3.f);
-    image_pointer[i] = static_cast<unsigned char>(sum);
-  }
-  return image;
-}
-
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormatRGBA>::Convert<phx::ImageFormatRGB>() const {
-  // just copy the image and remove alpha components
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormatRGB));
-  std::size_t max_entry = this->width_ * this->height_;
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_entry; ++i) {
-    image_pointer[i * 3 + 0] = this->buffer_[i * 4 + 0];
-    image_pointer[i * 3 + 1] = this->buffer_[i * 4 + 1];
-    image_pointer[i * 3 + 2] = this->buffer_[i * 4 + 2];
-  }
-  return image;
-}
-
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormat8bit>::Convert<phx::ImageFormatRGB>() const {
-  // just copy and multiply the components
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormatRGB));
-  std::size_t max_entry = this->width_ * this->height_;
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_entry; ++i) {
-    image_pointer[i * 3 + 0] = this->buffer_[i];
-    image_pointer[i * 3 + 1] = this->buffer_[i];
-    image_pointer[i * 3 + 2] = this->buffer_[i];
-  }
-  return image;
-}
-
-template <>
-template <>
-std::unique_ptr<Image>
-phx::ImageTyped<phx::ImageFormat8bit>::Convert<phx::ImageFormatRGBA>() const {
-  // just copy and multiply the components, alpha=255
-  auto image = phx::Image::CreateEmptyImage(this->width_, this->height_,
-                                            sizeof(phx::ImageFormatRGBA));
-  std::size_t max_entry = this->width_ * this->height_;
-  unsigned char* image_pointer = image->GetDataPointer();
-  for (std::size_t i = 0; i < max_entry; ++i) {
-    image_pointer[i * 4 + 0] = this->buffer_[i];
-    image_pointer[i * 4 + 1] = this->buffer_[i];
-    image_pointer[i * 4 + 2] = this->buffer_[i];
-    image_pointer[i * 4 + 3] = 255;
-  }
-  return image;
-}
-
 }  // namespace phx
diff --git a/library/phx/image.hpp b/library/phx/image.hpp
index a25b150a07587a4ae9ad54997b74d5e5a2173219..5f686d332ed717a515565219c031645531368082 100644
--- a/library/phx/image.hpp
+++ b/library/phx/image.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -28,9 +28,13 @@
 #include <windows.h>
 #endif  // _WIN32
 
-#include <memory>
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <mutex>
+#include <stdexcept>
 #include <string>
-#include <vector>
+#include <utility>
 
 #include "FreeImage.h"
 
@@ -41,290 +45,82 @@
 namespace phx {
 class PHOENIX_EXPORT Image : public Resource, public Loggable {
  public:
-  enum ImageSaveFlags {
-    ISF_DEFAULT = 0,
-
-    ISF_JPEG_QUALITY_10 = JPEG_QUALITYBAD,
-    ISF_JPEG_QUALITY_25 = JPEG_QUALITYAVERAGE,
-    ISF_JPEG_QUALITY_50 = JPEG_QUALITYNORMAL,
-    ISF_JPEG_QUALITY_75_DEFAULT = JPEG_QUALITYGOOD,
-    ISF_JPEG_QUALITY_100 = JPEG_QUALITYSUPERB,
-    ISF_JPEG_PROGRESSIVE = JPEG_PROGRESSIVE,
-    ISF_JPEG_OPTIMIZE_FILESIZE = JPEG_OPTIMIZE,
-    ISF_JPEG_BASELINE = JPEG_BASELINE,
-    ISF_JPEG_SUBSAMPLING_411 = JPEG_SUBSAMPLING_411,
-    ISF_JPEG_SUBSAMPLING_420 = JPEG_SUBSAMPLING_420,
-    ISF_JPEG_SUBSAMPLING_422 = JPEG_SUBSAMPLING_422,
-    ISF_JPEG_SUBSAMPLING_444 = JPEG_SUBSAMPLING_444,
-
-    ISF_PNG_DEFAULT_COMPRESSION = PNG_Z_DEFAULT_COMPRESSION,
-    ISF_PNG_NO_COMPRESSION = PNG_Z_NO_COMPRESSION,
-    ISF_PNG_BEST_SPEED = PNG_Z_BEST_SPEED,
-    ISF_PNG_BEST_COMPRESSION = PNG_Z_BEST_COMPRESSION,
-    ISF_PNG_INTERLACED = PNG_INTERLACED,
-
-    ISF_TIFF_CMYK = TIFF_CMYK,
-    ISF_TIFF_PACKBITS_COMPRESSION = TIFF_PACKBITS,
-    ISF_TIFF_DEFLATE_COMPRESSION = TIFF_DEFLATE,
-    ISF_TIFF_ADOBE_DEFLATE_COMPRESSION = TIFF_ADOBE_DEFLATE,
-    ISF_TIFF_NO_COMPRESSION = TIFF_NONE,
-    ISF_TIFF_LZW_COMPRESSION = TIFF_LZW,
-    ISF_TIFF_JPEG_COMPRESSION = TIFF_JPEG,
-    ISF_TIFF_LOGLUV_COMPRESSION = TIFF_LOGLUV,
-    ISF_TIFF_CCITT_GROUP_3_FAX_ENCODING = TIFF_CCITTFAX3,
-    ISF_TIFF_CCITT_GROUP_4_FAX_ENCODING = TIFF_CCITTFAX3,
-
-    ISF_TARGA_RLE_COMPRESSION = TARGA_SAVE_RLE,
-
-    ISF_BMP_RLE_COMPRESSION = BMP_SAVE_RLE,
-
-    ISF_EXR_FLOAT_FORMAT = EXR_FLOAT,
-    ISF_EXR_NO_COMPRESSION = EXR_NONE,
-    ISF_EXR_ZIP_COMPRESSION = EXR_ZIP,
-    ISF_EXR_PIZ_COMPRESSION = EXR_PIZ,
-    ISF_EXR_LOSSY_24BIT_FLOAT_COMPRESSION = EXR_PXR24,
-    ISF_EXR_LOSSY_44PERCENT_FLOAT_COMPRESSION = EXR_B44,
-    ISF_EXR_LUMINANCE_CHROMA_INSTEAD_OF_RGB = EXR_LC,
-
-    ISF_ICO_CREATE_ALPHA_CHANNEL = ICO_MAKEALPHA,
-
-    ISF_PNM_RAW_FORMAT = PNM_SAVE_RAW,
-    ISF_PNM_ASCII_FORMAT = PNM_SAVE_ASCII,
-
-    ISF_WEBP_LOSSLESS = WEBP_LOSSLESS,
-
-    ISF_JXR_LOSSLESS = JXR_LOSSLESS,
-    ISF_JXR_PROGRESSIVE = JXR_PROGRESSIVE
-  };
-
-  enum ImageLoadFlags {
-    ILF_DEFAULT = 0,
-
-    ILF_JPEG_FAST_QUALITY_DEFAULT = JPEG_FAST,
-    ILF_JPEG_ACCURATE_QUALITY = JPEG_ACCURATE,
-    ILF_JPEG_LOAD_CMYK_AS_IS = JPEG_CMYK,
-    ILF_JPEG_EXIF_ROTATE = JPEG_EXIFROTATE,
-    ILF_JPEG_CONVERT_GREYSCALE = JPEG_GREYSCALE,
-
-    ILF_PNG_IGNORE_GAMMA = PNG_IGNOREGAMMA,
-
-    ILF_TIFF_CMYK = TIFF_CMYK,
-
-    ILF_TARGA_CONVERT_TO_RGB888 = TARGA_LOAD_RGB888,
-
-    ILF_PCD_BASE_SIZE_768_512 = PCD_BASE,
-    ILF_PCD_BASEDIV4_SIZE_384_256 = PCD_BASEDIV4,
-    ILF_PCD_BASEDIV16_SIZE_192_128 = PCD_BASEDIV16,
-
-    ILF_PSD_LOAD_CMYK = PSD_CMYK,
-    ILF_PSD_LOAD_CIELAB = PSD_LAB,
-
-    ILF_RAW_LOAD_EMBEDDED_PREVIEW = RAW_PREVIEW,
-    ILF_RAW_LOAD_AS_24BIT_RGB = RAW_DISPLAY,
-    ILF_RAW_LOAD_HALF_SIZE = RAW_HALFSIZE,
-    ILF_RAW_LOAD_UNPROCESSED = RAW_UNPROCESSED
-  };
-
-  Image() = delete;
-  Image(std::size_t width, std::size_t height);
-  Image(std::size_t width, std::size_t height,
-        const std::vector<unsigned char>& buffer);
-  Image(const Image&) = default;
-  Image(Image&&) = default;
-  virtual ~Image() = default;
-
-  Image& operator=(const Image&) = default;
-  Image& operator=(Image&&) = default;
-
-  static std::unique_ptr<Image> CreateEmptyImage(
-      std::size_t width, std::size_t height, std::size_t bytes_per_pixel = 3);
-  static std::unique_ptr<Image> Load(const std::string& filename,
-                                     ImageLoadFlags flags = ILF_DEFAULT);
-
-  inline std::size_t GetWidth() const { return width_; }
-  inline std::size_t GetHeight() const { return height_; }
-  virtual std::size_t GetBytesPerPixel() const = 0;
-
-  inline unsigned char* GetDataPointer() { return buffer_.data(); }
-
-  bool Save(const std::string& filename,
-            ImageSaveFlags flags = ISF_DEFAULT) const;
-
-  bool GetIsEqual(const Image* other) const;
-
-  template <typename T>
-  std::unique_ptr<Image> Convert() const;
-
-  std::string ToString() const override;
-
- protected:
-  virtual FIBITMAP* CreateFreeImageRepresentation() const = 0;
-  virtual void SetFromFreeImageRepresentation(FIBITMAP* bitmap) = 0;
-
-  std::size_t width_;
-  std::size_t height_;
-  std::vector<unsigned char> buffer_;
-};
-
-class PHOENIX_EXPORT ImageFormat {};
-class PHOENIX_EXPORT ImageFormatRGB : public ImageFormat {
- public:
-  ImageFormatRGB(unsigned char r, unsigned char g, unsigned char b)
-      : r_(r), g_(g), b_(b) {}
-
-  bool operator==(const ImageFormatRGB& other) const {
-    return (r_ == other.r_ && g_ == other.g_ && b_ == other.b_);
-  }
-  bool operator!=(const ImageFormatRGB& other) const {
-    return (r_ != other.r_ || g_ != other.g_ || b_ != other.b_);
-  }
-
-  unsigned char r_;
-  unsigned char g_;
-  unsigned char b_;
-};
-class PHOENIX_EXPORT ImageFormatRGBA : public ImageFormat {
- public:
-  ImageFormatRGBA(unsigned char r, unsigned char g, unsigned char b,
-                  unsigned char a)
-      : r_(r), g_(g), b_(b), a_(a) {}
-
-  bool operator==(const ImageFormatRGBA& other) const {
-    return (r_ == other.r_ && g_ == other.g_ && b_ == other.b_ &&
-            a_ == other.a_);
-  }
-  bool operator!=(const ImageFormatRGBA& other) const {
-    return (r_ != other.r_ || g_ != other.g_ || b_ != other.b_ ||
-            a_ != other.a_);
+  explicit Image(const std::array<std::size_t, 2>& dimensions,
+                 const std::size_t bits_per_pixel = 8);
+  template <typename ColorType>
+  explicit Image(const ColorType& color,
+                 const std::array<std::size_t, 2>& dimensions,
+                 const std::size_t bits_per_pixel = 8) {
+    Initialize();
+    native_ = FreeImage_AllocateExT(FIT_BITMAP,
+                                    static_cast<std::int32_t>(dimensions[0]),
+                                    static_cast<std::int32_t>(dimensions[1]),
+                                    static_cast<std::int32_t>(bits_per_pixel),
+                                    &color[0], 0, nullptr, 0, 0, 0);
+    if (!native_) {
+      throw std::runtime_error("FreeImage_AllocateExT failed.");
+    }
   }
-
-  unsigned char r_;
-  unsigned char g_;
-  unsigned char b_;
-  unsigned char a_;
-};
-class PHOENIX_EXPORT ImageFormat8bit : public ImageFormat {
- public:
-  explicit ImageFormat8bit(unsigned char value) : value_(value) {}
-
-  bool operator==(const ImageFormat8bit& other) const {
-    return value_ == other.value_;
+  template <typename DataType>
+  explicit Image(DataType* data, const std::array<std::size_t, 2>& dimensions,
+                 const std::size_t bits_per_pixel = 8) {
+    Initialize();
+    native_ = FreeImage_ConvertFromRawBitsEx(
+        true, reinterpret_cast<std::uint8_t*>(data), FIT_BITMAP,
+        static_cast<std::int32_t>(dimensions[0]),
+        static_cast<std::int32_t>(dimensions[1]),
+        static_cast<std::int32_t>((bits_per_pixel * dimensions[0] + 31) / 32 *
+                                  4),
+        static_cast<std::int32_t>(bits_per_pixel), 0, 0, 0, false);
+    if (!native_) {
+      throw std::runtime_error("FreeImage_ConvertFromRawBitsEx failed.");
+    }
   }
-  bool operator!=(const ImageFormat8bit& other) const {
-    return value_ != other.value_;
+  explicit Image(const std::string& filepath,
+                 const std::int32_t native_flags = 0);
+  Image(const Image& that);
+  Image(Image&& temp) noexcept;
+  virtual ~Image();
+  Image& operator=(const Image& that);
+  Image& operator=(Image&& temp) noexcept;
+
+  bool Save(const std::string& filepath, const std::int32_t native_flags = 0);
+
+  std::array<std::size_t, 2> GetDimensions() const;
+  std::size_t GetPitch() const;
+  std::size_t GetBitsPerPixel() const;
+
+  bool IsEmpty() const;
+
+  template <typename type = std::uint8_t>
+  std::pair<type*, std::size_t> GetPixels() const {
+    return {reinterpret_cast<type*>(FreeImage_GetBits(native_)),
+            GetDimensions()[1] * GetPitch() / sizeof(type)};
   }
 
-  unsigned char value_;
-};
+  void SetPixelColor(const std::array<std::size_t, 2>& position,
+                     const std::array<std::uint8_t, 4>& color);
+  std::array<std::uint8_t, 4> GetPixelColor(
+      const std::array<std::size_t, 2>& position) const;
 
-template <typename T>
-class PHOENIX_EXPORT ImageTyped : public Image {
- public:
-  ImageTyped(std::size_t width, std::size_t height);
-  ImageTyped(std::size_t width, std::size_t height,
-             const std::vector<unsigned char>& buffer);
-  ImageTyped(const ImageTyped&) = default;
-  ImageTyped(ImageTyped&&) = default;
-  ~ImageTyped() = default;
-
-  ImageTyped& operator=(const ImageTyped&) = default;
-  ImageTyped& operator=(ImageTyped&&) = default;
-
-  std::size_t GetBytesPerPixel() const override { return sizeof(T); }
-  inline T GetPixel(std::size_t x, std::size_t y) const {
-    const T* pixel_pointer =
-        reinterpret_cast<const T*>(&buffer_[(y * width_ + x) * sizeof(T)]);
-    return *pixel_pointer;
-  }
+  void To4Bits();
+  void To8Bits();
+  void To16Bits();
+  void To24Bits();
+  void To32Bits();
 
-  inline void SetPixel(std::size_t x, std::size_t y, T pixel) {
-    T* pixel_pointer =
-        reinterpret_cast<T*>(&buffer_[(y * width_ + x) * sizeof(T)]);
-    *pixel_pointer = pixel;
-  }
-
-  template <typename Target_Type>
-  std::unique_ptr<Image> Convert() const;
+  std::string ToString() const override;
 
  protected:
-  FIBITMAP* CreateFreeImageRepresentation() const override;
-  void SetFromFreeImageRepresentation(FIBITMAP* bitmap) override;
-};
-
-template <typename T>
-phx::ImageTyped<T>::ImageTyped(std::size_t width, std::size_t height)
-    : Image(width, height) {
-  static_assert(std::is_base_of<phx::ImageFormat, T>::value,
-                "Image format must derive from phx::ImageFormat.");
-
-  // allocate space
-  buffer_.resize(width * height * sizeof(T));
-}
-template <typename T>
-phx::ImageTyped<T>::ImageTyped(std::size_t width, std::size_t height,
-                               const std::vector<unsigned char>& buffer)
-    : Image(width, height, buffer) {
-  static_assert(std::is_base_of<phx::ImageFormat, T>::value,
-                "Image format must derive from phx::ImageFormat.");
-
-  // allocate space
-  buffer_.resize(width * height * sizeof(T));
-}
-
-template <typename T>
-std::unique_ptr<Image> phx::Image::Convert() const {
-  if (this->GetBytesPerPixel() == 1) {
-    return static_cast<const phx::ImageTyped<phx::ImageFormat8bit>*>(this)
-        ->Convert<T>();
-  }
-  if (this->GetBytesPerPixel() == 3) {
-    return static_cast<const phx::ImageTyped<phx::ImageFormatRGB>*>(this)
-        ->Convert<T>();
-  }
-  if (this->GetBytesPerPixel() == 4) {
-    return static_cast<const phx::ImageTyped<phx::ImageFormatRGBA>*>(this)
-        ->Convert<T>();
-  }
-  return nullptr;
-}
+  static void Initialize();
 
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormatRGB>::Convert<ImageFormatRGB>()
-    const;
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormatRGB>::Convert<ImageFormatRGBA>()
-    const;
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormatRGB>::Convert<ImageFormat8bit>()
-    const;
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormatRGBA>::Convert<ImageFormatRGB>()
-    const;
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormatRGBA>::Convert<ImageFormatRGBA>()
-    const;
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormatRGBA>::Convert<ImageFormat8bit>()
-    const;
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormat8bit>::Convert<ImageFormatRGB>()
-    const;
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormat8bit>::Convert<ImageFormatRGBA>()
-    const;
-template <>
-template <>
-std::unique_ptr<Image> ImageTyped<ImageFormat8bit>::Convert<ImageFormat8bit>()
-    const;
+  void Replace(FIBITMAP* native);
+  bool SwapRedBlue(FIBITMAP* dib);
 
+  FIBITMAP* native_;
+  FREE_IMAGE_FORMAT format_ = FIF_UNKNOWN;
+  static std::once_flag once_flag_;
+};  // namespace phx
 }  // namespace phx
 
 #endif  // LIBRARY_PHX_IMAGE_HPP_
diff --git a/library/phx/image_loader.cpp b/library/phx/image_loader.cpp
index 3818b1250036fb68946acd71f347d28a34dbe464..7f5fe30dcd006665e5f32cf1598ce1095b01a86d 100644
--- a/library/phx/image_loader.cpp
+++ b/library/phx/image_loader.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -23,13 +23,56 @@
 #include "phx/image_loader.hpp"
 
 #include <memory>
+#include <string>
+#include <utility>
 
 #include "phx/image.hpp"
+#include "phx/logger.hpp"
 #include "phx/resources_path.hpp"
 
 namespace phx {
 std::unique_ptr<Resource> ImageLoader::Load(
-    const ResourceDeclaration& filename) {
-  return Image::Load(resources_root + filename)->Convert<ImageFormatRGBA>();
+    const ResourceDeclaration& declaration) {
+  if (declaration.find("file_name") == declaration.end() ||
+      !declaration["file_name"].is_string()) {
+    warn(
+        "declaration {} cannot be parsed by ImageLoader, it does not "
+        "contain a file_name key",
+        declaration.dump());
+    return nullptr;
+  }
+  auto image =
+      std::make_unique<Image>(declaration["file_name"].get<std::string>());
+  int bit_format = 32;
+  if (declaration.find("bit_format") == declaration.end() ||
+      !declaration["bit_format"].is_number_integer()) {
+    debug("declaration {} does not contain bit_format, use 32",
+          declaration.dump());
+  } else {
+    bit_format = declaration["bit_format"].get<int>();
+  }
+
+  switch (bit_format) {
+    case 4:
+      image->To4Bits();
+      break;
+    case 8:
+      image->To8Bits();
+      break;
+    case 16:
+      image->To16Bits();
+      break;
+    case 24:
+      image->To24Bits();
+      break;
+    default:
+      warn("[ImageLoader] bit_format {} not supported, use 32 bits instead",
+           bit_format);
+    case 32:
+      image->To32Bits();
+      break;
+  }
+
+  return std::move(image);
 }
 }  // namespace phx
diff --git a/library/phx/image_loader.hpp b/library/phx/image_loader.hpp
index 017d7aa3ad643ff99560993509d55cfc1c6caae2..131a58643e471b11c4d9f5e7e9149e79af2a82a8 100644
--- a/library/phx/image_loader.hpp
+++ b/library/phx/image_loader.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -40,7 +40,8 @@ class ImageLoader final : public ResourceLoadStrategy {
   ImageLoader &operator=(const ImageLoader &) = delete;
   ImageLoader &operator=(ImageLoader &&) = delete;
 
-  std::unique_ptr<Resource> Load(const ResourceDeclaration &filename) override;
+  std::unique_ptr<Resource> Load(
+      const ResourceDeclaration &declaration) override;
 };
 }  // namespace phx
 
diff --git a/library/phx/input_system.cpp b/library/phx/input_system.cpp
index 046e535ef239fbe3ac50ad09a0a6e2e00f05241a..a8b8ece9fdbc1af2b9a6d6a0927c60dd17d8142b 100644
--- a/library/phx/input_system.cpp
+++ b/library/phx/input_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/input_system.hpp b/library/phx/input_system.hpp
index 53b06edd4fcdc45bb6e3a0bea974dd16668bc3af..6c2d1875f47623552b9bc21bdcbb386d77096b36 100644
--- a/library/phx/input_system.hpp
+++ b/library/phx/input_system.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/light.cpp b/library/phx/light.cpp
index 4c36491ebb0ef4de48cbff8bfefb96e93d890d69..3f571a0381c07386d280f4f362f09f5b3d17d380 100644
--- a/library/phx/light.cpp
+++ b/library/phx/light.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/light.hpp b/library/phx/light.hpp
index 23544710b2a8f2edef7acf3e714365ce312c9f4c..5a11219010eca50a2698680a66c4e5b9cbb8f4b3 100644
--- a/library/phx/light.hpp
+++ b/library/phx/light.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/loggable.hpp b/library/phx/loggable.hpp
index c8ee7ce966e31e4d1a6622100c9a4abe2dc621fa..f6c21e4466c6158f92e0c48d078a60fb2366fb49 100644
--- a/library/phx/loggable.hpp
+++ b/library/phx/loggable.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/logger.cpp b/library/phx/logger.cpp
index e8a1e00b4d3a48f08823eacc4a235289b0b0527c..578b7255a88895ea4c98595c0e61d22b7249fcc9 100644
--- a/library/phx/logger.cpp
+++ b/library/phx/logger.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/logger.hpp b/library/phx/logger.hpp
index 9201975d630c9c06064abb4cce8d4c6d028270db..83a962d845b6bd057d491088dff5bfacebaaf199 100644
--- a/library/phx/logger.hpp
+++ b/library/phx/logger.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/material.cpp b/library/phx/material.cpp
index 92e870696abc14eb917a36de96e8921fdaadc960..63810ec40e612451032117ef1a52c8ce57faa7c8 100644
--- a/library/phx/material.cpp
+++ b/library/phx/material.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -89,16 +89,17 @@ void Material::SetTexture(Image* image,
     gl::texture_handle handle(*texture->get());
     handle.set_resident(false);
   }
+  auto dimensions = image->GetDimensions();
   (*texture) = std::make_shared<gl::texture_2d>();
   (*texture)->set_min_filter(GL_LINEAR_MIPMAP_LINEAR);
   (*texture)->set_mag_filter(GL_LINEAR);
   (*texture)->set_wrap_s(GL_REPEAT);
   (*texture)->set_wrap_t(GL_REPEAT);
-  (*texture)->set_storage(8, GL_RGBA8, static_cast<GLsizei>(image->GetWidth()),
-                          static_cast<GLsizei>(image->GetHeight()));
-  (*texture)->set_sub_image(0, 0, 0, static_cast<GLsizei>(image->GetWidth()),
-                            static_cast<GLsizei>(image->GetHeight()), GL_RGBA,
-                            GL_UNSIGNED_BYTE, image->GetDataPointer());
+  (*texture)->set_storage(8, GL_RGBA8, static_cast<GLsizei>(dimensions[0]),
+                          static_cast<GLsizei>(dimensions[1]));
+  (*texture)->set_sub_image(0, 0, 0, static_cast<GLsizei>(dimensions[0]),
+                            static_cast<GLsizei>(dimensions[1]), GL_RGBA,
+                            GL_UNSIGNED_BYTE, image->GetPixels().first);
   (*texture)->generate_mipmap();
   gl::texture_handle handle(*texture->get());
   handle.set_resident(true);
diff --git a/library/phx/material.hpp b/library/phx/material.hpp
index 6ab47d3936f5a118d49d5366c1248208b15cf3d3..4a51fff148501d114972656629bd1439ff76dea3 100644
--- a/library/phx/material.hpp
+++ b/library/phx/material.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/material_handle.cpp b/library/phx/material_handle.cpp
index 6b440f1934f109446ba40ff8a359bbcd77e50e57..67059f6cac8f098c38c705ce999d22286b17fca6 100644
--- a/library/phx/material_handle.cpp
+++ b/library/phx/material_handle.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/material_handle.hpp b/library/phx/material_handle.hpp
index be74d424ab0c89bbac7c17d2e087999116d08c9c..5f9a71380d5941a6cef266b26d71370f2a9daa4f 100644
--- a/library/phx/material_handle.hpp
+++ b/library/phx/material_handle.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/mesh.cpp b/library/phx/mesh.cpp
index dd5c0c16ec77638d26cc9e9cde212c2c15557e9f..f5eacf34226273c7fd427d8b45dbacafd5ded3b4 100644
--- a/library/phx/mesh.cpp
+++ b/library/phx/mesh.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/mesh.hpp b/library/phx/mesh.hpp
index 51d3b17aae229ce51ab635b701afc844e335c16f..3471314ace53f72623cd03db7d50cd8b38697873 100644
--- a/library/phx/mesh.hpp
+++ b/library/phx/mesh.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/mesh_handle.cpp b/library/phx/mesh_handle.cpp
index bf028ecfcc969126d09f0c8045cb66cd70c3318d..cae2c5ddd8dc8a3016d3a6159621913e2cb418e9 100644
--- a/library/phx/mesh_handle.cpp
+++ b/library/phx/mesh_handle.cpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
diff --git a/library/phx/mesh_handle.hpp b/library/phx/mesh_handle.hpp
index e07bc228ab70683c19f344bda8ef09d4ee96a6f3..a9a01cd181bd5419810eebfdaf894e8ca6aade6d 100644
--- a/library/phx/mesh_handle.hpp
+++ b/library/phx/mesh_handle.hpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
diff --git a/library/phx/model.cpp b/library/phx/model.cpp
index 657409b1eb5dfd69edd94f7ec01f6b17b0c6aed3..a97604037e6f72888cb786023f37c3a2b4bb0966 100644
--- a/library/phx/model.cpp
+++ b/library/phx/model.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/model.hpp b/library/phx/model.hpp
index 383b445ac906f33e15c365eb96931798e41ab26f..5a26f3a0befaf5d514dcbc7f9f670a0a38383a8a 100644
--- a/library/phx/model.hpp
+++ b/library/phx/model.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/nameable.hpp b/library/phx/nameable.hpp
index 9cd2748033c14064675620a71440cf720f385e64..dc27b6f6de800cdf4b2e4e14cad4af90603b4cf8 100644
--- a/library/phx/nameable.hpp
+++ b/library/phx/nameable.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/opengl_image_buffer_data.cpp b/library/phx/opengl_image_buffer_data.cpp
index 18cf57938928218ca49e463d2ffa3d7b5f1d835f..5a87d2f8a4308c9466b77c468df451d19357dbd1 100644
--- a/library/phx/opengl_image_buffer_data.cpp
+++ b/library/phx/opengl_image_buffer_data.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -68,16 +68,16 @@ template <>
 std::unique_ptr<phx::Image> phx::OpenGLImageBufferData<
     phx::OpenGLImageBufferDataType_Float32>::CreateImage() const {
   // create empty image
-  auto image = Image::CreateEmptyImage(this->width_, this->height_, 1);
+  auto image =
+      std::make_unique<Image>(std::array<std::size_t, 2>{{width_, height_}}, 8);
+
   // copy data, convert float to byte
-  unsigned char* image_data_pointer = image->GetDataPointer();
-  std::size_t byte_count = image->GetWidth() * image->GetHeight() * 1;
-  for (std::size_t i = 0; i < byte_count; ++i) {
-    const float* pixel_pointer =
+  const auto image_data_pointer = image->GetPixels();
+  for (std::size_t i = 0; i < image_data_pointer.second; ++i) {
+    const auto pixel_pointer =
         reinterpret_cast<const float*>(&buffer_data_[i * 4]);
-    unsigned char pixel_byte = static_cast<unsigned char>(
+    image_data_pointer.first[i] = static_cast<unsigned char>(
         std::max(0.f, std::min(255.f, std::round(*pixel_pointer * 255.f))));
-    image_data_pointer[i] = pixel_byte;
   }
   return image;
 }
@@ -87,27 +87,18 @@ std::unique_ptr<OpenGLImageBufferData<OpenGLImageBufferDataType_Float32>>
 phx::OpenGLImageBufferData<OpenGLImageBufferDataType_Float32>::CreateFromImage(
     phx::Image* image) {
   // check image type
-  std::unique_ptr<phx::Image> converted_image;
-  if (image->GetBytesPerPixel() != 1) {
-    // convert image!
-    converted_image = image->Convert<phx::ImageFormat8bit>();
-    image = converted_image.get();
-  }
+  if (image->GetBitsPerPixel() != 8) image->To8Bits();
   // copy width, height and data
-  std::unique_ptr<OpenGLImageBufferData<OpenGLImageBufferDataType_Float32>>
-      buffer = std::make_unique<
-          OpenGLImageBufferData<OpenGLImageBufferDataType_Float32>>(
-          image->GetWidth(), image->GetHeight());
-  std::size_t byte_count = image->GetWidth() * image->GetHeight() * 1;
-  unsigned char* image_data_pointer = image->GetDataPointer();
-
-  OpenGLImageBufferData<OpenGLImageBufferDataType_Float32>& buffer_ref =
-      *buffer.get();
-
-  for (std::size_t i = 0; i < byte_count; ++i) {
+  auto dimensions = image->GetDimensions();
+  auto buffer = std::make_unique<
+      OpenGLImageBufferData<OpenGLImageBufferDataType_Float32>>(dimensions[0],
+                                                                dimensions[1]);
+  const auto image_data_pointer = image->GetPixels();
+  auto& buffer_ref = *buffer.get();
+  for (std::size_t i = 0; i < image_data_pointer.second; ++i) {
     float* float_pointer =
         reinterpret_cast<float*>(&buffer_ref.buffer_data_[i * 4]);
-    *float_pointer = static_cast<float>(image_data_pointer[i]) / 255.f;
+    *float_pointer = static_cast<float>(image_data_pointer.first[i]) / 255.f;
   }
   return buffer;
 }
diff --git a/library/phx/opengl_image_buffer_data.hpp b/library/phx/opengl_image_buffer_data.hpp
index 531869bf09e78727fceaca657b4de54712059247..f697e4468592ae0219a5d722bf51a6047dfed3b6 100644
--- a/library/phx/opengl_image_buffer_data.hpp
+++ b/library/phx/opengl_image_buffer_data.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -34,6 +34,8 @@
 
 #include "phx/export.hpp"
 #include "phx/image.hpp"
+#include "phx/resource_manager.hpp"
+#include "phx/resource_utils.hpp"
 
 namespace phx {
 
@@ -49,9 +51,7 @@ struct PHOENIX_EXPORT OpenGLImageBufferDataType_RGB
   unsigned char g_;
   unsigned char b_;
   static std::string GetFormatString() { return "RGB"; }
-  static OpenGLImageBufferDataType_RGB GetMaxValue() {
-    return {255, 255, 255};
-  }
+  static OpenGLImageBufferDataType_RGB GetMaxValue() { return {255, 255, 255}; }
   static OpenGLImageBufferDataType_RGB GetMinValue() { return {0, 0, 0}; }
 };
 
@@ -262,37 +262,29 @@ phx::OpenGLImageBufferData<OpenGLImageBufferDataType_Float32>::CreateFromImage(
 template <typename T>
 std::unique_ptr<OpenGLImageBufferData<T>>
 phx::OpenGLImageBufferData<T>::CreateFromImage(phx::Image* image) {
-  // check image type
-  std::unique_ptr<Image> converted_image;
-  if (image->GetBytesPerPixel() != sizeof(T)) {
-    // convert first
+  if (image->GetBitsPerPixel() != sizeof(T) * 8) {
     switch (sizeof(T)) {
       case 1:
-        converted_image = image->Convert<phx::ImageFormat8bit>();
+        image->To8Bits();
         break;
       case 3:
-        converted_image = image->Convert<phx::ImageFormatRGB>();
+        image->To24Bits();
         break;
       case 4:
-        converted_image = image->Convert<phx::ImageFormatRGBA>();
+        image->To32Bits();
         break;
       default:
         return nullptr;
     }
-    image = converted_image.get();
   }
   // copy width, height and data outright
+  auto dimensions = image->GetDimensions();
   std::unique_ptr<OpenGLImageBufferData<T>> buffer =
-      std::make_unique<OpenGLImageBufferData<T>>(image->GetWidth(),
-                                                 image->GetHeight());
-  std::size_t byte_count = image->GetWidth() * image->GetHeight() * sizeof(T);
-  unsigned char* image_data_pointer = image->GetDataPointer();
-
+      std::make_unique<OpenGLImageBufferData<T>>(dimensions[0], dimensions[1]);
+  const auto image_data_pointer = image->GetPixels();
   OpenGLImageBufferData<T>& buffer_ref = *buffer.get();
-
-  for (std::size_t i = 0; i < byte_count; ++i) {
-    buffer_ref.buffer_data_[i] = image_data_pointer[i];
-  }
+  for (std::size_t i = 0; i < image_data_pointer.second; ++i)
+    buffer_ref.buffer_data_[i] = image_data_pointer.first[i];
   return buffer;
 }
 
@@ -304,15 +296,9 @@ phx::OpenGLImageBufferData<OpenGLImageBufferDataType_Float32>::CreateFromImage(
 
 template <typename T>
 std::unique_ptr<phx::Image> phx::OpenGLImageBufferData<T>::CreateImage() const {
-  // create empty image
-  auto image = Image::CreateEmptyImage(this->width_, this->height_, sizeof(T));
-  // copy data outright
-  unsigned char* image_data_pointer = image->GetDataPointer();
-  std::size_t byte_count = image->GetWidth() * image->GetHeight() * sizeof(T);
-  for (std::size_t i = 0; i < byte_count; ++i) {
-    image_data_pointer[i] = buffer_data_[i];
-  }
-  return image;
+  return std::make_unique<phx::Image>(
+      const_cast<std::uint8_t*>(buffer_data_.data()),
+      std::array<std::size_t, 2>{{width_, height_}}, sizeof(T) * 8);
 }
 
 // different behavior for Float32!
@@ -360,15 +346,16 @@ void phx::OpenGLImageBufferData<T>::SaveToFilePNG(
     }
   }
 
-  auto image = this->CreateImage();
+  const auto image = CreateImage();
   image->Save(filename_corrected);
 }
 
 template <typename T>
 phx::OpenGLImageBufferData<T> phx::OpenGLImageBufferData<T>::ReadFromFilePNG(
     const std::string& filename) {
-  auto image = phx::Image::Load(filename);
-  auto buffer = OpenGLImageBufferData<T>::CreateFromImage(image.get());
+  auto img_proxy = phx::ResourceUtils::LoadResourceFromFile(filename, {}, true);
+  auto image = img_proxy->GetAs<phx::Image>();
+  auto buffer = OpenGLImageBufferData<T>::CreateFromImage(image);
   auto* buffer_pointer = buffer.release();
   return *buffer_pointer;
 }
diff --git a/library/phx/openvr_mesh_loader.cpp b/library/phx/openvr_mesh_loader.cpp
deleted file mode 100644
index db7fa66081279a43804326152382b126202ac8d9..0000000000000000000000000000000000000000
--- a/library/phx/openvr_mesh_loader.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-//------------------------------------------------------------------------------
-// Project Phoenix
-//
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualization Group.
-//------------------------------------------------------------------------------
-//                                 License
-//
-// Licensed under the 3-Clause BSD License (the "License");
-// you may not use this file except in compliance with the License.
-// See the file LICENSE for the full text.
-// You may obtain a copy of the License at
-//
-//     https://opensource.org/licenses/BSD-3-Clause
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//------------------------------------------------------------------------------
-
-#include "openvr_mesh_loader.hpp"
-
-#include <memory>
-#include <string>
-
-#include "boost/lexical_cast.hpp"
-
-#include "material.hpp"
-
-namespace phx {
-
-OpenvrMeshLoader::OpenvrMeshLoader(HMD *hmd) : hmd_(hmd) {}
-
-std::unique_ptr<phx::Resource> OpenvrMeshLoader::Load(
-    const ResourceDeclaration &file_name) {
-  std::string name = file_name.substr(0, file_name.rfind('.'));
-  HMD::Controller controller = HMD::Controller::LEFT_CONTROLLER;
-  if (name == "controller_left") {
-    controller = HMD::Controller::LEFT_CONTROLLER;
-  } else if (name == "controller_right") {
-    controller = HMD::Controller::RIGHT_CONTROLLER;
-  } else if (name == "material") {
-    return hmd_->GetControllerMaterial(controller);
-  } else if (name.find("texture:") != std::string::npos) {
-    return hmd_->GetControllerTexture(boost::lexical_cast<int>(name.substr(8)));
-  } else {
-    return nullptr;
-  }
-  auto resource = hmd_->GetControllerMesh(controller);
-  return resource;
-}
-
-}  // namespace phx
diff --git a/library/phx/openvr_resource_loader.cpp b/library/phx/openvr_resource_loader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d4aa3b4822893a9cfb6a39a764847dacfb491742
--- /dev/null
+++ b/library/phx/openvr_resource_loader.cpp
@@ -0,0 +1,80 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
+//------------------------------------------------------------------------------
+//                                 License
+//
+// Licensed under the 3-Clause BSD License (the "License");
+// you may not use this file except in compliance with the License.
+// See the file LICENSE for the full text.
+// You may obtain a copy of the License at
+//
+//     https://opensource.org/licenses/BSD-3-Clause
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//------------------------------------------------------------------------------
+
+#include "openvr_resource_loader.hpp"
+
+#include <memory>
+#include <string>
+
+#include "logger.hpp"
+#include "material.hpp"
+
+namespace phx {
+
+OpenVRResourceLoader::OpenVRResourceLoader(HMD *hmd) : hmd_(hmd) {}
+
+std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load(
+    const ResourceDeclaration &declaration) {
+  if (declaration.find("OpenVR_type") == declaration.end() ||
+      !declaration["OpenVR_type"].is_string()) {
+    warn(
+        "declaration {} cannot be parsed by OpenVRResourceLoader, it does not "
+        "contain a OpenVR_type key",
+        declaration.dump());
+    return nullptr;
+  }
+  std::string type = declaration["OpenVR_type"];
+  HMD::Controller controller_side = HMD::Controller::LEFT_CONTROLLER;
+  if (declaration.find("side") != declaration.end() &&
+      declaration["side"] == "right") {
+    controller_side = HMD::Controller::RIGHT_CONTROLLER;
+  }
+  // otherwise default left
+
+  if (type == "material") {
+    return hmd_->GetControllerMaterial(controller_side);
+  }
+  if (type == "texture") {
+    int texture_id = 0;
+    if (declaration.find("texture_id") == declaration.end() ||
+        !declaration["texture_id"].is_number_integer()) {
+      warn(
+          "declaration {} cannot be parsed by OpenvrResourceLoader, it does "
+          "not contain a texture_id key",
+          declaration.dump());
+    } else {
+      texture_id = declaration["texture_id"];
+    }
+    return hmd_->GetControllerTexture(texture_id);
+  }
+  if (type == "mesh") {
+    return hmd_->GetControllerMesh(controller_side);
+  }
+
+  warn(
+      "OpenVRResource with OpenVR_type {}, cannot be loaded. Full declaration: "
+      "{}",
+      type, declaration.dump());
+  return nullptr;
+}
+
+}  // namespace phx
diff --git a/library/phx/openvr_mesh_loader.hpp b/library/phx/openvr_resource_loader.hpp
similarity index 68%
rename from library/phx/openvr_mesh_loader.hpp
rename to library/phx/openvr_resource_loader.hpp
index 646df8553e77f97eba54797a8963113ea0df569b..5c68b9e89d3a2f73e3d4a0bb9d94cdf80371c830 100644
--- a/library/phx/openvr_mesh_loader.hpp
+++ b/library/phx/openvr_resource_loader.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -20,8 +20,8 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#ifndef LIBRARY_PHX_OPENVR_MESH_LOADER_HPP_
-#define LIBRARY_PHX_OPENVR_MESH_LOADER_HPP_
+#ifndef LIBRARY_PHX_OPENVR_RESOURCE_LOADER_HPP_
+#define LIBRARY_PHX_OPENVR_RESOURCE_LOADER_HPP_
 
 #include <memory>
 #include <vector>
@@ -39,15 +39,15 @@ SUPPRESS_WARNINGS_END
 namespace phx {
 class Mesh;
 
-class PHOENIX_EXPORT OpenvrMeshLoader final : public ResourceLoadStrategy {
+class PHOENIX_EXPORT OpenVRResourceLoader final : public ResourceLoadStrategy {
  public:
-  explicit OpenvrMeshLoader(HMD *hmd);
-  OpenvrMeshLoader(const OpenvrMeshLoader &) = delete;
-  OpenvrMeshLoader(OpenvrMeshLoader &&) = delete;
-  ~OpenvrMeshLoader() override = default;
+  explicit OpenVRResourceLoader(HMD *hmd);
+  OpenVRResourceLoader(const OpenVRResourceLoader &) = delete;
+  OpenVRResourceLoader(OpenVRResourceLoader &&) = delete;
+  ~OpenVRResourceLoader() override = default;
 
-  OpenvrMeshLoader &operator=(const OpenvrMeshLoader &) = delete;
-  OpenvrMeshLoader &operator=(OpenvrMeshLoader &&) = delete;
+  OpenVRResourceLoader &operator=(const OpenVRResourceLoader &) = delete;
+  OpenVRResourceLoader &operator=(OpenVRResourceLoader &&) = delete;
 
   std::unique_ptr<Resource> Load(const ResourceDeclaration &file_name) override;
 
@@ -57,4 +57,4 @@ class PHOENIX_EXPORT OpenvrMeshLoader final : public ResourceLoadStrategy {
 
 }  // namespace phx
 
-#endif  // LIBRARY_PHX_OPENVR_MESH_LOADER_HPP_
+#endif  // LIBRARY_PHX_OPENVR_RESOURCE_LOADER_HPP_
diff --git a/library/phx/phoenix.cpp b/library/phx/phoenix.cpp
index c4f9875d31d7ee14631da281750b859b6514b674..002e4614c95dd6b8b182039b350695dd97418032 100644
--- a/library/phx/phoenix.cpp
+++ b/library/phx/phoenix.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/phoenix.hpp b/library/phx/phoenix.hpp
index c156fdb87c51718d4c6e17eacf25c2399a15b24d..141221ce1f5aaafd5569e6e89d0979e0d34f3049 100644
--- a/library/phx/phoenix.hpp
+++ b/library/phx/phoenix.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/projection.cpp b/library/phx/projection.cpp
index 3ff100d63a82e9545f8495b66f28d58d03c08633..81b915481df519d8dd512505209b7c7b961abf5a 100644
--- a/library/phx/projection.cpp
+++ b/library/phx/projection.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/projection.hpp b/library/phx/projection.hpp
index 9a1a8296d601df85d1868907b13e8fe2518b03e8..3acec40ce95d14ac23957110bf0255567481eb59 100644
--- a/library/phx/projection.hpp
+++ b/library/phx/projection.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/render_pass.hpp b/library/phx/render_pass.hpp
index 524fc7f6e91419b5390589f4ee38e85692408282..1009afed720765d7aa1419e00f57fb2be02997ac 100644
--- a/library/phx/render_pass.hpp
+++ b/library/phx/render_pass.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/render_target.cpp b/library/phx/render_target.cpp
index 17a2c6c5469eb7cda956f27eed118d9072054453..f1db70c4d69494583f66fd6887149bf33d71f23c 100644
--- a/library/phx/render_target.cpp
+++ b/library/phx/render_target.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/render_target.hpp b/library/phx/render_target.hpp
index 24a87c1490bc14bb72f78676854501470ffd80d5..4f9b65a69ebb1a780c0c6ac9176523a7afa78236 100644
--- a/library/phx/render_target.hpp
+++ b/library/phx/render_target.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/rendering_system.cpp b/library/phx/rendering_system.cpp
index bdc240d2ebfb01773fd36cf8b9e2c68b45b892e7..15a17eb3ef60a938bec74ec15002c0bc98ff0310 100644
--- a/library/phx/rendering_system.cpp
+++ b/library/phx/rendering_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -47,18 +47,20 @@
 
 namespace phx {
 
-RenderingSystem::RenderingSystem(Engine* engine) : System(engine) {}
+RenderingSystem::RenderingSystem(Engine* engine, DisplaySystem* display_system)
+    : System(engine) {
+  if (!display_system) error("RenderingSystem needs a valid DisplaySystem.");
+  InitializeOpenGL();
+  InitializeRenderTargets();
+  SetupFramegraph();
+}
 
-void RenderingSystem::Initialize() {
+void RenderingSystem::InitializeOpenGL() {
   if (!gl::initialize()) {
     error("Initializing gl failed");
   }
   std::string prefix = "[RenderingSystem] OpenGl Error: ";
   gl::print_error(prefix.c_str());
-
-  InitializeRenderTargets();
-
-  SetupFramegraph();
 }
 
 void RenderingSystem::InitializeRenderTargets() {
diff --git a/library/phx/rendering_system.hpp b/library/phx/rendering_system.hpp
index 89571e9195ed736c8fbc377bdf6d46f98933c025..43ca9b6df6fd09ce363ab4c7e2ee76a73fec3ff7 100644
--- a/library/phx/rendering_system.hpp
+++ b/library/phx/rendering_system.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -28,6 +28,7 @@
 #include <string>
 #include <vector>
 
+#include "phx/display_system.hpp"
 #include "phx/engine.hpp"
 #include "phx/export.hpp"
 #include "phx/frame_graph.hpp"
@@ -45,7 +46,6 @@ class PHOENIX_EXPORT RenderingSystem : public System {
   RenderingSystem(RenderingSystem&&) = default;
   virtual ~RenderingSystem() = default;
 
-  void Initialize() override;
   void Update(const FrameTimer::TimeInfo& time_info) override;
 
   FrameGraph* GetFrameGraph() const;
@@ -58,11 +58,16 @@ class PHOENIX_EXPORT RenderingSystem : public System {
   RenderTarget* GetRightRenderTarget() const;
   RenderTarget* GetLeftRenderTarget() const;
 
+ protected:
+  RenderingSystem(Engine* engine, DisplaySystem* display_system);
+
  private:
+  void InitializeOpenGL();
   void InitializeRenderTargets();
   void SetupFramegraph();
 
-  friend RenderingSystem* Engine::CreateSystem<RenderingSystem>();
+  template <typename SystemType, typename... SystemArguments>
+  friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments);
   explicit RenderingSystem(Engine* engine);
 
   std::unique_ptr<FrameGraph> frame_graph_;
diff --git a/library/phx/resource.hpp b/library/phx/resource.hpp
index 42f6bf74ebc770693c0bce1737b179d9a9c16f7d..b3a1d9698e5e322c303d8182b3e4631838e2c069 100644
--- a/library/phx/resource.hpp
+++ b/library/phx/resource.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/resource_declaration.hpp b/library/phx/resource_declaration.hpp
index 9b7f20914fff4f621694a574697859b2e2c28065..570068d5727cfe64d7ad78196fa2b3acbaf44528 100644
--- a/library/phx/resource_declaration.hpp
+++ b/library/phx/resource_declaration.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -23,12 +23,10 @@
 #ifndef LIBRARY_PHX_RESOURCE_DECLARATION_HPP_
 #define LIBRARY_PHX_RESOURCE_DECLARATION_HPP_
 
-#include <string>
+#include "json.hpp"
 
 namespace phx {
-// @TODO: Replace resource declaration by something more sensible once it is
-// available.
-using ResourceDeclaration = std::string;
+using ResourceDeclaration = nlohmann::json;
 }  // namespace phx
 
 #endif  // LIBRARY_PHX_RESOURCE_DECLARATION_HPP_
diff --git a/library/phx/resource_load_strategy.hpp b/library/phx/resource_load_strategy.hpp
index a38974174753998d834838d4547c31786d029b59..35c4f954eb8cf3f30bbb52cdeb7ffb0d40eeb067 100644
--- a/library/phx/resource_load_strategy.hpp
+++ b/library/phx/resource_load_strategy.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/resource_manager.cpp b/library/phx/resource_manager.cpp
index b90b633f35a48b9a2edeb9cf96c2bbecb3bad0ed..158befcea4a783d3111d9d3e1f18b3008e62495c 100644
--- a/library/phx/resource_manager.cpp
+++ b/library/phx/resource_manager.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -21,6 +21,7 @@
 //------------------------------------------------------------------------------
 #include "resource_manager.hpp"
 
+#include <algorithm>
 #include <memory>
 #include <string>
 #include <utility>
@@ -39,16 +40,15 @@ ResourceManager::ResourceManager() {
 }
 
 void ResourceManager::RegisterResourceType(
-    const std::string &file_extension,
-    std::unique_ptr<ResourceLoadStrategy> loader) {
+    const std::string &type, std::unique_ptr<ResourceLoadStrategy> loader) {
   // we intentionally overwrite loaders if they exist (assuming the caller
   // knows what she's doing).
-  loaders_[file_extension] = std::move(loader);
+  loaders_by_type_[type] = std::move(loader);
 }
 
 phx::ResourceProxy *ResourceManager::DeclareResource(
     const ResourceDeclaration &declaration) {
-  auto resource_hash = std::hash<std::string>{}(declaration);
+  auto resource_hash = std::hash<std::string>{}(declaration.dump());
 
   auto resource_entry = resources_.find(resource_hash);
   if (resource_entry != resources_.end()) {
@@ -62,28 +62,29 @@ phx::ResourceProxy *ResourceManager::DeclareResource(
 
 std::unique_ptr<phx::Resource> ResourceManager::Load(
     const ResourceDeclaration &declaration) {
-  auto loader = this->DetermineLoaderFromFileExtension(declaration);
+  ResourceLoadStrategy *loader = nullptr;
+  if (declaration.find("TYPE") != declaration.end()) {
+    loader = DetermineLoader(declaration["TYPE"]);
+  } else {
+    warn(
+        "No Loader for Resource Declaration {}, can be determined, since it "
+        " does not contain a TYPE key",
+        declaration.dump());
+  }
   if (loader == nullptr) return nullptr;
   return loader->Load(declaration);
 }
 
-phx::ResourceLoadStrategy *ResourceManager::DetermineLoaderFromFileExtension(
-    const std::string &file_name) const {
-  auto extension = this->ExtractFileExtension(file_name);
-
-  auto loader_iterator = loaders_.find(extension);
-  if (loader_iterator == loaders_.end()) {
-    phx::warn("No matching loader for file extension {}", extension);
+phx::ResourceLoadStrategy *ResourceManager::DetermineLoader(
+    const std::string &type) const {
+  auto loader_iterator = loaders_by_type_.find(type);
+  if (loader_iterator == loaders_by_type_.end()) {
+    phx::warn("No matching loader for TYPE {}", type);
     return nullptr;
   }
   return loader_iterator->second.get();
 }
 
-std::string ResourceManager::ExtractFileExtension(
-    const std::string &file_name) const {
-  return file_name.substr(file_name.rfind('.'));
-}
-
 void ResourceManager::RegisterMeshResourceExtensions() {
   this->RegisterResourceType(".obj", std::make_unique<AssimpModelLoader>());
 }
diff --git a/library/phx/resource_manager.hpp b/library/phx/resource_manager.hpp
index 064d5a95f65e3b1dd5458bff87deb67b4ba80523..926c47832edc9a5d039b4ab95560f9b1e7e67f55 100644
--- a/library/phx/resource_manager.hpp
+++ b/library/phx/resource_manager.hpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality&  Immersive Visualization Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
@@ -47,7 +47,7 @@ class PHOENIX_EXPORT ResourceManager final : public singleton<ResourceManager> {
 
   ~ResourceManager() = default;
 
-  void RegisterResourceType(const std::string& file_extension,
+  void RegisterResourceType(const std::string& type,
                             std::unique_ptr<ResourceLoadStrategy> loader);
 
   ResourceProxy* DeclareResource(const ResourceDeclaration& declaration);
@@ -63,17 +63,14 @@ class PHOENIX_EXPORT ResourceManager final : public singleton<ResourceManager> {
 
   std::unique_ptr<Resource> Load(const ResourceDeclaration& declaration);
 
-  ResourceLoadStrategy* DetermineLoaderFromFileExtension(
-      const std::string& file_name) const;
-
-  std::string ExtractFileExtension(const std::string& file_name) const;
+  ResourceLoadStrategy* DetermineLoader(const std::string& type) const;
 
  private:
   void RegisterShaderResourceExtensions();
   void RegisterMeshResourceExtensions();
   void RegisterImageResourceExtensions();
 
-  std::map<std::string, std::unique_ptr<ResourceLoadStrategy>> loaders_;
+  std::map<std::string, std::unique_ptr<ResourceLoadStrategy>> loaders_by_type_;
   std::map<std::size_t, std::unique_ptr<ResourceProxy>> resources_;
 };
 
diff --git a/library/phx/resource_proxy.cpp b/library/phx/resource_proxy.cpp
index b8649a27a87ab5749454295ac9515214e1d624b9..b2908d0b56521f2c6f7026a7845e5906f3a8cac8 100644
--- a/library/phx/resource_proxy.cpp
+++ b/library/phx/resource_proxy.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/resource_proxy.hpp b/library/phx/resource_proxy.hpp
index 19e6ed5f30f1f0d2a539b1a2e124496b817dfb36..abcb5b7a8bbacc7fdd1705a49042b40f51febbc3 100644
--- a/library/phx/resource_proxy.hpp
+++ b/library/phx/resource_proxy.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/resource_utils.cpp b/library/phx/resource_utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6f88334f32210acfc6e6bc1d17460808e0235e5
--- /dev/null
+++ b/library/phx/resource_utils.cpp
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
+//------------------------------------------------------------------------------
+//                                 License
+//
+// Licensed under the 3-Clause BSD License (the "License");
+// you may not use this file except in compliance with the License.
+// See the file LICENSE for the full text.
+// You may obtain a copy of the License at
+//
+//     https://opensource.org/licenses/BSD-3-Clause
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//------------------------------------------------------------------------------
+#include "resource_utils.hpp"
+
+#include <algorithm>
+#include <string>
+
+#include "phx/resource_manager.hpp"
+#include "phx/resources_path.hpp"
+
+namespace phx {
+ResourceDeclaration ResourceUtils::DeclarationFromFile(
+    const std::string& file_name, nlohmann::json additional_info /*= {}*/,
+    bool absolute_path /*= false*/) {
+  ResourceDeclaration declaration = {{"TYPE", ExtractFileExtension(file_name)}};
+  if (absolute_path) {
+    declaration["file_name"] = file_name;
+  } else {
+    declaration["file_name"] = resources_root + file_name;
+  }
+  for (nlohmann::json::iterator it = additional_info.begin();
+       it != additional_info.end(); ++it) {
+    declaration[it.key()] = it.value();
+  }
+  return declaration;
+}
+std::string ResourceUtils::ExtractFileExtension(const std::string& file_name) {
+  std::string extension = file_name.substr(file_name.rfind('.'));
+  std::transform(extension.begin(), extension.end(), extension.begin(),
+                 ::tolower);
+
+  return extension;
+}
+
+phx::ResourceProxy* ResourceUtils::LoadResourceFromFile(
+    const std::string& file_name, nlohmann::json additional_info /*= {}*/,
+    bool absolute_path /*= false*/) {
+  auto proxy = phx::ResourceManager::instance().DeclareResource(
+      DeclarationFromFile(file_name, additional_info, absolute_path));
+  proxy->Load();
+  return proxy;
+}
+
+}  // namespace phx
diff --git a/library/phx/resource_utils.hpp b/library/phx/resource_utils.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a7546f4b94a86bb9f0b72d456d5512fc485d0cc
--- /dev/null
+++ b/library/phx/resource_utils.hpp
@@ -0,0 +1,58 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
+//------------------------------------------------------------------------------
+//                                 License
+//
+// Licensed under the 3-Clause BSD License (the "License");
+// you may not use this file except in compliance with the License.
+// See the file LICENSE for the full text.
+// You may obtain a copy of the License at
+//
+//     https://opensource.org/licenses/BSD-3-Clause
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//------------------------------------------------------------------------------
+
+#ifndef LIBRARY_PHX_RESOURCE_UTILS_HPP_
+#define LIBRARY_PHX_RESOURCE_UTILS_HPP_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "json.hpp"
+
+#include "phx/export.hpp"
+#include "phx/logger.hpp"
+#include "phx/resource_declaration.hpp"
+#include "phx/resource_load_strategy.hpp"
+#include "phx/resource_proxy.hpp"
+#include "phx/singleton.hpp"
+
+namespace phx {
+/**
+ *  The ResourceManager is the central instance for the management of all
+ *  externally provided content, aka resources. Resources have to be declared
+ *  first before being available in the system.
+ */
+class PHOENIX_EXPORT ResourceUtils final {
+ public:
+  static std::string ExtractFileExtension(const std::string& file_name);
+  static ResourceDeclaration DeclarationFromFile(
+      const std::string& file_name, nlohmann::json additional_info = {},
+      bool absolute_path = false);
+  static ResourceProxy* LoadResourceFromFile(
+      const std::string& file_name, nlohmann::json additional_info = {},
+      bool absolute_path = false);
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_RESOURCE_UTILS_HPP_
diff --git a/library/phx/virtual_platform.hpp b/library/phx/runtime_component.hpp
similarity index 59%
rename from library/phx/virtual_platform.hpp
rename to library/phx/runtime_component.hpp
index 7b6ac8861285938e3d6597a87d1d4cae6e126383..226edd3859ea0ee4e3650e1e5a305417722c1410 100644
--- a/library/phx/virtual_platform.hpp
+++ b/library/phx/runtime_component.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -20,8 +20,8 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#ifndef LIBRARY_PHX_VIRTUAL_PLATFORM_HPP_
-#define LIBRARY_PHX_VIRTUAL_PLATFORM_HPP_
+#ifndef LIBRARY_PHX_RUNTIME_COMPONENT_HPP_
+#define LIBRARY_PHX_RUNTIME_COMPONENT_HPP_
 
 #include "phx/export.hpp"
 
@@ -29,16 +29,26 @@
 
 namespace phx {
 
-class PHOENIX_EXPORT VirtualPlatform : public Component {
+enum RuntimeComponentType {
+  USER_PLATFORM,
+  HEAD,
+  LEFT_EYE,
+  RIGHT_EYE,
+  LEFT_CONTROLLER,
+  RIGHT_CONTROLLER
+};
+
+template <RuntimeComponentType t>
+class PHOENIX_EXPORT RuntimeComponent : public Component {
  public:
-  VirtualPlatform() = default;
-  VirtualPlatform(const VirtualPlatform&) = delete;
-  VirtualPlatform(VirtualPlatform&&) = default;
-  virtual ~VirtualPlatform() = default;
-  VirtualPlatform& operator=(const VirtualPlatform&) = delete;
-  VirtualPlatform& operator=(VirtualPlatform&&) = default;
+  RuntimeComponent<t>() = default;
+  RuntimeComponent<t>(const RuntimeComponent<t>&) = delete;
+  RuntimeComponent<t>(RuntimeComponent<t>&&) = default;
+  virtual ~RuntimeComponent<t>() = default;
+  RuntimeComponent<t>& operator=(const RuntimeComponent<t>&) = delete;
+  RuntimeComponent<t>& operator=(RuntimeComponent<t>&&) = default;
 };
 
 }  // namespace phx
 
-#endif  // LIBRARY_PHX_VIRTUAL_PLATFORM_HPP_
+#endif  // LIBRARY_PHX_RUNTIME_COMPONENT_HPP_
diff --git a/library/phx/scene.cpp b/library/phx/scene.cpp
index ac39d194ca8a5c2982d389b01b9a2de515833d93..3cadf3e1433d32120e4919ea218e06c9534ae393 100644
--- a/library/phx/scene.cpp
+++ b/library/phx/scene.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -31,14 +31,15 @@
 #include <vector>
 
 #include "phx/transform.hpp"
-#include "phx/virtual_platform.hpp"
+#include "phx/runtime_component.hpp"
 
 namespace phx {
 
 Scene::Scene() {
   phx::Entity* virtual_platform = CreateEntity();
   virtual_platform->AddComponent<phx::Transform>();
-  virtual_platform->AddComponent<phx::VirtualPlatform>();
+  virtual_platform
+      ->AddComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>();
 }
 
 Entity* Scene::CreateEntity() {
diff --git a/library/phx/scene.hpp b/library/phx/scene.hpp
index b8f3a73eb1bf9ae7fb9a5d6eb57699c6eebde585..71fd7260aa62b72183d5d2cd7fb1cf182069c68e 100644
--- a/library/phx/scene.hpp
+++ b/library/phx/scene.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/scene_loader.cpp b/library/phx/scene_loader.cpp
index 75f8858f2fb79981844a6018237ef05206a91dc4..0c02dc743854508df0e4a20290b9806287c08800 100644
--- a/library/phx/scene_loader.cpp
+++ b/library/phx/scene_loader.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -28,14 +28,14 @@
 #include "mesh_handle.hpp"
 #include "model.hpp"
 #include "resource_manager.hpp"
+#include "resource_utils.hpp"
 #include "transform.hpp"
 
 namespace phx {
 
 bool SceneLoader::InsertModelIntoScene(const std::string& file_name,
                                        Scene* scene) {
-  auto model_proxy = ResourceManager::instance().DeclareResource(file_name);
-  model_proxy->Load();
+  auto model_proxy = ResourceUtils::LoadResourceFromFile(file_name);
   if (!model_proxy->IsOnline()) {
     return false;
   }
diff --git a/library/phx/scene_loader.hpp b/library/phx/scene_loader.hpp
index 948b02e926343be597c98112e6e3ac55ec92a2d6..2ba77eb4e1dcfc6544fbfb18c6beb50c95a15a87 100644
--- a/library/phx/scene_loader.hpp
+++ b/library/phx/scene_loader.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/setup.cpp b/library/phx/setup.cpp
index 46cedef1cc9a4cd1241135c5ed89486bcd80c581..28773ff4351a4bf94d37bc9ff23b667c470d208c 100644
--- a/library/phx/setup.cpp
+++ b/library/phx/setup.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -45,8 +45,6 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine() {
   engine->CreateSystem<BehaviorSystem>();
   engine->CreateSystem<InputSystem>()->AddQuitCallback(
       [engine_ptr]() { engine_ptr->Stop(); });
-  engine->CreateSystem<TrackingSystem>();
-  engine->CreateSystem<RenderingSystem>();
 
   auto displaysys = engine->CreateSystem<DisplaySystem>();
   if (HMD::IsHMDPresent()) {
@@ -55,6 +53,8 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine() {
   } else {
     displaysys->CreateWindow("Phoenix", glm::uvec2(100, 100));
   }
+  engine->CreateSystem<RenderingSystem>(engine->GetSystem<DisplaySystem>());
+  engine->CreateSystem<TrackingSystem>(engine->GetSystem<DisplaySystem>());
 
   return engine;
 }
diff --git a/library/phx/setup.hpp b/library/phx/setup.hpp
index 802c0461caf7a03e099471f3b25917144085af52..d96286b93bd00da09675b298b3c2c5508acfbeae 100644
--- a/library/phx/setup.hpp
+++ b/library/phx/setup.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/shader_loader.cpp b/library/phx/shader_loader.cpp
index 4cf1b8216b0188012a9841b8f78a69a42be7ec9f..01b5697cfd98149dbea819ec9c25a37c41df5c27 100644
--- a/library/phx/shader_loader.cpp
+++ b/library/phx/shader_loader.cpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
@@ -34,8 +34,8 @@
 namespace phx {
 
 std::unique_ptr<phx::Resource> ShaderLoader::Load(
-    const ResourceDeclaration& file_name) {
-  auto shader_source = this->LoadShaderSouce(file_name);
+    const ResourceDeclaration& declaration) {
+  auto shader_source = this->LoadShaderSource(declaration["file_name"]);
   if (shader_source.empty()) return nullptr;
 
   auto shader = std::make_unique<ShaderSource>();
@@ -43,12 +43,10 @@ std::unique_ptr<phx::Resource> ShaderLoader::Load(
   return shader;
 }
 
-std::string ShaderLoader::LoadShaderSouce(const std::string& file_name) {
-  std::string complete_filename = phx::resources_root + file_name;
-  std::ifstream infile{complete_filename.c_str(), std::ios::in};
+std::string ShaderLoader::LoadShaderSource(const std::string& file_name) {
+  std::ifstream infile{file_name.c_str(), std::ios::in};
   if (!infile.good()) {
-    phx::warn("Unable to read shader from requested file {}",
-              complete_filename);
+    phx::warn("Unable to read shader from requested file {}", file_name);
     return "";
   }
 
diff --git a/library/phx/shader_loader.hpp b/library/phx/shader_loader.hpp
index 56fc6bcd013f2edc2f5544b65aa43593d2291ec1..324daa8eef9dc5a65b5241ff2db6baa23a6acdcd 100644
--- a/library/phx/shader_loader.hpp
+++ b/library/phx/shader_loader.hpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
@@ -40,10 +40,11 @@ class ShaderLoader final : public ResourceLoadStrategy {
   ShaderLoader& operator=(const ShaderLoader&) = delete;
   ShaderLoader& operator=(ShaderLoader&&) = delete;
 
-  std::unique_ptr<Resource> Load(const ResourceDeclaration& file_name) override;
+  std::unique_ptr<Resource> Load(
+      const ResourceDeclaration& declaration) override;
 
  protected:
-  std::string LoadShaderSouce(const std::string& file_name);
+  std::string LoadShaderSource(const std::string& file_name);
 
  private:
 };
diff --git a/library/phx/shader_program.cpp b/library/phx/shader_program.cpp
index 0569749d84cf231974b591961cc481c26f24102e..3532ffaa356cefa15e15be00daa3df13ce2b3e9d 100644
--- a/library/phx/shader_program.cpp
+++ b/library/phx/shader_program.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/shader_program.hpp b/library/phx/shader_program.hpp
index 5ee4f1befb7b40f1ba1fd4544a68a872314a5fd5..2c6d92dc7b0be2d6c6d23a509257dd9d659a2e3b 100644
--- a/library/phx/shader_program.hpp
+++ b/library/phx/shader_program.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/shader_source.cpp b/library/phx/shader_source.cpp
index c3838403da90d212c175bc1b421e4831ef64754e..ef36646791d67f6674a9ecf282fd9c44b42f78a5 100644
--- a/library/phx/shader_source.cpp
+++ b/library/phx/shader_source.cpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
diff --git a/library/phx/shader_source.hpp b/library/phx/shader_source.hpp
index b68c47ca4ce12f56c8d8737f68405d3f2414377a..0b9c0d8c8342c1a1aa598289607255482488d874 100644
--- a/library/phx/shader_source.hpp
+++ b/library/phx/shader_source.hpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
@@ -30,7 +30,7 @@
 #include "phx/resource.hpp"
 
 namespace phx {
-class PHOENIX_EXPORT ShaderSource  final : public Resource {
+class PHOENIX_EXPORT ShaderSource final : public Resource {
  public:
   ShaderSource() = default;
   ShaderSource(const ShaderSource&) = default;
diff --git a/library/phx/singleton.hpp b/library/phx/singleton.hpp
index 31409d39a69f604163c2d29e43d58087e2e7a89d..cabe1ab8714e216fb9e73031b1aac91315bc7748 100644
--- a/library/phx/singleton.hpp
+++ b/library/phx/singleton.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/splash_screen.cpp b/library/phx/splash_screen.cpp
index 48a1b7c71ceb5603aec5d8f862fabbc1187f1809..fa0afd0b127671c40fa939a4dde90e82390d6c0c 100644
--- a/library/phx/splash_screen.cpp
+++ b/library/phx/splash_screen.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -28,37 +28,41 @@
 #include <memory>
 #include <string>
 
+#include "phx/image.hpp"
+#include "phx/resource_manager.hpp"
+#include "phx/resource_utils.hpp"
 #include "phx/resources_path.hpp"
 
 namespace phx {
 
 phx::SplashScreen::SplashScreen(Window* window) : window_(window) {
-  splash_image_ = phx::Image::Load(std::string(resources_root) +
-                                   "textures/splash_progress.png")
-                      ->Convert<phx::ImageFormatRGBA>();
-  progressbar_image_ = phx::Image::CreateEmptyImage(1, 1, 4);
-  auto progressbar_image_typed =
-      static_cast<phx::ImageTyped<phx::ImageFormatRGBA>*>(
-          progressbar_image_.get());
-  progressbar_image_typed->SetPixel(0, 0, {255, 255, 255, 255});
+  splash_image_proxy_ =
+      ResourceUtils::LoadResourceFromFile("textures/splash_progress.png");
+
+  progressbar_image_ =
+      std::make_unique<Image>(std::array<std::size_t, 2>{{1ull, 1ull}}, 32);
+  progressbar_image_->SetPixelColor({{0, 0}}, {{255, 255, 255, 255}});
+
   default_framebuffer_ = std::make_unique<gl::framebuffer>(0);
 }
 
 void SplashScreen::Draw() {
+  Image* splash_image = splash_image_proxy_->GetAs<Image>();
+  auto splash_dimensions = splash_image->GetDimensions();
+  auto progress_dimensions = progressbar_image_->GetDimensions();
   gl::initialize();
   gl::framebuffer splash_buff;
   gl::texture_2d splash_tex;
   splash_buff.bind();
   splash_tex.bind();
   splash_tex.set_storage(1, GL_RGBA8,
-                         static_cast<GLsizei>(splash_image_->GetWidth()),
-                         static_cast<GLsizei>(splash_image_->GetHeight()));
+                         static_cast<GLsizei>(splash_dimensions[0]),
+                         static_cast<GLsizei>(splash_dimensions[1]));
   splash_buff.attach_texture<GL_TEXTURE_2D>(GL_COLOR_ATTACHMENT0, splash_tex,
                                             0);
-  splash_tex.set_sub_image(
-      0, 0, 0, static_cast<GLsizei>(splash_image_->GetWidth()),
-      static_cast<GLsizei>(splash_image_->GetHeight()), GL_RGBA,
-      GL_UNSIGNED_BYTE, splash_image_->GetDataPointer());
+  splash_tex.set_sub_image(0, 0, 0, static_cast<GLsizei>(splash_dimensions[0]),
+                           static_cast<GLsizei>(splash_dimensions[1]), GL_RGBA,
+                           GL_UNSIGNED_BYTE, splash_image->GetPixels().first);
   gl::print_error("OpenGl Error creating Splash Screen texture: ");
   splash_buff.unbind();
   splash_tex.unbind();
@@ -67,30 +71,30 @@ void SplashScreen::Draw() {
   gl::texture_2d progress_tex;
   progress_buff.bind();
   progress_tex.bind();
-  progress_tex.set_storage(
-      1, GL_RGBA8, static_cast<GLsizei>(progressbar_image_->GetWidth()),
-      static_cast<GLsizei>(progressbar_image_->GetHeight()));
+  progress_tex.set_storage(1, GL_RGBA8,
+                           static_cast<GLsizei>(progress_dimensions[0]),
+                           static_cast<GLsizei>(progress_dimensions[1]));
   progress_buff.attach_texture<GL_TEXTURE_2D>(GL_COLOR_ATTACHMENT0,
                                               progress_tex, 0);
   progress_tex.set_sub_image(
-      0, 0, 0, static_cast<GLsizei>(progressbar_image_->GetWidth()),
-      static_cast<GLsizei>(progressbar_image_->GetHeight()), GL_RGBA,
-      GL_UNSIGNED_BYTE, progressbar_image_->GetDataPointer());
+      0, 0, 0, static_cast<GLsizei>(progress_dimensions[0]),
+      static_cast<GLsizei>(progress_dimensions[1]), GL_RGBA, GL_UNSIGNED_BYTE,
+      progressbar_image_->GetPixels().first);
   gl::print_error("OpenGl Error creating Progress Bar texture: ");
   progress_buff.unbind();
   progress_tex.unbind();
 
   auto win_size = window_->GetSize();
 
-  auto x_offset = (win_size.x - splash_image_->GetWidth()) / 2;
-  auto y_offset = (win_size.y - splash_image_->GetHeight()) / 2;
+  auto x_offset = (win_size.x - splash_dimensions[0]) / 2;
+  auto y_offset = (win_size.y - splash_dimensions[1]) / 2;
 
   default_framebuffer_->blit(
-      splash_buff, 0, 0, static_cast<GLsizei>(splash_image_->GetWidth()),
-      static_cast<GLsizei>(splash_image_->GetHeight()),
+      splash_buff, 0, 0, static_cast<GLsizei>(splash_dimensions[0]),
+      static_cast<GLsizei>(splash_dimensions[1]),
       static_cast<GLsizei>(x_offset), static_cast<GLsizei>(y_offset),
-      static_cast<GLsizei>(splash_image_->GetWidth() + x_offset),
-      static_cast<GLsizei>(splash_image_->GetHeight() + y_offset),
+      static_cast<GLsizei>(splash_dimensions[0] + x_offset),
+      static_cast<GLsizei>(splash_dimensions[1] + y_offset),
       GL_COLOR_BUFFER_BIT, GL_LINEAR);
 
   gl::print_error("OpenGl Error blitting Splash Screen: ");
@@ -103,8 +107,8 @@ void SplashScreen::Draw() {
   auto progressbar_x_offset = x_offset + 98;
   auto progressbar_y_offset = y_offset + 15;
   default_framebuffer_->blit(
-      progress_buff, 0, 0, static_cast<GLsizei>(progressbar_image_->GetWidth()),
-      static_cast<GLsizei>(progressbar_image_->GetHeight()),
+      progress_buff, 0, 0, static_cast<GLsizei>(progress_dimensions[0]),
+      static_cast<GLsizei>(progress_dimensions[1]),
       static_cast<GLsizei>(progressbar_x_offset),
       static_cast<GLsizei>(progressbar_y_offset),
       static_cast<GLsizei>(progressbar_current_pixel_width +
diff --git a/library/phx/splash_screen.hpp b/library/phx/splash_screen.hpp
index f6ed4195c8294bad068b31f2b6b2e948bba28baa..d1fc1044df205acae2d36c28d8e23ed92a37f989 100644
--- a/library/phx/splash_screen.hpp
+++ b/library/phx/splash_screen.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -27,6 +27,7 @@
 
 #include "phx/export.hpp"
 #include "phx/image.hpp"
+#include "phx/resource_proxy.hpp"
 #include "phx/window.hpp"
 
 #include "gl/framebuffer.hpp"
@@ -48,7 +49,7 @@ class PHOENIX_EXPORT SplashScreen {
 
  private:
   std::unique_ptr<gl::framebuffer> default_framebuffer_;
-  std::unique_ptr<Image> splash_image_;
+  ResourceProxy* splash_image_proxy_;
   std::unique_ptr<Image> progressbar_image_;
   Window* window_;
 
diff --git a/library/phx/stream_helpers.cpp b/library/phx/stream_helpers.cpp
index c14de1320ae26cfecc97fb46992db4b785795f97..1ca8820eb960d4386816c8d1cdeda28da3f9dff1 100644
--- a/library/phx/stream_helpers.cpp
+++ b/library/phx/stream_helpers.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/stream_helpers.hpp b/library/phx/stream_helpers.hpp
index 13c601a432c77757636fc50545ef2b0fb5cac15c..671241428cccbe62c224065b2e5db66044ac8f92 100644
--- a/library/phx/stream_helpers.hpp
+++ b/library/phx/stream_helpers.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/system.cpp b/library/phx/system.cpp
index 7037bd3b2cd1a52f447d15aa8de0b3437b13484d..b4ae86cd80cb4032346641978f13acff50f9eb75 100644
--- a/library/phx/system.cpp
+++ b/library/phx/system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/system.hpp b/library/phx/system.hpp
index 67cf22620ec56aecad5026cd02e175d4a464621c..ffdf3b01cba3197b9f29417977be323980aa587b 100644
--- a/library/phx/system.hpp
+++ b/library/phx/system.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -37,9 +37,7 @@ class PHOENIX_EXPORT System : public Loggable {
   System() = delete;
   virtual ~System() = default;
 
-  virtual void Initialize() {}
   virtual void Update(const FrameTimer::TimeInfo&) {}
-  virtual void Terminate() {}
 
   Engine* GetEngine() const;
 
diff --git a/library/phx/time_info.hpp b/library/phx/time_info.hpp
index 98e2cf5f168159a44f7967aa80e83437443a686d..735ae8c9d963c654d878a2698e1a277605f66821 100644
--- a/library/phx/time_info.hpp
+++ b/library/phx/time_info.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/tracking_system.cpp b/library/phx/tracking_system.cpp
index 4bbc94e380945cc77d57a1541d888206dce6f6a0..ae836ae207255f3163e21681f2e0b1f9b5963a07 100644
--- a/library/phx/tracking_system.cpp
+++ b/library/phx/tracking_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -26,47 +26,17 @@
 
 #include "display_system.hpp"
 #include "hmd.hpp"
+#include "logger.hpp"
 #include "projection.hpp"
+#include "runtime_component.hpp"
 #include "transform.hpp"
-#include "virtual_platform.hpp"
 
 namespace phx {
-TrackingSystem::TrackingSystem(Engine* engine) : System(engine) {}
 
-void TrackingSystem::Initialize() {
-  const auto hmd = engine_->GetSystem<DisplaySystem>()->GetHMD();
-  if (hmd == nullptr) return;
-  auto scene = engine_->GetScene();
-  const auto virtual_platform =
-      scene->GetEntitiesWithComponents<VirtualPlatform>()[0];
-  const auto virtual_platform_transform =
-      virtual_platform->GetFirstComponent<Transform>();
-  hmd_entity_ = scene->CreateEntity();
-  hmd_entity_->SetName(RUNTIME_ENTITY_NAME_HMD);
-  auto hmd_transform = hmd_entity_->AddComponent<Transform>();
-  hmd_transform->SetParent(virtual_platform_transform);
-  left_eye_entity_ = scene->CreateEntity();
-  left_eye_entity_->SetName(RUNTIME_ENTITY_NAME_EYE_LEFT);
-  auto left_eye_transform = left_eye_entity_->AddComponent<Transform>();
-  left_eye_transform->SetParent(hmd_transform);
-  left_eye_entity_->AddComponent<Projection>();
-  right_eye_entity_ = scene->CreateEntity();
-  right_eye_entity_->SetName(RUNTIME_ENTITY_NAME_EYE_RIGHT);
-  auto right_eye_transform = right_eye_entity_->AddComponent<Transform>();
-  right_eye_entity_->AddComponent<Projection>();
-  right_eye_transform->SetParent(hmd_transform);
-
-  left_controller_entity_ = scene->CreateEntity();
-  left_controller_entity_->SetName(RUNTIME_ENTITY_NAME_CONTROLLER_LEFT);
-  auto left_controller_transform =
-      left_controller_entity_->AddComponent<Transform>();
-  left_controller_transform->SetParent(virtual_platform_transform);
-
-  right_controller_entity_ = scene->CreateEntity();
-  right_controller_entity_->SetName(RUNTIME_ENTITY_NAME_CONTROLLER_RIGHT);
-  auto right_controller_transform =
-      right_controller_entity_->AddComponent<Transform>();
-  right_controller_transform->SetParent(virtual_platform_transform);
+TrackingSystem::TrackingSystem(Engine* engine, DisplaySystem* display_system)
+    : System(engine) {
+  if (!display_system) error("TrackingSystem needs a valid DisplaySystem.");
+  CreateRuntimeEntities();
 }
 
 void TrackingSystem::Update(const FrameTimer::TimeInfo&) {
@@ -90,6 +60,54 @@ void TrackingSystem::Update(const FrameTimer::TimeInfo&) {
       hmd->GetRightControllerTransformation());
 }
 
+void TrackingSystem::CreateRuntimeEntities() {
+  const auto hmd = engine_->GetSystem<DisplaySystem>()->GetHMD();
+  if (hmd == nullptr) {
+    return;
+  }
+  auto scene = engine_->GetScene();
+  if (scene == nullptr) {
+    return;
+  }
+  const auto virtual_platforms = scene->GetEntitiesWithComponents<
+      phx::RuntimeComponent<phx::USER_PLATFORM>>();
+  if (!virtual_platforms.empty()) {
+    const auto virtual_platform = virtual_platforms[0];
+    const auto virtual_platform_transform =
+        virtual_platform->GetFirstComponent<Transform>();
+
+    hmd_entity_ = scene->CreateEntity();
+    hmd_entity_->AddComponent<RuntimeComponent<HEAD>>();
+    auto hmd_transform = hmd_entity_->AddComponent<Transform>();
+    hmd_transform->SetParent(virtual_platform_transform);
+    left_eye_entity_ = scene->CreateEntity();
+
+    left_eye_entity_->AddComponent<RuntimeComponent<LEFT_EYE>>();
+    auto left_eye_transform = left_eye_entity_->AddComponent<Transform>();
+    left_eye_transform->SetParent(hmd_transform);
+    left_eye_entity_->AddComponent<Projection>();
+    right_eye_entity_ = scene->CreateEntity();
+
+    right_eye_entity_->AddComponent<RuntimeComponent<RIGHT_EYE>>();
+    auto right_eye_transform = right_eye_entity_->AddComponent<Transform>();
+    right_eye_entity_->AddComponent<Projection>();
+    right_eye_transform->SetParent(hmd_transform);
+
+    left_controller_entity_ = scene->CreateEntity();
+    left_controller_entity_->AddComponent<RuntimeComponent<LEFT_CONTROLLER>>();
+    auto left_controller_transform =
+        left_controller_entity_->AddComponent<Transform>();
+    left_controller_transform->SetParent(virtual_platform_transform);
+
+    right_controller_entity_ = scene->CreateEntity();
+    right_controller_entity_
+        ->AddComponent<RuntimeComponent<RIGHT_CONTROLLER>>();
+    auto right_controller_transform =
+        right_controller_entity_->AddComponent<Transform>();
+    right_controller_transform->SetParent(virtual_platform_transform);
+  }
+}
+
 std::string TrackingSystem::ToString() const { return "Tracking System"; }
 
 }  // namespace phx
diff --git a/library/phx/tracking_system.hpp b/library/phx/tracking_system.hpp
index 98db7c306b96a1db8a3d88a80369fa7ccd70fa2e..4022d7e7f2c28358e62a4d665862f84ea1228751 100644
--- a/library/phx/tracking_system.hpp
+++ b/library/phx/tracking_system.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -25,6 +25,7 @@
 
 #include <string>
 
+#include "phx/display_system.hpp"
 #include "phx/engine.hpp"
 #include "phx/export.hpp"
 #include "phx/system.hpp"
@@ -41,13 +42,18 @@ class PHOENIX_EXPORT TrackingSystem : public System {
   TrackingSystem& operator=(const TrackingSystem&) = delete;
   TrackingSystem& operator=(TrackingSystem&&) = default;
 
-  void Initialize() override;
   void Update(const FrameTimer::TimeInfo&) override;
 
   std::string ToString() const override;
 
+ protected:
+  TrackingSystem(Engine* engine, DisplaySystem* display_system);
+
  private:
-  friend TrackingSystem* Engine::CreateSystem<TrackingSystem>();
+  void CreateRuntimeEntities();
+
+  template <typename SystemType, typename... SystemArguments>
+  friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments);
   explicit TrackingSystem(Engine* engine);
 
   Entity* hmd_entity_ = nullptr;
diff --git a/library/phx/transform.cpp b/library/phx/transform.cpp
index cbeac031b27a566841e43f2b38954ade0207a6ef..160668d0cc6b8ffbde7987a3087473a7c2e3cd8e 100644
--- a/library/phx/transform.cpp
+++ b/library/phx/transform.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -30,6 +30,8 @@
 
 #include "transform.hpp"
 
+#include "phx/entity.hpp"
+#include "phx/logger.hpp"
 #include "phx/stream_helpers.hpp"
 
 namespace phx {
@@ -174,6 +176,10 @@ glm::vec3 Transform::Forward() const {
 void Transform::SetParent(Transform* parent) { SetParent(parent, true); }
 
 void Transform::SetParent(Transform* parent, bool maintainGlobalPosition) {
+  if (!CheckIfParentIsValid(parent)) {
+    error("The transform to be set as parent is not within the same scene.");
+    return;
+  }
   glm::mat4 global_matrix_cache = GetGlobalMatrix();
   Hierarchical::SetParent(parent);
   if (maintainGlobalPosition) {
@@ -183,6 +189,17 @@ void Transform::SetParent(Transform* parent, bool maintainGlobalPosition) {
   }
 }
 
+bool Transform::CheckIfParentIsValid(Transform* parent) {
+  if (!parent) return true;
+  auto entity_this = GetEntity();
+  auto entity_parent = parent->GetEntity();
+  if (entity_this && entity_parent &&
+      entity_this->GetScene() == entity_parent->GetScene()) {
+    return true;
+  }
+  return false;
+}
+
 std::string Transform::ToString() const {
   std::ostringstream sstr;
   sstr << "TransformComponent: " << std::endl;
diff --git a/library/phx/transform.hpp b/library/phx/transform.hpp
index edf6d31da57111788f2dd016638fb945ae1a3532..ed5e8d25b9e69908f07f53429aaf79ea2d07af62 100644
--- a/library/phx/transform.hpp
+++ b/library/phx/transform.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -105,6 +105,7 @@ class PHOENIX_EXPORT Transform : public Component,
   std::string ToString() const override;
 
  private:
+  bool CheckIfParentIsValid(Transform* parent);
   void UpdateLocalMatrix();
   void UpdateGlobalMatricesRecursively();
 
diff --git a/library/phx/virtual_platform.cpp b/library/phx/virtual_platform.cpp
deleted file mode 100644
index 62f6d2ac7b75260abdd8ef4890a3e403cb7c573c..0000000000000000000000000000000000000000
--- a/library/phx/virtual_platform.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-//------------------------------------------------------------------------------
-// Project Phoenix
-//
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualization Group.
-//------------------------------------------------------------------------------
-//                                 License
-//
-// Licensed under the 3-Clause BSD License (the "License");
-// you may not use this file except in compliance with the License.
-// See the file LICENSE for the full text.
-// You may obtain a copy of the License at
-//
-//     https://opensource.org/licenses/BSD-3-Clause
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//------------------------------------------------------------------------------
-
-#include "virtual_platform.hpp"
-
-namespace phx {
-
-}  // namespace phx
diff --git a/library/phx/window.cpp b/library/phx/window.cpp
index f909e5292b587f53ac90d53ce772c89698a110ea..2c61f93c5c80657361ccb00814be1c910bbc2425 100644
--- a/library/phx/window.cpp
+++ b/library/phx/window.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/library/phx/window.hpp b/library/phx/window.hpp
index 001f2d3c4617902a463ca545698fe1cdb4434f70..edd41bd4cf3715036775b30de75ba836b955c6f5 100644
--- a/library/phx/window.hpp
+++ b/library/phx/window.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/resources/shader/phong.frag b/resources/shader/phong.frag
index 34621a73dcc081aa952e320043ef3cc3a8fc82ae..da9439dcce7fd9c651aba3982d8371b1a1578890 100644
--- a/resources/shader/phong.frag
+++ b/resources/shader/phong.frag
@@ -1,3 +1,24 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
+//------------------------------------------------------------------------------
+//                                 License
+//
+// Licensed under the 3-Clause BSD License (the "License");
+// you may not use this file except in compliance with the License.
+// See the file LICENSE for the full text.
+// You may obtain a copy of the License at
+//
+//     https://opensource.org/licenses/BSD-3-Clause
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//------------------------------------------------------------------------------
 #version 450
 
 layout(location = 0) in vec3 in_position;
diff --git a/resources/shader/phong.vert b/resources/shader/phong.vert
index a260f6725f188116222e0c9481e398bd8caa444c..081732a26fe7fc176f08ea6d7836f3543da5d076 100644
--- a/resources/shader/phong.vert
+++ b/resources/shader/phong.vert
@@ -1,3 +1,24 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
+//------------------------------------------------------------------------------
+//                                 License
+//
+// Licensed under the 3-Clause BSD License (the "License");
+// you may not use this file except in compliance with the License.
+// See the file LICENSE for the full text.
+// You may obtain a copy of the License at
+//
+//     https://opensource.org/licenses/BSD-3-Clause
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//------------------------------------------------------------------------------
 #version 450
 
 layout(location = 0) in vec3 in_position;
diff --git a/resources/textures/test_uppercase.JPG b/resources/textures/test_uppercase.JPG
new file mode 100644
index 0000000000000000000000000000000000000000..9b121bad6af1212d1337b13d6408d5c8fe2f99e2
--- /dev/null
+++ b/resources/textures/test_uppercase.JPG
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f9a7136ec7d35d156e04cbb07706695369279ba7f6f86502e7654a4cf42624d2
+size 56740
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 05cafcf094986a02900f8a2628e66fa27472f974..0c516001fb8a2bb48d0353a54fac5d8469af3628 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
@@ -224,7 +224,7 @@ add_mocked_test(test_geometry_pass MOCK_GLEW MOCK_SDL)
 add_mocked_test(test_rendering_system MOCK_GLEW MOCK_SDL)
 add_mocked_test(test_shader MOCK_GLEW)
 add_mocked_test(test_display_system MOCK_SDL)
-add_mocked_test(test_engine MOCK_SDL MOCK_OPENVR)
+add_mocked_test(test_engine MOCK_SDL MOCK_OPENVR MOCK_GLEW)
 add_mocked_test(test_tracking_system MOCK_SDL MOCK_OPENVR)
 
 add_mocked_test(integration_test_model_rendering MOCK_OPENVR)
diff --git a/tests/src/integration_test_model_rendering.cpp b/tests/src/integration_test_model_rendering.cpp
index 6c9176ea0880983121bdbe6b8fdfa60e31b20b79..f4d4a601534826ecb357afdc8f5619d314b65221 100644
--- a/tests/src/integration_test_model_rendering.cpp
+++ b/tests/src/integration_test_model_rendering.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -34,6 +34,7 @@
 #include "phx/resource_declaration.hpp"
 #include "phx/resource_manager.hpp"
 #include "phx/resource_proxy.hpp"
+#include "phx/resource_utils.hpp"
 #include "phx/scene.hpp"
 #include "phx/setup.hpp"
 #include "phx/window.hpp"
@@ -50,10 +51,8 @@ extern template struct trompeloeil::reporter<trompeloeil::specialized>;
 
 phx::Entity* LoadBunny(glm::vec3 pos, float angleDeg,
                        unsigned int material_index, phx::Scene* scene) {
-  phx::ResourceDeclaration bunny_declaration{"0;models/bunny.obj"};
-  auto bunny_proxy =
-      phx::ResourceManager::instance().DeclareResource(bunny_declaration);
-  bunny_proxy->Load();
+  auto bunny_proxy = phx::ResourceUtils::LoadResourceFromFile(
+      "models/bunny.obj", {{"mesh_index", 0}});
 
   phx::Entity* bunny = scene->CreateEntity();
 
@@ -63,11 +62,8 @@ phx::Entity* LoadBunny(glm::vec3 pos, float angleDeg,
   phx::MaterialHandle* bunny_material_handle =
       bunny->AddComponent<phx::MaterialHandle>();
 
-  phx::ResourceDeclaration bunny_material_declaration{
-      "material:" + std::to_string(material_index) + ";models/bunny.obj"};
-  auto bunny_material_proxy = phx::ResourceManager::instance().DeclareResource(
-      bunny_material_declaration);
-  bunny_material_proxy->Load();
+  auto bunny_material_proxy = phx::ResourceUtils::LoadResourceFromFile(
+      "models/bunny.obj", {{"material_index", material_index}});
   bunny_material_handle->SetMaterialProxy(bunny_material_proxy);
 
   phx::Transform* bunny_transform = bunny->AddComponent<phx::Transform>();
@@ -118,7 +114,6 @@ SCENARIO(
     auto display_system = engine->GetSystem<phx::DisplaySystem>();
 
     WHEN("We render the scene") {
-      rendering_system->Initialize();
       rendering_system->Update(phx::FrameTimer::TimeInfo());
       display_system->Update(phx::FrameTimer::TimeInfo());
       THEN("the rendering matches our reference image") {
@@ -148,7 +143,6 @@ SCENARIO(
     auto display_system = engine->GetSystem<phx::DisplaySystem>();
 
     WHEN("We render the scene") {
-      rendering_system->Initialize();
       rendering_system->Update(phx::FrameTimer::TimeInfo{});
       display_system->Update(phx::FrameTimer::TimeInfo());
       THEN("the rendering matches our reference image") {
diff --git a/tests/src/integration_test_opengl_buffer_data_download.cpp b/tests/src/integration_test_opengl_buffer_data_download.cpp
index 52e2ebe82271ee8e3ae6a8587b98238cb17fcaa4..cad6ca25a7335d3f9725011eece775800fd9a960 100644
--- a/tests/src/integration_test_opengl_buffer_data_download.cpp
+++ b/tests/src/integration_test_opengl_buffer_data_download.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -90,11 +90,11 @@ class SceneSetupSimple {
     triangle->AddComponent<phx::Transform>();
 
     phx::ResourceManager::instance().RegisterResourceType(
-        ".custommaterial",
+        "custommaterial",
         std::make_unique<phx::DummyMaterialGenerator>(
             glm::vec3(), glm::vec3(), glm::vec3(1.f, 0.5f, 0.25f), 500.0f));
     auto material_proxy = phx::ResourceManager::instance().DeclareResource(
-        "dummy.custommaterial");
+        {{"TYPE", "custommaterial"}});
     material_proxy->Load();
 
     phx::MaterialHandle* material_handle =
@@ -108,11 +108,11 @@ class SceneSetupSimple {
     std::vector<unsigned int> indices = {0u, 1u, 2u};
 
     phx::ResourceManager::instance().RegisterResourceType(
-        ".staticmesh",
+        "staticmesh",
         std::make_unique<phx::DummyMeshLoader>(
             std::move(vertices), std::move(normals), std::move(indices)));
-    auto mesh_proxy =
-        phx::ResourceManager::instance().DeclareResource("dummy.staticmesh");
+    auto mesh_proxy = phx::ResourceManager::instance().DeclareResource(
+        {{"TYPE", "staticmesh"}});
     mesh_proxy->Load();
 
     auto mesh_handle = triangle->AddComponent<phx::MeshHandle>();
diff --git a/tests/src/integration_test_rendering.cpp b/tests/src/integration_test_rendering.cpp
index 2a5c27425a98ba3bd9423a1ac9e674c31adf3949..a4f6257c7c158ce19978f7480c0aa13d0062f89d 100644
--- a/tests/src/integration_test_rendering.cpp
+++ b/tests/src/integration_test_rendering.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -79,11 +79,11 @@ void CreateTestTriangleComponent(phx::Entity* triangle) {
   std::vector<unsigned int> indices = {0u, 1u, 2u};
 
   phx::ResourceManager::instance().RegisterResourceType(
-      ".staticmesh",
+      "staticmesh",
       std::make_unique<phx::DummyMeshLoader>(
           std::move(vertices), std::move(normals), std::move(indices)));
-  auto mesh_proxy =
-      phx::ResourceManager::instance().DeclareResource("dummy.staticmesh");
+  auto mesh_proxy = phx::ResourceManager::instance().DeclareResource(
+      {{"TYPE", "staticmesh"}});
   mesh_proxy->Load();
 
   phx::MeshHandle* mesh_handle = triangle->AddComponent<phx::MeshHandle>();
@@ -104,11 +104,11 @@ SCENARIO("We can render a simple triangle", "[phx][phx::Rendering]") {
     CreateTestTriangleComponent(triangle);
 
     phx::ResourceManager::instance().RegisterResourceType(
-        ".custommaterial", std::make_unique<phx::DummyMaterialGenerator>(
-                               glm::vec3(0.2f, 0.2f, 1.0f), glm::vec3(1, 1, 1),
-                               glm::vec3(), 500.0f));
+        "custommaterial", std::make_unique<phx::DummyMaterialGenerator>(
+                              glm::vec3(0.2f, 0.2f, 1.0f), glm::vec3(1, 1, 1),
+                              glm::vec3(), 500.0f));
     auto material_proxy = phx::ResourceManager::instance().DeclareResource(
-        "dummy.custommaterial");
+        {{"TYPE", "custommaterial"}});
     material_proxy->Load();
 
     phx::Transform* transform = triangle->AddComponent<phx::Transform>();
@@ -117,7 +117,6 @@ SCENARIO("We can render a simple triangle", "[phx][phx::Rendering]") {
     material_handle->SetMaterialProxy(material_proxy);
 
     WHEN("We render the scene") {
-      rendering_system->Initialize();
       rendering_system->Update(phx::FrameTimer::TimeInfo{});
       display_system->Update(phx::FrameTimer::TimeInfo{});
       THEN("the rendering matches our reference image") {
@@ -148,7 +147,6 @@ SCENARIO("If no material given a default red one is taken.",
     phx::Transform* transform = triangle->AddComponent<phx::Transform>();
 
     WHEN("We render the scene") {
-      rendering_system->Initialize();
       rendering_system->Update(phx::FrameTimer::TimeInfo{});
       display_system->Update(phx::FrameTimer::TimeInfo{});
       THEN("the rendering matches our reference image") {
diff --git a/tests/src/mocks/generation/Create_openGL_mock.py b/tests/src/mocks/generation/Create_openGL_mock.py
index da4120f75497a8efc4336a97a88ff9a46c6ed0fb..db07bbaff7b30f0607b5d2631536020020a8c638 100644
--- a/tests/src/mocks/generation/Create_openGL_mock.py
+++ b/tests/src/mocks/generation/Create_openGL_mock.py
@@ -1,7 +1,7 @@
 #------------------------------------------------------------------------------
 # Project Phoenix
 #
-# Copyright (c) 2017 RWTH Aachen University, Germany,
+# Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 # Virtual Reality & Immersive Visualization Group.
 #------------------------------------------------------------------------------
 #                                 License
diff --git a/tests/src/mocks/generation/README.md b/tests/src/mocks/generation/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7dffa42aa7e8e634200a9dbc2015757b186577c7
--- /dev/null
+++ b/tests/src/mocks/generation/README.md
@@ -0,0 +1,11 @@
+# Mock-Generation
+
+If a method is missing and a linker error is thrown, you have to add the methods 
+to the `functions_to_mock` list in the beginning of the python generator script.
+
+It is generated automatically on rerunning cmake.
+
+If you want to specify a specific `ALLOW_CALL` e.g. to have different needed return values, 
+add it to `allow_calls_provided`. Otherwise a standard allow call is generated.
+
+Either with its `glew` or its `gl` name, e.g., `glCreateProgram` or `__glewCompileShader`.
diff --git a/tests/src/mocks/generation/README.txt b/tests/src/mocks/generation/README.txt
deleted file mode 100644
index bf2e5cfc0d0e176134d253ab1ee97ccc66b68b68..0000000000000000000000000000000000000000
--- a/tests/src/mocks/generation/README.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-#------------------------------------------------------------------------------
-# Project Phoenix
-#
-# Copyright (c) 2017 RWTH Aachen University, Germany,
-# Virtual Reality & Immersive Visualization Group.
-#------------------------------------------------------------------------------
-#                                 License
-#
-# Licensed under the 3-Clause BSD License (the "License");
-# you may not use this file except in compliance with the License.
-# See the file LICENSE for the full text.
-# You may obtain a copy of the License at
-#
-#     https://opensource.org/licenses/BSD-3-Clause
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#------------------------------------------------------------------------------
-If a method is missing and a linker error is thrown, you have to add the methods 
-to the functions_to_mock list in the beginning of the python generator script.
-
-It is generated automatically on rerunning cmake.
-
-If you want to specify a specific ALLOW_CALL e.g. to have different needed return values, 
-add it to allow_calls_provided. Otherwise a standard allow call is generated.
-
-Either with its glew or its gl name, e.g. glCreateProgram or __glewCompileShader.
diff --git a/tests/src/mocks/generation/opengl_mock_template.cpp b/tests/src/mocks/generation/opengl_mock_template.cpp
index c3378b824cb7c2d5fe16e49552d535faeda2bd25..909308da6e3dd03667b13d5814987b8a9c7366df 100644
--- a/tests/src/mocks/generation/opengl_mock_template.cpp
+++ b/tests/src/mocks/generation/opengl_mock_template.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/mocks/generation/opengl_mock_template.hpp b/tests/src/mocks/generation/opengl_mock_template.hpp
index eae8271d8a2bab2289f9e42ecb87ac0b65b2d2b2..2c54bd1846058b290f79a2c03b4417ae453f0d33 100644
--- a/tests/src/mocks/generation/opengl_mock_template.hpp
+++ b/tests/src/mocks/generation/opengl_mock_template.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/mocks/openvr_mock.cpp b/tests/src/mocks/openvr_mock.cpp
index f2195f0f4deca39d1313a23ad2b1e520db001cdd..4b5fc468d4dab28ab2ae9cfed5a9a61a9469457c 100644
--- a/tests/src/mocks/openvr_mock.cpp
+++ b/tests/src/mocks/openvr_mock.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/mocks/openvr_mock.hpp b/tests/src/mocks/openvr_mock.hpp
index e80d97a10d44093d5bc8846bab28026d67887757..89ecec4ee202166ee92d3cf35ac315b825a7f496 100644
--- a/tests/src/mocks/openvr_mock.hpp
+++ b/tests/src/mocks/openvr_mock.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/mocks/sdl_mock.cpp b/tests/src/mocks/sdl_mock.cpp
index 88a99109470db27efed573504c615b46a6857705..702b3d6bd8eadbc6d6510b3905a0df1e749db600 100644
--- a/tests/src/mocks/sdl_mock.cpp
+++ b/tests/src/mocks/sdl_mock.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/mocks/sdl_mock.hpp b/tests/src/mocks/sdl_mock.hpp
index 3ffd555389d89bf974efaf9feb02539f59b01d5c..ee35b1f2ac01a8832277899f4b23a88fffca6d71 100644
--- a/tests/src/mocks/sdl_mock.hpp
+++ b/tests/src/mocks/sdl_mock.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test-phoenix.cpp b/tests/src/test-phoenix.cpp
index 34b10faf9a774162ebd78b2676ef6e4e16201d8a..282bba566a816573880a11d4ce23ca98e8765e60 100644
--- a/tests/src/test-phoenix.cpp
+++ b/tests/src/test-phoenix.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test-transform.cpp b/tests/src/test-transform.cpp
index d5b23f89f78c944c3d5b6154564c44b32b46f341..24d01a7eb2b411d5956267f2415e8275ef4ac2af 100644
--- a/tests/src/test-transform.cpp
+++ b/tests/src/test-transform.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -22,61 +22,69 @@
 
 #include <cstddef>
 
+#include <memory>
+
 #include "catch/catch.hpp"
 
 SUPPRESS_WARNINGS_BEGIN
 #include "glm/glm.hpp"
 SUPPRESS_WARNINGS_END
 
+#include "phx/entity.hpp"
+#include "phx/scene.hpp"
 #include "phx/transform.hpp"
 
 #include "phx/logger.hpp"
 #include "test_utilities/glm_mat4.hpp"
 #include "test_utilities/glm_quat.hpp"
 #include "test_utilities/glm_vec3.hpp"
+#include "test_utilities/log_capture.hpp"
 
 SCENARIO(
     "The transform component represents a 4x4 transformation matrix and "
     "provides functionality to translate, rotate and scale it.",
     "[phx][phx::Transform]") {
-  GIVEN("A new transform component.") {
-    phx::Transform transform;
+  GIVEN("A scene with an entity and a new transform component.") {
+    auto scene = std::make_shared<phx::Scene>();
+    auto entity = scene->CreateEntity();
+    phx::Transform* transform = entity->AddComponent<phx::Transform>();
 
     WHEN("We query its local translation.") {
-      auto translation = transform.GetLocalTranslation();
+      auto translation = transform->GetLocalTranslation();
       THEN("It should be equal to the zero vector.") {
         REQUIRE(translation == glm::vec3());
       }
     }
     WHEN("We query its local rotation.") {
-      auto rotation = transform.GetLocalRotation();
+      auto rotation = transform->GetLocalRotation();
       THEN("It should be equal to the unit quaternion.") {
         REQUIRE(rotation == glm::quat());
       }
     }
     WHEN("We query its local rotation in Euler angles.") {
-      auto rotation = transform.GetLocalRotationEuler();
+      auto rotation = transform->GetLocalRotationEuler();
       THEN("It should be equal to the zero vector.") {
         REQUIRE(rotation == glm::vec3());
       }
     }
     WHEN("We query its local scale.") {
-      auto scale = transform.GetLocalScale();
+      auto scale = transform->GetLocalScale();
       THEN("It should be equal to the unit vector.") {
         REQUIRE(scale == glm::vec3(1.0f));
       }
     }
     WHEN("We query its local transform matrix.") {
-      auto matrix = transform.GetLocalMatrix();
+      auto matrix = transform->GetLocalMatrix();
       THEN("It should be equal to the identity matrix.") {
         REQUIRE(matrix == glm::mat4());
       }
     }
 
     WHEN("We set its local translation to [1.0, 2.0, 3.0].") {
-      transform.SetLocalTranslation(glm::vec3(1.0f, 2.0f, 3.0f));
+      transform->SetLocalTranslation(glm::vec3(1.0f, 2.0f, 3.0f));
       THEN("The translation vector should be equal to [1.0, 2.0, 3.0].") {
-        REQUIRE(transform.GetLocalTranslation() == glm::vec3(1.0f, 2.0f, 3.0f));
+        REQUIRE(transform->GetLocalTranslation() ==
+                glm::vec3(1.0f, 2.0f, 3.0f));
       }
       THEN(
           "The transform matrix should be equal to the corresponding "
@@ -84,12 +92,12 @@ SCENARIO(
         const glm::mat4 translation_matrix(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
                                            0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
                                            1.0f, 2.0f, 3.0f, 1.0f);
-        REQUIRE(transform.GetLocalMatrix() == translation_matrix);
+        REQUIRE(transform->GetLocalMatrix() == translation_matrix);
       }
       WHEN("We translate it by [4.0, 5.0, 6.0].") {
-        transform.Translate(glm::vec3(4.0f, 5.0f, 6.0f));
+        transform->Translate(glm::vec3(4.0f, 5.0f, 6.0f));
         THEN("The translation vector should be equal to [5.0, 7.0, 9.0].") {
-          REQUIRE(transform.GetLocalTranslation() ==
+          REQUIRE(transform->GetLocalTranslation() ==
                   glm::vec3(5.0f, 7.0f, 9.0f));
         }
         THEN(
@@ -98,30 +106,30 @@ SCENARIO(
           const glm::mat4 translation_matrix(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
                                              0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
                                              5.0f, 7.0f, 9.0f, 1.0f);
-          REQUIRE(transform.GetLocalMatrix() == translation_matrix);
+          REQUIRE(transform->GetLocalMatrix() == translation_matrix);
         }
       }
       WHEN("We reset it.") {
-        transform.Reset();
+        transform->Reset();
         THEN(
             "The local translation vector should be equal to the zero "
             "vector.") {
-          REQUIRE(transform.GetLocalTranslation() == glm::vec3());
+          REQUIRE(transform->GetLocalTranslation() == glm::vec3());
         }
         THEN(
             "The local transform matrix should be equal to the identity "
             "matrix.") {
-          REQUIRE(transform.GetLocalMatrix() == glm::mat4());
+          REQUIRE(transform->GetLocalMatrix() == glm::mat4());
         }
       }
     }
     WHEN("We set its local rotation to [0.43967, 0.36042, 0.82236, 0.02225].") {
-      transform.SetLocalRotation(
+      transform->SetLocalRotation(
           glm::quat(0.43967f, 0.36042f, 0.82236f, 0.02225f));
       THEN(
           "The rotation vector be equal to [0.43967, 0.36042, 0.82236, "
           "0.02225].") {
-        auto rotation = transform.GetLocalRotation();
+        auto rotation = transform->GetLocalRotation();
         REQUIRE(test_utilities::Approx<glm::quat>(rotation) ==
                 glm::quat(0.43967f, 0.36042f, 0.82236f, 0.02225f));
       }
@@ -131,27 +139,27 @@ SCENARIO(
         const glm::mat4 rotation_matrix(
             -0.35354f, 0.61235f, -0.70709f, 0.0f, 0.57322, 0.73920f, 0.35352f,
             0.0f, 0.73917f, -0.28033f, -0.61235f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
-        REQUIRE(test_utilities::Approx<glm::mat4>(transform.GetLocalMatrix()) ==
-                rotation_matrix);
+        REQUIRE(test_utilities::Approx<glm::mat4>(
+                    transform->GetLocalMatrix()) == rotation_matrix);
       }
       WHEN("We reset it.") {
-        transform.Reset();
+        transform->Reset();
         THEN("The local rotation should be equal to the identity quaternion.") {
-          REQUIRE(transform.GetLocalRotation() == glm::quat());
+          REQUIRE(transform->GetLocalRotation() == glm::quat());
         }
         THEN(
             "The local transform matrix should be equal to the identity "
             "matrix.") {
-          REQUIRE(transform.GetLocalMatrix() == glm::mat4());
+          REQUIRE(transform->GetLocalMatrix() == glm::mat4());
         }
       }
     }
     WHEN("We set its local rotation in Euler angles to [30.0, 45.0, 60.0].") {
-      transform.SetLocalRotationEuler(glm::vec3(30.0f, 45.0f, 60.0f));
+      transform->SetLocalRotationEuler(glm::vec3(30.0f, 45.0f, 60.0f));
       THEN(
           "The local rotation vector should be equal to [30.0f, 45.0f, "
           "60.0f].") {
-        auto rotation = transform.GetLocalRotationEuler();
+        auto rotation = transform->GetLocalRotationEuler();
         REQUIRE(test_utilities::Approx<glm::vec3>(rotation) ==
                 glm::vec3(30.0f, 45.0f, 60.0f));
       }
@@ -161,15 +169,15 @@ SCENARIO(
         const glm::mat4 rotation_matrix(
             0.35356f, 0.61237f, -0.70710f, 0.0f, -0.57322, 0.73919f, 0.35355f,
             0.0f, 0.73919f, 0.28033f, 0.61237f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
-        REQUIRE(test_utilities::Approx<glm::mat4>(transform.GetLocalMatrix()) ==
-                rotation_matrix);
+        REQUIRE(test_utilities::Approx<glm::mat4>(
+                    transform->GetLocalMatrix()) == rotation_matrix);
       }
       WHEN("We rotate it in Euler angles by [5.0, 10.0, 15.0].") {
-        transform.RotateEuler(glm::vec3(5.0f, 10.0f, 15.0f));
+        transform->RotateEuler(glm::vec3(5.0f, 10.0f, 15.0f));
         THEN(
             "The local rotation vector should be equal to [45.76f, 44.63f, "
             "85.70f].") {
-          auto rotation = transform.GetLocalRotationEuler();
+          auto rotation = transform->GetLocalRotationEuler();
           REQUIRE(test_utilities::Approx<glm::vec3>(rotation) ==
                   glm::vec3(45.7610f, 44.6317f, 85.7065f));
         }
@@ -181,25 +189,25 @@ SCENARIO(
                                           0.75112f, 0.43512f, 0.49647f, 0.0f,
                                           0.0f, 0.0f, 0.0f, 1.0f);
           REQUIRE(test_utilities::Approx<glm::mat4>(
-                      transform.GetLocalMatrix()) == rotation_matrix);
+                      transform->GetLocalMatrix()) == rotation_matrix);
         }
       }
       WHEN("We reset it.") {
-        transform.Reset();
+        transform->Reset();
         THEN("The local rotation vector should be equal to the zero vector.") {
-          REQUIRE(transform.GetLocalRotationEuler() == glm::vec3());
+          REQUIRE(transform->GetLocalRotationEuler() == glm::vec3());
         }
         THEN(
             "The local transform matrix should be equal to the identity "
             "matrix.") {
-          REQUIRE(transform.GetLocalMatrix() == glm::mat4());
+          REQUIRE(transform->GetLocalMatrix() == glm::mat4());
         }
       }
     }
     WHEN("We set its local scale to [1.0, 2.0, 3.0].") {
-      transform.SetLocalScale(glm::vec3(1.0f, 2.0f, 3.0f));
+      transform->SetLocalScale(glm::vec3(1.0f, 2.0f, 3.0f));
       THEN("The scale vector should be equal to [1.0, 2.0, 3.0].") {
-        REQUIRE(transform.GetLocalScale() == glm::vec3(1.0f, 2.0f, 3.0f));
+        REQUIRE(transform->GetLocalScale() == glm::vec3(1.0f, 2.0f, 3.0f));
       }
       THEN(
           "The local transform matrix should be equal to the corresponding "
@@ -207,12 +215,12 @@ SCENARIO(
         const glm::mat4 scale_matrix(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f,
                                      0.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f,
                                      0.0f, 1.0f);
-        REQUIRE(transform.GetLocalMatrix() == scale_matrix);
+        REQUIRE(transform->GetLocalMatrix() == scale_matrix);
       }
       WHEN("We scale it by [4.0, 5.0, 6.0].") {
-        transform.Scale(glm::vec3(4.0f, 5.0f, 6.0f));
+        transform->Scale(glm::vec3(4.0f, 5.0f, 6.0f));
         THEN("The local scale vector should be equal to [5.0, 7.0, 9.0].") {
-          REQUIRE(transform.GetLocalScale() == glm::vec3(5.0f, 7.0f, 9.0f));
+          REQUIRE(transform->GetLocalScale() == glm::vec3(5.0f, 7.0f, 9.0f));
         }
         THEN(
             "The local transform matrix should be equal to the corresponding "
@@ -220,34 +228,34 @@ SCENARIO(
           const glm::mat4 scale_matrix(5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 7.0f, 0.0f,
                                        0.0f, 0.0f, 0.0f, 9.0f, 0.0f, 0.0f, 0.0f,
                                        0.0f, 1.0f);
-          REQUIRE(transform.GetLocalMatrix() == scale_matrix);
+          REQUIRE(transform->GetLocalMatrix() == scale_matrix);
         }
       }
       WHEN("We reset it.") {
-        transform.Reset();
+        transform->Reset();
         THEN(
             "The local translation vector should be equal to the zero "
             "vector.") {
-          REQUIRE(transform.GetLocalTranslation() == glm::vec3());
+          REQUIRE(transform->GetLocalTranslation() == glm::vec3());
         }
         THEN(
             "The local transform matrix should be equal to the identity "
             "matrix.") {
-          REQUIRE(transform.GetLocalMatrix() == glm::mat4());
+          REQUIRE(transform->GetLocalMatrix() == glm::mat4());
         }
       }
     }
     WHEN("We look at [0.0, 0.0, -1.0] with up vector [0.0, 1.0, 0.0]") {
-      transform.LookAt(glm::vec3(0.0, 0.0, -1.0), glm::vec3(0.0, 1.0, 0.0));
+      transform->LookAt(glm::vec3(0.0, 0.0, -1.0), glm::vec3(0.0, 1.0, 0.0));
       THEN(
           "The local rotation quaternion should be equal to the identity "
           "quaternion.") {
-        REQUIRE(transform.GetLocalRotation() == glm::quat());
+        REQUIRE(transform->GetLocalRotation() == glm::quat());
       }
       THEN(
           "The local transform matrix should be equal to the identity "
           "matrix.") {
-        REQUIRE(transform.GetLocalMatrix() == glm::mat4());
+        REQUIRE(transform->GetLocalMatrix() == glm::mat4());
       }
     }
   }
@@ -256,70 +264,82 @@ SCENARIO(
 SCENARIO(
     "The transform component enables hierarchical arrangement of transforms.",
     "[phx][phx::Transform]") {
-  GIVEN("Two transform components.") {
-    phx::Transform child, parent;
+  GIVEN("Two entities in a scene with transform components.") {
+    auto scene = std::make_shared<phx::Scene>();
+    auto entity1 = scene->CreateEntity();
+    auto entity2 = scene->CreateEntity();
+    phx::Transform* child = entity1->AddComponent<phx::Transform>();
+    phx::Transform* parent = entity2->AddComponent<phx::Transform>();
 
     WHEN("We query the first transform's parent.") {
-      auto parent_ptr = child.GetParent();
+      auto parent_ptr = child->GetParent();
       THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); }
     }
 
     WHEN("We set the first transform's parent to the second.") {
-      child.SetParent(&parent);
+      child->SetParent(parent);
 
       WHEN("We query the first transform's parent.") {
-        auto parent_ptr = child.GetParent();
+        auto parent_ptr = child->GetParent();
         THEN("It should equal the second transform.") {
-          REQUIRE(parent_ptr == &parent);
+          REQUIRE(parent_ptr == parent);
         }
       }
 
       WHEN("We query the second transform's child count.") {
-        auto child_count = parent.GetChildCount();
+        auto child_count = parent->GetChildCount();
         THEN("Its size should equal 1.") { REQUIRE(child_count == 1); }
       }
 
       WHEN("We query the second transform's first child.") {
-        auto child_ptr = parent.GetChild(0);
+        auto child_ptr = parent->GetChild(0);
         THEN("It should equal the first transform.") {
-          REQUIRE(child_ptr == &child);
+          REQUIRE(child_ptr == child);
         }
       }
 
       WHEN("We iterate the second transform's children.") {
         auto iteration_count = 0;
-        for (auto& current : parent) iteration_count++;
+        for (auto& current : *parent) iteration_count++;
         THEN("It should consist of a single iteration.") {
           REQUIRE(iteration_count == 1);
         }
       }
 
       WHEN("We set the first transform's parent to nullptr.") {
-        child.SetParent(nullptr);
+        child->SetParent(nullptr);
         WHEN("We query the transform's parent.") {
-          auto parent_ptr = child.GetParent();
+          auto parent_ptr = child->GetParent();
           THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); }
         }
       }
     }
   }
   GIVEN("A hierarchy of transforms: A( B, C( D ) )") {
-    phx::Transform A, B, C, D;
-    B.SetParent(&A);
-    C.SetParent(&A);
-    D.SetParent(&C);
+    auto scene = std::make_shared<phx::Scene>();
+    auto entity1 = scene->CreateEntity();
+    auto entity2 = scene->CreateEntity();
+    auto entity3 = scene->CreateEntity();
+    auto entity4 = scene->CreateEntity();
+    phx::Transform* A = entity1->AddComponent<phx::Transform>();
+    phx::Transform* B = entity2->AddComponent<phx::Transform>();
+    phx::Transform* C = entity3->AddComponent<phx::Transform>();
+    phx::Transform* D = entity4->AddComponent<phx::Transform>();
+    B->SetParent(A);
+    C->SetParent(A);
+    D->SetParent(C);
 
     WHEN("We set A's local translation to (1, 0, 0).") {
-      A.SetLocalTranslation(glm::vec3(1.f, 0.f, 0.f));
+      A->SetLocalTranslation(glm::vec3(1.f, 0.f, 0.f));
       THEN("The local translation of B, C and D is still (0, 0, 0).") {
-        REQUIRE(B.GetLocalTranslation() == glm::vec3());
-        REQUIRE(C.GetLocalTranslation() == glm::vec3());
-        REQUIRE(D.GetLocalTranslation() == glm::vec3());
+        REQUIRE(B->GetLocalTranslation() == glm::vec3());
+        REQUIRE(C->GetLocalTranslation() == glm::vec3());
+        REQUIRE(D->GetLocalTranslation() == glm::vec3());
       }
       THEN("The global translation of B, C and D is (1, 0, 0).") {
-        REQUIRE(B.GetGlobalTranslation() == glm::vec3(1.f, 0.f, 0.f));
-        REQUIRE(C.GetGlobalTranslation() == glm::vec3(1.f, 0.f, 0.f));
-        REQUIRE(D.GetGlobalTranslation() == glm::vec3(1.f, 0.f, 0.f));
+        REQUIRE(B->GetGlobalTranslation() == glm::vec3(1.f, 0.f, 0.f));
+        REQUIRE(C->GetGlobalTranslation() == glm::vec3(1.f, 0.f, 0.f));
+        REQUIRE(D->GetGlobalTranslation() == glm::vec3(1.f, 0.f, 0.f));
       }
       THEN(
           "The global transform matrix of B, C and D should be equal to the "
@@ -327,77 +347,80 @@ SCENARIO(
         const glm::mat4 trans_matrix(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
                                      0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
                                      0.0f, 1.0f);
-        REQUIRE(B.GetGlobalMatrix() == trans_matrix);
-        REQUIRE(C.GetGlobalMatrix() == trans_matrix);
-        REQUIRE(D.GetGlobalMatrix() == trans_matrix);
+        REQUIRE(B->GetGlobalMatrix() == trans_matrix);
+        REQUIRE(C->GetGlobalMatrix() == trans_matrix);
+        REQUIRE(D->GetGlobalMatrix() == trans_matrix);
       }
       WHEN("We set C's global translation to (2, 1, 0).") {
-        C.SetGlobalTranslation(glm::vec3(2.f, 1.f, 0.f));
+        C->SetGlobalTranslation(glm::vec3(2.f, 1.f, 0.f));
         THEN("It's local translation is (1, 1, 0)") {
-          REQUIRE(C.GetLocalTranslation() == glm::vec3(1.f, 1.f, 0.f));
+          REQUIRE(C->GetLocalTranslation() == glm::vec3(1.f, 1.f, 0.f));
         }
         THEN("B's global translation is still (1, 0, 0).") {
-          REQUIRE(B.GetGlobalTranslation() == glm::vec3(1.f, 0.f, 0.f));
+          REQUIRE(B->GetGlobalTranslation() == glm::vec3(1.f, 0.f, 0.f));
         }
         THEN("D's global translation is (2, 1, 0).") {
-          REQUIRE(D.GetGlobalTranslation() == glm::vec3(2.f, 1.f, 0.f));
+          REQUIRE(D->GetGlobalTranslation() == glm::vec3(2.f, 1.f, 0.f));
         }
       }
     }
 
     WHEN("We set A's local rotation to the (20, 30, 40) euler angles.") {
-      A.SetLocalRotationEuler(glm::vec3(20.f, 30.f, 40.f));
+      A->SetLocalRotationEuler(glm::vec3(20.f, 30.f, 40.f));
       THEN("The local rotation of B, C and D is still (0, 0, 0).") {
-        REQUIRE(B.GetLocalRotationEuler() == glm::vec3());
-        REQUIRE(C.GetLocalRotationEuler() == glm::vec3());
-        REQUIRE(D.GetLocalRotationEuler() == glm::vec3());
+        REQUIRE(B->GetLocalRotationEuler() == glm::vec3());
+        REQUIRE(C->GetLocalRotationEuler() == glm::vec3());
+        REQUIRE(D->GetLocalRotationEuler() == glm::vec3());
       }
       THEN("The global rotation of B, C and D is (20, 30, 40).") {
-        REQUIRE(test_utilities::Approx<glm::quat>(B.GetGlobalRotationEuler()) ==
-                glm::vec3(20.f, 30.f, 40.f));
-        REQUIRE(test_utilities::Approx<glm::quat>(C.GetGlobalRotationEuler()) ==
-                glm::vec3(20.f, 30.f, 40.f));
-        REQUIRE(test_utilities::Approx<glm::quat>(D.GetGlobalRotationEuler()) ==
-                glm::vec3(20.f, 30.f, 40.f));
+        REQUIRE(
+            test_utilities::Approx<glm::quat>(B->GetGlobalRotationEuler()) ==
+            glm::vec3(20.f, 30.f, 40.f));
+        REQUIRE(
+            test_utilities::Approx<glm::quat>(C->GetGlobalRotationEuler()) ==
+            glm::vec3(20.f, 30.f, 40.f));
+        REQUIRE(
+            test_utilities::Approx<glm::quat>(D->GetGlobalRotationEuler()) ==
+            glm::vec3(20.f, 30.f, 40.f));
       }
       THEN(
           "The global transform matrix of B, C and D should be equal to the "
           "corresponding rotation matrix.") {
         const glm::mat4 rot_matrix = glm::mat4_cast(
             glm::quat(glm::radians(glm::vec3(20.f, 30.f, 40.f))));
-        REQUIRE(test_utilities::Approx<glm::mat4>(B.GetGlobalMatrix()) ==
+        REQUIRE(test_utilities::Approx<glm::mat4>(B->GetGlobalMatrix()) ==
                 rot_matrix);
-        REQUIRE(test_utilities::Approx<glm::mat4>(C.GetGlobalMatrix()) ==
+        REQUIRE(test_utilities::Approx<glm::mat4>(C->GetGlobalMatrix()) ==
                 rot_matrix);
-        REQUIRE(test_utilities::Approx<glm::mat4>(D.GetGlobalMatrix()) ==
+        REQUIRE(test_utilities::Approx<glm::mat4>(D->GetGlobalMatrix()) ==
                 rot_matrix);
       }
       WHEN("We set C's global rotation to (20, 30, 50).") {
-        C.SetGlobalRotationEuler(glm::vec3(20.f, 30.f, 50.f));
+        C->SetGlobalRotationEuler(glm::vec3(20.f, 30.f, 50.f));
         THEN("B's global rotation is still (20, 30, 40).") {
           REQUIRE(
-              test_utilities::Approx<glm::vec3>(B.GetGlobalRotationEuler()) ==
+              test_utilities::Approx<glm::vec3>(B->GetGlobalRotationEuler()) ==
               glm::vec3(20.f, 30.f, 40.f));
         }
         THEN("D's global rotation is (20, 30, 50).") {
           REQUIRE(
-              test_utilities::Approx<glm::vec3>(D.GetGlobalRotationEuler()) ==
+              test_utilities::Approx<glm::vec3>(D->GetGlobalRotationEuler()) ==
               glm::vec3(20.f, 30.f, 50.f));
         }
       }
     }
 
     WHEN("We set A's local scale to (1, 2, 3).") {
-      A.SetLocalScale(glm::vec3(1.f, 2.f, 3.f));
+      A->SetLocalScale(glm::vec3(1.f, 2.f, 3.f));
       THEN("The local scale of B, C and D is still (1, 1, 1).") {
-        REQUIRE(B.GetLocalScale() == glm::vec3(1.f, 1.f, 1.f));
-        REQUIRE(C.GetLocalScale() == glm::vec3(1.f, 1.f, 1.f));
-        REQUIRE(D.GetLocalScale() == glm::vec3(1.f, 1.f, 1.f));
+        REQUIRE(B->GetLocalScale() == glm::vec3(1.f, 1.f, 1.f));
+        REQUIRE(C->GetLocalScale() == glm::vec3(1.f, 1.f, 1.f));
+        REQUIRE(D->GetLocalScale() == glm::vec3(1.f, 1.f, 1.f));
       }
       THEN("The global scale of B, C and D is (1, 2, 3).") {
-        REQUIRE(B.GetGlobalScale() == glm::vec3(1.f, 2.f, 3.f));
-        REQUIRE(C.GetGlobalScale() == glm::vec3(1.f, 2.f, 3.f));
-        REQUIRE(D.GetGlobalScale() == glm::vec3(1.f, 2.f, 3.f));
+        REQUIRE(B->GetGlobalScale() == glm::vec3(1.f, 2.f, 3.f));
+        REQUIRE(C->GetGlobalScale() == glm::vec3(1.f, 2.f, 3.f));
+        REQUIRE(D->GetGlobalScale() == glm::vec3(1.f, 2.f, 3.f));
       }
       THEN(
           "The global transform matrix of B, C and D should be equal to the "
@@ -405,65 +428,65 @@ SCENARIO(
         const glm::mat4 scale_matrix(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f,
                                      0.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f,
                                      0.0f, 1.0f);
-        REQUIRE(B.GetGlobalMatrix() == scale_matrix);
-        REQUIRE(C.GetGlobalMatrix() == scale_matrix);
-        REQUIRE(D.GetGlobalMatrix() == scale_matrix);
+        REQUIRE(B->GetGlobalMatrix() == scale_matrix);
+        REQUIRE(C->GetGlobalMatrix() == scale_matrix);
+        REQUIRE(D->GetGlobalMatrix() == scale_matrix);
       }
       WHEN("We set C's global scale to (2, 4, 6).") {
-        C.SetGlobalScale(glm::vec3(2.f, 4.f, 6.f));
+        C->SetGlobalScale(glm::vec3(2.f, 4.f, 6.f));
         THEN("It's local scale is (2, 2, 2)") {
-          REQUIRE(C.GetLocalScale() == glm::vec3(2.f, 2.f, 2.f));
+          REQUIRE(C->GetLocalScale() == glm::vec3(2.f, 2.f, 2.f));
         }
         THEN("B's global scale is still (1, 2, 3).") {
-          REQUIRE(B.GetGlobalScale() == glm::vec3(1.f, 2.f, 3.f));
+          REQUIRE(B->GetGlobalScale() == glm::vec3(1.f, 2.f, 3.f));
         }
         THEN("D's global scale is (2, 4, 6).") {
-          REQUIRE(D.GetGlobalScale() == glm::vec3(2.f, 4.f, 6.f));
+          REQUIRE(D->GetGlobalScale() == glm::vec3(2.f, 4.f, 6.f));
         }
       }
     }
 
     WHEN("We set A's global translation to some arbitrary values.") {
-      A.SetGlobalTranslation(glm::vec3(18.0f, 0.5f, 13.37f));
-      A.SetGlobalRotationEuler(glm::vec3(12.f, 0.f, 42.f));
-      A.SetGlobalScale(glm::vec3(1.f, 2.f, 4.f));
+      A->SetGlobalTranslation(glm::vec3(18.0f, 0.5f, 13.37f));
+      A->SetGlobalRotationEuler(glm::vec3(12.f, 0.f, 42.f));
+      A->SetGlobalScale(glm::vec3(1.f, 2.f, 4.f));
       THEN("The local matrix of B, C and D is still identity.") {
-        REQUIRE(B.GetLocalMatrix() == glm::mat4());
-        REQUIRE(C.GetLocalMatrix() == glm::mat4());
-        REQUIRE(D.GetLocalMatrix() == glm::mat4());
+        REQUIRE(B->GetLocalMatrix() == glm::mat4());
+        REQUIRE(C->GetLocalMatrix() == glm::mat4());
+        REQUIRE(D->GetLocalMatrix() == glm::mat4());
       }
       THEN("The global matrix of B, C and D is the same as A's.") {
-        REQUIRE(B.GetGlobalMatrix() == A.GetGlobalMatrix());
-        REQUIRE(C.GetGlobalMatrix() == A.GetGlobalMatrix());
-        REQUIRE(D.GetGlobalMatrix() == A.GetGlobalMatrix());
+        REQUIRE(B->GetGlobalMatrix() == A->GetGlobalMatrix());
+        REQUIRE(C->GetGlobalMatrix() == A->GetGlobalMatrix());
+        REQUIRE(D->GetGlobalMatrix() == A->GetGlobalMatrix());
       }
       WHEN("We set C's global matrix to identity.") {
-        C.SetGlobalMatrix(glm::mat4());
+        C->SetGlobalMatrix(glm::mat4());
         THEN("It's local matrix is the inverse of A's.") {
-          REQUIRE(C.GetLocalMatrix() == glm::inverse(A.GetLocalMatrix()));
+          REQUIRE(C->GetLocalMatrix() == glm::inverse(A->GetLocalMatrix()));
         }
         THEN("B's global matrix is still the same as A's.") {
-          REQUIRE(B.GetGlobalMatrix() == A.GetGlobalMatrix());
+          REQUIRE(B->GetGlobalMatrix() == A->GetGlobalMatrix());
         }
         THEN("D's global matrix is identity.") {
-          REQUIRE(test_utilities::Approx<glm::mat4>(D.GetGlobalMatrix()) ==
+          REQUIRE(test_utilities::Approx<glm::mat4>(D->GetGlobalMatrix()) ==
                   glm::mat4());
         }
 
         WHEN(
             "We set C's parent to bue nullptr, maintaining its global "
             "position.") {
-          C.SetParent(nullptr);
+          C->SetParent(nullptr);
           THEN("C's global and local matrices are identity.") {
-            REQUIRE(test_utilities::Approx<glm::mat4>(C.GetLocalMatrix()) ==
+            REQUIRE(test_utilities::Approx<glm::mat4>(C->GetLocalMatrix()) ==
                     glm::mat4());
-            REQUIRE(test_utilities::Approx<glm::mat4>(C.GetGlobalMatrix()) ==
+            REQUIRE(test_utilities::Approx<glm::mat4>(C->GetGlobalMatrix()) ==
                     glm::mat4());
           }
           THEN("D's global and local matrices are identity.") {
-            REQUIRE(test_utilities::Approx<glm::mat4>(D.GetLocalMatrix()) ==
+            REQUIRE(test_utilities::Approx<glm::mat4>(D->GetLocalMatrix()) ==
                     glm::mat4());
-            REQUIRE(test_utilities::Approx<glm::mat4>(D.GetGlobalMatrix()) ==
+            REQUIRE(test_utilities::Approx<glm::mat4>(D->GetGlobalMatrix()) ==
                     glm::mat4());
           }
         }
@@ -471,21 +494,87 @@ SCENARIO(
         WHEN(
             "We set C's parent to bue nullptr, not maintaining its global "
             "position.") {
-          C.SetParent(nullptr, false);
+          C->SetParent(nullptr, false);
           THEN(
               "C's global and local matrices are the inverse of A's global "
               "matrix.") {
-            REQUIRE(test_utilities::Approx<glm::mat4>(C.GetLocalMatrix()) ==
-                    glm::inverse(A.GetGlobalMatrix()));
-            REQUIRE(test_utilities::Approx<glm::mat4>(C.GetGlobalMatrix()) ==
-                    glm::inverse(A.GetGlobalMatrix()));
+            REQUIRE(test_utilities::Approx<glm::mat4>(C->GetLocalMatrix()) ==
+                    glm::inverse(A->GetGlobalMatrix()));
+            REQUIRE(test_utilities::Approx<glm::mat4>(C->GetGlobalMatrix()) ==
+                    glm::inverse(A->GetGlobalMatrix()));
           }
           THEN("D's global matrix is the inverse of A's global matrix.") {
-            REQUIRE(test_utilities::Approx<glm::mat4>(D.GetGlobalMatrix()) ==
-                    glm::inverse(A.GetGlobalMatrix()));
+            REQUIRE(test_utilities::Approx<glm::mat4>(D->GetGlobalMatrix()) ==
+                    glm::inverse(A->GetGlobalMatrix()));
           }
         }
       }
     }
   }
 }
+
+SCENARIO("The transform component checks if its parent is in the same scene.",
+         "[phx][phx::Transform]") {
+  GIVEN(
+      "A scene s1 with two entities e1, e2 with transform components t1, t2.") {
+    auto s1 = phx::Scene();
+    auto e1 = s1.CreateEntity();
+    auto t1 = e1->AddComponent<phx::Transform>();
+    auto e2 = s1.CreateEntity();
+    auto t2 = e2->AddComponent<phx::Transform>();
+
+    WHEN("We set t2 to be the parent of t1.") {
+      t1->SetParent(t2);
+      THEN("t2 is the parent of t1.") {
+        REQUIRE(t1->GetParent() == t2);
+        REQUIRE(t2->GetChild(0) == t1);
+      }
+    }
+
+    GIVEN("A second scene s2 with an entity e3 with a transform t3.") {
+      auto s2 = phx::Scene();
+      auto e3 = s2.CreateEntity();
+      auto t3 = e3->AddComponent<phx::Transform>();
+
+      WHEN("We set t3 to be the parent of t1.") {
+        auto log_capture = std::make_shared<test_utilities::LogCapture>();
+        phx::logger =
+            std::make_shared<spdlog::logger>("logcapture", log_capture);
+        phx::logger->set_pattern("%v");
+
+        t1->SetParent(t3);
+        THEN("An error is logged.") {
+          REQUIRE(*log_capture ==
+                  "The transform to be set as parent is not within the same "
+                  "scene.");
+        }
+        THEN("t3 is not the parent of t1.") {
+          REQUIRE(t1->GetParent() != t3);
+          REQUIRE(t3->GetChildCount() == 0);
+        }
+      }
+    }
+
+    GIVEN("A transform t4 without a scene.") {
+      phx::Transform t4;
+
+      WHEN("We set t4 to be the parent of t1.") {
+        auto log_capture = std::make_shared<test_utilities::LogCapture>();
+        phx::logger =
+            std::make_shared<spdlog::logger>("logcapture", log_capture);
+        phx::logger->set_pattern("%v");
+
+        t1->SetParent(&t4);
+        THEN("An error is logged.") {
+          REQUIRE(*log_capture ==
+                  "The transform to be set as parent is not within the same "
+                  "scene.");
+        }
+        THEN("t4 is not the parent of t1.") {
+          REQUIRE(t1->GetParent() != &t4);
+          REQUIRE(t4.GetChildCount() == 0);
+        }
+      }
+    }
+  }
+}
diff --git a/tests/src/test_assimp_loader.cpp b/tests/src/test_assimp_loader.cpp
index e12db0c6da1b704da18c30e7bebc956d05179172..e143adb324cb38808c92fa9d68f35eabac6d5173 100644
--- a/tests/src/test_assimp_loader.cpp
+++ b/tests/src/test_assimp_loader.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -20,6 +20,7 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
+#include <iostream>
 #include <memory>
 #include <string>
 
@@ -28,7 +29,8 @@
 #include "phx/assimp_model_loader.hpp"
 #include "phx/logger.hpp"
 #include "phx/mesh.hpp"
-#include "phx/resources_path.hpp"
+#include "phx/resource_proxy.hpp"
+#include "phx/resource_utils.hpp"
 
 #include "test_utilities/log_capture.hpp"
 
@@ -50,23 +52,22 @@ SCENARIO("The assimp loader loads models using the Assimp library.",
   GIVEN("A plain loader") {
     phx::CreateDefaultLogger();
     TestLogger test_logger;
-    phx::AssimpModelLoader loader;
 
     WHEN("We load an invalid file") {
-      std::string invalid_name{"invalid.obj"};
-      auto invalid_mesh = loader.Load(invalid_name);
+      phx::ResourceUtils::LoadResourceFromFile("invalid.obj");
 
       THEN("We get an error message printed to the console.") {
-        std::string expected_error_message{
-            "Load Model: " + invalid_name + "Error loading model file \"" +
-            std::string(phx::resources_root) + invalid_name + "\"."};
-        REQUIRE(*test_logger.GetCapture() == expected_error_message);
+        std::string captured_message = test_logger.GetCapture()->ToString();
+        std::cout << "Captured error message is: " << captured_message
+                  << std::endl;
+        REQUIRE(captured_message.find("Error loading model") !=
+                std::string::npos);
       }
     }
     WHEN("We load the stanford bunny.") {
-      const std::string bunny_filename{"0;models/bunny.obj"};
-      auto resource = loader.Load(bunny_filename);
-      auto mesh = dynamic_cast<phx::Mesh*>(resource.get());
+      auto resource_proxy = phx::ResourceUtils::LoadResourceFromFile(
+          "models/bunny.obj", {{"mesh_index", 0}});
+      auto mesh = dynamic_cast<phx::Mesh*>(resource_proxy->GetAs<phx::Mesh>());
 
       THEN("there is a mesh") {
         REQUIRE(mesh != nullptr);
diff --git a/tests/src/test_behavior_system.cpp b/tests/src/test_behavior_system.cpp
index e7e2510fafc7f69ffbe6efd14585f3c04b293426..493787f420b89df4f281d8ada3add8156c1bd94e 100644
--- a/tests/src/test_behavior_system.cpp
+++ b/tests/src/test_behavior_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_clear_pass.cpp b/tests/src/test_clear_pass.cpp
index ac2b3b15995a310a19620083527bc0e52c09c8fa..8ae007bdce6b795fb265c8e4685feaa95e5043df 100644
--- a/tests/src/test_clear_pass.cpp
+++ b/tests/src/test_clear_pass.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_display_system.cpp b/tests/src/test_display_system.cpp
index a6b1156bab4cf655c6eda4ffa16e1858367c71ef..5c7e6b07219fc8858c4c319cd2e456f06c833d2a 100644
--- a/tests/src/test_display_system.cpp
+++ b/tests/src/test_display_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -52,8 +52,9 @@ SCENARIO(
         "scope.") {
       REQUIRE_CALL(sdl_mock.Get(), SDL_VideoInit(nullptr)).RETURN(0);
       REQUIRE_CALL(sdl_mock.Get(), SDL_VideoQuit());
-      phx::DisplaySystem displaySystem(nullptr);
-      displaySystem.Initialize();
+      phx::Engine engine;
+      phx::DisplaySystem* displaySystem =
+          engine.CreateSystem<phx::DisplaySystem>();
 
       WHEN("We create a window.") {
         THEN("The window is created.") {
@@ -71,13 +72,13 @@ SCENARIO(
               .RETURN(0);
           REQUIRE_CALL(sdl_mock.Get(), SDL_GL_CreateContext(_))
               .RETURN(reinterpret_cast<SDL_GLContext>(1u));
-          displaySystem.CreateWindow("TestWindow", glm::uvec2(0, 0),
-                                     glm::uvec2(640, 480));
+          displaySystem->CreateWindow("TestWindow", glm::uvec2(0, 0),
+                                      glm::uvec2(640, 480));
 
           WHEN("DisplaySystem Update is called") {
             THEN("SDL_GL_SwapWindow is called") {
               REQUIRE_CALL(sdl_mock.Get(), SDL_GL_SwapWindow(_));
-              displaySystem.Update(phx::FrameTimer::TimeInfo());
+              displaySystem->Update(phx::FrameTimer::TimeInfo());
             }
           }
         }
diff --git a/tests/src/test_engine.cpp b/tests/src/test_engine.cpp
index c2ab61aa862246e028c7c915e4f43a2305958a5a..3256e671385c5be1d8f79da1df7c43b876786ea3 100644
--- a/tests/src/test_engine.cpp
+++ b/tests/src/test_engine.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -43,6 +43,7 @@ SUPPRESS_WARNINGS_BEGIN
 #include "mocks/openvr_mock.hpp"
 SUPPRESS_WARNINGS_END
 #include "mocks/sdl_mock.hpp"
+#include "mocks/opengl_mock.hpp"
 
 using trompeloeil::_;
 using trompeloeil::ne;
@@ -278,6 +279,7 @@ SCENARIO("An engine provides access to the behaviors", "[phx][phx::Engine]") {
 
 SCENARIO("An engine can be setup by the default setup", "[phx][phx::Engine]") {
   SDL_MOCK_ALLOW_ANY_CALL
+  OPENGL_MOCK_ALLOW_ANY_CALL
   ALLOW_CALL(openvr_mock.Get(), VR_IsHmdPresent()).RETURN(false);
   GIVEN("Nothing") {
     WHEN("An engine is setup with the default setup") {
diff --git a/tests/src/test_entity.cpp b/tests/src/test_entity.cpp
index d33fde145d09a0b019dba9aa06228a267a13f89e..12baf3feefaa045485aa2f7824abb3b32d680949 100644
--- a/tests/src/test_entity.cpp
+++ b/tests/src/test_entity.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_frame_graph.cpp b/tests/src/test_frame_graph.cpp
index a93aa6969eef0005a33ad61ca889f5f5d2b4482e..cd98e045f714f0e7015a8ca5f574279275a40138 100644
--- a/tests/src/test_frame_graph.cpp
+++ b/tests/src/test_frame_graph.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_geometry_pass.cpp b/tests/src/test_geometry_pass.cpp
index 2d4ff8f306d519cea584a9827eee5df1378b241d..0111f5a71f2c0c2789d535a427d7857223ad4352 100644
--- a/tests/src/test_geometry_pass.cpp
+++ b/tests/src/test_geometry_pass.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_image.cpp b/tests/src/test_image.cpp
index 579e3199ae9fd64fa367153f7f3b7e17f31a38df..db69d982d9a5a7e3b6dc858599c2c55a454fa352 100644
--- a/tests/src/test_image.cpp
+++ b/tests/src/test_image.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -21,6 +21,9 @@
 //------------------------------------------------------------------------------
 
 #include <algorithm>
+#include <array>
+#include <cstddef>
+#include <memory>
 #include "catch/catch.hpp"
 #include "phx/image.hpp"
 #include "phx/opengl_image_buffer_data.hpp"
@@ -28,38 +31,36 @@
 
 SCENARIO("We can create an empty image.", "[phx][phx::Image]") {
   GIVEN("An empty image") {
-    auto image = phx::Image::CreateEmptyImage(320, 240, 3);
+    auto image = std::make_unique<phx::Image>(
+        std::array<std::size_t, 2>{{320, 240}}, 24);
 
     WHEN("We check its dimensions") {
-      std::size_t width = image->GetWidth();
-      std::size_t height = image->GetHeight();
-      std::size_t bytes_per_pixel = image->GetBytesPerPixel();
+      auto dimensions = image->GetDimensions();
+      std::size_t bits_per_pixel = image->GetBitsPerPixel();
 
       THEN("They match what we defined.") {
-        REQUIRE(width == 320);
-        REQUIRE(height == 240);
-        REQUIRE(bytes_per_pixel == 3);
+        REQUIRE(dimensions[0] == 320);
+        REQUIRE(dimensions[1] == 240);
+        REQUIRE(bits_per_pixel == 24);
       }
     }
 
     WHEN("We check its pixels") {
-      auto image_typed =
-          dynamic_cast<phx::ImageTyped<phx::ImageFormatRGB>*>(image.get());
+      auto pixels = image->GetPixels();
 
       THEN("All are black.") {
-        REQUIRE(image_typed != nullptr);
+        REQUIRE(pixels.first != nullptr);
 
-        phx::ImageFormatRGB black_pixel = {0, 0, 0};
-
-        REQUIRE(image_typed->GetPixel(10, 10) == black_pixel);
-        REQUIRE(image_typed->GetPixel(30, 100) == black_pixel);
-        REQUIRE(image_typed->GetPixel(20, 90) == black_pixel);
-        REQUIRE(image_typed->GetPixel(80, 230) == black_pixel);
+        std::array<std::uint8_t, 4> black_pixel = {{0, 0, 0, 0}};
+        REQUIRE(image->GetPixelColor({{10, 10}}) == black_pixel);
+        REQUIRE(image->GetPixelColor({{30, 100}}) == black_pixel);
+        REQUIRE(image->GetPixelColor({{20, 90}}) == black_pixel);
+        REQUIRE(image->GetPixelColor({{80, 230}}) == black_pixel);
       }
     }
 
     THEN("We can get its raw buffer pointer, e.g., for uploads to the GPU.") {
-      const unsigned char* data_pointer = image->GetDataPointer();
+      const unsigned char* data_pointer = image->GetPixels().first;
 
       // check first and last byte, should be 0
       REQUIRE(data_pointer[0] == 0);
@@ -68,153 +69,107 @@ SCENARIO("We can create an empty image.", "[phx][phx::Image]") {
   }
 }
 
-// TODO(acd): Fix according to the changes to the new BPP conversions.
-// SCENARIO("Images can be loaded from and saved to disk.", "[phx][phx::Image]")
-// {
-//  GIVEN("An RGB image with some pixels set in some colors") {
-//    auto image = phx::Image::CreateEmptyImage(320, 240, 3);
-//    auto image_typed =
-//        static_cast<phx::ImageTyped<phx::ImageFormatRGB>*>(image.get());
-//    image_typed->SetPixel(10, 10, {255, 128, 64});
-//    image_typed->SetPixel(50, 50, {128, 128, 128});
-//
-//    WHEN("We save it as a PNG") {
-//      bool success = image->Save("test_image.png");
-//      REQUIRE(success);
-//
-//      WHEN("We load the file again") {
-//        auto image_load = phx::Image::Load("test_image.png");
-//
-//        THEN("It is not null and the images are identical") {
-//          REQUIRE(image_load != nullptr);
-//
-//          bool equal = image_load->GetIsEqual(image.get());
-//          REQUIRE(equal);
-//        }
-//      }
-//
-//      // clean up
-//      std::remove("test_image.png");
-//    }
-//
-//    WHEN("We save it as a BMP") {
-//      bool success = image->Save("test_image.bmp");
-//      REQUIRE(success);
-//      WHEN("We load it again") {
-//        auto image_load = phx::Image::Load("test_image.bmp");
-//        THEN("The images are identical") {
-//          bool equal =
-//              image_load != nullptr && image->GetIsEqual(image_load.get());
-//          REQUIRE(equal);
-//        }
-//      }
-//      // clean up
-//      std::remove("test_image.bmp");
-//    }
-//
-//    WHEN("We save it as a TGA") {
-//      bool success = image->Save("test_image.tga");
-//      REQUIRE(success);
-//      WHEN("We load it again") {
-//        auto image_load = phx::Image::Load("test_image.tga");
-//        THEN("The images are identical") {
-//          bool equal =
-//              image_load != nullptr && image->GetIsEqual(image_load.get());
-//          REQUIRE(equal);
-//        }
-//      }
-//      // clean up
-//      std::remove("test_image.tga");
-//    }
-//
-//    WHEN("We save it as a TIF") {
-//      bool success = image->Save("test_image.tif");
-//      REQUIRE(success);
-//      WHEN("We load it again") {
-//        auto image_load = phx::Image::Load("test_image.tif");
-//        THEN("The images are identical") {
-//          bool equal =
-//              image_load != nullptr && image->GetIsEqual(image_load.get());
-//          REQUIRE(equal);
-//        }
-//      }
-//      // clean up
-//      std::remove("test_image.tif");
-//    }
-//
-//    WHEN("We save it as a JPG") {
-//      bool success = image->Save("test_image.jpg");
-//      REQUIRE(success);
-//      WHEN("We load it again") {
-//        auto image_load = phx::Image::Load("test_image.jpg");
-//        THEN("Loading worked and the images are highly similar") {
-//          REQUIRE(image_load != nullptr);
-//          auto buffer_load =
-//              phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>::
-//                  CreateFromImage(image_load.get());
-//          auto buffer_save = phx::OpenGLImageBufferData<
-//              phx::OpenGLImageBufferDataType_RGB>::CreateFromImage(image.get());
-//          REQUIRE(buffer_load != nullptr);
-//          REQUIRE(buffer_save != nullptr);
-//          test_utilities::OpenGLBufferComparison::REQUIRE_SIMILARITY(
-//              *buffer_load.get(), *buffer_save.get(), 0.999);
-//        }
-//      }
-//      // clean up
-//      std::remove("test_image.jpg");
-//    }
-//  }
-//
-//  GIVEN("An RGBA image with some pixels set in some colors") {
-//    auto image = phx::Image::CreateEmptyImage(320, 240, 4);
-//    auto image_typed =
-//        static_cast<phx::ImageTyped<phx::ImageFormatRGBA>*>(image.get());
-//    image_typed->SetPixel(10, 10, {255, 128, 64, 255});
-//    image_typed->SetPixel(50, 50, {128, 128, 128, 255});
-//
-//    WHEN("We save it as a PNG") {
-//      bool success = image->Save("test_image_rgba.png");
-//      REQUIRE(success);
-//
-//      WHEN("We load the file again") {
-//        auto image_load = phx::Image::Load("test_image_rgba.png");
-//
-//        THEN("It is not null, and the images are identical") {
-//          REQUIRE(image_load != nullptr);
-//
-//          bool equal = image_load->GetIsEqual(image.get());
-//          REQUIRE(equal);
-//        }
-//      }
-//
-//      // clean up
-//      std::remove("test_image_rgba.png");
-//    }
-//  }
-//
-//  GIVEN("An 8 bit (gray scale) image with some pixels set in some colors") {
-//    auto image = phx::Image::CreateEmptyImage(320, 240, 1);
-//    auto image_typed =
-//        static_cast<phx::ImageTyped<phx::ImageFormat8bit>*>(image.get());
-//    image_typed->SetPixel(10, 10, phx::ImageFormat8bit(192));
-//    image_typed->SetPixel(50, 50, phx::ImageFormat8bit(23));
-//
-//    WHEN("We save it as a PNG") {
-//      bool success = image->Save("test_image_8bit.png");
-//      REQUIRE(success);
-//
-//      WHEN("We load the file again") {
-//        auto image_load = phx::Image::Load("test_image_8bit.png");
-//
-//        THEN("It is not null and the images are identical") {
-//          REQUIRE(image_load != nullptr);
-//          bool equal = image_load->GetIsEqual(image.get());
-//          REQUIRE(equal);
-//        }
-//      }
-//
-//      // clean up
-//      std::remove("test_image_8bit.png");
-//    }
-//  }
-//}
+SCENARIO("Images can be loaded from and saved to disk.", "[phx][phx::Image]") {
+  GIVEN("An RGB image with some pixels set in some colors") {
+    auto image = std::make_unique<phx::Image>(
+        std::array<std::size_t, 2>{{320, 240}}, 24);
+    image->SetPixelColor({{10, 10}}, {{255, 128, 64}});
+    image->SetPixelColor({{50, 50}}, {{128, 128, 128}});
+
+    WHEN("We save it as a PNG") {
+      bool success = image->Save("test_image.png");
+      REQUIRE(success);
+      WHEN("We load the file again") {
+        auto image_load = std::make_unique<phx::Image>(
+            "test_image.png");  // Throws on failure.
+      }
+      std::remove("test_image.png");
+    }
+
+    WHEN("We save it as a BMP") {
+      bool success = image->Save("test_image.bmp");
+      REQUIRE(success);
+      WHEN("We load it again") {
+        auto image_load = std::make_unique<phx::Image>(
+            "test_image.bmp");  // Throws on failure.
+      }
+      std::remove("test_image.bmp");
+    }
+
+    WHEN("We save it as a TGA") {
+      bool success = image->Save("test_image.tga");
+      REQUIRE(success);
+      WHEN("We load it again") {
+        auto image_load = std::make_unique<phx::Image>(
+            "test_image.tga");  // Throws on failure.
+      }
+      std::remove("test_image.tga");
+    }
+
+    WHEN("We save it as a TIF") {
+      bool success = image->Save("test_image.tif");
+      REQUIRE(success);
+      WHEN("We load it again") {
+        auto image_load = std::make_unique<phx::Image>(
+            "test_image.tif");  // Throws on failure.
+      }
+      std::remove("test_image.tif");
+    }
+
+    WHEN("We save it as a JPG") {
+      bool success = image->Save("test_image.jpg");
+      REQUIRE(success);
+      WHEN("We load it again") {
+        auto image_load = std::make_unique<phx::Image>("test_image.jpg");
+        THEN("Loading worked and the images are highly similar") {
+          REQUIRE(image_load != nullptr);
+          auto buffer_load =
+              phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>::
+                  CreateFromImage(image_load.get());
+          auto buffer_save = phx::OpenGLImageBufferData<
+              phx::OpenGLImageBufferDataType_RGB>::CreateFromImage(image.get());
+          REQUIRE(buffer_load != nullptr);
+          REQUIRE(buffer_save != nullptr);
+          test_utilities::OpenGLBufferComparison::REQUIRE_SIMILARITY(
+              *buffer_load.get(), *buffer_save.get(), 0.999);
+        }
+      }
+      std::remove("test_image.jpg");
+    }
+  }
+
+  GIVEN("An RGBA image with some pixels set in some colors") {
+    auto image = std::make_unique<phx::Image>(
+        std::array<std::size_t, 2>{{320, 240}}, 32);
+    image->SetPixelColor({{10, 10}}, {{255, 128, 64, 255}});
+    image->SetPixelColor({{50, 50}}, {{128, 128, 128, 255}});
+
+    WHEN("We save it as a PNG") {
+      bool success = image->Save("test_image_rgba.png");
+      REQUIRE(success);
+
+      WHEN("We load the file again") {
+        auto image_load = std::make_unique<phx::Image>(
+            "test_image_rgba.png");  // Throws on failure.
+      }
+      std::remove("test_image_rgba.png");
+    }
+  }
+
+  GIVEN("An 8 bit (gray scale) image with some pixels set in some colors") {
+    auto image =
+        std::make_unique<phx::Image>(std::array<std::size_t, 2>{{320, 240}}, 8);
+    image->SetPixelColor({{10, 10}}, {{192}});
+    image->SetPixelColor({{50, 50}}, {{23}});
+
+    WHEN("We save it as a PNG") {
+      bool success = image->Save("test_image_8bit.png");
+      REQUIRE(success);
+      WHEN("We load the file again") {
+        auto image_load = std::make_unique<phx::Image>(
+            "test_image_8bit.png");  // Throws on failure.
+      }
+      std::remove("test_image_8bit.png");
+    }
+  }
+}
diff --git a/tests/src/test_input_system.cpp b/tests/src/test_input_system.cpp
index 3b5cef6e88206fcb8f6c13b80b8a1388ba6bc2b0..63b20191aeab02742ca194a7d22f6b52ea716afa 100644
--- a/tests/src/test_input_system.cpp
+++ b/tests/src/test_input_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_light.cpp b/tests/src/test_light.cpp
index 0fd01abaaae77970579e3729510b988e28421a96..409151931f5c6cc0436438731486864007b8beff 100644
--- a/tests/src/test_light.cpp
+++ b/tests/src/test_light.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_material.cpp b/tests/src/test_material.cpp
index 198768e286a6c1d948f1d910333294181f9f8771..8b97af72d9295751bb61760364f1b597a98da095 100644
--- a/tests/src/test_material.cpp
+++ b/tests/src/test_material.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_mesh.cpp b/tests/src/test_mesh.cpp
index 6dac769a60afc751b2ee82ecfeed898c24c4e814..cf5ef60e92b77e9623d7ed006613a375c57df68a 100644
--- a/tests/src/test_mesh.cpp
+++ b/tests/src/test_mesh.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_model.cpp b/tests/src/test_model.cpp
index 40ff471cfd423780bf4a879a428023a71d595cfc..e12c98e706b069845518997d1ce74d15cee0b88b 100644
--- a/tests/src/test_model.cpp
+++ b/tests/src/test_model.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -27,17 +27,16 @@
 #include "phx/assimp_model_loader.hpp"
 #include "phx/model.hpp"
 #include "phx/resource_manager.hpp"
+#include "phx/resource_utils.hpp"
 
 SCENARIO("A Model can be loaded that contains multiple meshes",
          "[phx][phx::Model]") {
   GIVEN("A fresh resource manager...") {
     GIVEN("A file name of an .obj file...") {
-      std::string mesh_file{"models/2MeshTest/2meshTest.obj"};
-      phx::ResourceDeclaration mesh_declaration{mesh_file};
+      std::string model_file_name{"models/2MeshTest/2meshTest.obj"};
       WHEN("A model resource is declared and loaded") {
         auto model_proxy =
-            phx::ResourceManager::instance().DeclareResource(mesh_declaration);
-        model_proxy->Load();
+            phx::ResourceUtils::LoadResourceFromFile(model_file_name);
         phx::Model* model = model_proxy->GetAs<phx::Model>();
         THEN("the returned model contains 2 meshes") {
           REQUIRE(model->GetMeshes().size() == 2u);
diff --git a/tests/src/test_opengl_buffer_data.cpp b/tests/src/test_opengl_buffer_data.cpp
index 68a45b914791781d88988e6dbd0ff32487f88e2c..da2958389ac079df5b9681364847f15d9dd1c7e4 100644
--- a/tests/src/test_opengl_buffer_data.cpp
+++ b/tests/src/test_opengl_buffer_data.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -21,10 +21,12 @@
 //------------------------------------------------------------------------------
 
 #include <array>
+#include <string>
 
 #include "catch/catch.hpp"
 
 #include "phx/opengl_image_buffer_data.hpp"
+#include "phx/resources_path.hpp"
 #include "test_utilities/opengl_buffer_data_comparison.hpp"
 
 namespace {
@@ -72,7 +74,7 @@ const phx::OpenGLImageBufferDataType_Float32 neutral_float32_color(122.0f /
   REQUIRE(pixel.a_ == color.a_);
 
 #define REQUIRE_PIXEL_V_COLOR(pixel, color) \
-  REQUIRE(pixel.value_ == Approx(color.value_));
+  REQUIRE(pixel.value_ == Approx(color.value_).epsilon(0.01));
 
 #ifdef __clang__
 #pragma clang diagnostic pop
@@ -203,16 +205,17 @@ SCENARIO("OpenGLImageBufferData can be written to and read from disk",
     WHEN(
         "We set a pixel to any RGBA color, save it to disk as a png, and read "
         "it again") {
+      std::string buffer_file_name{"testbufferfile_rgba.png"};
       buffer.SetPixel(::any_pixel.x, ::any_pixel.y, ::any_rgba_color);
-      buffer.SaveToFilePNG("testbufferfile_rgba.png");
+      buffer.SaveToFilePNG(phx::resources_root + buffer_file_name);
 
       auto buffer2 =
           phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGBA>::
-              ReadFromFilePNG("testbufferfile_rgba.png");
+              ReadFromFilePNG(phx::resources_root + buffer_file_name);
       THEN("They are identical") { REQUIRE(buffer == buffer2); }
 
       // clean up: delete file
-      std::remove("testbufferfile_rgba.png");
+      std::remove((phx::resources_root + buffer_file_name).c_str());
     }
   }
 
@@ -241,15 +244,19 @@ SCENARIO("OpenGLImageBufferData can be written to and read from disk",
       buffer.SetPixel(::any_pixel.x, ::any_pixel.y, ::any_rgb_color);
 
       // we can leave out the extension, it will be added
-      buffer.SaveToFilePNG("testbufferfile_rgb");
+
+      buffer.SaveToFilePNG(std::string(phx::resources_root) +
+                           "testbufferfile_rgb");
 
       auto buffer2 =
           phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>::
-              ReadFromFilePNG("testbufferfile_rgb.png");
+              ReadFromFilePNG(std::string(phx::resources_root) +
+                              "testbufferfile_rgb.png");
       THEN("They are identical") { REQUIRE(buffer == buffer2); }
 
       // clean up: delete file
-      std::remove("testbufferfile_rgb.png");
+      std::remove((std::string(phx::resources_root) + "testbufferfile_rgb.png")
+                      .c_str());
     }
   }
 
diff --git a/tests/src/test_opengl_buffer_data_comparison.cpp b/tests/src/test_opengl_buffer_data_comparison.cpp
index 4054e1f4600c1b62e625657498df403b3b531ccc..4696e60ead7f64e50b95edd713cfac8013fbfcda 100644
--- a/tests/src/test_opengl_buffer_data_comparison.cpp
+++ b/tests/src/test_opengl_buffer_data_comparison.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_projection.cpp b/tests/src/test_projection.cpp
index 41ba917388cb67fff804fc4b4ba76fd1853a5467..896bdab742f7f778d9c2db50e8e70a93dc75ebdf 100644
--- a/tests/src/test_projection.cpp
+++ b/tests/src/test_projection.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_rendering_system.cpp b/tests/src/test_rendering_system.cpp
index f1585600d2e93f1a1d93752abcf2b1ebb6564584..4c83660c7f36a14b96d0264636bdf7294d8c2cbd 100644
--- a/tests/src/test_rendering_system.cpp
+++ b/tests/src/test_rendering_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -48,11 +48,11 @@ SCENARIO(
   phx::Engine engine;
 
   GIVEN("A rendering system.") {
+    auto diplay_system = engine.CreateSystem<phx::DisplaySystem>();
     phx::RenderingSystem* rendering_system =
-        engine.CreateSystem<phx::RenderingSystem>();
+        engine.CreateSystem<phx::RenderingSystem>(diplay_system);
 
     WHEN("We initialize the system") {
-      rendering_system->Initialize();
       THEN(
           "a default frame graph is created, that has 0 render passes, "
           "since there is currently to much inter-relation between the "
diff --git a/tests/src/test_resource_manager.cpp b/tests/src/test_resource_manager.cpp
index b65b14fdbc9737c73f3066cbc4956aa59efd23fb..a8dfb054ac1b747c05ef9b6c3687bb913ff1abfa 100644
--- a/tests/src/test_resource_manager.cpp
+++ b/tests/src/test_resource_manager.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -36,6 +36,7 @@
 #include "phx/resource_declaration.hpp"
 #include "phx/resource_manager.hpp"
 #include "phx/resource_proxy.hpp"
+#include "phx/resource_utils.hpp"
 #include "phx/shader_source.hpp"
 
 SCENARIO(
@@ -45,8 +46,10 @@ SCENARIO(
   GIVEN("A fresh resource manager...") {
     auto &manager = phx::ResourceManager::instance();
     GIVEN("A file name of an .obj file...") {
-      std::string mesh_file{"0;models/bunny.obj"};
-      phx::ResourceDeclaration mesh_declaration{mesh_file};
+      std::string mesh_file_name{"models/bunny.obj"};
+      phx::ResourceDeclaration mesh_declaration =
+          phx::ResourceUtils::DeclarationFromFile(mesh_file_name,
+                                                  {{"mesh_index", 0}});
       WHEN("A resource is declared") {
         auto mesh_proxy = manager.DeclareResource(mesh_declaration);
         THEN("the returned key is a proxy for an unloaded resource") {
@@ -72,8 +75,10 @@ SCENARIO(
     "[phx][phx::ResourceManager]") {
   GIVEN("A fresh resource manager and a declared mesh resource") {
     auto &manager = phx::ResourceManager::instance();
-    std::string mesh_file{"0;models/bunny.obj"};
-    phx::ResourceDeclaration mesh_declaration{mesh_file};
+    std::string mesh_file_name{"models/bunny.obj"};
+    phx::ResourceDeclaration mesh_declaration =
+        phx::ResourceUtils::DeclarationFromFile(mesh_file_name,
+                                                {{"mesh_index", 0}});
     auto mesh_proxy = manager.DeclareResource(mesh_declaration);
     WHEN("A resource has not yet been loaded") {
       THEN("its state is still \"offline\".") {
@@ -128,7 +133,8 @@ SCENARIO("Shaders are resources", "[phx][phx::ResourceManager]") {
     GIVEN("A shader file name") {
       const std::string shader_file{"shader/test.frag"};
       WHEN("A shader is declared") {
-        auto shader_proxy = manager.DeclareResource(shader_file);
+        auto shader_proxy = manager.DeclareResource(
+            phx::ResourceUtils::DeclarationFromFile(shader_file));
         THEN("a valid resource proxy is returned") {
           REQUIRE(shader_proxy != nullptr);
           REQUIRE_FALSE(shader_proxy->IsOnline());
@@ -163,10 +169,9 @@ SCENARIO("Images are resources", "[phx][phx::ResourceManager]") {
     auto &manager = phx::ResourceManager::instance();
     GIVEN("An image file name") {
       const std::string image_file{"textures/test.jpg"};
-      manager.RegisterResourceType(".jpg",
-                                   std::make_unique<phx::ImageLoader>());
       WHEN("An image is declared") {
-        auto image_proxy = manager.DeclareResource(image_file);
+        auto image_proxy = manager.DeclareResource(
+            phx::ResourceUtils::DeclarationFromFile(image_file));
         THEN("a valid resource proxy is returned") {
           REQUIRE(image_proxy != nullptr);
           REQUIRE_FALSE(image_proxy->IsOnline());
@@ -179,7 +184,7 @@ SCENARIO("Images are resources", "[phx][phx::ResourceManager]") {
           THEN("the image data is available through the proxy") {
             auto image = image_proxy->GetAs<phx::Image>();
             REQUIRE(image != nullptr);
-            REQUIRE(image->GetWidth() > 0);
+            REQUIRE(image->GetDimensions()[0] > 0);
           }
           WHEN("The image is unloaded again") {
             image_proxy->Unload();
@@ -193,5 +198,20 @@ SCENARIO("Images are resources", "[phx][phx::ResourceManager]") {
         }
       }
     }
+    GIVEN(
+        "An image file name containing uppercase letters in the file "
+        "extension") {
+      const std::string image_file{"textures/test_uppercase.JPG"};
+      WHEN("An image is declared and loaded") {
+        auto image_proxy = manager.DeclareResource(
+            phx::ResourceUtils::DeclarationFromFile(image_file));
+        image_proxy->Load();
+        THEN("the image data is available through the proxy") {
+          auto image = image_proxy->GetAs<phx::Image>();
+          REQUIRE(image != nullptr);
+          REQUIRE(image->GetDimensions()[0] > 0);
+        }
+      }
+    }
   }
 }
diff --git a/tests/src/test_resource_utils.cpp b/tests/src/test_resource_utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a59ff75f4ed472611e9d7906ceec1cd0cecb9870
--- /dev/null
+++ b/tests/src/test_resource_utils.cpp
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
+//------------------------------------------------------------------------------
+//                                 License
+//
+// Licensed under the 3-Clause BSD License (the "License");
+// you may not use this file except in compliance with the License.
+// See the file LICENSE for the full text.
+// You may obtain a copy of the License at
+//
+//     https://opensource.org/licenses/BSD-3-Clause
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//------------------------------------------------------------------------------
+
+#include <string>
+
+#include "catch/catch.hpp"
+
+#include "phx/resource_declaration.hpp"
+#include "phx/resource_proxy.hpp"
+#include "phx/resource_utils.hpp"
+#include "phx/resources_path.hpp"
+
+SCENARIO("Resource utils can extract file extensions.",
+         "[phx][phx::ResourceUtils]") {
+  GIVEN("A file name") {
+    std::string file_name{"C:/some_path/to/file.34.ext"};
+
+    THEN("It can extract the correct extension.") {
+      std::string extension =
+          phx::ResourceUtils::ExtractFileExtension(file_name);
+      REQUIRE(extension == ".ext");
+    }
+  }
+  GIVEN("A file name in capital case") {
+    std::string file_name{"C:/some_path/to/file.34.EXT"};
+
+    THEN("It can extract the correct lowercase extension.") {
+      std::string extension =
+          phx::ResourceUtils::ExtractFileExtension(file_name);
+      REQUIRE(extension == ".ext");
+    }
+  }
+}
+
+SCENARIO("Resource utils can declarare a file resource.",
+         "[phx][phx::ResourceUtils]") {
+  GIVEN("A file name with absolute path") {
+    std::string file_name{"C:/some_path/to/file.34.ext"};
+
+    THEN("It can declare it") {
+      phx::ResourceDeclaration declaration =
+          phx::ResourceUtils::DeclarationFromFile(file_name, {}, true);
+      REQUIRE(declaration["TYPE"] == ".ext");
+      REQUIRE(declaration["file_name"] == file_name);
+    }
+  }
+  GIVEN("A file name with relative path") {
+    std::string file_name{"file.png"};
+
+    THEN("It can declare it") {
+      phx::ResourceDeclaration declaration =
+          phx::ResourceUtils::DeclarationFromFile(file_name);
+      REQUIRE(declaration["TYPE"] == ".png");
+      REQUIRE(declaration["file_name"] == phx::resources_root + file_name);
+    }
+  }
+  GIVEN("Additional key, value pairs are provided") {
+    std::string file_name{"file.png"};
+    std::string key = "some_key";
+    int value = 42;
+
+    THEN("these are in the resulting declaration") {
+      phx::ResourceDeclaration declaration =
+          phx::ResourceUtils::DeclarationFromFile(file_name, {{key, value}},
+                                                  false);
+      REQUIRE(declaration[key] == value);
+    }
+  }
+}
+
+SCENARIO("Resource utils can load a file resource.",
+         "[phx][phx::ResourceUtils]") {
+  GIVEN("A file name") {
+    std::string file_name{"textures/splash_progress.png"};
+
+    THEN("It can load it") {
+      phx::ResourceProxy* proxy =
+          phx::ResourceUtils::LoadResourceFromFile(file_name);
+      REQUIRE(proxy->IsOnline());
+    }
+  }
+}
diff --git a/tests/src/test_scene.cpp b/tests/src/test_scene.cpp
index 9e8b29258515b3c4d15c67a4c23f15440dc20d31..e52dd2fe8a58e7393ec827280dd92517ff0d4f62 100644
--- a/tests/src/test_scene.cpp
+++ b/tests/src/test_scene.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -23,8 +23,8 @@
 #include "catch/catch.hpp"
 
 #include "phx/entity.hpp"
+#include "phx/runtime_component.hpp"
 #include "phx/scene.hpp"
-#include "phx/virtual_platform.hpp"
 
 SCENARIO("A scene keeps track of entities.", "[phx][phx::Scene]") {
   GIVEN("An empty scene") {
@@ -33,7 +33,8 @@ SCENARIO("A scene keeps track of entities.", "[phx][phx::Scene]") {
     THEN("It should have 1 entity (the virtual platform).") {
       REQUIRE(scene.GetNumberOfEntities() == 1);
       REQUIRE(
-          scene.GetEntities()[0]->GetFirstComponent<phx::VirtualPlatform>());
+          scene.GetEntities()[0]
+              ->GetFirstComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>());
     }
 
     WHEN("We create an entity in the scene") {
@@ -62,8 +63,9 @@ SCENARIO("A scene keeps track of entities.", "[phx][phx::Scene]") {
         scene.RemoveEntity(entity);
         THEN("the scene has 1 entity (the virtual platform).") {
           REQUIRE(scene.GetNumberOfEntities() == 1);
-          REQUIRE(
-            scene.GetEntities()[0]->GetFirstComponent<phx::VirtualPlatform>());
+          REQUIRE(scene.GetEntities()[0]
+                      ->GetFirstComponent<
+                          phx::RuntimeComponent<phx::USER_PLATFORM>>());
         }
       }
     }
diff --git a/tests/src/test_scene_loader.cpp b/tests/src/test_scene_loader.cpp
index edc9b318c0f3836007aa1d64405acca1da70dcaa..61ded29bd2c21f78b3fd5509555206180aa45feb 100644
--- a/tests/src/test_scene_loader.cpp
+++ b/tests/src/test_scene_loader.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/src/test_shader.cpp b/tests/src/test_shader.cpp
index 72bccfbc2ced2635f54eba5d14e5606f701d0d70..e7f87ff0af3852cefe14bb4baf9159f19767f295 100644
--- a/tests/src/test_shader.cpp
+++ b/tests/src/test_shader.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -33,6 +33,7 @@ SUPPRESS_WARNINGS_END
 #include "catch/catch.hpp"
 
 #include "phx/resource_manager.hpp"
+#include "phx/resource_utils.hpp"
 #include "phx/shader_program.hpp"
 
 #include "mocks/opengl_mock.hpp"
@@ -58,9 +59,8 @@ SCENARIO("We can load shaders.", "[phx][phx::Shader]") {
         REQUIRE_CALL(open_gl_mock, glCompileShader(1u));
         REQUIRE_CALL(open_gl_mock, glAttachShader(1u, 1u));
 
-        auto fragment_proxy = phx::ResourceManager::instance().DeclareResource(
-            "shader/test.frag");
-        fragment_proxy->Load();
+        auto fragment_proxy =
+            phx::ResourceUtils::LoadResourceFromFile("shader/test.frag");
 
         bool success = shader_program.SetShaderProxy(
             phx::ShaderProgram::FRAGMENT, fragment_proxy);
@@ -83,12 +83,10 @@ SCENARIO("We can load shaders.", "[phx][phx::Shader]") {
             .SIDE_EFFECT(*_3 = 1);
         ALLOW_CALL(open_gl_mock, glCreateShader(_)).RETURN(1u);
 
-        auto vertex_proxy = phx::ResourceManager::instance().DeclareResource(
-            "shader/phong.vert");
-        vertex_proxy->Load();
-        auto fragment_proxy = phx::ResourceManager::instance().DeclareResource(
-            "shader/phong.frag");
-        fragment_proxy->Load();
+        auto vertex_proxy =
+            phx::ResourceUtils::LoadResourceFromFile("shader/phong.vert");
+        auto fragment_proxy =
+            phx::ResourceUtils::LoadResourceFromFile("shader/phong.frag");
 
         bool success = shader_program.SetShaderProxy(phx::ShaderProgram::VERTEX,
                                                      vertex_proxy);
@@ -110,11 +108,9 @@ SCENARIO("Shader Programs link themselves if they should be binded.",
     phx::ShaderProgram shader_program;
 
     auto vertex_proxy =
-        phx::ResourceManager::instance().DeclareResource("shader/phong.vert");
-    vertex_proxy->Load();
+        phx::ResourceUtils::LoadResourceFromFile("shader/phong.vert");
     auto fragment_proxy =
-        phx::ResourceManager::instance().DeclareResource("shader/phong.frag");
-    fragment_proxy->Load();
+        phx::ResourceUtils::LoadResourceFromFile("shader/phong.frag");
 
     bool success =
         shader_program.SetShaderProxy(phx::ShaderProgram::VERTEX, vertex_proxy);
diff --git a/tests/src/test_tracking_system.cpp b/tests/src/test_tracking_system.cpp
index 020144909774aee5df60f5ec3154a72bfc420010..ea6f58a16e8e944ec443a99d475d82d88192aeff 100644
--- a/tests/src/test_tracking_system.cpp
+++ b/tests/src/test_tracking_system.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -34,10 +34,10 @@ SUPPRESS_WARNINGS_END
 #include "phx/display_system.hpp"
 #include "phx/entity.hpp"
 #include "phx/projection.hpp"
+#include "phx/runtime_component.hpp"
 #include "phx/scene.hpp"
 #include "phx/tracking_system.hpp"
 #include "phx/transform.hpp"
-#include "phx/virtual_platform.hpp"
 
 #include "test_utilities/glm_mat4.hpp"
 
@@ -68,19 +68,21 @@ SCENARIO(
 
   GIVEN(
       "The display system has an HMD and the engine has a scene with a "
-      "virtual platform.") {
+      "user platform.") {
     display_system->CreateHMD();
     auto scene = std::make_shared<phx::Scene>();
     engine.SetScene(scene);
     auto platform_trans_mat =
         glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
                   1.0f, 0.0f, 1.0f, -1.0f, 0.5f, 1.0f);
-    scene->GetEntitiesWithComponents<phx::VirtualPlatform>()[0]
+    scene
+        ->GetEntitiesWithComponents<
+            phx::RuntimeComponent<phx::USER_PLATFORM>>()[0]
         ->GetFirstComponent<phx::Transform>()
         ->SetLocalMatrix(platform_trans_mat);
     WHEN("A tracking system is created and initialized.") {
-      auto tracking_system = engine.CreateSystem<phx::TrackingSystem>();
-      tracking_system->Initialize();
+      auto tracking_system =
+          engine.CreateSystem<phx::TrackingSystem>(display_system);
       THEN("The HMD and the user's eyes are represented in the scene.") {
         auto entities = scene->GetEntitiesWithComponents<phx::Transform>();
         bool platform_present = false;
@@ -90,13 +92,14 @@ SCENARIO(
         bool left_controller_present = false;
         bool right_controller_present = false;
         for (auto entity : entities) {
-          if (entity->GetFirstComponent<phx::VirtualPlatform>()) {
+          if (entity->GetFirstComponent<
+                  phx::RuntimeComponent<phx::USER_PLATFORM>>()) {
             THEN("There is only one virtual platform.") {
               REQUIRE(platform_present == false);
             }
             platform_present = true;
           }
-          if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_HMD) {
+          if (entity->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()) {
             THEN("There is only one HMD.") { REQUIRE(hmd_present == false); }
             THEN("The HMD has a transform component.") {
               REQUIRE(entity->GetFirstComponent<phx::Transform>());
@@ -110,11 +113,13 @@ SCENARIO(
               REQUIRE(entity->GetFirstComponent<phx::Transform>()
                           ->GetParent()
                           ->GetEntity()
-                          ->GetFirstComponent<phx::VirtualPlatform>());
+                          ->GetFirstComponent<
+                              phx::RuntimeComponent<phx::USER_PLATFORM>>());
             }
             hmd_present = true;
           }
-          if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_EYE_LEFT) {
+          if (entity
+                  ->GetFirstComponent<phx::RuntimeComponent<phx::LEFT_EYE>>()) {
             THEN("There is only one left eye.") {
               REQUIRE(left_eye_present == false);
             }
@@ -130,14 +135,16 @@ SCENARIO(
               REQUIRE(
                   entity->GetFirstComponent<phx::Transform>()->GetParent() !=
                   nullptr);
-              REQUIRE(entity->GetFirstComponent<phx::Transform>()
-                          ->GetParent()
-                          ->GetEntity()
-                          ->GetName() == phx::RUNTIME_ENTITY_NAME_HMD);
+              REQUIRE(
+                  entity->GetFirstComponent<phx::Transform>()
+                      ->GetParent()
+                      ->GetEntity()
+                      ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>());
             }
             left_eye_present = true;
           }
-          if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_EYE_RIGHT) {
+          if (entity->GetFirstComponent<
+                  phx::RuntimeComponent<phx::RIGHT_EYE>>()) {
             THEN("There is only one right eye.") {
               REQUIRE(right_eye_present == false);
             }
@@ -153,15 +160,17 @@ SCENARIO(
               REQUIRE(
                   entity->GetFirstComponent<phx::Transform>()->GetParent() !=
                   nullptr);
-              REQUIRE(entity->GetFirstComponent<phx::Transform>()
-                          ->GetParent()
-                          ->GetEntity()
-                          ->GetName() == phx::RUNTIME_ENTITY_NAME_HMD);
+              REQUIRE(
+                  entity->GetFirstComponent<phx::Transform>()
+                      ->GetParent()
+                      ->GetEntity()
+                      ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>());
             }
             right_eye_present = true;
           }
 
-          if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_CONTROLLER_LEFT) {
+          if (entity->GetFirstComponent<
+                  phx::RuntimeComponent<phx::LEFT_CONTROLLER>>()) {
             THEN("There is only one left controller.") {
               REQUIRE(left_controller_present == false);
             }
@@ -177,11 +186,13 @@ SCENARIO(
               REQUIRE(entity->GetFirstComponent<phx::Transform>()
                           ->GetParent()
                           ->GetEntity()
-                          ->GetFirstComponent<phx::VirtualPlatform>());
+                          ->GetFirstComponent<
+                              phx::RuntimeComponent<phx::USER_PLATFORM>>());
             }
             left_controller_present = true;
           }
-          if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_CONTROLLER_RIGHT) {
+          if (entity->GetFirstComponent<
+                  phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()) {
             THEN("There is only one right controller.") {
               REQUIRE(right_controller_present == false);
             }
@@ -197,7 +208,8 @@ SCENARIO(
               REQUIRE(entity->GetFirstComponent<phx::Transform>()
                           ->GetParent()
                           ->GetEntity()
-                          ->GetFirstComponent<phx::VirtualPlatform>());
+                          ->GetFirstComponent<
+                              phx::RuntimeComponent<phx::USER_PLATFORM>>());
             }
             right_controller_present = true;
           }
@@ -242,7 +254,8 @@ SCENARIO(
           bool left_controller_present = false;
           bool right_controller_present = false;
           for (auto entity : entities) {
-            if (entity->GetFirstComponent<phx::VirtualPlatform>()) {
+            if (entity->GetFirstComponent<
+                    phx::RuntimeComponent<phx::USER_PLATFORM>>()) {
               THEN("The platform transformation did not change.") {
                 REQUIRE(test_utilities::Approx<glm::mat4>(
                             entity->GetFirstComponent<phx::Transform>()
@@ -250,7 +263,7 @@ SCENARIO(
               }
               platform_present = true;
             }
-            if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_HMD) {
+            if (entity->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()) {
               THEN(
                   "The hmd transformation changes to the one provided by "
                   "OpenVR.") {
@@ -260,7 +273,8 @@ SCENARIO(
               }
               hmd_present = true;
             }
-            if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_EYE_LEFT) {
+            if (entity->GetFirstComponent<
+                    phx::RuntimeComponent<phx::LEFT_EYE>>()) {
               THEN(
                   "The left eye transformation changes to the one provided "
                   "by OpenVR.") {
@@ -270,7 +284,8 @@ SCENARIO(
               }
               left_eye_present = true;
             }
-            if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_EYE_RIGHT) {
+            if (entity->GetFirstComponent<
+                    phx::RuntimeComponent<phx::RIGHT_EYE>>()) {
               THEN(
                   "The right eye transformation changes to the one provided "
                   "by OpenVR.") {
@@ -280,11 +295,12 @@ SCENARIO(
               }
               right_eye_present = true;
             }
-            if (entity->GetName() == phx::RUNTIME_ENTITY_NAME_CONTROLLER_LEFT) {
+            if (entity->GetFirstComponent<
+                    phx::RuntimeComponent<phx::LEFT_CONTROLLER>>()) {
               left_controller_present = true;
             }
-            if (entity->GetName() ==
-                phx::RUNTIME_ENTITY_NAME_CONTROLLER_RIGHT) {
+            if (entity->GetFirstComponent<
+                    phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()) {
               right_controller_present = true;
             }
           }
diff --git a/tests/src/test_virtual_platform.cpp b/tests/src/test_virtual_platform.cpp
deleted file mode 100644
index 5e5a940e83748142135bed700678ba21b2fcefb9..0000000000000000000000000000000000000000
--- a/tests/src/test_virtual_platform.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-//------------------------------------------------------------------------------
-// Project Phoenix
-//
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
-//------------------------------------------------------------------------------
-//                                 License
-//
-// Licensed under the 3-Clause BSD License (the "License");
-// you may not use this file except in compliance with the License.
-// See the file LICENSE for the full text.
-// You may obtain a copy of the License at
-//
-//     https://opensource.org/licenses/BSD-3-Clause
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//------------------------------------------------------------------------------
-
-#include "catch/catch.hpp"
-
-#include "phx/virtual_platform.hpp"
-
-SCENARIO("The virtual platform represents the reference frame for the user.",
-         "[phx][phx::VirtualPlatform]") {
-  GIVEN("A virtual platform component.") { phx::VirtualPlatform platform; }
-}
diff --git a/tests/src/tests.cpp b/tests/src/tests.cpp
index fe328dea4695b2615b441d5e88b8221f79414797..470c22fbbdaf5a1a78617debe061cf17ac89d6bb 100644
--- a/tests/src/tests.cpp
+++ b/tests/src/tests.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/test_utilities/approx.hpp b/tests/test_utilities/approx.hpp
index cdb20f8c4e6c3ffa36b86c3bd1b3339aa1be052e..16f031df651358ce770b8c74690a4f4ee00a4dcb 100644
--- a/tests/test_utilities/approx.hpp
+++ b/tests/test_utilities/approx.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/test_utilities/dummy_material_generator.cpp b/tests/test_utilities/dummy_material_generator.cpp
index fadd5ad70ed291d17984f51275bcd972b6a9fc52..9c3140db47361bbd25dc3a989e89a18fb4ad2923 100644
--- a/tests/test_utilities/dummy_material_generator.cpp
+++ b/tests/test_utilities/dummy_material_generator.cpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
@@ -42,9 +42,7 @@ DummyMaterialGenerator::DummyMaterialGenerator(glm::vec3 diffuse_color,
 }
 
 std::unique_ptr<phx::Resource> DummyMaterialGenerator::Load(
-    const ResourceDeclaration &declaration) {
-  auto dummy_declaration =
-      declaration;  // do this only to mitigate "unused parameter" error!
+    const ResourceDeclaration&) {
   auto new_material = std::make_unique<phx::Material>();
   *new_material = *material_;
   return new_material;
diff --git a/tests/test_utilities/dummy_material_generator.hpp b/tests/test_utilities/dummy_material_generator.hpp
index 60750209ae140a5194d55c697b9e0ea655b626cd..d3522def0d021b21f42a48607cf696937819a269 100644
--- a/tests/test_utilities/dummy_material_generator.hpp
+++ b/tests/test_utilities/dummy_material_generator.hpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
diff --git a/tests/test_utilities/dummy_mesh_generator.cpp b/tests/test_utilities/dummy_mesh_generator.cpp
index 26a8a58d778e1b1f7d1842627b09c021100af225..ba4e062170120ba0aae1f3a36de3d55a06c7e760 100644
--- a/tests/test_utilities/dummy_mesh_generator.cpp
+++ b/tests/test_utilities/dummy_mesh_generator.cpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
@@ -42,9 +42,7 @@ DummyMeshLoader::DummyMeshLoader(std::vector<glm::vec3> &&vertices,
 }
 
 std::unique_ptr<phx::Resource> DummyMeshLoader::Load(
-    const ResourceDeclaration &declaration) {
-  auto dummy_declaration =
-      declaration;  // do this only to mitigate "unused parameter" error!
+    const ResourceDeclaration &) {
   auto new_mesh = std::make_unique<phx::Mesh>();
   *new_mesh = *mesh_;
   return new_mesh;
diff --git a/tests/test_utilities/dummy_mesh_generator.hpp b/tests/test_utilities/dummy_mesh_generator.hpp
index 2f05c5ae822d1d6c949151ece938f4ff1d983ae8..4ddf397cacdf3e8e83494f392fde3b332c628cc0 100644
--- a/tests/test_utilities/dummy_mesh_generator.hpp
+++ b/tests/test_utilities/dummy_mesh_generator.hpp
@@ -1,8 +1,8 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
-// Virtual Reality & Immersive Visualisation Group.
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
 //
diff --git a/tests/test_utilities/glm_mat4.hpp b/tests/test_utilities/glm_mat4.hpp
index 26446c8d1c56a60ed86cc62f12c8f43d8909076e..3a0428365a52c6ab0e2e6c881bfb64a763b27e6c 100644
--- a/tests/test_utilities/glm_mat4.hpp
+++ b/tests/test_utilities/glm_mat4.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/test_utilities/glm_quat.hpp b/tests/test_utilities/glm_quat.hpp
index b7ece2750655e7d9ffca4f63088a70d20758490c..6d3d385ad4271744dc3e7e4034718366bc417b57 100644
--- a/tests/test_utilities/glm_quat.hpp
+++ b/tests/test_utilities/glm_quat.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/test_utilities/glm_vec3.hpp b/tests/test_utilities/glm_vec3.hpp
index 55456b0b57ff7e06196339a2478685595fc73b77..70415bcd3a383bb6e1ddd3feee145d42dd448660 100644
--- a/tests/test_utilities/glm_vec3.hpp
+++ b/tests/test_utilities/glm_vec3.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/test_utilities/log_capture.cpp b/tests/test_utilities/log_capture.cpp
index 19e574590e4cc58a53b6f62982beb67b2636b4f2..e349c7835eb465d1c6224541b90f600013406981 100644
--- a/tests/test_utilities/log_capture.cpp
+++ b/tests/test_utilities/log_capture.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/test_utilities/log_capture.hpp b/tests/test_utilities/log_capture.hpp
index 2b9f75ea8e40658d2fe8b75a7d22f8414c10d2c8..53a258f406e908e9c181fd8c5ee3414faccc2d17 100644
--- a/tests/test_utilities/log_capture.hpp
+++ b/tests/test_utilities/log_capture.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/test_utilities/opengl_buffer_data_comparison.cpp b/tests/test_utilities/opengl_buffer_data_comparison.cpp
index bc6bca932ac3fe05d95cfadc293674d38dd51409..2f64ad4cd8e28b8473b2c297d38dc45fb9019ae6 100644
--- a/tests/test_utilities/opengl_buffer_data_comparison.cpp
+++ b/tests/test_utilities/opengl_buffer_data_comparison.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -30,12 +30,12 @@ namespace test_utilities {
 
 std::string OpenGLBufferComparison::GetFormatString(phx::Image* image) {
   if (image == nullptr) return "";
-  switch (image->GetBytesPerPixel()) {
-    case 1:
+  switch (image->GetBitsPerPixel()) {
+    case 8:
       return "Byte";
-    case 3:
+    case 24:
       return "RGB";
-    case 4:
+    case 32:
       return "RGBA";
     default:
       return "Unknown format";
diff --git a/tests/test_utilities/opengl_buffer_data_comparison.hpp b/tests/test_utilities/opengl_buffer_data_comparison.hpp
index 1fe026e9393765e7fd96ffc1ecfbc27c614f82e1..eed037ce414255f39e7d0c8f2c79a10377453a42 100644
--- a/tests/test_utilities/opengl_buffer_data_comparison.hpp
+++ b/tests/test_utilities/opengl_buffer_data_comparison.hpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -30,6 +30,7 @@
 #include <memory>
 #include <random>
 #include <sstream>
+#include <stdexcept>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -38,6 +39,7 @@
 
 #include "phx/image.hpp"
 #include "phx/opengl_image_buffer_data.hpp"
+#include "phx/resource_manager.hpp"
 
 #include "test_utilities/reference_image_path.hpp"
 
@@ -60,7 +62,7 @@ class OpenGLBufferComparison {
   static void REQUIRE_REFERENCE_IMAGE_SIMILARITY(
       const phx::OpenGLImageBufferData<T>& buffer_test,
       const std::string& filename_reference_image,
-      double minimumSimilarity /*= 1.0*/);
+      double minimumSimilarity = 1.0, unsigned int bit_format = 32);
 
  private:
   template <typename T>
@@ -205,14 +207,24 @@ template <typename T>
 void OpenGLBufferComparison::REQUIRE_REFERENCE_IMAGE_SIMILARITY(
     const phx::OpenGLImageBufferData<T>& buffer_test,
     const std::string& filename_reference_image,
-    double minimumSimilarity /*= 1.0*/) {
+    double minimumSimilarity /*= 1.0*/, unsigned int bit_format /*= 32*/) {
   double similarity = -std::numeric_limits<double>::infinity();
 
   std::string filename_with_path =
       test_utilities::reference_image_root + filename_reference_image;
 
-  auto ref_image = phx::Image::Load(filename_with_path);
-  similarity = ComputeSimilarity(buffer_test, ref_image.get());
+  auto img_proxy = phx::ResourceManager::instance().DeclareResource(
+      phx::ResourceUtils::DeclarationFromFile(
+          filename_with_path, {{"bit_format", bit_format}}, true));
+
+  phx::Image* ref_image = nullptr;
+  try {
+    img_proxy->Load();
+    ref_image = img_proxy->GetAs<phx::Image>();
+  } catch (const std::exception&) {
+  }
+
+  similarity = ComputeSimilarity(buffer_test, ref_image);
 
   if (similarity < minimumSimilarity) {
     std::unique_ptr<
@@ -220,22 +232,21 @@ void OpenGLBufferComparison::REQUIRE_REFERENCE_IMAGE_SIMILARITY(
         buffer_diff = nullptr;
     if (ref_image != nullptr) {
       auto buffer_ref =
-          phx::OpenGLImageBufferData<T>::CreateFromImage(ref_image.get());
+          phx::OpenGLImageBufferData<T>::CreateFromImage(ref_image);
       buffer_diff =
           phx::OpenGLImageBufferData<T>::CreateDifferenceMagnitudeBuffer(
               buffer_test, *buffer_ref.get());
     }
 
     auto filenames =
-        SaveTemporaryFiles(buffer_test, ref_image.get(), buffer_diff.get());
+        SaveTemporaryFiles(buffer_test, ref_image, buffer_diff.get());
     std::string file1 = std::get<0>(filenames);
     std::string file2 = std::get<1>(filenames);
     std::string file_diff = std::get<2>(filenames);
 
-    OutputFailMessage(buffer_test, filename_with_path, similarity,
-                      ref_image.get());
+    OutputFailMessage(buffer_test, filename_with_path, similarity, ref_image);
     if (ref_image != nullptr) {
-      OutputComparisonInfo(minimumSimilarity, similarity, ref_image.get(),
+      OutputComparisonInfo(minimumSimilarity, similarity, ref_image,
                            buffer_diff.get(), file1, file2, file_diff);
     } else {
       OutputRefImageMissingInfo(file1);
@@ -351,10 +362,11 @@ void OpenGLBufferComparison::OutputFailMessage(
             << "has been compared against a reference image path" << std::endl
             << "     " << filename_reference_image << std::endl
             << "     ("
-            << (similarity < 0 ? "file does not exist"
-                               : std::to_string(ref_image->GetWidth()) + " x " +
-                                     std::to_string(ref_image->GetHeight()) +
-                                     ", " + GetFormatString(ref_image))
+            << (similarity < 0
+                    ? "file does not exist"
+                    : std::to_string(ref_image->GetDimensions()[0]) + " x " +
+                          std::to_string(ref_image->GetDimensions()[1]) + ", " +
+                          GetFormatString(ref_image))
             << ")" << std::endl
             << std::endl;
 }
diff --git a/tests/test_utilities/tests/src/test_log_capture.cpp b/tests/test_utilities/tests/src/test_log_capture.cpp
index e9f3e7124c3b82b3490846e3c0bd7fb992025738..4202c170e8e0d4346c08e55234c3f3b66abbd1c8 100644
--- a/tests/test_utilities/tests/src/test_log_capture.cpp
+++ b/tests/test_utilities/tests/src/test_log_capture.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
diff --git a/tests/test_utilities/tests/src/test_reference_images.cpp b/tests/test_utilities/tests/src/test_reference_images.cpp
index 98e5ba7217cf8f073d9e32b06c6002c1e829695a..c6b3a2236bfb8e9db0127989c611fe13d981d975 100644
--- a/tests/test_utilities/tests/src/test_reference_images.cpp
+++ b/tests/test_utilities/tests/src/test_reference_images.cpp
@@ -1,7 +1,7 @@
 //------------------------------------------------------------------------------
 // Project Phoenix
 //
-// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 // Virtual Reality & Immersive Visualization Group.
 //------------------------------------------------------------------------------
 //                                 License
@@ -20,6 +20,7 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
+#include <cstddef>
 #include <cstdio>
 #include <limits>
 #include <memory>
@@ -28,6 +29,8 @@
 #include "catch/catch.hpp"
 
 #include "phx/opengl_image_buffer_data.hpp"
+#include "phx/resource_manager.hpp"
+#include "phx/resource_utils.hpp"
 
 #include "test_utilities/opengl_buffer_data_comparison.hpp"
 #include "test_utilities/reference_image_path.hpp"
@@ -36,9 +39,8 @@ std::unique_ptr<phx::Image> CreateCircularGradientImage(std::size_t width,
                                                         std::size_t height);
 std::unique_ptr<phx::Image> CreateCircularGradientImage(std::size_t width,
                                                         std::size_t height) {
-  auto image = phx::Image::CreateEmptyImage(width, height, 3);
-  auto image_typed =
-      static_cast<phx::ImageTyped<phx::ImageFormatRGB>*>(image.get());
+  auto image = std::make_unique<phx::Image>(
+      std::array<std::size_t, 2>{{width, height}}, 24);
   const float center_x = static_cast<float>(width) * 0.5f;
   const float center_y = static_cast<float>(height) * 0.5f;
   const float max_dist =
@@ -53,7 +55,7 @@ std::unique_ptr<phx::Image> CreateCircularGradientImage(std::size_t width,
       float color = 1.f - (dist_to_center / max_dist);
       unsigned char color_byte =
           static_cast<unsigned char>(std::round(color * 255.f));
-      image_typed->SetPixel(x, y, {color_byte, color_byte, color_byte});
+      image->SetPixelColor({{x, y}}, {{color_byte, color_byte, color_byte}});
     }
   }
   return image;
@@ -112,11 +114,14 @@ SCENARIO(
         THEN(
             "If they accept, the reference image now exists and is identical "
             "to the buffer") {
-          auto test_ref_image = phx::Image::Load(filename_with_path);
+          auto img_proxy = phx::ResourceUtils::LoadResourceFromFile(
+              filename_with_path, {}, true);
+          auto test_ref_image = img_proxy->GetAs<phx::Image>();
+
           REQUIRE(test_ref_image != nullptr);
           auto test_ref_image_buffer =
               phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>::
-                  CreateFromImage(test_ref_image.get());
+                  CreateFromImage(test_ref_image);
           test_utilities::OpenGLBufferComparison::REQUIRE_SIMILARITY(
               buffer, *test_ref_image_buffer.get(), 1.0);
 
@@ -151,11 +156,13 @@ SCENARIO(
         THEN(
             "If they accept, the reference image now exists and is identical "
             "to the buffer") {
-          auto test_ref_image = phx::Image::Load(filename_with_path);
+          auto img_proxy = phx::ResourceUtils::LoadResourceFromFile(
+              filename_with_path, {}, true);
+          auto test_ref_image = img_proxy->GetAs<phx::Image>();
           REQUIRE(test_ref_image != nullptr);
           auto test_ref_image_buffer =
               phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>::
-                  CreateFromImage(test_ref_image.get());
+                  CreateFromImage(test_ref_image);
           test_utilities::OpenGLBufferComparison::REQUIRE_SIMILARITY(
               buffer, *test_ref_image_buffer.get(), 1.0);