diff --git a/.gitattributes b/.gitattributes
index 44b7e7df6e16968f484ac85f7825c490e95212e7..ab4bad912a5118f8cfc7104e9fbbb47094d204cf 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -21,4 +21,6 @@ README.md 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
+*.MTL filter=lfs diff=lfs merge=lfs -text
+*.stl filter=lfs diff=lfs merge=lfs -text
+*.tga filter=lfs diff=lfs merge=lfs -text
diff --git a/.gitignore b/.gitignore
index 1bca81dc46774127805661e5efe50517b113d6c6..e94c1603938d51c06722f2be0cf75d58c4aabe88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ BUILD*
 *.pyc
 x64/
 TAGS
+dev_*/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0cff5604add6f8058e9a2d2398ea0540b0142e0d..fb793833316cc4a6c99c10ac6483c31799b9b8c8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -30,7 +30,7 @@ stages:
 #  Windows
 #-------------------------------------------------------------------------------
 
-# casual builds
+# common builds
 
 conan:windows:msvc++14.1:
   stage: conan
@@ -188,91 +188,157 @@ test:windows_nightly:msvc++14.1:
 #  Linux
 #-------------------------------------------------------------------------------
 
-# casual builds
+# clang
 
-conan:linux:gcc5.3.1:
+# common build
+
+conan:linux:clang:
   stage: conan
   except:
     - schedules
   tags:
-    - gcc5.3.1
+    - CentOS
     - opengl
+    - clang
   script:
     - mkdir build
     - cd build
-    - export CC=gcc
-    - export CXX=g++
-    - conan install .. --build=missing -s compiler="gcc" -s compiler.version="5.3" -s compiler.libcxx="libstdc++11" -s build_type=Release
+    - export CC=clang
+    - export CXX=clang++
+    - conan install .. --build=missing -s compiler="clang" -s compiler.version="4.0" -s compiler.libcxx="libstdc++11" -s build_type=Release
   artifacts:
     paths:
-    - build
-    expire_in: 1 week
+      - build
+    expire_in: 1 week      
+
+cmake:linux:clang:
+  stage: cmake
+  except:
+    - schedules
+  tags:
+    - CentOS
+    - opengl
+    - clang
+  dependencies:
+    - conan:linux:clang
+  script:
+    - cd build
+    - export CC=clang
+    - export CXX=clang++
+    - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release     
+  artifacts:
+    untracked: true
+    paths:
+      - build
+    expire_in: 1 week    
     
-conan:linux:gcc6.3.1:
-  stage: conan
+build:linux:clang:
+  stage: build
   except:
     - schedules
   tags:
-    - gcc6.3.1
+    - CentOS
     - opengl
+    - clang
+  dependencies:
+    - cmake:linux:clang
   script:
-    - mkdir build
     - cd build
-    - export CC=gcc
-    - export CXX=g++
-    - conan install .. --build=missing -s compiler="gcc" -s compiler.version="6.3" -s compiler.libcxx="libstdc++11" -s build_type=Release
+    - export CC=clang
+    - export CXX=clang++
+    - make Cpplint-Test-Suite
+    - make Cppcheck-Test-Suite
+    - make -j8
   artifacts:
     paths:
     - build
     expire_in: 1 week
+  allow_failure: true 
 
-cmake:linux:gcc5.3.1:
-  stage: cmake
+test:linux:clang:
+  stage: test
   except:
     - schedules
   tags:
-    - gcc5.3.1
+    - CentOS
     - opengl
+    - clang
   dependencies:
-    - conan:linux:gcc5.3.1
+    - build:linux:clang
   script:
     - cd build
-    - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
+    - export CC=clang
+    - export CXX=clang++
+    - make Unit-Test-Suite  
+
+# nightly build
+
+clear:linux_nightly:clang:
+  stage: clear
+  only:
+    - schedules
+  tags:
+    - nightly
+    - clang
+  script:
+    - echo $CONAN_USER_HOME
+    - cd /home/gitlab-runner/dev
+    - rm -rf ./conan_nightly_clang
+    - mkdir conan_nightly_clang
+
+conan:linux_nightly:clang:
+  stage: conan
+  only:
+    - schedules
+  tags:
+    - nightly
+    - clang
+  script:
+    - conan remote add rwth-vr--bintray https://api.bintray.com/conan/rwth-vr/conan
+    - conan remote add bincrafters_public https://api.bintray.com/conan/bincrafters/public-conan
+    - mkdir build
+    - cd build
+    - export CC=clang
+    - export CXX=clang++
+    - conan install .. --build=missing -s compiler="clang" -s compiler.version="4.0" -s compiler.libcxx="libstdc++11" -s build_type=Release
   artifacts:
-    untracked: true
     paths:
-    - build
-    expire_in: 1 week
+      - build
+    expire_in: 1 week      
 
-cmake:linux:gcc6.3.1:
+cmake:linux_nightly:clang:
   stage: cmake
-  except:
+  only:
     - schedules
   tags:
-    - gcc6.3.1
-    - opengl
+    - nightly
+    - clang
   dependencies:
-    - conan:linux:gcc6.3.1
+    - conan:linux_nightly:clang
   script:
     - cd build
-    - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
+    - export CC=clang
+    - export CXX=clang++
+    - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release     
   artifacts:
     untracked: true
     paths:
-    - build
-    expire_in: 1 week
-
-build:linux:gcc5.3.1:
+      - build
+    expire_in: 1 week    
+    
+build:linux_nightly:clang:
   stage: build
-  except:
+  only:
     - schedules
   tags:
-    - gcc5.3.1
-    - opengl
+    - nightly
+    - clang
   dependencies:
-    - cmake:linux:gcc5.3.1
+    - cmake:linux_nightly:clang
   script:
     - cd build
+    - export CC=clang
+    - export CXX=clang++
     - make Cpplint-Test-Suite
     - make Cppcheck-Test-Suite
     - make -j8
@@ -280,54 +346,235 @@ build:linux:gcc5.3.1:
     paths:
     - build
     expire_in: 1 week
-  allow_failure: true
+  allow_failure: true 
 
-build:linux:gcc6.3.1:
-  stage: build
+test:linux_nightly:clang:
+  stage: test
+  only:
+    - schedules
+  tags:
+    - nightly
+    - clang
+  dependencies:
+    - build:linux_nightly:clang
+  script:
+    - cd build
+    - export CC=clang
+    - export CXX=clang++
+    - make Unit-Test-Suite
+    
+# gcc
+
+# common builds
+
+# conan:linux:gcc5.3.1:
+  # stage: conan
+  # except:
+    # - schedules
+  # tags:
+    # - gcc5.3.1
+    # - opengl
+  # script:
+    # - mkdir build
+    # - cd build
+    # - export CC=gcc
+    # - export CXX=g++
+    # - conan install .. --build=missing -s compiler="gcc" -s compiler.version="5.3" -s compiler.libcxx="libstdc++11" -s build_type=Release
+  # artifacts:
+    # paths:
+    # - build
+    # expire_in: 1 week
+    
+# conan:linux:gcc6.3.1:
+  # stage: conan
+  # except:
+    # - schedules
+  # tags:
+    # - gcc6.3.1
+    # - opengl
+  # script:
+    # - mkdir build
+    # - cd build
+    # - export CC=gcc
+    # - export CXX=g++
+    # - conan install .. --build=missing -s compiler="gcc" -s compiler.version="6.3" -s compiler.libcxx="libstdc++11" -s build_type=Release
+  # artifacts:
+    # paths:
+    # - build
+    # expire_in: 1 week
+
+conan:linux:gcc7.3.1:
+  stage: conan
   except:
     - schedules
   tags:
-    - gcc6.3.1
+    - gcc7.3.1
+    - opengl
+  script:
+    - mkdir build
+    - cd build
+    - export CC=gcc
+    - export CXX=g++
+    - conan install .. --build=missing -s compiler="gcc" -s compiler.version="7.3" -s compiler.libcxx="libstdc++11" -s build_type=Release
+  artifacts:
+    paths:
+    - build
+    expire_in: 1 week    
+    
+# cmake:linux:gcc5.3.1:
+  # stage: cmake
+  # except:
+    # - schedules
+  # tags:
+    # - gcc5.3.1
+    # - opengl
+  # dependencies:
+    # - conan:linux:gcc5.3.1
+  # script:
+    # - cd build
+    # - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
+  # artifacts:
+    # untracked: true
+    # paths:
+    # - build
+    # expire_in: 1 week
+
+# cmake:linux:gcc6.3.1:
+  # stage: cmake
+  # except:
+    # - schedules
+  # tags:
+    # - gcc6.3.1
+    # - opengl
+  # dependencies:
+    # - conan:linux:gcc6.3.1
+  # script:
+    # - cd build
+    # - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
+  # artifacts:
+    # untracked: true
+    # paths:
+    # - build
+    # expire_in: 1 week
+
+cmake:linux:gcc7.3.1:
+  stage: cmake
+  except:
+    - schedules
+  tags:
+    - gcc7.3.1
     - opengl
   dependencies:
-    - cmake:linux:gcc6.3.1
+    - conan:linux:gcc7.3.1
   script:
     - cd build
-    - make Cpplint-Test-Suite
-    - make Cppcheck-Test-Suite
-    - make -j8
+    - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
   artifacts:
+    untracked: true
     paths:
     - build
-    expire_in: 1 week
-  allow_failure: true
+    expire_in: 1 week    
+    
+# build:linux:gcc5.3.1:
+  # stage: build
+  # except:
+    # - schedules
+  # tags:
+    # - gcc5.3.1
+    # - opengl
+  # dependencies:
+    # - cmake:linux:gcc5.3.1
+  # script:
+    # - cd build
+    # - make Cpplint-Test-Suite
+    # - make Cppcheck-Test-Suite
+    # - make -j8
+  # artifacts:
+    # paths:
+    # - build
+    # expire_in: 1 week
+  # allow_failure: true
 
-test:linux:gcc5.3.1:
-  stage: test
+# build:linux:gcc6.3.1:
+  # stage: build
+  # except:
+    # - schedules
+  # tags:
+    # - gcc6.3.1
+    # - opengl
+  # dependencies:
+    # - cmake:linux:gcc6.3.1
+  # script:
+    # - cd build
+    # - make Cpplint-Test-Suite
+    # - make Cppcheck-Test-Suite
+    # - make -j8
+  # artifacts:
+    # paths:
+    # - build
+    # expire_in: 1 week
+  # allow_failure: true
+
+build:linux:gcc7.3.1:
+  stage: build
   except:
     - schedules
   tags:
-    - gcc5.3.1
+    - gcc7.3.1
     - opengl
   dependencies:
-    - build:linux:gcc5.3.1
+    - cmake:linux:gcc7.3.1
   script:
+    - gcc --version
     - cd build
-    - make Unit-Test-Suite
+    - make Cpplint-Test-Suite
+    - make Cppcheck-Test-Suite
+    - make -j8
+  artifacts:
+    paths:
+    - build
+    expire_in: 1 week
+  allow_failure: true  
+  
+# test:linux:gcc5.3.1:
+  # stage: test
+  # except:
+    # - schedules
+  # tags:
+    # - gcc5.3.1
+    # - opengl
+  # dependencies:
+    # - build:linux:gcc5.3.1
+  # script:
+    # - cd build
+    # - make Unit-Test-Suite
+
+# test:linux:gcc6.3.1:
+  # stage: test
+  # except:
+    # - schedules
+  # tags:
+    # - gcc6.3.1
+    # - opengl
+  # dependencies:
+    # - build:linux:gcc6.3.1
+  # script:
+    # - cd build
+    # - make Unit-Test-Suite
 
-test:linux:gcc6.3.1:
+test:linux:gcc7.3.1:
   stage: test
   except:
     - schedules
   tags:
-    - gcc6.3.1
+    - gcc7.3.1
     - opengl
   dependencies:
-    - build:linux:gcc6.3.1
+    - build:linux:gcc7.3.1
   script:
     - cd build
-    - make Unit-Test-Suite
-
+    - make Unit-Test-Suite    
+    
 # nightly builds
 
 clear:linux_nightly:gcc5.3.1:
@@ -355,6 +602,19 @@ clear:linux_nightly:gcc6.3.1:
     - cd /home/gitlab-runner/dev
     - rm -rf ./conan_nightly_6_3_1
     - mkdir conan_nightly_6_3_1
+ 
+clear:linux_nightly:gcc7.3.1:
+  stage: clear
+  only:
+    - schedules
+  tags:
+    - gcc7.3.1
+    - nightly
+  script:
+    - echo $CONAN_USER_HOME
+    - cd /home/gitlab-runner/dev
+    - rm -rf ./conan_nightly_7_3_1
+    - mkdir conan_nightly_7_3_1
 
 conan:linux_nightly:gcc5.3.1:
   stage: conan
@@ -396,6 +656,26 @@ conan:linux_nightly:gcc6.3.1:
     - build
     expire_in: 1 week
 
+conan:linux_nightly:gcc7.3.1:
+  stage: conan
+  only:
+    - schedules
+  tags:
+    - gcc7.3.1
+    - nightly
+  script:
+    - conan remote add rwth-vr--bintray https://api.bintray.com/conan/rwth-vr/conan
+    - conan remote add bincrafters_public https://api.bintray.com/conan/bincrafters/public-conan
+    - mkdir build
+    - cd build
+    - export CC=gcc
+    - export CXX=g++
+    - conan install .. --build=missing -s compiler="gcc" -s compiler.version="7.3" -s compiler.libcxx="libstdc++11" -s build_type=Release
+  artifacts:
+    paths:
+    - build
+    expire_in: 1 week
+    
 cmake:linux_nightly:gcc5.3.1:
   stage: cmake
   only:
@@ -432,6 +712,24 @@ cmake:linux_nightly:gcc6.3.1:
     - build
     expire_in: 1 week
 
+cmake:linux_nightly:gcc7.3.1:
+  stage: cmake
+  only:
+    - schedules
+  tags:
+    - gcc7.3.1
+    - nightly
+  dependencies:
+    - conan:linux_nightly:gcc7.3.1
+  script:
+    - cd build
+    - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
+  artifacts:
+    untracked: true
+    paths:
+    - build
+    expire_in: 1 week
+    
 build:linux_nightly:gcc5.3.1:
   stage: build
   only:
@@ -471,6 +769,26 @@ build:linux_nightly:gcc6.3.1:
     - build
     expire_in: 1 week
   allow_failure: true
+  
+build:linux_nightly:gcc7.3.1:
+  stage: build
+  only:
+    - schedules
+  tags:
+    - gcc7.3.1
+    - nightly
+  dependencies:
+    - cmake:linux_nightly:gcc7.3.1
+  script:
+    - cd build
+    - make Cpplint-Test-Suite
+    - make Cppcheck-Test-Suite
+    - make -j8
+  artifacts:
+    paths:
+    - build
+    expire_in: 1 week
+  allow_failure: true
 
 test:linux_nightly:gcc5.3.1:
   stage: test
@@ -496,4 +814,17 @@ test:linux_nightly:gcc6.3.1:
     - build:linux_nightly:gcc6.3.1
   script:
     - cd build
-    - make Unit-Test-Suite
\ No newline at end of file
+    - make Unit-Test-Suite
+    
+test:linux_nightly:gcc7.3.1:
+  stage: test
+  only:
+    - schedules
+  tags:
+    - gcc7.3.1
+    - nightly
+  dependencies:
+    - build:linux_nightly:gcc7.3.1
+  script:
+    - cd build
+    - make Unit-Test-Suite
diff --git a/README.md b/README.md
index f11bf1102f35d13fe0d395b182dd5e61a7efa8c9..7ab2218c7d663645340f5f5a9ed249da8d4e75be 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
 # Project Phoenix
 
-**Master:** [![build status](https://devhub.vr.rwth-aachen.de/VR-Group/Project_Phoenix/badges/master/build.svg)](https://devhub.vr.rwth-aachen.de/VR-Group/Project_Phoenix/commits/master)
+**Stable:** [![build status](https://devhub.vr.rwth-aachen.de/VR-Group/Project_Phoenix/badges/stable/build.svg)](https://devhub.vr.rwth-aachen.de/VR-Group/Project_Phoenix/commits/stable)
  
-**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)
+**Master:** [![build status](https://devhub.vr.rwth-aachen.de/VR-Group/Project_Phoenix/badges/master/build.svg)](https://devhub.vr.rwth-aachen.de/VR-Group/Project_Phoenix/commits/master)
 
 *Project Phoenix* is Copyright (c) 2017-2018 RWTH Aachen University, Germany,
 Virtual Reality & Immersive Visualization Group.
diff --git a/cmake/Testing.cmake b/cmake/Testing.cmake
index 36cf0f171ae76dfa442e590a7ef9c2c4beb462b2..52a327f9ba0994ac84538e5c671fcfdc1d3c9d35 100644
--- a/cmake/Testing.cmake
+++ b/cmake/Testing.cmake
@@ -28,12 +28,124 @@ include(WarningLevels)
 conan_or_find_package(catch REQUIRED)
 conan_or_find_package(trompeloeil REQUIRED)
 
+define_property(TARGET
+  PROPERTY testing__removes_libraries
+  BRIEF_DOCS "Libraries to be removed for mocking"
+  FULL_DOCS "Libraries to be removed for mocking")
+define_property(TARGET
+  PROPERTY testing__adds_libraries
+  BRIEF_DOCS "Libraries to be added for mocking"
+  FULL_DOCS "Libraries to be added for mocking")
+
 set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+set_property(GLOBAL PROPERTY TESTING__AVAILABLE_TESTS "")
+
+set(IS_BUILD_SERVER
+  FALSE CACHE BOOL
+  "Is this the build server? So we, e.g., simulate user input for tests requiring it."
+  )
+
 
-set_property(GLOBAL PROPERTY ADDED_TESTS "")
+function(CREATE_TEST_MAIN)
+  testing__add_library_(NAME test_main ${ARGV})
+  set_property(TARGET test_main PROPERTY FOLDER "Tests")
+endfunction()
+
+
+function(CREATE_MOCK_MAIN)
+  testing__add_library_(NAME mock_main ${ARGV})
+  set_property(TARGET mock_main PROPERTY FOLDER "Tests/Mocks")
+endfunction()
+
+
+function(TESTING__ADD_LIBRARY_)
+  set(options)
+  set(oneValueArgs NAME)
+  set(multiValueArgs SOURCES HEADERS INCLUDE_DIRECTORIES LIBRARIES )
+  cmake_parse_arguments(TESTING__ADD_LIBRARY_
+    "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+  add_library(${TESTING__ADD_LIBRARY__NAME}
+    ${TESTING__ADD_LIBRARY__SOURCES}
+    ${TESTING__ADD_LIBRARY__HEADERS}
+    )
+  target_include_directories(${TESTING__ADD_LIBRARY__NAME}
+    PUBLIC ${TESTING__ADD_LIBRARY__INCLUDE_DIRECTORIES}
+    )
+  target_link_libraries(${TESTING__ADD_LIBRARY__NAME}
+    ${TESTING__ADD_LIBRARY__LIBRARIES}
+    )
+  set_property(TARGET ${TESTING__ADD_LIBRARY__NAME} PROPERTY POSITION_INDEPENDENT_CODE ON)
+endfunction()
+
+
+function(ADD_TESTS)
+  set(options )
+  set(oneValueArgs NAME)
+  set(multiValueArgs
+    SOURCES HEADERS INCLUDE_DIRECTORIES LIBRARIES PATH_TO_ADD)
+  cmake_parse_arguments(ADD_TESTS_
+    "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+  foreach(TEST_SOURCE_FILE ${ADD_TESTS__SOURCES})
+    add_single_test(
+      NAME ${TEST_NAME}
+      SOURCE ${TEST_SOURCE_FILE}
+      INCLUDE_DIRECTORIES ${ADD_TESTS__INCLUDE_DIRECTORIES}
+      LIBRARIES ${ADD_TESTS__LIBRARIES}
+      PATH_TO_ADD ${ADD_TESTS__PATH_TO_ADD}
+      )
+  endforeach()
+endfunction()
+
+
+function(ADD_SINGLE_TEST)
+  set(options )
+  set(oneValueArgs)
+  set(multiValueArgs SOURCE INCLUDE_DIRECTORIES LIBRARIES PATH_TO_ADD)
+  cmake_parse_arguments(ADD_SINGLE_TEST_
+    "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
+  get_filename_component(
+    TEST_NAME
+    ${ADD_SINGLE_TEST__SOURCE} NAME_WE)
 
-function(CONFIGURE_MSVC_USERFILE TARGET_NAME PATH_TO_ADD)
+  add_executable(${TEST_NAME}
+    ${ADD_SINGLE_TEST__SOURCE}
+    )
+  target_include_directories(${TEST_NAME}
+    PRIVATE ${ADD_SINGLE_TEST__INCLUDE_DIRECTORIES}
+    )
+  target_link_libraries(${TEST_NAME}
+    ${ADD_SINGLE_TEST__LIBRARIES}
+    ${CONAN_OR_CMAKE_catch}
+    test_main mock_main
+    )
+  set_warning_levels_RWTH(${TEST_NAME})
+  if(IS_BUILD_SERVER)
+    target_compile_definitions(${TEST_NAME} PUBLIC -DIS_BUILD_SERVER)
+  endif()
+  set_property(TARGET ${TEST_NAME}
+    PROPERTY FOLDER "Tests")
+  source_group("Source Files"
+    FILES ${ADD_SINGLE_TEST__SOURCE})
+
+  if(ADD_SINGLE_TEST__PATH_TO_ADD AND WIN32 AND MSVC)
+    CONFIGURE_MSVC_USERFILE_(${TEST_NAME}
+      ${ADD_SINGLE_TEST__PATH_TO_ADD})
+  endif()
+  
+  add_test(NAME ${TEST_NAME}
+    COMMAND ${TEST_NAME}
+    )
+  testing__set_timeout_(${TEST_NAME})
+
+  set_property(GLOBAL APPEND PROPERTY
+    TESTING__AVAILABLE_TESTS "${TEST_NAME}")  
+endfunction()
+
+
+function(CONFIGURE_MSVC_USERFILE_ TARGET_NAME PATH_TO_ADD)
   file(TO_NATIVE_PATH "${PATH_TO_ADD}/Release" _DLL_PATH_RELEASE)
   file(TO_NATIVE_PATH "${PATH_TO_ADD}/Debug" _DLL_PATH_DEBUG)
   set(SOURCE_USER_FILE
@@ -43,132 +155,157 @@ function(CONFIGURE_MSVC_USERFILE TARGET_NAME PATH_TO_ADD)
   configure_file(${SOURCE_USER_FILE} ${DESTINATION_USER_FILE} @ONLY)
 endfunction()
 
-function(ADD_TEST_TARGET_INTERNAL_
-    NAME SOURCES HEADERS INCLUDE_DIRECTORIES LINK_LIBRARIES PATH_TO_ADD)
-  add_executable(${NAME} ${SOURCES} ${HEADERS})
-  target_include_directories(${NAME} PRIVATE ${INCLUDE_DIRECTORIES})
-  target_link_libraries(${NAME} ${LINK_LIBRARIES})
-  target_link_libraries(${NAME} ${CONAN_OR_CMAKE_catch})
-   
-  if(WIN32 AND MSVC)
-    CONFIGURE_MSVC_USERFILE(${NAME} ${PATH_TO_ADD})
-  endif()
-endfunction()
 
-function(ADD_TEST_CATCH_INTERNAL_ 
-    NAME SOURCES HEADERS)
-  
-  add_test(NAME ${NAME} COMMAND ${NAME})
-  if(NOT ${NAME} MATCHES "integration")
-    set_tests_properties(${NAME} PROPERTIES TIMEOUT 10.0)
+function(TESTING__SET_TIMEOUT_ TARGET)
+  if(NOT ${TARGET} MATCHES "integration")
+    set_tests_properties(${TARGET} PROPERTIES TIMEOUT 10.0)
   else()
-    set_tests_properties(${NAME} PROPERTIES TIMEOUT 120.0)
+    set_tests_properties(${TARGET} PROPERTIES TIMEOUT 120.0)
   endif()
-  set_warning_levels_RWTH(${NAME})
+endfunction()
 
-  set_property(TARGET ${NAME} PROPERTY FOLDER "Tests")
-  source_group("Source Files" FILES ${SOURCES} ${HEADERS})
-  
-  set_property(GLOBAL APPEND PROPERTY ADDED_TESTS "${NAME}")
-  
-  if(IS_BUILD_SERVER)
-    target_compile_definitions(${NAME} PUBLIC -DIS_BUILD_SERVER)
-  endif()
+
+function(ADD_MOCK)
+  set(options )
+  set(oneValueArgs NAME)
+  set(multiValueArgs
+    SOURCES
+    HEADERS
+    INCLUDE_DIRECTORIES
+    INCLUDE_DIRECTORIES_OF
+    COMPILE_OPTIONS
+    REMOVES_LIBRARIES
+    ADDS_LIBRARIES
+  )
+  cmake_parse_arguments(ADD_MOCK_
+    "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+  string(TOUPPER ${ADD_MOCK__NAME} ADD_MOCK__NAME_UPPER)
+  string(TOLOWER ${ADD_MOCK__NAME} ADD_MOCK__NAME_LOWER)
   
-endfunction()
+  add_library(${ADD_MOCK__NAME} SHARED
+    ${ADD_MOCK__SOURCES}
+    ${ADD_MOCK__HEADERS}
+    )
+  target_link_libraries(${ADD_MOCK__NAME}
+    ${CONAN_OR_CMAKE_trompeloeil}
+    mock_main test_main
+    )
+  foreach(CURRENT_TARGET ${ADD_MOCK__INCLUDE_DIRECTORIES_OF})
+    get_target_property(CURRENT_INCLUCE_DIRECTORIES
+      ${CURRENT_TARGET} INTERFACE_INCLUDE_DIRECTORIES)
+    if (CURRENT_INCLUCE_DIRECTORIES)
+      target_include_directories(${ADD_MOCK__NAME}
+	    PUBLIC ${CURRENT_INCLUCE_DIRECTORIES}
+	  )
+    endif()
+  endforeach()
+  target_include_directories(${ADD_MOCK__NAME}
+    PUBLIC ${ADD_MOCK__INCLUDE_DIRECTORIES}
+    )
+  target_compile_definitions(${ADD_MOCK__NAME}
+    PRIVATE ${ADD_MOCK__NAME_UPPER}_BUILD
+    )
+  target_compile_options(${ADD_MOCK__NAME}
+    PUBLIC ${ADD_MOCK__COMPILE_OPTIONS}
+    )
 
-function(ADD_TEST_INTERNAL_ 
-    NAME SOURCES HEADERS INCLUDE_DIRECTORIES LINK_LIBRARIES PATH_TO_ADD)
-  ADD_TEST_TARGET_INTERNAL_("${NAME}" "${SOURCES}" "${HEADERS}" "${INCLUDE_DIRECTORIES}" "${LINK_LIBRARIES}" "${PATH_TO_ADD}")
-  ADD_TEST_CATCH_INTERNAL_("${NAME}" "${SOURCES}" "${HEADERS}")
-endfunction()
+  set_property(TARGET ${ADD_MOCK__NAME} PROPERTY FOLDER "Tests/Mocks")
 
+  set_target_properties(${ADD_MOCK__NAME}
+    PROPERTIES testing__removes_libraries "${ADD_MOCK__REMOVES_LIBRARIES}")
+  set_target_properties(${ADD_MOCK__NAME}
+    PROPERTIES testing__adds_libraries "${ADD_MOCK__ADDS_LIBRARIES}")
 
+  generate_export_header(${ADD_MOCK__NAME}
+    EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/mocks/${ADD_MOCK__NAME_LOWER}_export.hpp
+    )
+  set_warning_levels_rwth(${ADD_MOCK__NAME})
+endfunction()
 
 
-function(CREATE_CATCH_MAIN_INTERNAL_
-    NAME SOURCE INCLUDE_DIRECTORIES)
-  add_library(${NAME} ${SOURCE})
-  target_include_directories(${NAME} PRIVATE ${INCLUDE_DIRECTORIES})
-  target_link_libraries(${NAME} ${CONAN_OR_CMAKE_catch} ${CONAN_OR_CMAKE_trompeloeil})
-  set_property(TARGET ${NAME} PROPERTY FOLDER "Tests")
-  source_group("Source Files" FILES ${SOURCE})
+function(AUTOREMOVE_MOCKED_TEST_SOURCE_FROM)
+  set_property(GLOBAL PROPERTY TESTING__SOURCES__AUTOREMOVE_MOCKED ${ARGN})
 endfunction()
 
+function(GET_UNMOCKED_TEST_SOURCES test_list)
+  get_property(TEST_SOURCES_LIST
+    GLOBAL PROPERTY TESTING__SOURCES__AUTOREMOVE_MOCKED) 
+  set(${test_list} ${TEST_SOURCES_LIST} PARENT_SCOPE)
+endfunction()
 
-function(ADD_TEST_CATCH)
-  # parse arguments
+function(add_mocked_test cpp_file)
   set(options )
-  set(oneValueArgs NAME CATCH_MAIN)
-  set(multiValueArgs
-    SOURCES HEADERS INCLUDE_DIRECTORIES LINK_LIBRARIES PATH_TO_ADD)
-  cmake_parse_arguments(ADD_TEST_CATCH
+  set(oneValueArgs )
+  set(multiValueArgs LIBRARIES MOCKING_LIBRARIES_OF MOCKS)
+  cmake_parse_arguments(ADD_MOCKED_TEST
     "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
-  create_catch_main_internal_("${ADD_TEST_CATCH_NAME}_catch_main"
-    ${ADD_TEST_CATCH_CATCH_MAIN}
-    ${ADD_TEST_CATCH_INCLUDE_DIRECTORIES}
-  )
-
-  # remove catch_main file from sources
-  file(GLOB ADD_TEST_CATCH_CATCH_MAIN_ABSOLUTE ${ADD_TEST_CATCH_CATCH_MAIN})
-  list(REMOVE_ITEM ADD_TEST_CATCH_SOURCES ${ADD_TEST_CATCH_CATCH_MAIN_ABSOLUTE})
-
-  # add test for each test source file
-  foreach(TEST_SOURCE_FILE ${ADD_TEST_CATCH_SOURCES})
-    get_filename_component(TEST_NAME ${TEST_SOURCE_FILE} NAME_WE)
-    ADD_TEST_INTERNAL_("${TEST_NAME}"
-      "${TEST_SOURCE_FILE}"
-      ""
-      "${ADD_TEST_CATCH_INCLUDE_DIRECTORIES}"
-      "${ADD_TEST_CATCH_LINK_LIBRARIES};${ADD_TEST_CATCH_NAME}_catch_main"
-      "${ADD_TEST_CATCH_PATH_TO_ADD}"
+ 
+  get_property(TEST_SOURCES_LIST
+    GLOBAL PROPERTY TESTING__SOURCES__AUTOREMOVE_MOCKED) 
+  list(REMOVE_ITEM TEST_SOURCES_LIST
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/${cpp_file}.cpp)
+  set_property(GLOBAL PROPERTY TESTING__SOURCES__AUTOREMOVE_MOCKED ${TEST_SOURCES_LIST})
+  
+  set(test_sources src/${cpp_file}.cpp)
+  add_single_test(
+    SOURCE ${test_sources}
     )
-  endforeach()
-endfunction()
+  add_dependencies(${cpp_file} ${ADD_MOCKED_TEST_LIBRARIES})
+
+  set(LINK_LIBRARIES )
+  foreach(CURR_TARGET ${ADD_MOCKED_TEST_LIBRARIES})
+    get_target_property(CURR_LINK_LIBRARIES ${CURR_TARGET} LINK_LIBRARIES)
+    list(APPEND LINK_LIBRARIES ${CURR_LINK_LIBRARIES})
 
-macro(EXTRACT_UNIT_AND_INTEGRATION_TEST_TARGETS)
-  get_property(added_tests GLOBAL PROPERTY ADDED_TESTS)
-  foreach(filename ${added_tests})
-  	if(${filename} MATCHES "integration_test")
-  	  message(STATUS "Integration Test: ${filename}")
-  	  set(INTEGRATION_TEST_TARGETS ${INTEGRATION_TEST_TARGETS} ${filename})
-  	else()
-  	  message(STATUS "Unit Test: ${filename}")
-  	  set(UNIT_TEST_TARGETS ${UNIT_TEST_TARGETS} ${filename})
-  	endif()
+    target_link_libraries(${cpp_file} $<TARGET_FILE:${CURR_TARGET}>)
+	
+	get_target_property(CURR_INCLUDE_DIRECTORIES
+      ${CURR_TARGET} INCLUDE_DIRECTORIES)
+    target_include_directories(${cpp_file} PRIVATE ${CURR_INCLUDE_DIRECTORIES})
+  endforeach()
+  list(REMOVE_DUPLICATES LINK_LIBRARIES)
+  #the libraries themselves must not be in LINK_LIBRARIES
+  foreach(CURR_TARGET ${ADD_MOCKED_TEST_LIBRARIES})
+    list(REMOVE_ITEM LINK_LIBRARIES ${CURR_TARGET})
   endforeach()
-endmacro()
 
-macro(ADD_TEST_SUITE)
-  set(options)
-  set(oneValueArgs NAME TEST_REGEX)
-  set(multiValueArgs DEPEND_ON_TARGETS)
-  cmake_parse_arguments(ADD_TEST_SUITE
-    "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-  #we have to escape the " for visual studio)
-  add_custom_target(${ADD_TEST_SUITE_NAME} COMMAND ctest --tests-regex \"${ADD_TEST_SUITE_TEST_REGEX}\" -C $<CONFIG>)
-   # This comment just ends the escaped signs, for VS highlighting "
-  if(ADD_TEST_SUITE_DEPEND_ON_TARGETS)
-    add_dependencies(${ADD_TEST_SUITE_NAME} ${ADD_TEST_SUITE_DEPEND_ON_TARGETS})
-  endif()
-  set_property(TARGET ${ADD_TEST_SUITE_NAME} PROPERTY FOLDER "_Test-Suites_")
-endmacro()
+  foreach(MOCK ${ADD_MOCKED_TEST_MOCKS})
+    get_target_property(LIBRARIES_TO_BE_REMOVED ${MOCK} testing__removes_libraries)
+    foreach(LIBRARY_TO_BE_REMOVED ${LIBRARIES_TO_BE_REMOVED})
+      list(REMOVE_ITEM LINK_LIBRARIES ${LIBRARY_TO_BE_REMOVED})
+    endforeach()
+    
+    get_target_property(LIBRARIES_TO_BE_ADDED ${MOCK} testing__adds_libraries)
+    foreach(LIBRARY_TO_BE_ADDED ${LIBRARIES_TO_BE_ADDED})
+      list(APPEND LINK_LIBRARIES ${LIBRARY_TO_BE_ADDED})
+    endforeach()
 
+    target_link_libraries(${cpp_file} ${MOCK})
+  endforeach()
+  
+  target_link_libraries(${cpp_file}
+    ${CONAN_OR_CMAKE_trompeloeil}
+    ${CONAN_OR_CMAKE_catch}
+    ${LINK_LIBRARIES}
+  )
+endfunction()
+#-------------------------------------------------------------------------------
 
 
 function(CREATE_TEST_SUITES)
-	EXTRACT_UNIT_AND_INTEGRATION_TEST_TARGETS()
+	testing__extract_unit_and_integration_test_targets_()
 
 	ADD_TEST_SUITE(
 	  NAME "Unit-Test-Suite"
 	  TEST_REGEX "^test"
-	  DEPEND_ON_TARGETS ${UNIT_TEST_TARGETS})
+	  DEPEND_ON_TARGETS ${TESTING__UNIT_TESTS})
 
 	ADD_TEST_SUITE(
 	  NAME "Integration-Test-Suite"
 	  TEST_REGEX "^integration"
-	  DEPEND_ON_TARGETS ${INTEGRATION_TEST_TARGETS})
+	  DEPEND_ON_TARGETS ${TESTING__INTEGRATION_TESTS})
 	
 	ADD_TEST_SUITE(
 	  NAME "Cpplint-Test-Suite"
@@ -178,3 +315,36 @@ function(CREATE_TEST_SUITES)
 	  NAME "Cppcheck-Test-Suite"
 	  TEST_REGEX "cppcheck")
 endfunction()
+
+
+function(ADD_TEST_SUITE)
+  set(options)
+  set(oneValueArgs NAME TEST_REGEX)
+  set(multiValueArgs DEPEND_ON_TARGETS)
+  cmake_parse_arguments(ADD_TEST_SUITE
+    "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+  
+  #we have to escape the " for visual studio)
+  add_custom_target(${ADD_TEST_SUITE_NAME} COMMAND ctest --tests-regex \"${ADD_TEST_SUITE_TEST_REGEX}\" -C $<CONFIG>)
+   # This comment just ends the escaped signs, for VS highlighting "
+  if(ADD_TEST_SUITE_DEPEND_ON_TARGETS)
+    add_dependencies(${ADD_TEST_SUITE_NAME} ${ADD_TEST_SUITE_DEPEND_ON_TARGETS})
+  endif()
+  set_property(TARGET ${ADD_TEST_SUITE_NAME} PROPERTY FOLDER "_Test-Suites_")
+endfunction()
+
+
+function(TESTING__EXTRACT_UNIT_AND_INTEGRATION_TEST_TARGETS_)
+  set(TESTING__INTEGRATION_TESTS )
+  set(TESTING__UNIT_TESTS )
+
+  get_property(TESTING__AVAILABLE_TESTS GLOBAL PROPERTY TESTING__AVAILABLE_TESTS)
+  foreach(CURRENT_TEST ${TESTING__AVAILABLE_TESTS})
+    if(${CURRENT_TEST} MATCHES "integration_test")
+      list(APPEND TESTING__INTEGRATION_TESTS ${CURRENT_TEST})
+    else()
+      list(APPEND TESTING__UNIT_TESTS ${CURRENT_TEST})
+    endif()
+  endforeach()
+endfunction()
+
diff --git a/cmake/WarningLevels.cmake b/cmake/WarningLevels.cmake
index 56a8e549bb1067c10917f306c3ef5e7b68d45396..cfe1929d1499691c9051370dd51170a008947a62 100644
--- a/cmake/WarningLevels.cmake
+++ b/cmake/WarningLevels.cmake
@@ -34,6 +34,7 @@ set(WARNING_LEVELS_RWTH_CLANG
   -Wno-weak-vtables
   -Wno-exit-time-destructors
   -Wno-global-constructors
+  -Wno-padded
 )
 
 set(WARNING_LEVELS_RWTH_GCC
diff --git a/cmake/cppcheck.cmake b/cmake/cppcheck.cmake
index 0f0416429527034f4cb413afef17df299c27f293..686a8abcdc7132a7208ec6163aaed78d9768d5c5 100644
--- a/cmake/cppcheck.cmake
+++ b/cmake/cppcheck.cmake
@@ -36,7 +36,7 @@ else()
   message(STATUS "Use cppcheck from: ${CPPCHECK_COMMAND}")
 endif()
 
-set(CPPCHECK_ARGUMENTS --enable=warning,performance,portability,missingInclude,unusedFunction,style --error-exitcode=1 --quiet --verbose)
+set(CPPCHECK_ARGUMENTS --enable=warning,performance,portability,missingInclude,style --suppress=unusedFunction --error-exitcode=1 --quiet --verbose)
 if(MSVC)
   list(APPEND CPPCHECK_ARGUMENTS --template=vs)
 elseif(CLANG)
diff --git a/cmake/cpplint.cmake b/cmake/cpplint.cmake
index 9342399d3f15c119b3fd1fddeb4fe741fa8dc999..30f125900088e662e140b531bbd9804c3b373ec8 100644
--- a/cmake/cpplint.cmake
+++ b/cmake/cpplint.cmake
@@ -77,5 +77,5 @@ function(ADD_TEST_CPPLINT)
     COMMAND ${PYTHON_EXECUTABLE} ${CPPLINT_COMMAND} ${CPPLINT_OUTPUT} ${CPPLINT_ARGS}
     ${ADD_TEST_CPPLINT_UNPARSED_ARGUMENTS}
     )
-  set_tests_properties(${ADD_TEST_CPPLINT_NAME} PROPERTIES TIMEOUT 20.0)
+  set_tests_properties(${ADD_TEST_CPPLINT_NAME} PROPERTIES TIMEOUT 100.0)
 endfunction()
diff --git a/cmake/suppress_warnings.hpp.in b/cmake/suppress_warnings.hpp.in
index 5a9ae8a7c8ad2d395ce944f1a859c6d09c11b93c..ddd2a44b761befa44ce4e8096990ea918503ce1b 100644
--- a/cmake/suppress_warnings.hpp.in
+++ b/cmake/suppress_warnings.hpp.in
@@ -40,10 +40,8 @@
   _Pragma("clang diagnostic ignored \"-Wsign-conversion\"")               \
   _Pragma("clang diagnostic ignored \"-Wnewline-eof\"")                   \
   _Pragma("clang diagnostic ignored \"-Wnon-virtual-dtor\"")              \
+  _Pragma("clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"") \
   _Pragma("clang diagnostic ignored \"-Wextra-semi\"")
-#define SUPPRESS_WARNINGS_BEGIN_PADDED \
-  _Pragma("clang diagnostic push")                                        \
-  _Pragma("clang diagnostic ignored \"-Wpadded\"")
 #define SUPPRESS_WARNINGS_BEGIN_MISSING_DECLARATIONS                      \
   _Pragma("clang diagnostic push")                                        \
   _Pragma("clang diagnostic ignored \"-Wmissing-prototypes\"")
@@ -51,7 +49,6 @@
 
 #elif defined _MSC_VER
 #define SUPPRESS_WARNINGS_BEGIN __pragma(warning(push, 0));
-#define SUPPRESS_WARNINGS_BEGIN_PADDED  __pragma(warning(push));
 #define SUPPRESS_WARNINGS_BEGIN_MISSING_DECLARATIONS  __pragma(warning(push));
 #define SUPPRESS_WARNINGS_END __pragma(warning(pop));
 
@@ -69,9 +66,6 @@
   _Pragma("GCC diagnostic ignored \"-Wextra\"")                           \
   _Pragma("GCC diagnostic ignored \"-Wmissing-declarations\"")            \
   _Pragma("GCC diagnostic ignored \"-Wpadded\"")
-#define SUPPRESS_WARNINGS_BEGIN_PADDED                                    \
-  _Pragma("GCC diagnostic push")                                          \
-  _Pragma("GCC diagnostic ignored \"-Wpadded\"")
 #define SUPPRESS_WARNINGS_BEGIN_MISSING_DECLARATIONS                      \
   _Pragma("GCC diagnostic push")                                          \
   _Pragma("GCC diagnostic ignored \"-Wmissing-declarations\"")
diff --git a/conanfile.py b/conanfile.py
index 41cacdd49a281bd7bdcfdcb1c544221f16fb7ea6..59a1b0577c94876c30fe7cfd4408ae6aef9d48e4 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -24,7 +24,7 @@ from conans import ConanFile, CMake
 
 class ProjectPhoenix(ConanFile):
     name = "phx"
-    version = "18.03.0"
+    version = "18.06.0"
     license = "3-Clause BSD License"
     description = """Project Phoenix"""
     settings = "os", "compiler", "build_type", "arch"
@@ -33,14 +33,14 @@ class ProjectPhoenix(ConanFile):
                 ("boost_optional/1.66.0@bincrafters/testing"),
                 ("boost_signals2/1.66.0@bincrafters/testing"), #still not working in stable, change whenever available (also boost_optional)
                 ("catch/1.12.0@RWTH-VR/thirdparty"),
-                ("cppcheck/1.82@RWTH-VR/thirdparty"),
+                ("cppcheck/1.84@RWTH-VR/thirdparty"),
                 ("cpplint/e8ffd7c@RWTH-VR/thirdparty"),
                 ("freeimage/3.17.0_2@RWTH-VR/thirdparty"),
-                ("gl/1.1.1@RWTH-VR/thirdparty"),
-                ("glm/0.9.8.5@g-truc/stable"),
+                ("gl/1.1.6@RWTH-VR/thirdparty"),
+                ("glm/0.9.9.0@g-truc/stable"),
                 ("jsonformoderncpp/3.0.1@vthiery/stable"),
                 ("openvr/1.0.12@RWTH-VR/thirdparty"),
-                ("sdl2/2.0.7@bincrafters/stable"),
+                ("sdl2/2.0.8@bincrafters/stable"),
                 ("spdlog/0.16.3@bincrafters/stable"),
                 ("trompeloeil/v29@rollbear/stable"))
     generators = "cmake"
@@ -64,7 +64,9 @@ class ProjectPhoenix(ConanFile):
        self.copy("*.dll", dst="tests/Release", src="bin")
        self.copy("*.dll", dst="demos/viewer/Debug", src="bin")
        self.copy("*.dll", dst="demos/viewer/Release", src="bin")
+       self.copy("*.dll", dst="demos/combustion_demo/Debug", src="bin")
+       self.copy("*.dll", dst="demos/combustion_demo/Release", src="bin")
        self.copy("*.so", dst="lib", src="lib")
-       self.copy("*.so*", dst="lib", src="lib")
+       self.copy("*.so.*", dst="lib", src="lib")
        self.copy("*.dylib", dst="lib", src="lib")
        self.copy("*.dylib", dst="tests", src="lib")
diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt
index ea9e625480443f4a735aee5a0754c18485e22390..5418319efff8cbaf7308ecb1ab73d1de0a5e63f3 100644
--- a/demos/CMakeLists.txt
+++ b/demos/CMakeLists.txt
@@ -21,3 +21,4 @@
 #------------------------------------------------------------------------------
 
 add_subdirectory(viewer)
+add_subdirectory(combustion_demo)
\ No newline at end of file
diff --git a/demos/combustion_demo/CMakeLists.txt b/demos/combustion_demo/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6746db4d55f561712c96aefc61bc47eca30df255
--- /dev/null
+++ b/demos/combustion_demo/CMakeLists.txt
@@ -0,0 +1,47 @@
+#------------------------------------------------------------------------------
+# 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.
+#------------------------------------------------------------------------------
+
+file(GLOB COMBUSTION_DEMO_SOURCES src/*.cpp)
+file(GLOB COMBUSTION_DEMO_HEADERS src/*.hpp)
+
+add_executable(combustion_demo
+  ${COMBUSTION_DEMO_SOURCES}
+  ${COMBUSTION_DEMO_HEADERS}
+  resource_file.rc
+)
+
+target_include_directories(combustion_demo
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
+  phoenix
+)
+target_link_libraries(combustion_demo
+  phoenix
+)
+set_warning_levels_RWTH(combustion_demo)
+add_test_cpplint(NAME "combustion_demo--cpplint"
+  ${COMBUSTION_DEMO_SOURCES}
+  ${COMBUSTION_DEMO_HEADERS}
+)
+
+add_test_cppcheck(NAME "combustion_demo--cppcheck"
+  ${COMBUSTION_DEMO_SOURCES}
+)
diff --git a/demos/combustion_demo/debug.ico b/demos/combustion_demo/debug.ico
new file mode 100644
index 0000000000000000000000000000000000000000..d50bba3f00e11284dacdb4fc5204d44719d06f17
Binary files /dev/null and b/demos/combustion_demo/debug.ico differ
diff --git a/demos/combustion_demo/phoenix.ico b/demos/combustion_demo/phoenix.ico
new file mode 100644
index 0000000000000000000000000000000000000000..eb61a4bbc71865920433a7e6a017048146f26ba9
Binary files /dev/null and b/demos/combustion_demo/phoenix.ico differ
diff --git a/demos/combustion_demo/resource_file.rc b/demos/combustion_demo/resource_file.rc
new file mode 100644
index 0000000000000000000000000000000000000000..9d43170e1f42673464a80b382527e7e1de273191
--- /dev/null
+++ b/demos/combustion_demo/resource_file.rc
@@ -0,0 +1,7 @@
+// Icon Resource Definition
+#define MAIN_ICON 102
+#if defined(_DEBUG)
+MAIN_ICON               ICON                    "debug.ico"
+#else
+MAIN_ICON               ICON                    "phoenix.ico"
+#endif
\ No newline at end of file
diff --git a/demos/combustion_demo/src/combustion_demo.cpp b/demos/combustion_demo/src/combustion_demo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..12764cf1d6e2dcf5ed6d1069ddbd4e6bf9d030b4
--- /dev/null
+++ b/demos/combustion_demo/src/combustion_demo.cpp
@@ -0,0 +1,215 @@
+//------------------------------------------------------------------------------
+// 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 <chrono>
+#include <future>
+#include <memory>
+#include <vector>
+
+#include "desk_behavior.hpp"
+#include "desktop_navigation_behavior.hpp"
+#include "phx/core/runtime_component.hpp"
+#include "phx/display/display_system_openvr.hpp"
+#include "phx/display/display_system_window.hpp"
+#include "phx/input/device_system.hpp"
+#include "phx/input/input_system.hpp"
+#include "phx/rendering/auxiliary/splash_screen.hpp"
+#include "phx/rendering/components/mesh_handle.hpp"
+#include "phx/rendering/components/mesh_render_settings.hpp"
+#include "phx/rendering/components/skybox.hpp"
+#include "phx/resources/loaders/assimp_model_loader.hpp"
+#include "phx/resources/loaders/scene_loader.hpp"
+#include "phx/setup.hpp"
+#include "vr_controller_interaction_behavior.hpp"
+
+#if defined __clang__
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#endif
+
+int main(int, char**) {
+  std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(true);
+  auto scene = engine->GetScene();
+  auto rendering_system = engine->GetSystem<phx::RenderingSystem>();
+  rendering_system->SetEnabled(false);
+  auto openvr_system = engine->GetSystem<phx::DisplaySystemOpenVR>();
+  auto device_system = engine->GetSystem<phx::DeviceSystem>();
+
+  scene->CreateEntity()->AddComponent<phx::Skybox>();
+
+  phx::SplashScreen* splash = engine->CreateSystem<phx::SplashScreen>(
+      engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow());
+  engine->MoveSystemBefore(splash,
+                           engine->GetSystem<phx::DisplaySystemWindow>());
+
+  auto assimp_loader = static_cast<phx::AssimpModelLoader*>(
+      phx::ResourceManager::instance().GetLoaderForType(".stl"));
+
+  assimp_loader->SetProgressUpdateCallback(
+      [splash](float progress) { splash->SetLoadProgress(progress); });
+
+  phx::InputSystem* input_system = engine->GetSystem<phx::InputSystem>();
+
+  input_system->AddKeyPressCallback([&engine](char key) {
+    if (key == 'q') engine->Stop();
+  });
+
+  auto right_controller_entities = scene->GetEntitiesWithComponents<
+      phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>();
+  VRControllerInteractionBehavior* right_interaction_behavior = nullptr;
+  if (right_controller_entities.size() >= 1) {
+    auto right_controller_entity = right_controller_entities[0];
+    right_interaction_behavior =
+        right_controller_entity->AddComponent<VRControllerInteractionBehavior>(
+            device_system);
+  }
+
+  auto handle = std::async([&right_interaction_behavior, &scene,
+                            rendering_system, splash, input_system,
+                            openvr_system]() {
+    auto model_surface_entity = phx::SceneLoader::InsertModelIntoScene(
+        "models/combustion/combustion_opt.stl", scene.get());
+    auto surface_transform =
+        model_surface_entity->GetFirstComponent<phx::Transform>();
+    auto surface_material_handle =
+        surface_transform->GetChild(0)
+            ->GetEntity()
+            ->GetFirstComponent<phx::MaterialHandle>();
+    auto surface_material = surface_material_handle->GetMaterial();
+    surface_material->SetAmbientColor(glm::vec3(0.1f));
+    surface_material->SetDiffuseColor(glm::vec3(1.0f));
+    surface_material->SetSpecularColor(glm::vec3(0.4f));
+    surface_material->SetShininess(100.0);
+
+    auto model_boundingbox = phx::SceneLoader::InsertModelIntoScene(
+        "models/combustion/data_box.stl", scene.get());
+    auto boundingbox_transform =
+        model_boundingbox->GetFirstComponent<phx::Transform>();
+    auto boundigbox_mesh_entity =
+        boundingbox_transform->GetChild(0)->GetEntity();
+    auto boundingbox_mesh_handle =
+        boundigbox_mesh_entity->GetFirstComponent<phx::MeshHandle>();
+    auto render_settings =
+        boundigbox_mesh_entity->AddComponent<phx::MeshRenderSettings>();
+    render_settings->SetWireframeMode(true);
+
+    std::array<glm::vec3, 2> bbox =
+        boundingbox_mesh_handle->GetMesh()->GetBoundingBox();
+    glm::vec3 bbox_diff = bbox[1] - bbox[0];
+    glm::vec3 center_vec = bbox_diff * 0.5f * 0.001f;
+    surface_transform->SetLocalTranslation(-center_vec);
+    boundingbox_transform->SetLocalTranslation(
+        glm::vec3(-center_vec[0], center_vec[1], -center_vec[2]));
+
+    auto vis_root = scene->CreateEntity();
+    auto vis_root_transform = vis_root->AddComponent<phx::Transform>();
+    surface_transform->SetParent(vis_root_transform, false);
+    boundingbox_transform->SetParent(vis_root_transform, false);
+    vis_root_transform->SetLocalScale(glm::vec3(0.001f, 0.001f, 0.001f));
+    vis_root_transform->SetLocalTranslation(glm::vec3(-0.4f, 0.02f, -0.25f));
+
+    auto floor_entity = phx::SceneLoader::InsertModelIntoScene(
+        "models/cube/cube.obj", scene.get());
+    auto floor_transform = floor_entity->GetFirstComponent<phx::Transform>();
+    floor_transform->SetLocalScale(glm::vec3(1000.0f, 0.05f, 1000.0f));
+    auto floor_material = floor_transform->GetChild(0)
+                              ->GetEntity()
+                              ->GetFirstComponent<phx::MaterialHandle>();
+    floor_material->GetMaterial()->SetDiffuseColor(glm::vec3(0.5f, 0.5f, 0.5f));
+    floor_material->GetMaterial()->SetSpecularColor(
+        glm::vec3(1.0f, 1.0f, 1.0f));
+    floor_material->GetMaterial()->SetAmbientColor(glm::vec3(0.1f, 0.1f, 0.1f));
+
+    if (right_interaction_behavior)
+      right_interaction_behavior->SetTarget(vis_root_transform);
+
+    auto desk_entity = phx::SceneLoader::InsertModelIntoScene(
+        "models/cube/cube2.obj", scene.get());
+    auto desk_transform = desk_entity->GetFirstComponent<phx::Transform>();
+    desk_transform->SetLocalScale(glm::vec3(2.0f, 0.03f, 1.0f));
+    auto desk_material = desk_transform->GetChild(0)
+                             ->GetEntity()
+                             ->GetFirstComponent<phx::MaterialHandle>();
+    desk_material->GetMaterial()->SetDiffuseColor(glm::vec3(0.5f, 1.0f, 0.5f));
+    desk_material->GetMaterial()->SetSpecularColor(glm::vec3(1.0f, 1.0f, 1.0f));
+    desk_material->GetMaterial()->SetAmbientColor(glm::vec3(0.1f, 0.2f, 0.1f));
+
+    auto desk_root = scene->CreateEntity();
+    auto desk_root_transform = desk_root->AddComponent<phx::Transform>();
+    desk_root_transform->SetLocalTranslation(glm::vec3(0.0f, 0.8f, 0.0f));
+    desk_transform->SetParent(desk_root_transform, false);
+    vis_root_transform->SetParent(desk_root_transform, false);
+    desk_root->AddComponent<DeskBehavior>(openvr_system);
+
+    rendering_system->SetEnabled(true);
+    splash->SetEnabled(false);
+  });
+
+  std::vector<glm::quat> light_dirs{
+      glm::quat(glm::angleAxis(-0.25f * glm::pi<float>(), glm::vec3(1, 0, 0))),
+      glm::quat(glm::angleAxis(0.25f * glm::pi<float>(), glm::vec3(0, 1, 0))),
+      glm::quat(glm::angleAxis(-0.25f * glm::pi<float>(), glm::vec3(0, 1, 0))),
+      glm::quat(glm::angleAxis(0.75f * glm::pi<float>(), glm::vec3(1, 0, 0)))};
+  std::vector<glm::vec3> light_colors{
+      glm::vec3(1.0, 1.0, 1.0), glm::vec3(1.0, 1.0, 1.0),
+      glm::vec3(1.0, 1.0, 1.0), glm::vec3(1.0, 1.0, 1.0)};
+  std::vector<float> light_intensities{1.0f, 0.9f, 0.8f, 0.7f};
+
+  for (std::size_t i = 0; i < light_dirs.size(); i++) {
+    phx::Entity* light_entity = scene->CreateEntity();
+    phx::Transform* light_transform =
+        light_entity->AddComponent<phx::Transform>();
+    light_transform->SetLocalRotation(light_dirs[i]);
+    phx::Light* light = light_entity->AddComponent<phx::Light>();
+    light->SetType(phx::Light::Type::kDirectional);
+    light->SetColor(light_colors[i]);
+    light->SetIntensity(light_intensities[i]);
+  }
+
+  auto virtual_platform_transform =
+      scene
+          ->GetEntitiesWithComponents<
+              phx::RuntimeComponent<phx::USER_PLATFORM>>()[0]
+          ->GetFirstComponent<phx::Transform>();
+
+  virtual_platform_transform->SetLocalTranslation(glm::vec3(.0f, .0f, .0f));
+
+  phx::Entity* camera =
+      scene->GetEntitiesWithComponents<phx::RenderTarget>()[0];
+  if (camera->GetFirstComponent<phx::RuntimeComponent<phx::LEFT_EYE>>() ==
+          nullptr &&
+      camera->GetFirstComponent<phx::RuntimeComponent<phx::RIGHT_EYE>>() ==
+          nullptr) {
+    auto camera_projection = camera->GetFirstComponent<phx::Projection>();
+    camera_projection->SetPerspective(glm::radians(68.0f), 4.0f / 3.0f, 0.01f,
+                                      1000.0f);
+  }
+
+  if (!openvr_system) {
+    camera->AddComponent<DesktopNavigationBehavior>(input_system);
+    camera->GetFirstComponent<phx::Transform>()->Translate(
+        glm::vec3(0.0f, 1.0f, 1.0f));
+  }
+
+  engine->Run();
+
+  return EXIT_SUCCESS;
+}
diff --git a/demos/combustion_demo/src/desk_behavior.cpp b/demos/combustion_demo/src/desk_behavior.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..380884774f1140111f27f87a6f7ee735392fe611
--- /dev/null
+++ b/demos/combustion_demo/src/desk_behavior.cpp
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+// 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 "desk_behavior.hpp"
+
+#include <vector>
+
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/detail/type_vec3.hpp"
+#include "glm/glm.hpp"
+SUPPRESS_WARNINGS_END
+
+#include "phx/core/entity.hpp"
+#include "phx/core/runtime_component.hpp"
+#include "phx/core/scene.hpp"
+#include "phx/display/display_system_openvr.hpp"
+#include "phx/rendering/components/transform.hpp"
+
+DeskBehavior::DeskBehavior(phx::DisplaySystemOpenVR* openvr_system)
+    : openvr_system_(openvr_system), position_set_(false) {}
+
+void DeskBehavior::OnUpdate() {
+  if (!openvr_system_ || position_set_) return;
+
+  const auto scene = GetEntity()->GetScene();
+
+  const auto left_controller = scene->GetEntitiesWithComponents<
+      phx::RuntimeComponent<phx::LEFT_CONTROLLER>>()[0];
+  const auto right_controller = scene->GetEntitiesWithComponents<
+      phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()[0];
+
+  const auto left_controller_translation =
+      left_controller->GetFirstComponent<phx::Transform>()
+          ->GetGlobalTranslation();
+  const auto right_controller_translation =
+      right_controller->GetFirstComponent<phx::Transform>()
+          ->GetGlobalTranslation();
+  const auto difference =
+      left_controller_translation - right_controller_translation;
+  const auto center = right_controller_translation + difference / 2.0f;
+
+  const auto right = glm::normalize(difference);
+  const auto up = glm::vec3(0, 1, 0);
+  const auto forward = glm::cross(right, up);
+
+  auto transform = GetEntity()->GetFirstComponent<phx::Transform>();
+  transform->LookAt(transform->GetLocalTranslation() + forward);
+  transform->SetLocalTranslation(center + 0.3f * forward - 0.075f * up);
+
+  position_set_ = true;
+}
diff --git a/demos/viewer/src/navigation_behavior.hpp b/demos/combustion_demo/src/desk_behavior.hpp
similarity index 61%
rename from demos/viewer/src/navigation_behavior.hpp
rename to demos/combustion_demo/src/desk_behavior.hpp
index ac2e06c9ef98424e746f42a41d3880e5079781da..970d1b4add5cf342bdc3618316b2bca37869bcc4 100644
--- a/demos/viewer/src/navigation_behavior.hpp
+++ b/demos/combustion_demo/src/desk_behavior.hpp
@@ -20,25 +20,34 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#ifndef DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_
-#define DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_
+#ifndef DEMOS_COMBUSTION_DEMO_SRC_DESK_BEHAVIOR_HPP_
+#define DEMOS_COMBUSTION_DEMO_SRC_DESK_BEHAVIOR_HPP_
+
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/glm.hpp"
+SUPPRESS_WARNINGS_END
 
 #include "phx/display/display_system_openvr.hpp"
 #include "phx/scripting/behavior.hpp"
 
-class NavigationBehavior : public phx::Behavior {
+class DeskBehavior : public phx::Behavior {
  public:
-  explicit NavigationBehavior(phx::DisplaySystemOpenVR* display_system_openvr);
-  NavigationBehavior(const NavigationBehavior& that) = default;
-  NavigationBehavior(NavigationBehavior&& temp) = default;
-  virtual ~NavigationBehavior() = default;
-  NavigationBehavior& operator=(const NavigationBehavior& that) = default;
-  NavigationBehavior& operator=(NavigationBehavior&& temp) = default;
+  explicit DeskBehavior(phx::DisplaySystemOpenVR* openvr_system_);
+  DeskBehavior(const DeskBehavior& that) = default;
+  DeskBehavior(DeskBehavior&& temp) = default;
+  virtual ~DeskBehavior() = default;
+  DeskBehavior& operator=(const DeskBehavior& that) = default;
+  DeskBehavior& operator=(DeskBehavior&& temp) = default;
 
   void OnUpdate() override;
 
  protected:
-  phx::DisplaySystemOpenVR* display_system_openvr_;
+  phx::DisplaySystemOpenVR* openvr_system_;
+
+ private:
+  bool position_set_;
 };
 
-#endif  // DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_
+#endif  // DEMOS_COMBUSTION_DEMO_SRC_DESK_BEHAVIOR_HPP_
diff --git a/demos/combustion_demo/src/desktop_navigation_behavior.cpp b/demos/combustion_demo/src/desktop_navigation_behavior.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0491b8c21da54044d239d8ebce43394ab9c54027
--- /dev/null
+++ b/demos/combustion_demo/src/desktop_navigation_behavior.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 "desktop_navigation_behavior.hpp"
+
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/detail/type_vec3.hpp"
+#include "glm/glm.hpp"
+SUPPRESS_WARNINGS_END
+
+#include "phx/core/entity.hpp"
+#include "phx/rendering/components/transform.hpp"
+
+DesktopNavigationBehavior::DesktopNavigationBehavior(
+    phx::InputSystem* input_system)
+    : input_system_(input_system) {
+  input_system_->AddMouseMoveCallback(
+      [this](int x, int y) { OnMouseMove(x, y); });
+  input_system_->AddMousePressCallback(
+      [this](unsigned btn) { OnMousePress(btn); });
+  input_system_->AddMouseReleaseCallback(
+      [this](unsigned btn) { OnMouseRelease(btn); });
+}
+
+void DesktopNavigationBehavior::OnUpdate() {
+  const auto transform = GetEntity()->GetFirstComponent<phx::Transform>();
+
+  if (rotation_mode_) {
+    euler_angles_[0] += glm::radians(accumulated_mouse_pos_[1] * 0.0885416666f);
+    euler_angles_[1] += glm::radians(accumulated_mouse_pos_[0] * 0.0885416666f);
+
+    transform->SetGlobalRotation(euler_angles_);
+  }
+  if (strafe_mode_) {
+    transform->Translate(transform->Up() *
+                         static_cast<float>(accumulated_mouse_pos_[1]) *
+                         0.005f);
+    transform->Translate(transform->Right() *
+                         static_cast<float>(accumulated_mouse_pos_[0]) *
+                         -0.005f);
+  }
+  if (translation_mode_) {
+    transform->Translate(transform->Forward() *
+                         static_cast<float>(accumulated_mouse_pos_[1]) *
+                         0.005f);
+    transform->Translate(transform->Right() *
+                         static_cast<float>(accumulated_mouse_pos_[0]) *
+                         -0.005f);
+  }
+
+  accumulated_mouse_pos_ = glm::ivec2{0, 0};
+}
+
+void DesktopNavigationBehavior::OnMouseMove(int x, int y) {
+  accumulated_mouse_pos_ += glm::ivec2{x, y};
+}
+
+void DesktopNavigationBehavior::OnMousePress(unsigned btn) {
+  if (btn == 1) {
+    rotation_mode_ = true;
+  }
+  if (btn == 2) {
+    strafe_mode_ = true;
+  }
+  if (btn == 3) {
+    translation_mode_ = true;
+  }
+}
+
+void DesktopNavigationBehavior::OnMouseRelease(unsigned btn) {
+  if (btn == 1) {
+    rotation_mode_ = false;
+  }
+  if (btn == 2) {
+    strafe_mode_ = false;
+  }
+  if (btn == 3) {
+    translation_mode_ = false;
+  }
+}
diff --git a/demos/combustion_demo/src/desktop_navigation_behavior.hpp b/demos/combustion_demo/src/desktop_navigation_behavior.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c394669cc307d679652fc69e9c4442bb20843625
--- /dev/null
+++ b/demos/combustion_demo/src/desktop_navigation_behavior.hpp
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+// 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 DEMOS_COMBUSTION_DEMO_SRC_DESKTOP_NAVIGATION_BEHAVIOR_HPP_
+#define DEMOS_COMBUSTION_DEMO_SRC_DESKTOP_NAVIGATION_BEHAVIOR_HPP_
+
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/glm.hpp"
+SUPPRESS_WARNINGS_END
+
+#include "phx/input/input_system.hpp"
+#include "phx/scripting/behavior.hpp"
+
+class DesktopNavigationBehavior : public phx::Behavior {
+ public:
+  explicit DesktopNavigationBehavior(phx::InputSystem* input_system);
+  DesktopNavigationBehavior(const DesktopNavigationBehavior& that) = default;
+  DesktopNavigationBehavior(DesktopNavigationBehavior&& temp) = default;
+  virtual ~DesktopNavigationBehavior() = default;
+  DesktopNavigationBehavior& operator=(const DesktopNavigationBehavior& that) =
+      default;
+  DesktopNavigationBehavior& operator=(DesktopNavigationBehavior&& temp) =
+      default;
+
+  void OnUpdate() override;
+  void OnMouseMove(int x, int y);
+  void OnMousePress(unsigned btn);
+  void OnMouseRelease(unsigned btn);
+
+ protected:
+  phx::InputSystem* input_system_;
+
+ private:
+  glm::ivec2 accumulated_mouse_pos_ = glm::ivec2(0);
+  glm::vec3 euler_angles_ = glm::vec3(0.0f);
+  bool rotation_mode_ = false;
+  bool strafe_mode_ = false;
+  bool translation_mode_ = false;
+};
+
+#endif  // DEMOS_COMBUSTION_DEMO_SRC_DESKTOP_NAVIGATION_BEHAVIOR_HPP_
diff --git a/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp b/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d113a15013a3ef318979fae5e1fbf739d8451ae
--- /dev/null
+++ b/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp
@@ -0,0 +1,105 @@
+//------------------------------------------------------------------------------
+// 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 "vr_controller_interaction_behavior.hpp"
+
+#include <vector>
+
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/detail/type_vec3.hpp"
+#include "glm/glm.hpp"
+#include "glm/gtc/matrix_access.hpp"
+SUPPRESS_WARNINGS_END
+
+#include "phx/core/engine.hpp"
+#include "phx/core/entity.hpp"
+#include "phx/core/runtime_component.hpp"
+#include "phx/core/scene.hpp"
+#include "phx/display/display_system_openvr.hpp"
+#include "phx/display/hmd.hpp"
+#include "phx/rendering/components/transform.hpp"
+
+VRControllerInteractionBehavior::VRControllerInteractionBehavior(
+    phx::DeviceSystem* device_system)
+    : device_system_(device_system) {}
+
+void VRControllerInteractionBehavior::OnButtonSignal(
+    phx::VRController::ButtonId id, phx::VRController::ButtonEvent event) {
+  const auto transform = GetEntity()->GetFirstComponent<phx::Transform>();
+  if (target_ != nullptr) {
+    if (event == phx::VRController::BUTTON_PRESSED &&
+        id == vr::EVRButtonId::k_EButton_SteamVR_Trigger) {
+      if (transform != nullptr)
+        target_->SetParent(GetEntity()->GetFirstComponent<phx::Transform>());
+    }
+    if (event == phx::VRController::BUTTON_RELEASED &&
+        id == vr::EVRButtonId::k_EButton_SteamVR_Trigger) {
+      target_->SetParent(nullptr);
+    }
+  }
+}
+
+void VRControllerInteractionBehavior::OnUpdate() {
+  if (side_ == phx::VRController::INVALID_CONTROLLER) {
+    RegisterOnDeviceSignal();
+  }
+}
+
+void VRControllerInteractionBehavior::SetTarget(phx::Transform* target) {
+  target_ = target;
+}
+
+phx::Transform* VRControllerInteractionBehavior::GetTarget() const {
+  return target_;
+}
+
+void VRControllerInteractionBehavior::RegisterOnDeviceSignal() {
+  if (GetEntity()
+          ->GetFirstComponent<phx::RuntimeComponent<phx::LEFT_CONTROLLER>>() !=
+      nullptr)
+    side_ = phx::VRController::LEFT_CONTROLLER;
+  else if (GetEntity()
+               ->GetFirstComponent<
+                   phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>() != nullptr)
+    side_ = phx::VRController::RIGHT_CONTROLLER;
+  else
+    phx::warn(
+        "Added VRControllerInteractionBehavior to a non-controller entity");
+
+  phx::VRController* controller = nullptr;
+  for (auto cont : device_system_->GetDevices<phx::VRController>()) {
+    if (cont->GetSide() == side_) {
+      controller = cont;
+      break;
+    }
+  }
+
+  if (controller != nullptr) {
+    controller->RegisterButtonSignal(
+        [this](phx::VRController::ButtonId id,
+               phx::VRController::ButtonEvent event) {
+          this->OnButtonSignal(id, event);
+        });
+  }
+}
diff --git a/demos/combustion_demo/src/vr_controller_interaction_behavior.hpp b/demos/combustion_demo/src/vr_controller_interaction_behavior.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c66c364edaf9e77ec3f5dd6938facee53789c175
--- /dev/null
+++ b/demos/combustion_demo/src/vr_controller_interaction_behavior.hpp
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+// 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 DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_INTERACTION_BEHAVIOR_HPP_
+#define DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_INTERACTION_BEHAVIOR_HPP_
+
+#include "phx/input/device_system.hpp"
+#include "phx/input/vr_controller.hpp"
+#include "phx/rendering/components/transform.hpp"
+#include "phx/scripting/behavior.hpp"
+
+class VRControllerInteractionBehavior : public phx::Behavior {
+ public:
+  explicit VRControllerInteractionBehavior(phx::DeviceSystem* device_system);
+  VRControllerInteractionBehavior(const VRControllerInteractionBehavior& that) =
+      default;
+  VRControllerInteractionBehavior(VRControllerInteractionBehavior&& temp) =
+      default;
+  virtual ~VRControllerInteractionBehavior() = default;
+  VRControllerInteractionBehavior& operator=(
+      const VRControllerInteractionBehavior& that) = default;
+  VRControllerInteractionBehavior& operator=(
+      VRControllerInteractionBehavior&& temp) = default;
+
+  void OnUpdate() override;
+  void OnButtonSignal(phx::VRController::ButtonId id,
+                      phx::VRController::ButtonEvent event);
+
+  void SetTarget(phx::Transform* target);
+  phx::Transform* GetTarget() const;
+
+ protected:
+  phx::DeviceSystem* device_system_;
+
+ private:
+  void RegisterOnDeviceSignal();
+
+  phx::Transform* target_ = nullptr;
+  phx::VRController::ControllerSide side_ =
+      phx::VRController::INVALID_CONTROLLER;
+};
+
+#endif  // DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_INTERACTION_BEHAVIOR_HPP_
diff --git a/demos/viewer/src/navigation_behavior.cpp b/demos/viewer/src/navigation_behavior.cpp
deleted file mode 100644
index e9c914fd4d8658c03d65d1362203764739d3e2ed..0000000000000000000000000000000000000000
--- a/demos/viewer/src/navigation_behavior.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-//------------------------------------------------------------------------------
-// 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 "navigation_behavior.hpp"
-
-#include <vector>
-
-#include "phx/suppress_warnings.hpp"
-
-SUPPRESS_WARNINGS_BEGIN
-#include "glm/detail/type_vec3.hpp"
-#include "glm/glm.hpp"
-#include "glm/gtc/matrix_access.hpp"
-SUPPRESS_WARNINGS_END
-
-#include "phx/core/engine.hpp"
-#include "phx/core/entity.hpp"
-#include "phx/core/scene.hpp"
-#include "phx/display/display_system_openvr.hpp"
-#include "phx/display/hmd.hpp"
-#include "phx/rendering/components/transform.hpp"
-
-NavigationBehavior::NavigationBehavior(
-    phx::DisplaySystemOpenVR* display_system_openvr)
-    : display_system_openvr_(display_system_openvr) {}
-
-void NavigationBehavior::OnUpdate() {
-  // If there exists an HMD and a transform.
-  phx::HMD* hmd = nullptr;
-  if (display_system_openvr_ != nullptr) {
-    hmd = display_system_openvr_->GetHMD();
-  }
-  const auto transform = GetEntity()->GetFirstComponent<phx::Transform>();
-  if (hmd == nullptr || transform == nullptr) return;
-
-  auto indices = hmd->GetControllerIndices();
-
-  for (auto i = 0u; i < indices.size(); ++i) {
-    vr::VRControllerState_t controller_state;
-    vr::VRSystem()->GetControllerState(indices[i], &controller_state,
-                                       sizeof controller_state);
-
-    // Set the transform based on whether the trigger is pressed.
-    if (controller_state.ulButtonTouched &
-        vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_SteamVR_Trigger))
-      transform->Translate(
-          -0.05F * glm::column(hmd->GetRightControllerTransformation(), 2));
-    if (controller_state.ulButtonTouched &
-        vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_SteamVR_Touchpad))
-      transform->Translate(
-          0.05F * glm::column(hmd->GetRightControllerTransformation(), 2));
-  }
-}
diff --git a/demos/viewer/src/viewer.cpp b/demos/viewer/src/viewer.cpp
index 928ce7dd2192d4cbd195c2302dc02a31a7d53f38..17a69139c70cb2d80c4dde43be2d62e00d319dd6 100644
--- a/demos/viewer/src/viewer.cpp
+++ b/demos/viewer/src/viewer.cpp
@@ -21,7 +21,7 @@
 //------------------------------------------------------------------------------
 
 #include <chrono>
-#include <iostream>
+#include <future>
 #include <memory>
 #include <vector>
 
@@ -33,36 +33,39 @@
 #include "phx/display/display_system_openvr.hpp"
 #include "phx/display/display_system_window.hpp"
 #include "phx/display/window.hpp"
+#include "phx/input/device_system.hpp"
 #include "phx/input/input_system.hpp"
 #include "phx/rendering/auxiliary/splash_screen.hpp"
 #include "phx/rendering/components/light.hpp"
 #include "phx/rendering/components/transform.hpp"
 #include "phx/resources/loaders/assimp_model_loader.hpp"
-#include "phx/resources/loaders/openvr_resource_loader.hpp"
 #include "phx/resources/loaders/scene_loader.hpp"
 #include "phx/resources/resource_manager.hpp"
 #include "phx/setup.hpp"
 
-#include "navigation_behavior.hpp"
 #include "viewer_system.hpp"
+#include "vrcontroller_navigation_behavior.hpp"
 
 #if defined __clang__
 #pragma clang diagnostic ignored "-Wmissing-prototypes"
 #endif
 
 int main(int, char**) {
-  std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine();
+  std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(true);
   auto scene = engine->GetScene();
+  auto rendering_system = engine->GetSystem<phx::RenderingSystem>();
+  rendering_system->SetEnabled(false);
 
-  phx::SplashScreen splash(
+  phx::SplashScreen* splash = engine->CreateSystem<phx::SplashScreen>(
       engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow());
-  splash.Draw();
+  engine->MoveSystemBefore(splash,
+                           engine->GetSystem<phx::DisplaySystemWindow>());
+
   auto assimp_loader = static_cast<phx::AssimpModelLoader*>(
       phx::ResourceManager::instance().GetLoaderForType(".obj"));
-  assimp_loader->SetProgressUpdateCallback([&splash](float progress) {
-    splash.SetLoadProgress(progress);
-    splash.Draw();
-  });
+
+  assimp_loader->SetProgressUpdateCallback(
+      [splash](float progress) { splash->SetLoadProgress(progress); });
 
   phx::InputSystem* input_system = engine->GetSystem<phx::InputSystem>();
   ViewerSystem* viewer_system = engine->CreateSystem<ViewerSystem>();
@@ -73,8 +76,13 @@ int main(int, char**) {
       viewer_system->SetShowFramerate(!viewer_system->GetShowFramerate());
   });
 
-  phx::SceneLoader::InsertModelIntoScene(
-      "models/UniversityScene/Univers20171013.obj", scene.get());
+  auto handle = std::async([&scene, rendering_system, splash]() {
+    phx::SceneLoader::InsertModelIntoScene("models/bunny.obj", scene.get());
+    // "models/UniversityScene/Univers20171013.obj"
+
+    rendering_system->SetEnabled(true);
+    splash->SetEnabled(false);
+  });
 
   std::vector<glm::quat> light_dirs{
       glm::quat(glm::angleAxis(-0.25f * glm::pi<float>(), glm::vec3(1, 0, 0))),
@@ -105,8 +113,9 @@ int main(int, char**) {
   virtual_platform_transform->SetLocalTranslation(start_position);
   phx::info("The  virtual platform's start position is: {}", start_position);
 
-  virtual_platform->AddComponent<NavigationBehavior>(
-      engine->GetSystem<phx::DisplaySystemOpenVR>());
+  virtual_platform->AddComponent<VRControllerNavigationBehavior>(
+      engine->GetSystem<phx::DeviceSystem>(),
+      phx::VRController::RIGHT_CONTROLLER);
 
   engine->Run();
 
diff --git a/demos/viewer/src/viewer_system.hpp b/demos/viewer/src/viewer_system.hpp
index 82dc5c9536685b356d43e3442c08b7aaff71e4dd..dcb4d7168a8d7c85fa9139653846d83a9ef33ab4 100644
--- a/demos/viewer/src/viewer_system.hpp
+++ b/demos/viewer/src/viewer_system.hpp
@@ -31,14 +31,12 @@
 #include "phx/core/frame_timer.hpp"
 #include "phx/core/system.hpp"
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 class ViewerSystem : public phx::System {
  public:
   ViewerSystem() = delete;
   ViewerSystem(const ViewerSystem&) = delete;
   ViewerSystem(ViewerSystem&&) = default;
-  ~ViewerSystem() = default;
+  ~ViewerSystem() override = default;
 
   bool GetShowFramerate() const;
   void SetShowFramerate(bool show_framerate);
@@ -58,6 +56,4 @@ class ViewerSystem : public phx::System {
   explicit ViewerSystem(phx::Engine* engine);
 };
 
-SUPPRESS_WARNINGS_END
-
 #endif  // DEMOS_VIEWER_SRC_VIEWER_SYSTEM_HPP_
diff --git a/demos/viewer/src/vrcontroller_navigation_behavior.cpp b/demos/viewer/src/vrcontroller_navigation_behavior.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9acee895c5d9784aa53d6c017de0fd72a8e5b7ba
--- /dev/null
+++ b/demos/viewer/src/vrcontroller_navigation_behavior.cpp
@@ -0,0 +1,99 @@
+//------------------------------------------------------------------------------
+// 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 "vrcontroller_navigation_behavior.hpp"
+
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/detail/type_vec3.hpp"
+#include "glm/glm.hpp"
+#include "glm/gtc/matrix_access.hpp"
+SUPPRESS_WARNINGS_END
+
+#include "phx/core/entity.hpp"
+#include "phx/core/logger.hpp"
+#include "phx/core/runtime_component.hpp"
+#include "phx/display/hmd.hpp"
+#include "phx/rendering/components/transform.hpp"
+
+VRControllerNavigationBehavior::VRControllerNavigationBehavior(
+    phx::DeviceSystem* device_system, phx::VRController::ControllerSide side)
+    : device_system_(device_system), side_(side) {
+  phx::VRController* controller = GetController();
+
+  if (controller != nullptr) {
+    controller->RegisterButtonSignal(
+        [this](phx::VRController::ButtonId id,
+               phx::VRController::ButtonEvent event) {
+          this->OnButtonSignal(id, event);
+        });
+  }
+}
+
+void VRControllerNavigationBehavior::OnUpdate() {
+  const auto transform = GetEntity()->GetFirstComponent<phx::Transform>();
+  phx::VRController* controller = GetController();
+  if (transform != nullptr && controller != nullptr && button_pressed_) {
+    glm::mat4 controller_pos = controller->GetPose();
+    glm::vec3 controller_forward = glm::column(controller_pos, 2);
+    float speed =
+        speed_ * static_cast<float>(time_info_->time_since_last_frame.count());
+    speed *=
+        -1.0f * controller->GetAxesValue(phx::VRController::AXES_TRACKPAD)[1];
+    transform->Translate(speed * controller_forward);
+  }
+
+  phx::info("speed_: {}, axes: {}, time: {}, button_pressed: {}", speed_,
+            controller->GetAxesValue(phx::VRController::AXES_TRACKPAD),
+            time_info_->time_since_last_frame.count(), button_pressed_);
+}
+
+void VRControllerNavigationBehavior::OnButtonSignal(
+    phx::VRController::ButtonId id, phx::VRController::ButtonEvent event) {
+  if (event == phx::VRController::BUTTON_TOUCH) {
+    if (id == vr::EVRButtonId::k_EButton_SteamVR_Touchpad) {
+      button_pressed_ = true;
+    }
+  }
+  if (event == phx::VRController::BUTTON_UNTOUCH &&
+      id == vr::EVRButtonId::k_EButton_SteamVR_Touchpad) {
+    button_pressed_ = false;
+  }
+}
+
+float VRControllerNavigationBehavior::GetNavigationSpeed() const {
+  return speed_;
+}
+
+void VRControllerNavigationBehavior::SetNavigationSpeed(float speed) {
+  speed_ = speed;
+}
+
+phx::VRController* VRControllerNavigationBehavior::GetController() {
+  for (auto controller : device_system_->GetDevices<phx::VRController>()) {
+    if (controller->GetSide() == side_) {
+      return controller;
+    }
+  }
+  return nullptr;
+}
diff --git a/demos/viewer/src/vrcontroller_navigation_behavior.hpp b/demos/viewer/src/vrcontroller_navigation_behavior.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2423ca508a6ea343fc986a77e7deb9613ef4afd5
--- /dev/null
+++ b/demos/viewer/src/vrcontroller_navigation_behavior.hpp
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+// 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 DEMOS_VIEWER_SRC_VRCONTROLLER_NAVIGATION_BEHAVIOR_HPP_
+#define DEMOS_VIEWER_SRC_VRCONTROLLER_NAVIGATION_BEHAVIOR_HPP_
+
+#include "phx/input/device_system.hpp"
+#include "phx/input/vr_controller.hpp"
+#include "phx/rendering/components/transform.hpp"
+#include "phx/scripting/behavior.hpp"
+
+class VRControllerNavigationBehavior : public phx::Behavior {
+ public:
+  explicit VRControllerNavigationBehavior(
+      phx::DeviceSystem* device_system, phx::VRController::ControllerSide side);
+  VRControllerNavigationBehavior(const VRControllerNavigationBehavior& that) =
+      default;
+  VRControllerNavigationBehavior(VRControllerNavigationBehavior&& temp) =
+      default;
+  virtual ~VRControllerNavigationBehavior() = default;
+  VRControllerNavigationBehavior& operator=(
+      const VRControllerNavigationBehavior& that) = default;
+  VRControllerNavigationBehavior& operator=(
+      VRControllerNavigationBehavior&& temp) = default;
+
+  void OnUpdate() override;
+  void OnButtonSignal(phx::VRController::ButtonId id,
+                      phx::VRController::ButtonEvent event);
+
+  float GetNavigationSpeed() const;
+  void SetNavigationSpeed(float speed);
+
+ private:
+  phx::DeviceSystem* device_system_;
+  phx::VRController* GetController();
+
+  float speed_ = 0.5f;  // m/s
+  bool button_pressed_ = false;
+  phx::VRController::ControllerSide side_ =
+      phx::VRController::INVALID_CONTROLLER;
+};
+
+#endif  // DEMOS_VIEWER_SRC_VRCONTROLLER_NAVIGATION_BEHAVIOR_HPP_
diff --git a/library/phx/core/engine.cpp b/library/phx/core/engine.cpp
index a122ee4c77fea5633ce5f77167247121f1903f35..ee1dc39af9649f1dba9870c73b8012aec0b04293 100644
--- a/library/phx/core/engine.cpp
+++ b/library/phx/core/engine.cpp
@@ -69,7 +69,8 @@ bool Engine::IsRunning() const { return is_running_; }
 void Engine::UpdateSystems() {
   // before updating all systems, update the frame timer
   frame_timer_.Tick();
-  for (auto& system : systems_) system->Update(frame_timer_.GetTimeInfo());
+  for (auto& system : systems_)
+    if (system->GetEnabled()) system->Update(frame_timer_.GetTimeInfo());
 }
 
 std::string Engine::ToString() const {
diff --git a/library/phx/core/engine.hpp b/library/phx/core/engine.hpp
index 8f2f9f38ce3c43499539959cf0065bc0f5cf5e5c..fabc9c0bd93fe3fa98a91c9c1dd28950dc96b83c 100644
--- a/library/phx/core/engine.hpp
+++ b/library/phx/core/engine.hpp
@@ -43,21 +43,19 @@ SUPPRESS_WARNINGS_END
 #include "phx/core/frame_timer.hpp"
 #include "phx/core/scene.hpp"
 #include "phx/core/system.hpp"
+#include "phx/export.hpp"
 #include "phx/scripting/behavior.hpp"
 #include "phx/utility/aspects/loggable.hpp"
 #include "phx/utility/orderable_list.hpp"
-#include "phx/export.hpp"
 
 namespace phx {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 class PHOENIX_EXPORT Engine final : public Loggable {
  public:
   Engine();
   Engine(const Engine&) = delete;
   Engine(Engine&&) = default;
-  ~Engine();
+  ~Engine() override;
 
   Engine& operator=(const Engine&) = delete;
   Engine& operator=(Engine&&) = default;
@@ -165,8 +163,6 @@ std::vector<std::tuple<Components*...>> Engine::GetFirstComponentsMany() const {
   return scene_->GetFirstComponentsMany<Components...>();
 }
 
-SUPPRESS_WARNINGS_END
-
 }  // namespace phx
 
 #endif  // LIBRARY_PHX_CORE_ENGINE_HPP_
diff --git a/library/phx/core/entity.hpp b/library/phx/core/entity.hpp
index d27cbcc4566d5e42ede9cc56ee7fe6db2ef2f62f..adfac064637694a8a3c8a65d30b9ace7ced65a5c 100644
--- a/library/phx/core/entity.hpp
+++ b/library/phx/core/entity.hpp
@@ -47,7 +47,7 @@ class PHOENIX_EXPORT Entity : public Nameable, public Loggable {
   explicit Entity(const std::string& name) : Nameable(name) {}
   Entity(const Entity&) = delete;
   Entity(Entity&&) = default;
-  virtual ~Entity() = default;
+  ~Entity() override = default;
 
   Entity& operator=(const Entity&) = delete;
   Entity& operator=(Entity&&) = default;
diff --git a/library/phx/core/scene.hpp b/library/phx/core/scene.hpp
index 6d4ba7d6b353587bbbc4f963ec86c058b66388e9..b4eb2d82437ab78d620b7d74c231bd2b85e4f74e 100644
--- a/library/phx/core/scene.hpp
+++ b/library/phx/core/scene.hpp
@@ -49,7 +49,7 @@ class PHOENIX_EXPORT Scene : public Nameable, public Loggable {
   Scene();
   Scene(const Scene&) = delete;
   Scene(Scene&&) = default;
-  ~Scene() = default;
+  ~Scene() override = default;
 
   Scene& operator=(const Scene&) = delete;
   Scene& operator=(Scene&&) = default;
diff --git a/library/phx/core/system.cpp b/library/phx/core/system.cpp
index 0dfaeae161117e6b39f67ee6b4eb3a6ec4aa2111..b4aab2b1d4ed24765c88aa2a621958799ce8a3ba 100644
--- a/library/phx/core/system.cpp
+++ b/library/phx/core/system.cpp
@@ -28,4 +28,8 @@ namespace phx {
 System::System(Engine* engine) : engine_(engine) {}
 phx::Engine* System::GetEngine() const { return engine_; }
 
+void System::SetEnabled(bool enabled) { enabled_ = enabled; }
+
+bool System::GetEnabled() const { return enabled_; }
+
 }  // namespace phx
diff --git a/library/phx/core/system.hpp b/library/phx/core/system.hpp
index 0cfa0b180bc6348b92e5b524bf3d2772be35fe34..90b468e32f4249bdae32e9e82aec01f3f5421b64 100644
--- a/library/phx/core/system.hpp
+++ b/library/phx/core/system.hpp
@@ -41,6 +41,9 @@ class PHOENIX_EXPORT System : public Loggable {
 
   Engine* GetEngine() const;
 
+  void SetEnabled(bool enabled);
+  bool GetEnabled() const;
+
  protected:
   explicit System(Engine* engine);
   System(const System&) = delete;
@@ -50,6 +53,8 @@ class PHOENIX_EXPORT System : public Loggable {
   System& operator=(System&&) = default;
 
   Engine* engine_ = nullptr;
+
+  bool enabled_ = true;
 };
 
 }  // namespace phx
diff --git a/library/phx/display/display_system.hpp b/library/phx/display/display_system.hpp
index fd06a71bcaa5b4dc3f3f3298da08113f5ce1105c..96d78f7f662980cff91fb7294972ba6cad778344 100644
--- a/library/phx/display/display_system.hpp
+++ b/library/phx/display/display_system.hpp
@@ -37,7 +37,7 @@ class PHOENIX_EXPORT DisplaySystem : public System {
  public:
   DisplaySystem(const DisplaySystem&) = delete;
   DisplaySystem(DisplaySystem&&) = default;
-  ~DisplaySystem();
+  ~DisplaySystem() override;
 
   DisplaySystem& operator=(const DisplaySystem&) = delete;
   DisplaySystem& operator=(DisplaySystem&&) = default;
diff --git a/library/phx/display/display_system_openvr.cpp b/library/phx/display/display_system_openvr.cpp
index a455ed5a07f6b738c4fd939f5402f22b04e55a8c..be1642e41657adba5f027f9e02f125b0956be080 100644
--- a/library/phx/display/display_system_openvr.cpp
+++ b/library/phx/display/display_system_openvr.cpp
@@ -33,6 +33,7 @@
 #include "phx/core/logger.hpp"
 #include "phx/core/runtime_component.hpp"
 #include "phx/core/scene.hpp"
+#include "phx/input/device_system.hpp"
 #include "phx/rendering/rendering_system.hpp"
 
 #undef CreateWindow
@@ -50,28 +51,20 @@ DisplaySystemOpenVR::DisplaySystemOpenVR(Engine* engine)
 }
 DisplaySystemOpenVR::~DisplaySystemOpenVR() {}
 
-phx::HMD* DisplaySystemOpenVR::CreateHMD() {
-  if (hmd_ == nullptr) {
-    hmd_ = std::make_unique<HMD>();
-  } else {
-    warn(
-        "[DisplaySystemOpenVR] HMD already created, so far only one is "
-        "supported.");
-  }
-  return hmd_.get();
+phx::HMD* DisplaySystemOpenVR::GetHMD() {
+  auto hmds = engine_->GetSystem<DeviceSystem>()->GetDevices<HMD>();
+  if (hmds.size() == 0) return nullptr;
+  return hmds[0];
 }
 
-void DisplaySystemOpenVR::DestroyHMD() { hmd_.reset(); }
-
-phx::HMD* DisplaySystemOpenVR::GetHMD() { return hmd_.get(); }
-
 void DisplaySystemOpenVR::Update(const FrameTimer::TimeInfo&) {
-  if (hmd_ != nullptr) {
+  HMD* hmd = GetHMD();
+  if (hmd != nullptr) {
     if (left_render_target_ != nullptr && right_render_target_ != nullptr) {
       auto right_texture = right_render_target_->GetColorTexture();
-      hmd_->Submit(HMD::RIGHT_EYE, right_texture);
+      hmd->Submit(HMD::RIGHT_EYE, right_texture);
       auto left_texture = left_render_target_->GetColorTexture();
-      hmd_->Submit(HMD::LEFT_EYE, left_texture);
+      hmd->Submit(HMD::LEFT_EYE, left_texture);
     }
   }
 }
diff --git a/library/phx/display/display_system_openvr.hpp b/library/phx/display/display_system_openvr.hpp
index 0fcdec3ddac0296a707c9e39afb980c87babd653..0e195ed9ba54aa8682d84d4853a14074fcee5245 100644
--- a/library/phx/display/display_system_openvr.hpp
+++ b/library/phx/display/display_system_openvr.hpp
@@ -30,8 +30,8 @@
 #include "phx/display/display_system.hpp"
 #include "phx/display/hmd.hpp"
 #include "phx/display/window.hpp"
-#include "phx/rendering/backend/render_target.hpp"
 #include "phx/export.hpp"
+#include "phx/rendering/backend/render_target.hpp"
 
 namespace phx {
 
@@ -40,22 +40,17 @@ class PHOENIX_EXPORT DisplaySystemOpenVR : public DisplaySystem {
   explicit DisplaySystemOpenVR(Engine* engine);
   DisplaySystemOpenVR(const DisplaySystemOpenVR&) = delete;
   DisplaySystemOpenVR(DisplaySystemOpenVR&&) = default;
-  ~DisplaySystemOpenVR();
+  ~DisplaySystemOpenVR() override;
 
   DisplaySystemOpenVR& operator=(const DisplaySystemOpenVR&) = delete;
   DisplaySystemOpenVR& operator=(DisplaySystemOpenVR&&) = default;
 
-  HMD* CreateHMD();
-  void DestroyHMD();
   HMD* GetHMD();
 
   void Update(const FrameTimer::TimeInfo&) override;
 
   void CreateRenderTargets(Scene* scene);
 
- protected:
-  std::unique_ptr<HMD> hmd_;
-
  private:
   void RemoveRenderTargets();
   void SetEyeProjections(Scene* scene);
diff --git a/library/phx/display/display_system_window.hpp b/library/phx/display/display_system_window.hpp
index 1b41d76bb2835b793bed04e3dc284ed03013a6c9..cf8a3b559bbcc58796be3b1e4b34b76d01c36392 100644
--- a/library/phx/display/display_system_window.hpp
+++ b/library/phx/display/display_system_window.hpp
@@ -41,7 +41,7 @@ class PHOENIX_EXPORT DisplaySystemWindow : public DisplaySystem {
   explicit DisplaySystemWindow(Engine* engine);
   DisplaySystemWindow(const DisplaySystemWindow&) = delete;
   DisplaySystemWindow(DisplaySystemWindow&&) = default;
-  ~DisplaySystemWindow();
+  ~DisplaySystemWindow() override;
 
   DisplaySystemWindow& operator=(const DisplaySystemWindow&) = delete;
   DisplaySystemWindow& operator=(DisplaySystemWindow&&) = default;
diff --git a/library/phx/display/hmd.cpp b/library/phx/display/hmd.cpp
index c43329e2076bf3ad6dca6f6bb04affa19112f743..f8d72a6ffaf9c5b2cff31fb25c1dad7c02abc725 100644
--- a/library/phx/display/hmd.cpp
+++ b/library/phx/display/hmd.cpp
@@ -25,7 +25,6 @@
 #include <algorithm>
 #include <array>
 #include <memory>
-#include <stdexcept>
 #include <string>
 #include <utility>
 #include <vector>
@@ -42,16 +41,10 @@ SUPPRESS_WARNINGS_END
 
 #include "phx/core/logger.hpp"
 #include "phx/resources/resource_manager.hpp"
-#include "phx/resources/resource_pointer.hpp"
 
 namespace phx {
-HMD::HMD() {
-  vr::HmdError hmd_error = vr::VRInitError_None;
-  vr_system_ = vr::VR_Init(&hmd_error, vr::VRApplication_Scene);
-  if (vr_system_ == nullptr || hmd_error != vr::VRInitError_None) {
-    error("HMD cannot be initialized with error-code: {}", hmd_error);
-    throw std::runtime_error("OpenVR cannot be initialized!");
-  }
+HMD::HMD() : TrackedDevice() {
+  id_ = vr::k_unTrackedDeviceIndex_Hmd;
 
   uint32_t x, y;
   vr_system_->GetRecommendedRenderTargetSize(&x, &y);
@@ -60,12 +53,9 @@ HMD::HMD() {
   projection_right_ = GetProjectionMatrixFromOpenVR(vr::Hmd_Eye::Eye_Right);
   projection_left_ = GetProjectionMatrixFromOpenVR(vr::Hmd_Eye::Eye_Left);
 
-  eye_to_head_right_ = GetEyeToHeadMatrixFromOpenVR(vr::Hmd_Eye::Eye_Right);
-  eye_to_head_left_ = GetEyeToHeadMatrixFromOpenVR(vr::Hmd_Eye::Eye_Left);
+  UpdateEyeToHeadMatrices();
 }
 
-HMD::~HMD() { vr::VR_Shutdown(); }
-
 bool HMD::IsHMDPresent() { return vr::VR_IsHmdPresent(); }
 
 void HMD::Submit(Side side, gl::texture_2d* texture) {
@@ -75,18 +65,7 @@ void HMD::Submit(Side side, gl::texture_2d* texture) {
   vr::VRCompositor()->Submit(static_cast<vr::EVREye>(side), &vr_texture);
 }
 
-std::vector<vr::TrackedDeviceIndex_t> HMD::GetControllerIndices() {
-  std::vector<std::uint32_t> indices(vr::k_unMaxTrackedDeviceCount);
-  vr::VRSystem()->GetSortedTrackedDeviceIndicesOfClass(
-      vr::TrackedDeviceClass_Controller, indices.data(),
-      static_cast<std::uint32_t>(indices.size()));
-  return indices;
-}
-
-vr::ETrackedControllerRole HMD::GetControllerRoleForTrackedDeviceIndex(
-    vr::TrackedDeviceIndex_t device_index) const {
-  return vr_system_->GetControllerRoleForTrackedDeviceIndex(device_index);
-}
+void HMD::UpdateDeviceIndex() { id_ = vr::k_unTrackedDeviceIndex_Hmd; }
 
 const glm::uvec2& HMD::GetViewportSize() const { return viewport_size_; }
 
@@ -104,142 +83,26 @@ const glm::mat4& HMD::GetEyeToHeadMatrix(Side side) const {
     return eye_to_head_left_;
 }
 
-void HMD::UpdateTrackedDevices() {
-  vr::VRCompositor()->WaitGetPoses(tracked_device_poses_,
-                                   vr::k_unMaxTrackedDeviceCount, NULL, 0);
-}
+void HMD::Update() {
+  last_wait_get_poses_.resize(vr::k_unMaxTrackedDeviceCount);
 
-glm::mat4 HMD::GetHeadTransformation() {
-  if (tracked_device_poses_[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid) {
-    glm::mat4 head = TransformToGlmMatrix(
-        tracked_device_poses_[vr::k_unTrackedDeviceIndex_Hmd]
+  vr::VRCompositor()->WaitGetPoses(&last_wait_get_poses_[0],
+                                   vr::k_unMaxTrackedDeviceCount, NULL, 0);
+  if (last_wait_get_poses_[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid) {
+    pose_ = TransformToGlmMatrix(
+        last_wait_get_poses_[vr::k_unTrackedDeviceIndex_Hmd]
             .mDeviceToAbsoluteTracking);
-    last_head_transformation_ = head;
-    return head;
+  } else {
+    debug("[HMD] HMD pose is invalid, use the last valid one");
   }
-  debug("[HMD] HMD pose is invalid, use the last valid one");
-  return last_head_transformation_;
-}
-
-glm::mat4 HMD::GetTransformationForRole(vr::ETrackedControllerRole role) {
-  auto controller_indices = GetControllerIndices();
-  for (auto i = 0u; i < controller_indices.size(); ++i)
-    if (vr_system_->GetControllerRoleForTrackedDeviceIndex(
-            controller_indices[i]) == role)
-      return TransformToGlmMatrix(tracked_device_poses_[controller_indices[i]]
-                                      .mDeviceToAbsoluteTracking);
-  debug(
-      "[HMD::GetTransformationForRole] Unable to find tranformation for role "
-      "{}",
-      role);
-  return glm::mat4();
-}
-
-glm::mat4 HMD::GetLeftControllerTransformation() {
-  return GetTransformationForRole(vr::TrackedControllerRole_LeftHand);
-}
 
-glm::mat4 HMD::GetRightControllerTransformation() {
-  return GetTransformationForRole(vr::TrackedControllerRole_RightHand);
+  TrackedDevice::Update();
 }
 
-std::unique_ptr<phx::Mesh> HMD::GetControllerMesh(Controller controller) {
-  auto model = GetControllerModel(controller);
-  if (model == nullptr) {
-    return nullptr;
+void HMD::OnOpenVREvent(const vr::VREvent_t& event) {
+  if (event.eventType == vr::VREvent_IpdChanged) {
+    UpdateEyeToHeadMatrices();
   }
-  auto mesh = std::make_unique<phx::Mesh>();
-  std::vector<glm::vec3> vertices;
-  std::vector<glm::vec3> normals;
-  std::vector<glm::vec2> texcoords;
-  for (std::size_t i = 0; i < model->unVertexCount; i++) {
-    vertices.push_back(glm::vec3(model->rVertexData[i].vPosition.v[0],
-                                 model->rVertexData[i].vPosition.v[1],
-                                 model->rVertexData[i].vPosition.v[2]));
-    normals.push_back(glm::vec3(model->rVertexData[i].vNormal.v[0],
-                                model->rVertexData[i].vNormal.v[1],
-                                model->rVertexData[i].vNormal.v[2]));
-    texcoords.push_back(glm::vec2(model->rVertexData[i].rfTextureCoord[0],
-                                  model->rVertexData[i].rfTextureCoord[1]));
-  }
-  std::vector<unsigned int> indices;
-  for (std::size_t i = 0; i < model->unTriangleCount * 3; i++) {
-    indices.push_back(model->rIndexData[i]);
-  }
-  mesh->SetVertices(std::move(vertices));
-  mesh->SetNormals(std::move(normals));
-  mesh->SetTextureCoords(std::move(texcoords));
-  mesh->SetIndices(std::move(indices));
-  return mesh;
-}
-
-vr::RenderModel_t* HMD::GetControllerModel(Controller controller) {
-  std::string rendermodel_name;
-  rendermodel_name.resize(1024);
-  auto controller_indices = GetControllerIndices();
-  for (auto i = 0u; i < controller_indices.size(); ++i) {
-    if (vr_system_->GetControllerRoleForTrackedDeviceIndex(
-            controller_indices[i]) ==
-        static_cast<vr::ETrackedControllerRole>(controller)) {
-      vr::VRSystem()->GetStringTrackedDeviceProperty(
-          controller_indices[i], vr::Prop_RenderModelName_String,
-          &rendermodel_name[0], static_cast<uint32_t>(rendermodel_name.size()));
-    }
-  }
-
-  vr::RenderModel_t* model = nullptr;
-  while (vr::VRRenderModels()->LoadRenderModel_Async(
-             &rendermodel_name[0], &model) == vr::VRRenderModelError_Loading) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(50));
-  }
-  if (model == nullptr) {
-    return nullptr;
-  }
-
-  return model;
-}
-
-std::unique_ptr<Material> HMD::GetControllerMaterial(Controller controller) {
-  auto model = GetControllerModel(controller);
-  if (model == nullptr) return nullptr;
-
-  auto material = std::make_unique<phx::Material>();
-  material->SetAmbientColor(glm::vec3(0.1, 0.1, 0.1));
-  material->SetSpecularColor(glm::vec3(0.3, 0.3, 0.3));
-
-  auto texture = ResourceManager::instance().DeclareResource<Image>(
-      {{"TYPE", "openVR"},
-       {"OpenVR_type", "texture"},
-       {"texture_id", model->diffuseTextureId},
-       {"side",
-        controller == HMD::Controller::LEFT_CONTROLLER ? "left" : "right"}});
-  texture.Load();
-  material->SetDiffuseImage(texture);
-
-  return material;
-}
-
-std::unique_ptr<Image> HMD::GetControllerTexture(int id) {
-  vr::RenderModel_TextureMap_t* texture_map;
-  while (vr::VRRenderModels()->LoadTexture_Async(id, &texture_map) ==
-         vr::VRRenderModelError_Loading) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(50));
-  }
-  if (texture_map == nullptr) {
-    return nullptr;
-  }
-
-  std::vector<unsigned char> image_data(texture_map->unWidth *
-                                        texture_map->unHeight * 4);
-  std::copy(texture_map->rubTextureMapData,
-            texture_map->rubTextureMapData + image_data.size(),
-            image_data.begin());
-  auto image = std::make_unique<phx::Image>(
-      &image_data[0], std::array<std::size_t, 2>{
-                      {static_cast<std::size_t>(texture_map->unWidth),
-                       static_cast<std::size_t>(texture_map->unHeight)}}, 32);
-
-  return image;
 }
 
 glm::mat4 HMD::GetProjectionMatrixFromOpenVR(const vr::Hmd_Eye eye) {
@@ -256,18 +119,9 @@ glm::mat4 HMD::GetEyeToHeadMatrixFromOpenVR(const vr::Hmd_Eye eye) {
   return TransformToGlmMatrix(steamvr_eye_head_matrix);
 }
 
-glm::mat4 HMD::TransformToGlmMatrix(const vr::HmdMatrix34_t& mat) {
-  return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], 0.0f, mat.m[0][1],
-                   mat.m[1][1], mat.m[2][1], 0.0f, mat.m[0][2], mat.m[1][2],
-                   mat.m[2][2], 0.0f, mat.m[0][3], mat.m[1][3], mat.m[2][3],
-                   1.0f);
-}
-
-glm::mat4 HMD::TransformToGlmMatrix(const vr::HmdMatrix44_t& mat) {
-  return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0],
-                   mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1],
-                   mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2],
-                   mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]);
+void HMD::UpdateEyeToHeadMatrices() {
+  eye_to_head_right_ = GetEyeToHeadMatrixFromOpenVR(vr::Hmd_Eye::Eye_Right);
+  eye_to_head_left_ = GetEyeToHeadMatrixFromOpenVR(vr::Hmd_Eye::Eye_Left);
 }
 
 }  // namespace phx
diff --git a/library/phx/display/hmd.hpp b/library/phx/display/hmd.hpp
index 4bf398e9009ead18a97c8aec08fbdce9345be680..430b874be50cb123ba399f139baefd0910981fa0 100644
--- a/library/phx/display/hmd.hpp
+++ b/library/phx/display/hmd.hpp
@@ -35,20 +35,20 @@ SUPPRESS_WARNINGS_BEGIN
 #include "openvr.h"  //NOLINT
 SUPPRESS_WARNINGS_END
 
+#include "phx/export.hpp"
+#include "phx/input/tracked_device.hpp"
 #include "phx/resources/types/material.hpp"
 #include "phx/resources/types/mesh.hpp"
-#include "phx/export.hpp"
 
 #include "gl/texture.hpp"
 
 namespace phx {
 
-class OpenVRResourceLoader;
-
-class PHOENIX_EXPORT HMD {
+class PHOENIX_EXPORT HMD : public TrackedDevice {
  public:
   HMD();
-  ~HMD();
+  ~HMD() = default;
+
   HMD(const HMD&) = delete;
   HMD(HMD&&) = default;
 
@@ -60,56 +60,31 @@ class PHOENIX_EXPORT HMD {
     LEFT_EYE = vr::EVREye::Eye_Left
   };
 
-  enum Controller {
-    RIGHT_CONTROLLER = vr::TrackedControllerRole_RightHand,
-    LEFT_CONTROLLER = vr::TrackedControllerRole_LeftHand
-  };
+  void Update() override;
+  void OnOpenVREvent(const vr::VREvent_t& event) override;
 
   const glm::uvec2& GetViewportSize() const;
 
   const glm::mat4& GetProjectionMatrix(Side side) const;
   const glm::mat4& GetEyeToHeadMatrix(Side side) const;
 
-  void UpdateTrackedDevices();
-  glm::mat4 GetHeadTransformation();
-  glm::mat4 GetLeftControllerTransformation();
-  glm::mat4 GetRightControllerTransformation();
-
   static bool IsHMDPresent();
 
   void Submit(Side side, gl::texture_2d* texture);
 
-  std::vector<vr::TrackedDeviceIndex_t> GetControllerIndices();
-  vr::ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex(
-      vr::TrackedDeviceIndex_t device_index) const;
+  void UpdateDeviceIndex() override;
 
  private:
-  glm::mat4 GetTransformationForRole(vr::ETrackedControllerRole role);
-
   glm::mat4 GetProjectionMatrixFromOpenVR(const vr::Hmd_Eye eye);
   glm::mat4 GetEyeToHeadMatrixFromOpenVR(const vr::Hmd_Eye eye);
+  void UpdateEyeToHeadMatrices();
 
-  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);
-
-  vr::IVRSystem* vr_system_;
   glm::uvec2 viewport_size_;
 
   glm::mat4 projection_right_;
   glm::mat4 projection_left_;
   glm::mat4 eye_to_head_right_;
   glm::mat4 eye_to_head_left_;
-
-  vr::TrackedDevicePose_t tracked_device_poses_
-      [vr::k_unMaxTrackedDeviceCount];  // NOLINT(runtime/arrays)
-
-  glm::mat4 last_head_transformation_;
 };
 
 }  // namespace phx
diff --git a/library/phx/input/device.cpp b/library/phx/input/device.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5c68859d70073a2d10a384ed647c75afdd66c9b0
--- /dev/null
+++ b/library/phx/input/device.cpp
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+// 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 "phx/input/device.hpp"
+
+namespace phx {}  // namespace phx
diff --git a/library/phx/input/device.hpp b/library/phx/input/device.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c477639764a905efdc712809d5a74034be7aaf98
--- /dev/null
+++ b/library/phx/input/device.hpp
@@ -0,0 +1,38 @@
+//------------------------------------------------------------------------------
+// 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_INPUT_DEVICE_HPP_
+#define LIBRARY_PHX_INPUT_DEVICE_HPP_
+
+#include "phx/export.hpp"
+
+namespace phx {
+
+class PHOENIX_EXPORT Device {
+ public:
+  virtual ~Device() = default;
+  virtual void Update() = 0;
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_INPUT_DEVICE_HPP_
diff --git a/tests/test_utilities/dummy_material_generator.cpp b/library/phx/input/device_system.cpp
similarity index 57%
rename from tests/test_utilities/dummy_material_generator.cpp
rename to library/phx/input/device_system.cpp
index 4dcf8e004a7d1bd854f0f734247ece4c1d28d155..c19d7947ef0a3d69cfecb728c33ddf9de591405a 100644
--- a/tests/test_utilities/dummy_material_generator.cpp
+++ b/library/phx/input/device_system.cpp
@@ -20,32 +20,34 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#include "dummy_material_generator.hpp"
+#include "phx/input/device_system.hpp"
 
 #include <memory>
+#include <string>
 #include <utility>
-#include <vector>
 
-#include "phx/resources/types/material.hpp"
+#include "phx/core/logger.hpp"
 
 namespace phx {
+DeviceSystem::DeviceSystem(Engine* engine) : System(engine) {}
 
-DummyMaterialGenerator::DummyMaterialGenerator(glm::vec3 diffuse_color,
-                                               glm::vec3 specular_color,
-                                               glm::vec3 ambient_color,
-                                               float shininess)
-    : material_{std::make_unique<phx::Material>()} {
-  material_->SetAmbientColor(ambient_color);
-  material_->SetDiffuseColor(diffuse_color);
-  material_->SetSpecularColor(specular_color);
-  material_->SetShininess(shininess);
+DeviceSystem::~DeviceSystem() {}
+
+void DeviceSystem::Update(const FrameTimer::TimeInfo&) {
+  for (auto& device : devices_) {
+    device->Update();
+  }
 }
 
-std::unique_ptr<phx::Resource> DummyMaterialGenerator::Load(
-    const ResourceDeclaration&) {
-  auto new_material = std::make_unique<phx::Material>();
-  *new_material = *material_;
-  return new_material;
+void DeviceSystem::RemoveDevice(Device* device) {
+  auto iterator =
+      std::remove_if(devices_.begin(), devices_.end(),
+                     [device](const std::unique_ptr<Device>& iteratee) {
+                       return device == iteratee.get();
+                     });
+  devices_.erase(iterator, devices_.end());
 }
 
+std::string DeviceSystem::ToString() const { return "DeviceSystem"; }
+
 }  // namespace phx
diff --git a/library/phx/input/device_system.hpp b/library/phx/input/device_system.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..644b8b471f4a3ed5b0ce43f5628dc33c7128ff6d
--- /dev/null
+++ b/library/phx/input/device_system.hpp
@@ -0,0 +1,84 @@
+//------------------------------------------------------------------------------
+// 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_INPUT_DEVICE_SYSTEM_HPP_
+#define LIBRARY_PHX_INPUT_DEVICE_SYSTEM_HPP_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "phx/core/engine.hpp"
+#include "phx/core/system.hpp"
+#include "phx/export.hpp"
+#include "phx/input/device.hpp"
+
+namespace phx {
+class InputSystem;
+
+class PHOENIX_EXPORT DeviceSystem : public System {
+ public:
+  DeviceSystem() = delete;
+  DeviceSystem(const DeviceSystem&) = delete;
+  DeviceSystem(DeviceSystem&&) = default;
+  virtual ~DeviceSystem();
+
+  DeviceSystem& operator=(const DeviceSystem&) = delete;
+  DeviceSystem& operator=(DeviceSystem&&) = default;
+
+  void Update(const FrameTimer::TimeInfo& time_info) override;
+
+  template <typename DeviceType>
+  std::vector<DeviceType*> GetDevices() {
+    static_assert(std::is_base_of<Device, DeviceType>::value,
+                  "The type does not inherit from Device.");
+
+    std::vector<DeviceType*> devices;
+    for (const auto& device : devices_) {
+      auto casted_device = dynamic_cast<DeviceType*>(device.get());
+      if (casted_device != nullptr) {
+        devices.push_back(casted_device);
+      }
+    }
+    return devices;
+  }
+
+  template <typename DeviceType, typename... ComponentArguments>
+  DeviceType* AddDevice(ComponentArguments&&... arguments) {
+    devices_.push_back(std::make_unique<DeviceType>(arguments...));
+    return dynamic_cast<DeviceType*>(devices_.back().get());
+  }
+
+  void RemoveDevice(Device* device);
+
+  std::string ToString() const override;
+
+ private:
+  friend DeviceSystem* Engine::CreateSystem<DeviceSystem>();
+  explicit DeviceSystem(Engine* engine);
+
+  std::vector<std::unique_ptr<Device>> devices_;
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_INPUT_DEVICE_SYSTEM_HPP_
diff --git a/library/phx/input/input_system.cpp b/library/phx/input/input_system.cpp
index 04dc951c7076fed99d97ae33057d46f71f751e74..5b68fd671b36d533dc6a57822b7b4afba9b7590d 100644
--- a/library/phx/input/input_system.cpp
+++ b/library/phx/input/input_system.cpp
@@ -59,7 +59,7 @@ boost::signals2::connection InputSystem::AddKeyReleaseCallback(
 }
 
 boost::signals2::connection InputSystem::AddMouseMoveCallback(
-    const std::function<void(unsigned, unsigned)>& callback) {
+    const std::function<void(int, int)>& callback) {
   return mouse_move_signal_.connect(callback);
 }
 
@@ -89,8 +89,8 @@ void InputSystem::UpdateSDLEvents() {
         key_release_signal_(static_cast<char>(event.key.keysym.sym));
         break;
       case SDL_MOUSEMOTION:
-        mouse_move_signal_(static_cast<unsigned int>(event.motion.xrel),
-                           static_cast<unsigned int>(event.motion.yrel));
+        mouse_move_signal_(static_cast<int>(event.motion.xrel),
+                           static_cast<int>(event.motion.yrel));
         break;
       case SDL_MOUSEBUTTONDOWN:
         mouse_press_signal_(event.button.button);
diff --git a/library/phx/input/input_system.hpp b/library/phx/input/input_system.hpp
index 4793479991c68338442ba5c7a35c2b64837f9463..9862ea78119438869c1e37162909df8c9faba169 100644
--- a/library/phx/input/input_system.hpp
+++ b/library/phx/input/input_system.hpp
@@ -45,7 +45,7 @@ class PHOENIX_EXPORT InputSystem : public System {
   InputSystem() = delete;
   InputSystem(const InputSystem&) = delete;
   InputSystem(InputSystem&&) = default;
-  virtual ~InputSystem();
+  ~InputSystem() override;
 
   void Update(const FrameTimer::TimeInfo& time_info) override;
 
@@ -59,7 +59,7 @@ class PHOENIX_EXPORT InputSystem : public System {
   boost::signals2::connection AddKeyReleaseCallback(
       const std::function<void(char)>& callback);
   boost::signals2::connection AddMouseMoveCallback(
-      const std::function<void(unsigned int, unsigned int)>& callback);
+      const std::function<void(int, int)>& callback);
   boost::signals2::connection AddMousePressCallback(
       const std::function<void(unsigned int)>& callback);
   boost::signals2::connection AddMouseReleaseCallback(
@@ -73,20 +73,12 @@ class PHOENIX_EXPORT InputSystem : public System {
 
   void UpdateSDLEvents();
 
-  // TODO(@tvierjahn) check if Wpadded is here only emitted by some clang
-  // versions.
-  // There will be only one input system.Padding will not waste
-  // significant memory
-  SUPPRESS_WARNINGS_BEGIN_PADDED
-
   boost::signals2::signal<void()> quit_signal_;
   boost::signals2::signal<void(char)> key_press_signal_;
   boost::signals2::signal<void(char)> key_release_signal_;
-  boost::signals2::signal<void(unsigned int, unsigned int)> mouse_move_signal_;
+  boost::signals2::signal<void(int, int)> mouse_move_signal_;
   boost::signals2::signal<void(unsigned int)> mouse_press_signal_;
   boost::signals2::signal<void(unsigned int)> mouse_release_signal_;
-
-  SUPPRESS_WARNINGS_END
 };
 
 }  // namespace phx
diff --git a/library/phx/input/openvr_controller_behavior.cpp b/library/phx/input/openvr_controller_behavior.cpp
index cd229828f4a407039f843dc5219e07d7d5c6aaca..fefc8a7c802f0c27dc679072401cffb559017d98 100644
--- a/library/phx/input/openvr_controller_behavior.cpp
+++ b/library/phx/input/openvr_controller_behavior.cpp
@@ -31,14 +31,14 @@ namespace phx {
 void OpenVRControllerBehavior::OnUpdate() {
   phx::Scene* scene = GetEntity()->GetScene();
   phx::Entity* runtime_entity = nullptr;
-  if (side_ == Side::LEFT) {
+  if (side_ == VRController::LEFT_CONTROLLER) {
     auto runtime_entities = scene->GetEntitiesWithComponents<
         phx::RuntimeComponent<phx::LEFT_CONTROLLER>>();
     if (!runtime_entities.empty()) {
       runtime_entity = runtime_entities[0];
     }
   }
-  if (side_ == Side::RIGHT) {
+  if (side_ == VRController::RIGHT_CONTROLLER) {
     auto runtime_entities = scene->GetEntitiesWithComponents<
         phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>();
     if (!runtime_entities.empty()) {
@@ -53,9 +53,11 @@ void OpenVRControllerBehavior::OnUpdate() {
   }
 }
 
-void OpenVRControllerBehavior::SetSide(Side side) { side_ = side; }
+void OpenVRControllerBehavior::SetSide(VRController::ControllerSide side) {
+  side_ = side;
+}
 
-OpenVRControllerBehavior::Side OpenVRControllerBehavior::GetSide() const {
+VRController::ControllerSide OpenVRControllerBehavior::GetSide() const {
   return side_;
 }
 
diff --git a/library/phx/input/openvr_controller_behavior.hpp b/library/phx/input/openvr_controller_behavior.hpp
index c31643b67d511b4a813eb82e0d75db5f0039648d..9f8ce400b7a42d04937543ca2daf4df505361814 100644
--- a/library/phx/input/openvr_controller_behavior.hpp
+++ b/library/phx/input/openvr_controller_behavior.hpp
@@ -25,24 +25,21 @@
 
 #include "phx/suppress_warnings.hpp"
 
-#include "phx/scripting/behavior.hpp"
 #include "phx/export.hpp"
+#include "phx/input/vr_controller.hpp"
+#include "phx/scripting/behavior.hpp"
 
 namespace phx {
-SUPPRESS_WARNINGS_BEGIN_PADDED
 class PHOENIX_EXPORT OpenVRControllerBehavior : public Behavior {
  public:
-  enum Side { LEFT, RIGHT };
-
   void OnUpdate() override;
 
-  void SetSide(Side side);
-  Side GetSide() const;
+  void SetSide(VRController::ControllerSide side);
+  VRController::ControllerSide GetSide() const;
 
  private:
-  Side side_ = Side::LEFT;
+  VRController::ControllerSide side_ = VRController::LEFT_CONTROLLER;
 };
-SUPPRESS_WARNINGS_END
 }  // namespace phx
 
 #endif  // LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_BEHAVIOR_HPP_
diff --git a/library/phx/input/openvr_controller_system.cpp b/library/phx/input/openvr_controller_model_system.cpp
similarity index 75%
rename from library/phx/input/openvr_controller_system.cpp
rename to library/phx/input/openvr_controller_model_system.cpp
index aa92e57998c97dfd0d9a6180c660d2a2ac6a67e2..b1cffb951508ae025ec6b8962a1ddafdb8b05c94 100644
--- a/library/phx/input/openvr_controller_system.cpp
+++ b/library/phx/input/openvr_controller_model_system.cpp
@@ -20,7 +20,7 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#include "phx/input/openvr_controller_system.hpp"
+#include "phx/input/openvr_controller_model_system.hpp"
 
 #include <memory>
 #include <string>
@@ -29,6 +29,7 @@
 #include "phx/core/logger.hpp"
 #include "phx/display/display_system_openvr.hpp"
 #include "phx/input/openvr_controller_behavior.hpp"
+#include "phx/input/vr_controller.hpp"
 #include "phx/rendering/components/resource_component.hpp"
 #include "phx/rendering/components/transform.hpp"
 #include "phx/resources/loaders/openvr_resource_loader.hpp"
@@ -37,18 +38,11 @@
 
 namespace phx {
 
-OpenVRControllerSystem::OpenVRControllerSystem(Engine* engine,
-                                               DisplaySystem* display_system)
-    : System(engine) {
-  // find HMD
-  hmd_ = dynamic_cast<DisplaySystemOpenVR*>(display_system)->GetHMD();
-  if (hmd_ == nullptr) {
-    error("Cannot use OpenVRControllerSystem without an HMD!");
-    return;
-  }
-
+OpenVRControllerModelSystem::OpenVRControllerModelSystem(
+    Engine* engine, DeviceSystem* device_system)
+    : System(engine), device_system_(device_system) {
   auto& resource_manager = ResourceManager::instance();
-  auto openvr_loader = std::make_unique<OpenVRResourceLoader>(hmd_);
+  auto openvr_loader = std::make_unique<OpenVRResourceLoader>(device_system);
   resource_manager.RegisterResourceType("openVR", std::move(openvr_loader));
 }
 
@@ -57,11 +51,11 @@ OpenVRControllerSystem::OpenVRControllerSystem(Engine* engine,
 #pragma clang diagnostic ignored "-Wmissing-prototypes"
 #endif
 
-Entity* OpenVRControllerSystem::AddController(
-    phx::Scene* scene, OpenVRControllerBehavior::Side side) {
+Entity* OpenVRControllerModelSystem::AddController(
+    phx::Scene* scene, VRController::ControllerSide side) {
   auto& resource_manager = ResourceManager::instance();
   std::string side_string =
-      side == OpenVRControllerBehavior::LEFT ? "left" : "right";
+      side == VRController::LEFT_CONTROLLER ? "left" : "right";
 
   ResourceDeclaration mesh_declaration{
       {"TYPE", "openVR"}, {"OpenVR_type", "mesh"}, {"side", side_string}};
@@ -81,9 +75,9 @@ Entity* OpenVRControllerSystem::AddController(
   return nullptr;
 }
 
-Entity* OpenVRControllerSystem::AddControllerEntity(
+Entity* OpenVRControllerModelSystem::AddControllerEntity(
     phx::Scene* scene, ResourcePointer<Mesh> mesh,
-    ResourcePointer<Material> material, OpenVRControllerBehavior::Side side) {
+    ResourcePointer<Material> material, VRController::ControllerSide side) {
   Entity* controller = scene->CreateEntity();
   controller->AddComponent<ResourceComponent<Mesh>>(mesh);
   controller->AddComponent<Transform>();
@@ -97,10 +91,9 @@ Entity* OpenVRControllerSystem::AddControllerEntity(
 #pragma clang diagnostic pop
 #endif
 
-void OpenVRControllerSystem::Update(const FrameTimer::TimeInfo&) {
+void OpenVRControllerModelSystem::Update(const FrameTimer::TimeInfo&) {
   // check which controllers are active and update their scene representation,
   // if necessary
-  if (!hmd_) return;
   auto scene = GetEngine()->GetScene();
   if (scene == nullptr) return;
 
@@ -111,31 +104,30 @@ void OpenVRControllerSystem::Update(const FrameTimer::TimeInfo&) {
       GetEngine()->GetEntitiesWithComponents<OpenVRControllerBehavior>();
   for (auto entity : controller_entities) {
     if (entity->GetFirstComponent<OpenVRControllerBehavior>()->GetSide() ==
-        OpenVRControllerBehavior::LEFT) {
+        VRController::LEFT_CONTROLLER) {
       left_controller_entity = entity;
     } else {
       right_controller_entity = entity;
     }
   }
 
-  auto controller_indices = hmd_->GetControllerIndices();
   bool left_controller_active = false;
   bool right_controller_active = false;
-  for (auto idx : controller_indices) {
-    // is it a left controller?
-    auto role = hmd_->GetControllerRoleForTrackedDeviceIndex(idx);
-    if (role == vr::TrackedControllerRole_LeftHand) {
+
+  for (auto controller : device_system_->GetDevices<VRController>()) {
+    if (!controller->IsActive()) continue;
+    if (controller->GetSide() == VRController::LEFT_CONTROLLER) {
       // do we have a left controller in the scene?
       if (left_controller_entity == nullptr) {
         // create that controller
         left_controller_entity =
-            AddController(scene.get(), OpenVRControllerBehavior::LEFT);
+            AddController(scene.get(), VRController::LEFT_CONTROLLER);
       }
       left_controller_active = true;
-    } else if (role == vr::TrackedControllerRole_RightHand) {
+    } else if (controller->GetSide() == VRController::RIGHT_CONTROLLER) {
       if (right_controller_entity == nullptr) {
         right_controller_entity =
-            AddController(scene.get(), OpenVRControllerBehavior::RIGHT);
+            AddController(scene.get(), VRController::RIGHT_CONTROLLER);
       }
       right_controller_active = true;
     }
diff --git a/library/phx/input/openvr_controller_system.hpp b/library/phx/input/openvr_controller_model_system.hpp
similarity index 64%
rename from library/phx/input/openvr_controller_system.hpp
rename to library/phx/input/openvr_controller_model_system.hpp
index c382d2585a76e56a4c2cdcaa7215bb7c9fa09011..693e4d4d864091a269509d3f3f94a082536b5fc6 100644
--- a/library/phx/input/openvr_controller_system.hpp
+++ b/library/phx/input/openvr_controller_model_system.hpp
@@ -20,8 +20,8 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#ifndef LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_SYSTEM_HPP_
-#define LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_SYSTEM_HPP_
+#ifndef LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_MODEL_SYSTEM_HPP_
+#define LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_MODEL_SYSTEM_HPP_
 
 #include <memory>
 #include <vector>
@@ -34,40 +34,43 @@ SUPPRESS_WARNINGS_END
 
 #include "phx/core/engine.hpp"
 #include "phx/core/system.hpp"
-#include "phx/display/display_system.hpp"
 #include "phx/display/hmd.hpp"
-#include "phx/input/openvr_controller_behavior.hpp"
 #include "phx/export.hpp"
+#include "phx/input/device_system.hpp"
+#include "phx/input/openvr_controller_behavior.hpp"
+#include "phx/input/vr_controller.hpp"
 
 namespace phx {
-class PHOENIX_EXPORT OpenVRControllerSystem : public System {
+class PHOENIX_EXPORT OpenVRControllerModelSystem : public System {
  public:
-  OpenVRControllerSystem() = delete;
-  OpenVRControllerSystem(const OpenVRControllerSystem&) = delete;
-  OpenVRControllerSystem(OpenVRControllerSystem&&) = default;
-  virtual ~OpenVRControllerSystem() = default;
+  OpenVRControllerModelSystem() = delete;
+  OpenVRControllerModelSystem(const OpenVRControllerModelSystem&) = delete;
+  OpenVRControllerModelSystem(OpenVRControllerModelSystem&&) = default;
+  ~OpenVRControllerModelSystem() override = default;
 
-  OpenVRControllerSystem& operator=(const OpenVRControllerSystem&) = delete;
-  OpenVRControllerSystem& operator=(OpenVRControllerSystem&&) = default;
+  OpenVRControllerModelSystem& operator=(const OpenVRControllerModelSystem&) =
+      delete;
+  OpenVRControllerModelSystem& operator=(OpenVRControllerModelSystem&&) =
+      default;
 
   void Update(const FrameTimer::TimeInfo&) override;
 
  protected:
   template <typename SystemType, typename... SystemArguments>
   friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments);
-  OpenVRControllerSystem(Engine* engine, DisplaySystem* display_system);
+  OpenVRControllerModelSystem(Engine* engine, DeviceSystem* device_system);
 
  private:
   static Entity* AddControllerEntity(phx::Scene* scene,
                                      ResourcePointer<Mesh> mesh,
                                      ResourcePointer<Material> material,
-                                     OpenVRControllerBehavior::Side side);
+                                     VRController::ControllerSide side);
   static Entity* AddController(phx::Scene* scene,
-                               OpenVRControllerBehavior::Side side);
+                               VRController::ControllerSide side);
 
-  HMD* hmd_ = nullptr;
+  DeviceSystem* device_system_ = nullptr;
 };
 
 }  // namespace phx
 
-#endif  // LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_SYSTEM_HPP_
+#endif  // LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_MODEL_SYSTEM_HPP_
diff --git a/library/phx/input/tracked_device.cpp b/library/phx/input/tracked_device.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0800a10dc0068f407972ce018bb7a60a2d17636a
--- /dev/null
+++ b/library/phx/input/tracked_device.cpp
@@ -0,0 +1,139 @@
+//------------------------------------------------------------------------------
+// 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 "phx/input/tracked_device.hpp"
+
+#include <vector>
+
+#include "phx/core/entity.hpp"
+#include "phx/core/logger.hpp"
+
+namespace phx {
+
+vr::IVRSystem* TrackedDevice::vr_system_ = nullptr;
+int TrackedDevice::reference_counter_ = 0;
+std::mutex TrackedDevice::openVR_init_mutex_;
+TrackedDevice::OpenVREventDistributer TrackedDevice::vr_event_distributer;
+std::vector<vr::TrackedDevicePose_t> TrackedDevice::last_wait_get_poses_;
+
+TrackedDevice::TrackedDevice() {
+  openVR_init_mutex_.lock();
+  if (vr_system_ == nullptr) {
+    vr::EVRInitError init_error = vr::VRInitError_None;
+    vr_system_ = vr::VR_Init(&init_error, vr::VRApplication_Scene);
+    if (vr_system_ == nullptr || init_error != vr::VRInitError_None) {
+      error("OpenVR cannot be initialized with error-code: {}", init_error);
+      throw std::runtime_error("OpenVR cannot be initialized!");
+    }
+  }
+  vr_event_distributer.AddDevice(this);
+  reference_counter_++;
+  openVR_init_mutex_.unlock();
+}
+
+TrackedDevice::~TrackedDevice() {
+  openVR_init_mutex_.lock();
+  reference_counter_--;
+  if (reference_counter_ == 0) {
+    vr::VR_Shutdown();
+    vr_system_ = nullptr;
+  }
+  vr_event_distributer.RemoveDevice(this);
+  openVR_init_mutex_.unlock();
+}
+
+void TrackedDevice::Update() {
+  if (id_ == vr::k_unTrackedDeviceIndexInvalid) return;
+
+  vr::TrackedDevicePose_t tracked_device_pose;
+  if (last_wait_get_poses_.size() == vr::k_unMaxTrackedDeviceCount) {
+    // the HMD has aquired the poses of all devices with WaitGetPoses()
+    tracked_device_pose = last_wait_get_poses_[id_];
+  } else {
+    // this tracked device has to get its pose itself directly from openVR
+    // this method however has a higher lag
+    vr::VRControllerState_t controller_state;
+    vr_system_->GetControllerStateWithPose(
+        vr::TrackingUniverseStanding, id_, &controller_state,
+        sizeof(controller_state), &tracked_device_pose);
+  }
+  if (tracked_device_pose.bPoseIsValid) {
+    pose_ = TransformToGlmMatrix(tracked_device_pose.mDeviceToAbsoluteTracking);
+  } else {
+    debug(
+        "[TrackedDevice] Tracked Device pose is invalid, device is marked as "
+        "invalid. The actual implementation has to find another valid index");
+    id_ = vr::k_unTrackedDeviceIndexInvalid;
+  }
+
+  vr_event_distributer.Update();
+}
+
+glm::mat4 TrackedDevice::GetPose() const { return pose_; }
+
+bool TrackedDevice::IsActive() const {
+  return id_ != vr::k_unTrackedDeviceIndexInvalid;
+}
+
+void TrackedDevice::OpenVREventDistributer::AddDevice(TrackedDevice* device) {
+  tracked_devices_.push_back(device);
+}
+
+void TrackedDevice::OpenVREventDistributer::RemoveDevice(
+    TrackedDevice* device) {
+  auto iterator =
+      std::remove_if(tracked_devices_.begin(), tracked_devices_.end(),
+                     [device](Device* iteratee) { return device == iteratee; });
+  tracked_devices_.erase(iterator, tracked_devices_.end());
+}
+
+void TrackedDevice::OpenVREventDistributer::Update() {
+  vr::VREvent_t event;
+  while (vr::VRSystem()->PollNextEvent(&event, sizeof(event))) {
+    for (TrackedDevice* device : tracked_devices_) {
+      device->OnOpenVREvent(event);
+    }
+  }
+}
+glm::mat4 TrackedDevice::TransformToGlmMatrix(const vr::HmdMatrix34_t& mat) {
+  return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], 0.0f, mat.m[0][1],
+                   mat.m[1][1], mat.m[2][1], 0.0f, mat.m[0][2], mat.m[1][2],
+                   mat.m[2][2], 0.0f, mat.m[0][3], mat.m[1][3], mat.m[2][3],
+                   1.0f);
+}
+
+glm::mat4 TrackedDevice::TransformToGlmMatrix(const vr::HmdMatrix44_t& mat) {
+  return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0],
+                   mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1],
+                   mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2],
+                   mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]);
+}
+
+std::vector<vr::TrackedDeviceIndex_t> TrackedDevice::GetDeviceIndicesForClass(
+    vr::ETrackedDeviceClass device_class) {
+  std::vector<std::uint32_t> indices(vr::k_unMaxTrackedDeviceCount);
+  vr::VRSystem()->GetSortedTrackedDeviceIndicesOfClass(
+      device_class, indices.data(), static_cast<std::uint32_t>(indices.size()));
+  return indices;
+}
+
+}  // namespace phx
diff --git a/library/phx/input/tracked_device.hpp b/library/phx/input/tracked_device.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d02805708c3326d5f9e6cbf24954f938b4e8c79f
--- /dev/null
+++ b/library/phx/input/tracked_device.hpp
@@ -0,0 +1,87 @@
+//------------------------------------------------------------------------------
+// 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_INPUT_TRACKED_DEVICE_HPP_
+#define LIBRARY_PHX_INPUT_TRACKED_DEVICE_HPP_
+
+#include <vector>
+
+#include "phx/input/device.hpp"
+#include "phx/resources/types/material.hpp"
+#include "phx/resources/types/mesh.hpp"
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/mat4x4.hpp"
+
+#include "openvr.h"  //NOLINT
+SUPPRESS_WARNINGS_END
+
+#include "phx/export.hpp"
+
+namespace phx {
+
+class PHOENIX_EXPORT TrackedDevice : public Device {
+ public:
+  TrackedDevice();
+  virtual ~TrackedDevice();
+
+  virtual void Update();
+
+  glm::mat4 GetPose() const;
+  bool IsActive() const;
+
+  virtual void UpdateDeviceIndex() = 0;
+  virtual void OnOpenVREvent(const vr::VREvent_t& event) = 0;
+
+ protected:
+  class OpenVREventDistributer {
+   public:
+    void Update();
+    void AddDevice(TrackedDevice* device);
+    void RemoveDevice(TrackedDevice* device);
+
+   private:
+    std::vector<TrackedDevice*> tracked_devices_;
+  };
+
+  static vr::IVRSystem* vr_system_;
+  static OpenVREventDistributer vr_event_distributer;
+  static std::vector<vr::TrackedDevicePose_t> last_wait_get_poses_;
+  glm::mat4 pose_;
+
+  static glm::mat4 TransformToGlmMatrix(const vr::HmdMatrix34_t& mat);
+  static glm::mat4 TransformToGlmMatrix(const vr::HmdMatrix44_t& mat);
+
+  std::vector<vr::TrackedDeviceIndex_t> GetDeviceIndicesForClass(
+      vr::ETrackedDeviceClass device_class);
+
+  vr::TrackedDeviceIndex_t id_ = vr::k_unTrackedDeviceIndexInvalid;
+
+ private:
+  static int reference_counter_;
+  static std::mutex openVR_init_mutex_;
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_INPUT_TRACKED_DEVICE_HPP_
diff --git a/library/phx/input/vr_controller.cpp b/library/phx/input/vr_controller.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..97d39c52d8aa704808f833300da0e80049152fa4
--- /dev/null
+++ b/library/phx/input/vr_controller.cpp
@@ -0,0 +1,141 @@
+//------------------------------------------------------------------------------
+// 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 "phx/input/vr_controller.hpp"
+
+#include <string>
+#include <vector>
+
+#include "phx/core/logger.hpp"
+
+namespace phx {
+
+VRController::VRController(ControllerSide side) : TrackedDevice(), side_(side) {
+  VRController::Update();
+}
+
+void VRController::Update() {
+  if (id_ == vr::k_unTrackedDeviceIndexInvalid ||
+      GetControllerRoleForTrackedDeviceIndex(id_) != side_) {
+    UpdateDeviceIndex();
+  }
+  TrackedDevice::Update();
+}
+
+void VRController::OnOpenVREvent(const vr::VREvent_t& event) {
+  if (event.trackedDeviceIndex != id_) return;
+  if (event.eventType == vr::VREvent_ButtonPress) {
+    button_signal_(static_cast<ButtonId>(event.data.controller.button),
+                   ButtonEvent::BUTTON_PRESSED);
+  } else if (event.eventType == vr::VREvent_ButtonUnpress) {
+    button_signal_(static_cast<ButtonId>(event.data.controller.button),
+                   ButtonEvent::BUTTON_RELEASED);
+  } else if (event.eventType == vr::VREvent_ButtonTouch) {
+    button_signal_(static_cast<ButtonId>(event.data.controller.button),
+                   ButtonEvent::BUTTON_TOUCH);
+  } else if (event.eventType == vr::VREvent_ButtonUntouch) {
+    button_signal_(static_cast<ButtonId>(event.data.controller.button),
+                   ButtonEvent::BUTTON_UNTOUCH);
+  }
+}
+
+void VRController::UpdateDeviceIndex() {
+  auto controller_indices =
+      GetDeviceIndicesForClass(vr::TrackedDeviceClass_Controller);
+  id_ = vr::k_unTrackedDeviceIndexInvalid;
+  for (auto index : controller_indices) {
+    if (index != vr::k_unTrackedDeviceIndex_Hmd &&
+        GetControllerRoleForTrackedDeviceIndex(index) == side_) {
+      id_ = index;
+      return;
+    }
+  }
+}
+
+VRController::ControllerSide VRController::GetSide() const { return side_; }
+
+vr::RenderModel_t* VRController::GetModel() {
+  if (id_ == vr::k_unTrackedDeviceIndexInvalid) {
+    warn("Try to obtain controller model of an invalid controller");
+    return nullptr;
+  }
+
+  std::string rendermodel_name;
+  rendermodel_name.resize(vr::k_unMaxPropertyStringSize);
+
+  vr::VRSystem()->GetStringTrackedDeviceProperty(
+      id_, vr::Prop_RenderModelName_String, &rendermodel_name[0],
+      vr::k_unMaxPropertyStringSize);
+
+  vr::RenderModel_t* model = nullptr;
+  while (vr::VRRenderModels()->LoadRenderModel_Async(
+             &rendermodel_name[0], &model) == vr::VRRenderModelError_Loading) {
+    std::this_thread::sleep_for(std::chrono::milliseconds(50));
+  }
+
+  return model;
+}
+
+boost::signals2::connection VRController::RegisterButtonSignal(
+    const std::function<void(ButtonId, ButtonEvent)>& callback) {
+  return button_signal_.connect(callback);
+}
+
+glm::vec2 VRController::GetAxesValue(AxesId axes_id) {
+  if (!IsActive()) return glm::vec2(0.0f);
+
+  vr::TrackedDevicePose_t tracked_device_pose;
+  vr::VRControllerState_t controller_state;
+  vr_system_->GetControllerStateWithPose(
+      vr::TrackingUniverseStanding, id_, &controller_state,
+      sizeof(controller_state), &tracked_device_pose);
+
+  for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) {
+    vr::EVRControllerAxisType type = static_cast<vr::EVRControllerAxisType>(
+        vr_system_->GetInt32TrackedDeviceProperty(
+            id_, static_cast<vr::ETrackedDeviceProperty>(
+                     vr::Prop_Axis0Type_Int32 + i)));
+    if (type == static_cast<vr::EVRControllerAxisType>(axes_id)) {
+      vr::VRControllerAxis_t axes = controller_state.rAxis[i];
+      return glm::vec2(axes.x, axes.y);
+    }
+  }
+  warn("VRController device  does not provide the requested axes: {} ",
+       vr_system_->GetControllerAxisTypeNameFromEnum(
+           static_cast<vr::EVRControllerAxisType>(axes_id)));
+  return glm::vec2(0.0f);
+}
+
+VRController::ControllerSide
+VRController::GetControllerRoleForTrackedDeviceIndex(
+    vr::TrackedDeviceIndex_t device_index) const {
+  switch (vr_system_->GetControllerRoleForTrackedDeviceIndex(device_index)) {
+    case vr::TrackedControllerRole_LeftHand:
+      return LEFT_CONTROLLER;
+    case vr::TrackedControllerRole_RightHand:
+      return RIGHT_CONTROLLER;
+    case vr::TrackedControllerRole_Invalid:
+      break;
+  }
+  return INVALID_CONTROLLER;
+}
+}  // namespace phx
diff --git a/library/phx/input/vr_controller.hpp b/library/phx/input/vr_controller.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e9402b97223a14dd81f67606f35316c9be572e9a
--- /dev/null
+++ b/library/phx/input/vr_controller.hpp
@@ -0,0 +1,106 @@
+//------------------------------------------------------------------------------
+// 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_INPUT_VR_CONTROLLER_HPP_
+#define LIBRARY_PHX_INPUT_VR_CONTROLLER_HPP_
+
+#include <memory>
+
+#define BOOST_BIND_NO_PLACEHOLDERS
+// otherwise boosts _ placeholders conflict with trompeloeil ones
+#include "boost/signals2/connection.hpp"
+#include "boost/signals2/signal.hpp"
+
+#include "phx/input/tracked_device.hpp"
+#include "phx/resources/types/material.hpp"
+#include "phx/resources/types/mesh.hpp"
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/mat4x4.hpp"
+
+#include "openvr.h"  //NOLINT
+SUPPRESS_WARNINGS_END
+
+#include "phx/export.hpp"
+
+namespace phx {
+
+class OpenVRResourceLoader;
+
+class PHOENIX_EXPORT VRController : public TrackedDevice {
+ public:
+  enum ControllerSide {
+    RIGHT_CONTROLLER = vr::TrackedControllerRole_RightHand,
+    LEFT_CONTROLLER = vr::TrackedControllerRole_LeftHand,
+    INVALID_CONTROLLER = vr::TrackedControllerRole_Invalid
+  };
+
+  typedef vr::EVRButtonId ButtonId;
+
+  enum ButtonEvent {
+    BUTTON_PRESSED,
+    BUTTON_RELEASED,
+    BUTTON_TOUCH,
+    BUTTON_UNTOUCH
+  };
+
+  enum AxesId {
+    AXES_TRACKPAD = vr::k_eControllerAxis_TrackPad,
+    AXES_JOYSTICK = vr::k_eControllerAxis_Joystick,
+    AXES_TRIGGER = vr::k_eControllerAxis_Trigger
+  };
+
+  explicit VRController(ControllerSide side);
+
+  virtual ~VRController() = default;
+
+  void Update() override;
+  void OnOpenVREvent(const vr::VREvent_t& event) override;
+
+  void UpdateDeviceIndex() override;
+
+  ControllerSide GetSide() const;
+
+  vr::RenderModel_t* GetModel();
+
+  boost::signals2::connection RegisterButtonSignal(
+      const std::function<void(ButtonId, ButtonEvent)>& callback);
+
+  // x ranges from -1.0 to 1.0 for joysticks and track pads and from 0.0 to 1.0
+  // for triggers were 0 is fully released.
+  // y ranges from -1.0 to 1.0 for joysticks and track pads and is always 0.0
+  // for triggers.
+  glm::vec2 GetAxesValue(AxesId id);
+
+ private:
+  ControllerSide GetControllerRoleForTrackedDeviceIndex(
+      vr::TrackedDeviceIndex_t device_index) const;
+
+  boost::signals2::signal<void(ButtonId, ButtonEvent)> button_signal_;
+
+  ControllerSide side_;
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_INPUT_VR_CONTROLLER_HPP_
diff --git a/library/phx/rendering/auxiliary/splash_screen.cpp b/library/phx/rendering/auxiliary/splash_screen.cpp
index 7f84a9a8abd502f0afaf20be8149fb76088b07eb..c4910cd1deff54d8be0aa52b68a32103e39eba36 100644
--- a/library/phx/rendering/auxiliary/splash_screen.cpp
+++ b/library/phx/rendering/auxiliary/splash_screen.cpp
@@ -22,20 +22,19 @@
 
 #include "phx/rendering/auxiliary/splash_screen.hpp"
 
-#include <gl/all.hpp>
-
 #include <cmath>
 #include <memory>
 #include <string>
 
-#include "phx/resources/types/image.hpp"
 #include "phx/resources/resource_manager.hpp"
 #include "phx/resources/resource_utils.hpp"
+#include "phx/resources/types/image.hpp"
 #include "phx/resources_path.hpp"
 
 namespace phx {
 
-phx::SplashScreen::SplashScreen(Window* window) : window_(window) {
+phx::SplashScreen::SplashScreen(Engine* engine, Window* window)
+    : System(engine), window_(window) {
   splash_image_ = ResourceUtils::LoadResourceFromFile<Image>(
       "textures/splash_progress.png");
 
@@ -46,6 +45,8 @@ phx::SplashScreen::SplashScreen(Window* window) : window_(window) {
   default_framebuffer_ = std::make_unique<gl::framebuffer>(0);
 }
 
+void SplashScreen::Update(const FrameTimer::TimeInfo&) { Draw(); }
+
 void SplashScreen::Draw() {
   auto splash_dimensions = splash_image_->GetDimensions();
   auto progress_dimensions = progressbar_image_->GetDimensions();
@@ -62,7 +63,6 @@ void SplashScreen::Draw() {
   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();
 
@@ -79,7 +79,6 @@ void SplashScreen::Draw() {
       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();
 
@@ -96,12 +95,13 @@ void SplashScreen::Draw() {
       static_cast<GLsizei>(splash_dimensions[1] + y_offset),
       GL_COLOR_BUFFER_BIT, GL_LINEAR);
 
-  gl::print_error("OpenGl Error blitting Splash Screen: ");
-
   constexpr float progressbar_max_pixel_width = 198.f;
-  const unsigned int progressbar_current_pixel_width =
-      static_cast<unsigned int>(
-          std::round(load_progress_ * progressbar_max_pixel_width));
+  unsigned int progressbar_current_pixel_width;
+  {
+    std::lock_guard<std::mutex> lock_guard(load_progress_mutex_);
+    progressbar_current_pixel_width = static_cast<unsigned int>(
+        std::round(load_progress_ * progressbar_max_pixel_width));
+  }
   const int progressbar_pixel_height = 9;
 
   auto progressbar_x_offset = x_offset + 98;
@@ -115,11 +115,10 @@ void SplashScreen::Draw() {
                            progressbar_x_offset),
       static_cast<GLsizei>(progressbar_pixel_height + progressbar_y_offset),
       GL_COLOR_BUFFER_BIT, GL_LINEAR);
-
-  window_->Swap();
 }
 
 void SplashScreen::SetLoadProgress(float progress) {
+  std::lock_guard<std::mutex> lock_guard(load_progress_mutex_);
   load_progress_ = progress;
 }
 
diff --git a/library/phx/rendering/auxiliary/splash_screen.hpp b/library/phx/rendering/auxiliary/splash_screen.hpp
index 37511e99dc514bb5da407e26ef8f58c412267663..56faa94f741e3296f50dc891a5fe0f7d92428bbd 100644
--- a/library/phx/rendering/auxiliary/splash_screen.hpp
+++ b/library/phx/rendering/auxiliary/splash_screen.hpp
@@ -24,41 +24,46 @@
 #define LIBRARY_PHX_RENDERING_AUXILIARY_SPLASH_SCREEN_HPP_
 
 #include <memory>
+#include <mutex>
 
 #include "phx/suppress_warnings.hpp"
 
+#include "phx/core/system.hpp"
 #include "phx/display/window.hpp"
-#include "phx/resources/types/image.hpp"
-#include "phx/resources/resource_pointer.hpp"
 #include "phx/export.hpp"
+#include "phx/resources/resource_pointer.hpp"
+#include "phx/resources/types/image.hpp"
 
 #include "gl/framebuffer.hpp"
 
 namespace phx {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-class PHOENIX_EXPORT SplashScreen {
+class PHOENIX_EXPORT SplashScreen : public System {
  public:
-  explicit SplashScreen(Window* window);
-  SplashScreen(const SplashScreen&) = default;
-  SplashScreen(SplashScreen&&) = default;
-  ~SplashScreen() = default;
-
-  SplashScreen& operator=(const SplashScreen&) = default;
-  SplashScreen& operator=(SplashScreen&&) = default;
+  SplashScreen() = delete;
+  SplashScreen(Engine* engine, Window* window);
+  virtual ~SplashScreen() = default;
 
+  void Update(const FrameTimer::TimeInfo&) override;
   void Draw();
   void SetLoadProgress(float progress);
 
+ protected:
+  SplashScreen(const SplashScreen&) = delete;
+  SplashScreen(SplashScreen&&) = default;
+
+  SplashScreen& operator=(const SplashScreen&) = delete;
+  SplashScreen& operator=(SplashScreen&&) = default;
+
  private:
   std::unique_ptr<gl::framebuffer> default_framebuffer_;
   ResourcePointer<Image> splash_image_;
   std::unique_ptr<Image> progressbar_image_;
   Window* window_;
 
+  std::mutex load_progress_mutex_;
   float load_progress_ = 0.f;
 };
-SUPPRESS_WARNINGS_END
 
 }  // namespace phx
 
diff --git a/library/phx/rendering/backend/render_target.cpp b/library/phx/rendering/backend/render_target.cpp
index b8b1c611a6b5a0c5eaaa351dbeec3cd492b9f08e..0333923aab70419e1743028d20f19b397850417f 100644
--- a/library/phx/rendering/backend/render_target.cpp
+++ b/library/phx/rendering/backend/render_target.cpp
@@ -42,7 +42,6 @@ phx::RenderTarget::RenderTarget(const glm::uvec2 dimensions)
   const GLint width = static_cast<GLint>(dimensions.x);
   const GLint height = static_cast<GLint>(dimensions.y);
   color_texture_->set_storage(1, GL_RGB8, width, height);
-  gl::print_error("OpenGl Error creating Render Target Color Texture: ");
 
   attach_texture<GL_TEXTURE_2D>(GL_COLOR_ATTACHMENT0, *color_texture_, 0);
   color_texture_->unbind();
@@ -50,13 +49,11 @@ phx::RenderTarget::RenderTarget(const glm::uvec2 dimensions)
   depth_texture_ = std::make_unique<gl::texture_2d>();
   depth_texture_->bind();
   depth_texture_->set_storage(1, GL_DEPTH_COMPONENT24, width, height);
-  gl::print_error("OpenGl Error creating Render Target Depth Texture: ");
 
   attach_texture<GL_TEXTURE_2D>(GL_DEPTH_ATTACHMENT, *depth_texture_, 0);
   depth_texture_->unbind();
 
   if (!is_valid() || !is_complete()) {
-    gl::print_error("OpenGl Error creating Render Target: ");
     error(
         "[RenderTarget] Unable to create render target, status: {}, valid: {}",
         status(), is_valid());
diff --git a/library/phx/rendering/backend/render_target.hpp b/library/phx/rendering/backend/render_target.hpp
index be07213d041ca9e41aec02c17b72d5e00f56090f..bed8eda040f06f9c5916be24de53b12962be7908 100644
--- a/library/phx/rendering/backend/render_target.hpp
+++ b/library/phx/rendering/backend/render_target.hpp
@@ -43,7 +43,6 @@ SUPPRESS_WARNINGS_END
 
 namespace phx {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
 class PHOENIX_EXPORT RenderTarget : public Component, public gl::framebuffer {
  public:
   RenderTarget() = delete;
@@ -69,7 +68,6 @@ class PHOENIX_EXPORT RenderTarget : public Component, public gl::framebuffer {
 
   glm::uvec2 dimensions_;
 };
-SUPPRESS_WARNINGS_END
 
 }  // namespace phx
 
diff --git a/library/phx/rendering/backend/shader_program.hpp b/library/phx/rendering/backend/shader_program.hpp
index 93c6d7a56319bc436740a4e938fba132490a00ec..679130847e0cc4b611c486eb881fff57b752d819 100644
--- a/library/phx/rendering/backend/shader_program.hpp
+++ b/library/phx/rendering/backend/shader_program.hpp
@@ -37,19 +37,15 @@ SUPPRESS_WARNINGS_END
 #include "gl/program.hpp"
 #include "gl/shader.hpp"
 
+#include "phx/export.hpp"
 #include "phx/rendering/components/transform.hpp"
 #include "phx/rendering/render_passes/render_pass.hpp"
+#include "phx/resources/resource_pointer.hpp"
 #include "phx/resources/types/mesh.hpp"
 #include "phx/resources/types/shader_source.hpp"
-#include "phx/resources/resource_pointer.hpp"
-#include "phx/export.hpp"
 
 namespace phx {
 
-// TODO(@all) Will padding waste too much memory? How many shaders will we
-// have?
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 class PHOENIX_EXPORT ShaderProgram : public gl::program {
  public:
   enum ShaderChannel {
@@ -71,7 +67,7 @@ class PHOENIX_EXPORT ShaderProgram : public gl::program {
   ShaderProgram& operator=(ShaderProgram&&) = default;
 
   bool SetShader(ShaderChannel channel,
-                      ResourcePointer<ShaderSource> shader_source);
+                 ResourcePointer<ShaderSource> shader_source);
 
   template <typename T>
   void SetUniform(const std::string& name, T value) {
@@ -91,8 +87,6 @@ class PHOENIX_EXPORT ShaderProgram : public gl::program {
   std::array<ShaderHandle, MAX_SHADER_CHANNEL> shaders_;
 };
 
-SUPPRESS_WARNINGS_END
-
 }  // namespace phx
 
 #endif  // LIBRARY_PHX_RENDERING_BACKEND_SHADER_PROGRAM_HPP_
diff --git a/library/phx/rendering/components/light.hpp b/library/phx/rendering/components/light.hpp
index e8abc925810b1d63db905fad6a885e2843d2cb90..9c0403289ad1584c5ad8463b233cb6a001917679 100644
--- a/library/phx/rendering/components/light.hpp
+++ b/library/phx/rendering/components/light.hpp
@@ -36,8 +36,6 @@ SUPPRESS_WARNINGS_END
 
 namespace phx {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 class PHOENIX_EXPORT Light final : public Component {
  public:
   enum class Type {
@@ -80,8 +78,6 @@ class PHOENIX_EXPORT Light final : public Component {
   float spot_angle_ = 45.0f;  // Only for spot lights.
 };
 
-SUPPRESS_WARNINGS_END
-
 }  // namespace phx
 
 #endif  // LIBRARY_PHX_RENDERING_COMPONENTS_LIGHT_HPP_
diff --git a/library/phx/rendering/components/mesh_render_settings.cpp b/library/phx/rendering/components/mesh_render_settings.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a2907d581bad4d411c712ed357092391673441c
--- /dev/null
+++ b/library/phx/rendering/components/mesh_render_settings.cpp
@@ -0,0 +1,39 @@
+//------------------------------------------------------------------------------
+// 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 "phx/rendering/components/mesh_render_settings.hpp"
+
+namespace phx {
+
+const MeshRenderSettings MeshRenderSettings::default_settings_;
+
+void MeshRenderSettings::SetWireframeMode(const bool enabled) {
+  wireframe_mode_ = enabled;
+}
+
+bool MeshRenderSettings::GetWireframeMode() const { return wireframe_mode_; }
+
+const MeshRenderSettings* MeshRenderSettings::GetDefault() {
+  return &default_settings_;
+}
+
+}  // namespace phx
diff --git a/library/phx/rendering/components/mesh_render_settings.hpp b/library/phx/rendering/components/mesh_render_settings.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b35f1710e2ba330fad793266883a2603bae5d593
--- /dev/null
+++ b/library/phx/rendering/components/mesh_render_settings.hpp
@@ -0,0 +1,48 @@
+//------------------------------------------------------------------------------
+// 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_RENDERING_COMPONENTS_MESH_RENDER_SETTINGS_HPP_
+#define LIBRARY_PHX_RENDERING_COMPONENTS_MESH_RENDER_SETTINGS_HPP_
+
+#include "phx/core/component.hpp"
+#include "phx/export.hpp"
+
+namespace phx {
+/**
+ * A component holding a simple pointer to a mesh
+ */
+class PHOENIX_EXPORT MeshRenderSettings final : public Component {
+ public:
+  virtual ~MeshRenderSettings() = default;
+
+  void SetWireframeMode(bool enabled);
+  bool GetWireframeMode() const;
+
+  static const MeshRenderSettings* GetDefault();
+
+ private:
+  bool wireframe_mode_ = false;
+
+  static const MeshRenderSettings default_settings_;
+};
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_RENDERING_COMPONENTS_MESH_RENDER_SETTINGS_HPP_
diff --git a/library/phx/rendering/components/projection.cpp b/library/phx/rendering/components/projection.cpp
index 63b56491e4f5f2328ae7fbfab08b13b384a16101..bd34f4b9a64e5c742a734e21c7de3f38dc9075b6 100644
--- a/library/phx/rendering/components/projection.cpp
+++ b/library/phx/rendering/components/projection.cpp
@@ -27,13 +27,15 @@
 #include "phx/suppress_warnings.hpp"
 
 SUPPRESS_WARNINGS_BEGIN
+#include "glm/gtc/matrix_transform.hpp"
 #include "glm/mat4x4.hpp"
 SUPPRESS_WARNINGS_END
 
-#include "glm/gtc/matrix_transform.hpp"
-
 namespace phx {
 
+Projection::Projection(const glm::mat4& projection /*= glm::mat4(1.0f)*/)
+    : matrix_(projection) {}
+
 const glm::mat4& Projection::GetMatrix() const { return matrix_; }
 
 void Projection::SetPerspective(float fovy, float aspect, float near,
diff --git a/library/phx/rendering/components/projection.hpp b/library/phx/rendering/components/projection.hpp
index b347dae5a61e47dd5e9066eb687d964a5bb07b27..08e7418be4d80fa8cf622d188d558fc95a5b962d 100644
--- a/library/phx/rendering/components/projection.hpp
+++ b/library/phx/rendering/components/projection.hpp
@@ -50,7 +50,7 @@ class PHOENIX_EXPORT Projection final : public Component {
 
  protected:
   friend Entity;
-  Projection() = default;
+  explicit Projection(const glm::mat4& projection = glm::mat4(1.0f));
   Projection(const Projection&) = delete;
   Projection(Projection&&) = default;
 
diff --git a/library/phx/rendering/components/skybox.cpp b/library/phx/rendering/components/skybox.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7bdb46292429d8e58216671ecf8d095afd025bab
--- /dev/null
+++ b/library/phx/rendering/components/skybox.cpp
@@ -0,0 +1,100 @@
+//------------------------------------------------------------------------------
+// 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 "phx/rendering/components/skybox.hpp"
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "gl/texture.hpp"
+
+#include "phx/core/logger.hpp"
+#include "phx/resources/resource_utils.hpp"
+
+namespace phx {
+// order here based on the enum SkyboxSides
+std::vector<std::string> Skybox::skybox_sidenames_ = {
+    "right", "left", "top", "bottom", "back", "front"};
+
+Skybox::Skybox() {
+  if (!LoadSkyboxImages(GetDefaultSkyboxFolder(), GetDefaultFileExtension())) {
+    throw std::runtime_error("Default skybox could not be loaded.");
+  }
+}
+
+Skybox::Skybox(const std::string& path, const std::string& extension) {
+  // @ToDo:  Do we really have absolute pathes when user defines the skybox?
+  if (!LoadSkyboxImages(CheckPathSyntax(path),
+                        CheckExtensionSyntax(extension))) {
+    warn("Your skybox could not be loaded, so let's use our default instead.");
+    Skybox();
+  }
+}
+
+const std::vector<ResourcePointer<Image>>& Skybox::GetSkyboxImages() const {
+  return skybox_images_;
+}
+
+const ResourcePointer<Image>& Skybox::GetImageForSide(SkyboxSides ID) const {
+  if (ID < RIGHT || ID > FRONT) {
+    warn("Skybox side {} does not exist, RIGHT image is returned", ID);
+    return skybox_images_[0];
+  }
+
+  return skybox_images_[ID];
+}
+
+bool Skybox::LoadSkyboxImages(const std::string& skybox_folder,
+                              const std::string& file_extension) {
+  for (unsigned int i = static_cast<unsigned int>(RIGHT);
+       i <= static_cast<unsigned int>(FRONT); ++i) {
+    auto image = ResourceUtils::LoadResourceFromFile<Image>(
+        skybox_folder + skybox_sidenames_[i] + file_extension, {});
+    if (image == nullptr) {
+      warn("Skybox side {} could not be loaded. ",
+           skybox_folder + skybox_sidenames_[i] + file_extension);
+      return false;
+    }
+    skybox_images_.push_back(image);
+  }
+  return true;
+}
+
+std::string Skybox::CheckPathSyntax(const std::string& skybox_folder) {
+  // make it linux compatible
+  std::string tmp = skybox_folder;
+  std::replace(tmp.begin(), tmp.end(), '\\', '/');
+  // define it as path
+  if (tmp[tmp.size() - 1] != '/')
+    return (tmp + "/");
+  else
+    return tmp;
+}
+std::string Skybox::CheckExtensionSyntax(const std::string& file_extension) {
+  if (file_extension[0] != '.')
+    return ("." + file_extension);
+  else
+    return file_extension;
+}
+
+}  // namespace phx
diff --git a/library/phx/rendering/components/skybox.hpp b/library/phx/rendering/components/skybox.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a3ffcc68893e7365bedd199560b109e46e28bebc
--- /dev/null
+++ b/library/phx/rendering/components/skybox.hpp
@@ -0,0 +1,83 @@
+//------------------------------------------------------------------------------
+// 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_RENDERING_COMPONENTS_SKYBOX_HPP_
+#define LIBRARY_PHX_RENDERING_COMPONENTS_SKYBOX_HPP_
+
+#include <string>
+#include <vector>
+
+#include "phx/core/component.hpp"
+#include "phx/export.hpp"
+#include "phx/resources/resource_pointer.hpp"
+#include "phx/resources/types/image.hpp"
+
+namespace phx {
+
+class PHOENIX_EXPORT Skybox final : public Component {
+ public:
+  /**
+   * The order of this enum is based on the OpenGL cubemap faces:
+   * layer | cubemap face                   | side
+   * -------------------------------------------------
+   *  0    | GL_TEXTURE_CUBE_MAP_POSITIVE_X | right
+   *  1    | GL_TEXTURE_CUBE_MAP_NEGATIVE_X | left
+   *  2    | GL_TEXTURE_CUBE_MAP_POSITIVE_Y | top
+   *  3    | GL_TEXTURE_CUBE_MAP_NEGATIVE_Y | bottom
+   *  4    | GL_TEXTURE_CUBE_MAP_POSITIVE_Z | back
+   *  5    | GL_TEXTURE_CUBE_MAP_NEGATIVE_Z | front
+   */
+  enum SkyboxSides { RIGHT = 0, LEFT, TOP, BOTTOM, BACK, FRONT };
+
+  ~Skybox() override = default;
+
+  /* Order of images based on SkyboxSides */
+  const std::vector<ResourcePointer<Image>>& GetSkyboxImages() const;
+  const ResourcePointer<Image>& GetImageForSide(SkyboxSides ID) const;
+
+ protected:
+  friend Entity;
+  Skybox();
+  Skybox(const std::string& path, const std::string& extension);
+  Skybox(const Skybox&) = delete;
+  Skybox(Skybox&&) = default;
+
+  Skybox& operator=(const Skybox&) = delete;
+  Skybox& operator=(Skybox&&) = default;
+
+ private:
+  bool LoadSkyboxImages(const std::string& skybox_folder,
+                        const std::string& file_extension);
+
+  static std::string GetDefaultSkyboxFolder() { return "skyboxes/hw_morning/"; }
+  static std::string GetDefaultFileExtension() { return ".tga"; }
+
+  std::string CheckPathSyntax(const std::string& skybox_folder);
+  std::string CheckExtensionSyntax(const std::string& file_extension);
+
+  std::vector<ResourcePointer<Image>> skybox_images_;
+  static std::vector<std::string> skybox_sidenames_;
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_RENDERING_COMPONENTS_SKYBOX_HPP_
diff --git a/library/phx/rendering/components/transform.cpp b/library/phx/rendering/components/transform.cpp
index 9cbe8c26b0e003f5893cc3395494299036421175..fb0bcdf6db0d5f914a6a48e2c132f2b6581b1954 100644
--- a/library/phx/rendering/components/transform.cpp
+++ b/library/phx/rendering/components/transform.cpp
@@ -26,6 +26,7 @@
 #include "phx/suppress_warnings.hpp"
 
 SUPPRESS_WARNINGS_BEGIN
+#define GLM_ENABLE_EXPERIMENTAL
 #include "glm/gtc/matrix_transform.hpp"
 #include "glm/gtx/matrix_decompose.hpp"
 #include "glm/gtx/quaternion.hpp"
@@ -107,7 +108,7 @@ const glm::quat& Transform::GetGlobalRotation() const {
 }
 Transform& Transform::SetGlobalRotation(const glm::quat& rotation) {
   local_rotation_ =
-      parent_ ? rotation * glm::inverse(parent_->global_rotation_) : rotation;
+      parent_ ? glm::inverse(parent_->global_rotation_) * rotation : rotation;
   UpdateLocalMatrix();
   return *this;
 }
@@ -133,7 +134,7 @@ Transform& Transform::SetGlobalScale(const glm::vec3& scale) {
 const glm::mat4& Transform::GetGlobalMatrix() const { return global_matrix_; }
 Transform& Transform::SetGlobalMatrix(const glm::mat4& matrix) {
   local_matrix_ =
-      parent_ ? matrix * glm::inverse(parent_->global_matrix_) : matrix;
+      parent_ ? glm::inverse(parent_->global_matrix_) * matrix : matrix;
   glm::vec3 skew;
   glm::vec4 perspective;
   glm::decompose(local_matrix_, local_scale_, local_rotation_,
@@ -147,10 +148,10 @@ Transform& Transform::Translate(const glm::vec3& amount) {
   return SetLocalTranslation(amount + local_translation_);
 }
 Transform& Transform::Rotate(const glm::quat& amount) {
-  return SetLocalRotation(amount * local_rotation_);
+  return SetLocalRotation(local_rotation_ * amount);
 }
 Transform& Transform::RotateEuler(const glm::vec3& amount) {
-  return SetLocalRotation(glm::quat(glm::radians(amount)) * local_rotation_);
+  return SetLocalRotation(local_rotation_ * glm::quat(glm::radians(amount)));
 }
 Transform& Transform::Scale(const glm::vec3& amount) {
   return SetLocalScale(amount + local_scale_);
@@ -162,9 +163,9 @@ Transform& Transform::LookAt(const glm::vec3& target,
 }
 Transform& Transform::Reset() {
   local_translation_ = glm::vec3();
-  local_rotation_ = glm::quat();
+  local_rotation_ = glm::quat(1.0f, glm::vec3());
   local_scale_ = glm::vec3(1.0f);
-  local_matrix_ = glm::mat4();
+  local_matrix_ = glm::mat4(1.0f);
   return *this;
 }
 
diff --git a/library/phx/rendering/components/transform.hpp b/library/phx/rendering/components/transform.hpp
index b67c14855ba21e89aeb98eb9f80fda9f625a0fc9..d99ea32abd0e1b88bf4df46fa336a669d5206736 100644
--- a/library/phx/rendering/components/transform.hpp
+++ b/library/phx/rendering/components/transform.hpp
@@ -35,8 +35,8 @@ SUPPRESS_WARNINGS_BEGIN
 SUPPRESS_WARNINGS_END
 
 #include "phx/core/component.hpp"
-#include "phx/utility/aspects/hierarchical.hpp"
 #include "phx/export.hpp"
+#include "phx/utility/aspects/hierarchical.hpp"
 
 namespace phx {
 
@@ -44,7 +44,7 @@ namespace phx {
 class PHOENIX_EXPORT Transform : public Component,
                                  public Hierarchical<Transform> {
  public:
-  virtual ~Transform() = default;
+  ~Transform() override = default;
 
   // Getting/Setting Local transform
   const glm::vec3& GetLocalTranslation() const;
@@ -99,7 +99,7 @@ class PHOENIX_EXPORT Transform : public Component,
  protected:
   friend Entity;
   Transform(const glm::vec3& translation = glm::vec3(),
-            const glm::quat& rotation = glm::quat(),
+            const glm::quat& rotation = glm::quat(1.0f, glm::vec3()),
             const glm::vec3& scale = glm::vec3(1.0f));
   Transform(const glm::vec3& translation, const glm::vec3& rotation_euler,
             const glm::vec3& scale = glm::vec3(1.0f));
diff --git a/library/phx/rendering/render_passes/geometry_pass.cpp b/library/phx/rendering/render_passes/geometry_pass.cpp
index 6f27ac3e5eb1dfb3d7475fd16ee569f619ba1c5b..ba347d63c6a817c3cf9e1e28afc45379c64dd136 100644
--- a/library/phx/rendering/render_passes/geometry_pass.cpp
+++ b/library/phx/rendering/render_passes/geometry_pass.cpp
@@ -29,15 +29,18 @@
 #include <utility>
 #include <vector>
 
+SUPPRESS_WARNINGS_BEGIN
+#define GLM_ENABLE_EXPERIMENTAL
 #include "glm/glm.hpp"
 #include "glm/gtc/type_ptr.hpp"
 #include "glm/gtx/matrix_operation.hpp"
+SUPPRESS_WARNINGS_END
 
 #include "phx/core/logger.hpp"
 #include "phx/rendering/components/transform.hpp"
+#include "phx/resources/resource_utils.hpp"
 #include "phx/resources/types/mesh.hpp"
 #include "phx/resources/types/shader_source.hpp"
-#include "phx/resources/resource_utils.hpp"
 
 namespace phx {
 
@@ -67,6 +70,10 @@ void GeometryPass::UploadMeshData(
   mesh_cache_.clear();
   RenderOffset offset{0, 0};
   for (const auto& instance : rendering_instances) {
+    if (instance.material != nullptr) {
+      instance.material->UploadTextures();
+    }
+
     Mesh* mesh = instance.mesh;
     if (mesh_cache_.find(mesh) != mesh_cache_.end()) {
       continue;
@@ -223,7 +230,15 @@ void GeometryPass::Draw(const RenderingInstance& rendering_instance) {
   SetTransformShaderUniforms(model_matrix, view_matrix, projection_matrix);
   SetMaterialShaderUniforms(rendering_instance.material);
 
+  const MeshRenderSettings* render_settings =
+      rendering_instance.mesh_render_settings;
+  if (render_settings == nullptr) {
+    render_settings = MeshRenderSettings::GetDefault();
+  }
+
   glEnable(GL_DEPTH_TEST);
+  glPolygonMode(GL_FRONT_AND_BACK,
+                render_settings->GetWireframeMode() ? GL_LINE : GL_FILL);
   glDrawElements(
       GL_TRIANGLES,
       static_cast<GLsizei>(rendering_instance.mesh->GetIndices().size()),
@@ -289,41 +304,32 @@ void GeometryPass::SetLightShaderUniforms() {
   }
 }
 
-void GeometryPass::SetMaterialShaderUniforms(Material* material) {
-  glm::uvec4 texture_toggle(0, 0, 0, 0);
-  glm::vec3 ambient_color(1, 0, 0);
-  glm::vec3 diffuse_color(1, 0, 0);
-  glm::vec3 specular_color(1, 0, 0);
-  float shininess = 1.0f;
-  if (material != nullptr) {
-    ambient_color = material->GetAmbientColor();
-    diffuse_color = material->GetDiffuseColor();
-    specular_color = material->GetSpecularColor();
-
-    texture_toggle =
-        glm::uvec4(material->GetAmbientTexture() != nullptr ? 1u : 0u,
-                   material->GetDiffuseTexture() != nullptr ? 1u : 0u,
-                   material->GetSpecularTexture() != nullptr ? 1u : 0u, 0u);
-    if (material->GetAmbientTexture())
-      shader_program_->set_uniform_handle(
-          shader_program_->uniform_location("material.ambient_tex"),
-          gl::texture_handle(*material->GetAmbientTexture()));
-    if (material->GetDiffuseTexture())
-      shader_program_->set_uniform_handle(
-          shader_program_->uniform_location("material.diffuse_tex"),
-          gl::texture_handle(*material->GetDiffuseTexture()));
-    if (material->GetSpecularTexture())
-      shader_program_->set_uniform_handle(
-          shader_program_->uniform_location("material.specular_tex"),
-          gl::texture_handle(*material->GetSpecularTexture()));
-
-    shininess = material->GetShininess();
-  }
+void GeometryPass::SetMaterialShaderUniforms(const Material* material) {
+  if (material == nullptr) material = Material::GetDefault();
+
+  const glm::uvec4 texture_toggle =
+      glm::uvec4(material->GetAmbientTexture() != nullptr ? 1u : 0u,
+                 material->GetDiffuseTexture() != nullptr ? 1u : 0u,
+                 material->GetSpecularTexture() != nullptr ? 1u : 0u, 0u);
+  if (material->GetAmbientTexture())
+    shader_program_->set_uniform_handle(
+        shader_program_->uniform_location("material.ambient_tex"),
+        gl::texture_handle(*material->GetAmbientTexture()));
+  if (material->GetDiffuseTexture())
+    shader_program_->set_uniform_handle(
+        shader_program_->uniform_location("material.diffuse_tex"),
+        gl::texture_handle(*material->GetDiffuseTexture()));
+  if (material->GetSpecularTexture())
+    shader_program_->set_uniform_handle(
+        shader_program_->uniform_location("material.specular_tex"),
+        gl::texture_handle(*material->GetSpecularTexture()));
+
   shader_program_->SetUniform("material.texture_toggle", texture_toggle);
-  shader_program_->SetUniform("material.ambient", ambient_color);
-  shader_program_->SetUniform("material.diffuse", diffuse_color);
-  shader_program_->SetUniform("material.specular", specular_color);
-  shader_program_->SetUniform("material.shininess", shininess);
+  shader_program_->SetUniform("material.ambient", material->GetAmbientColor());
+  shader_program_->SetUniform("material.diffuse", material->GetDiffuseColor());
+  shader_program_->SetUniform("material.specular",
+                              material->GetSpecularColor());
+  shader_program_->SetUniform("material.shininess", material->GetShininess());
 }
 
 bool GeometryPass::IsValid() const {
diff --git a/library/phx/rendering/render_passes/geometry_pass.hpp b/library/phx/rendering/render_passes/geometry_pass.hpp
index f599a6a0799d4b8ad152c4e188288350165ba19e..8490d26f6411d99a9b2ecb797cfb8f19a3a756bf 100644
--- a/library/phx/rendering/render_passes/geometry_pass.hpp
+++ b/library/phx/rendering/render_passes/geometry_pass.hpp
@@ -41,16 +41,14 @@ SUPPRESS_WARNINGS_END
 #include "phx/rendering/backend/render_target.hpp"
 #include "phx/rendering/backend/shader_program.hpp"
 #include "phx/rendering/components/light.hpp"
+#include "phx/rendering/components/mesh_render_settings.hpp"
 #include "phx/rendering/components/projection.hpp"
 #include "phx/rendering/components/transform.hpp"
 #include "phx/rendering/render_passes/render_pass.hpp"
-#include "phx/resources/resource_pointer.hpp"
 #include "phx/resources/types/mesh.hpp"
 
 namespace phx {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 class PHOENIX_EXPORT GeometryPass : public RenderPass {
  public:
   explicit GeometryPass(RenderTarget* render_target);
@@ -64,6 +62,7 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass {
     Mesh* mesh = nullptr;
     Material* material = nullptr;
     Transform* transform = nullptr;
+    MeshRenderSettings* mesh_render_settings = nullptr;
   };
   struct RenderOffset {
     std::size_t vertex_offset;
@@ -86,8 +85,6 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass {
   bool IsValid() const;
 
  private:
-  // TODO(@all) Will padding waste too much memory?
-  SUPPRESS_WARNINGS_BEGIN_PADDED
   struct RenderingResource {
     RenderingResource() = default;
     RenderingResource(const RenderingResource&) = delete;
@@ -106,7 +103,6 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass {
     unsigned int vertex_buffer_size;
     unsigned int index_buffer_size;
   };
-  SUPPRESS_WARNINGS_END
 
   void SetUpShaders();
   void CreateRenderingResource();
@@ -122,7 +118,7 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass {
                                   const glm::mat4& view_matrix,
                                   const glm::mat4& projection_matrix);
   void SetLightShaderUniforms();
-  void SetMaterialShaderUniforms(Material* material);
+  void SetMaterialShaderUniforms(const Material* material);
 
   std::unique_ptr<ShaderProgram> shader_program_;
 
@@ -135,8 +131,6 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass {
   RenderTarget* render_target_;
 };
 
-SUPPRESS_WARNINGS_END
-
 }  // namespace phx
 
 #endif  // LIBRARY_PHX_RENDERING_RENDER_PASSES_GEOMETRY_PASS_HPP_
diff --git a/library/phx/rendering/render_passes/skybox_pass.cpp b/library/phx/rendering/render_passes/skybox_pass.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e0885f3f8a4d9dfd1f3d6589ba05bbbe180bbeee
--- /dev/null
+++ b/library/phx/rendering/render_passes/skybox_pass.cpp
@@ -0,0 +1,256 @@
+//------------------------------------------------------------------------------
+// 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 "phx/rendering/render_passes/skybox_pass.hpp"
+
+#include <memory>
+#include <vector>
+
+#include "gl/framebuffer.hpp"
+
+#include "phx/core/engine.hpp"
+#include "phx/core/logger.hpp"
+#include "phx/resources/resource_utils.hpp"
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#define GLM_ENABLE_EXPERIMENTAL
+#include "glm/gtx/quaternion.hpp"
+SUPPRESS_WARNINGS_END
+
+namespace phx {
+
+std::vector<glm::vec3> SkyboxPass::skybox_vertices_ = {
+    // right
+    {1.0f, -1.0f, -1.0f},
+    {1.0f, -1.0f, 1.0f},
+    {1.0f, 1.0f, 1.0f},
+    {1.0f, 1.0f, 1.0f},
+    {1.0f, 1.0f, -1.0f},
+    {1.0f, -1.0f, -1.0f},
+    // left
+    {-1.0f, -1.0f, 1.0f},
+    {-1.0f, -1.0f, -1.0f},
+    {-1.0f, 1.0f, -1.0f},
+    {-1.0f, 1.0f, -1.0f},
+    {-1.0f, 1.0f, 1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    // top
+    {-1.0f, 1.0f, -1.0f},
+    {1.0f, 1.0f, -1.0f},
+    {1.0f, 1.0f, 1.0f},
+    {1.0f, 1.0f, 1.0f},
+    {-1.0f, 1.0f, 1.0f},
+    {-1.0f, 1.0f, -1.0f},
+    // bottom
+    {-1.0f, -1.0f, -1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    {1.0f, -1.0f, -1.0f},
+    {1.0f, -1.0f, -1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    {1.0f, -1.0f, 1.0f},
+    // back
+    {-1.0f, -1.0f, 1.0f},
+    {-1.0f, 1.0f, 1.0f},
+    {1.0f, 1.0f, 1.0f},
+    {1.0f, 1.0f, 1.0f},
+    {1.0f, -1.0f, 1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    // front
+    {-1.0f, 1.0f, -1.0f},
+    {-1.0f, -1.0f, -1.0f},
+    {1.0f, -1.0f, -1.0f},
+    {1.0f, -1.0f, -1.0f},
+    {1.0f, 1.0f, -1.0f},
+    {-1.0f, 1.0f, -1.0f}};
+
+std::vector<glm::vec3> SkyboxPass::skybox_tex_coords_ = {
+    // left
+    {1.0f, 1.0f, 1.0f},
+    {1.0f, 1.0f, -1.0f},
+    {1.0f, -1.0f, -1.0f},
+    {1.0f, -1.0f, -1.0f},
+    {1.0f, -1.0f, 1.0f},
+    {1.0f, 1.0f, 1.0f},
+    // right
+    {-1.0f, 1.0f, -1.0f},
+    {-1.0f, 1.0f, 1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    {-1.0f, -1.0f, -1.0f},
+    {-1.0f, 1.0f, -1.0f},
+    // top
+    {1.0f, 1.0f, -1.0f},
+    {1.0f, 1.0f, 1.0f},
+    {-1.0f, 1.0f, 1.0f},
+    {-1.0f, 1.0f, 1.0f},
+    {-1.0f, 1.0f, -1.0f},
+    {1.0f, 1.0f, -1.0f},
+    // bottom
+    {1.0f, -1.0f, -1.0f},
+    {-1.0f, -1.0f, -1.0f},
+    {1.0f, -1.0f, 1.0f},
+    {1.0f, -1.0f, 1.0f},
+    {-1.0f, -1.0f, -1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    // back
+    {1.0f, 1.0f, 1.0f},
+    {1.0f, -1.0f, 1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    {-1.0f, -1.0f, 1.0f},
+    {-1.0f, 1.0f, 1.0f},
+    {1.0f, 1.0f, 1.0f},
+    // front
+    {1.0f, -1.0f, -1.0f},
+    {1.0f, 1.0f, -1.0f},
+    {-1.0f, 1.0f, -1.0f},
+    {-1.0f, 1.0f, -1.0f},
+    {-1.0f, -1.0f, -1.0f},
+    {1.0f, -1.0f, -1.0f},
+};
+
+SkyboxPass::SkyboxPass(RenderTarget* render_target)
+    : render_target_(render_target), skybox_(nullptr) {}
+
+void SkyboxPass::SetSkyboxComponent(const Skybox* skybox) {
+  if (skybox == nullptr || skybox == skybox_) return;
+
+  skybox_ = skybox;
+  SetUpCubeMap();
+}
+
+void SkyboxPass::Initialize() {
+  SetUpShaders();
+  SetUpSkyboxVAO();
+}
+
+void SkyboxPass::Execute() {
+  if (skybox_ == nullptr) return;
+
+  BindResources();
+  Draw();
+  UnbindResources();
+}
+
+void SkyboxPass::SetUpShaders() {
+  auto vertex_shader =
+      ResourceUtils::LoadResourceFromFile<ShaderSource>("shader/skybox.vert");
+  auto fragment_shader =
+      ResourceUtils::LoadResourceFromFile<ShaderSource>("shader/skybox.frag");
+
+  shader_program_ = std::make_unique<ShaderProgram>();
+  shader_program_->SetShader(ShaderProgram::VERTEX, vertex_shader);
+  shader_program_->SetShader(ShaderProgram::FRAGMENT, fragment_shader);
+  shader_program_->Link();
+  if (!shader_program_->link_status())
+    warn("[SkyboxPass::SetUpShaders] linking didn't work.");
+}
+
+void SkyboxPass::SetUpSkyboxVAO() {
+  if (vertex_array_ != nullptr) return;
+
+  vertex_buffer_ = std::make_unique<gl::buffer>();
+  vertex_buffer_->set_data(static_cast<GLsizeiptr>(sizeof(skybox_vertices_[0]) *
+                                                   skybox_vertices_.size()),
+                           &skybox_vertices_[0], GL_STATIC_DRAW);
+
+  tex_coord_buffer_ = std::make_unique<gl::buffer>();
+  tex_coord_buffer_->set_data(
+      static_cast<GLsizeiptr>(sizeof(skybox_tex_coords_[0]) *
+                              skybox_tex_coords_.size()),
+      &skybox_tex_coords_[0], GL_STATIC_DRAW);
+
+  vertex_array_ = std::make_unique<gl::vertex_array>();
+  vertex_array_->set_attribute_enabled(0, true);
+  vertex_array_->set_attribute_format(0, 3, GL_FLOAT);
+  vertex_array_->set_vertex_buffer(0, *vertex_buffer_, 0, 3 * sizeof(float));
+  vertex_array_->set_attribute_enabled(1, true);
+  vertex_array_->set_attribute_format(1, 3, GL_FLOAT);
+  vertex_array_->set_vertex_buffer(1, *tex_coord_buffer_, 0, 3 * sizeof(float));
+}
+
+void SkyboxPass::SetUpCubeMap() {
+  cubemap_ = std::make_unique<gl::cubemap_texture_array>();
+  cubemap_->set_min_filter(GL_LINEAR);
+  cubemap_->set_mag_filter(GL_LINEAR);
+  cubemap_->set_wrap_s(GL_CLAMP_TO_EDGE);
+  cubemap_->set_wrap_t(GL_CLAMP_TO_EDGE);
+  cubemap_->set_wrap_r(GL_CLAMP_TO_EDGE);
+
+  auto sides = skybox_->GetSkyboxImages();
+
+  auto dimensions = sides[0]->GetDimensions();
+  cubemap_->set_storage(1, GL_RGBA8, static_cast<GLsizei>(dimensions[0]),
+                        static_cast<GLsizei>(dimensions[1]),
+                        static_cast<GLsizei>(sides.size()));
+
+  for (size_t i = 0; i < sides.size(); ++i) {
+    auto& single_side = sides[i];
+
+    // dimension testing
+    if (single_side->GetDimensions() != dimensions) {
+      warn("Texture of skybox side {} is different to skybox-right-side", i);
+    }
+
+    // the zOffset which is the layer-face number for cubemap array texture
+    // can here be reduced to the layer number as shown in skybox.hpp,
+    // while the depth is 1 as we only upload one layer-face
+    // for more information see
+    // https://www.khronos.org/opengl/wiki/Cubemap_Texture#Cubemap_array_textures
+    cubemap_->set_sub_image(0, 0, 0, static_cast<GLint>(i),
+                            static_cast<GLsizei>(dimensions[0]),
+                            static_cast<GLsizei>(dimensions[1]), 1, GL_RGBA,
+                            GL_UNSIGNED_BYTE, single_side->GetPixels().first);
+  }
+}
+
+void SkyboxPass::BindResources() {
+  shader_program_->use();
+  render_target_->bind();
+  render_target_->SetViewport();
+  vertex_array_->bind();
+  cubemap_->bind();
+}
+void SkyboxPass::UnbindResources() {
+  cubemap_->unbind();
+  vertex_array_->unbind();
+  render_target_->unbind();
+  shader_program_->unuse();
+}
+
+void SkyboxPass::Draw() {
+  glm::mat4 projection_matrix = render_target_->GetProjection();
+  // we only require the rotation here, nothing else
+  glm::mat4 view_matrix = glm::mat4(glm::mat3(render_target_->GetView()));
+
+  shader_program_->SetUniform("projection_matrix", projection_matrix);
+  shader_program_->SetUniform("view_matrix", view_matrix);
+
+  // Advice: do not use set_seamless_cubemap_enabled here as it creates a seam
+  // in the ceiling and the floor.
+
+  gl::set_depth_mask(GL_FALSE);
+  glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(skybox_vertices_.size()));
+  gl::set_depth_mask(GL_TRUE);
+}
+
+}  // namespace phx
diff --git a/library/phx/rendering/render_passes/skybox_pass.hpp b/library/phx/rendering/render_passes/skybox_pass.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6c825a500e7f58c8c6cac4c60af0001a33ab2d67
--- /dev/null
+++ b/library/phx/rendering/render_passes/skybox_pass.hpp
@@ -0,0 +1,83 @@
+//------------------------------------------------------------------------------
+// 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_RENDERING_RENDER_PASSES_SKYBOX_PASS_HPP_
+#define LIBRARY_PHX_RENDERING_RENDER_PASSES_SKYBOX_PASS_HPP_
+
+#include <memory>
+#include <vector>
+
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "gl/buffer.hpp"
+#include "gl/vertex_array.hpp"
+SUPPRESS_WARNINGS_END
+
+#include "gl/texture.hpp"
+
+#include "phx/export.hpp"
+#include "phx/rendering/backend/render_target.hpp"
+#include "phx/rendering/backend/shader_program.hpp"
+#include "phx/rendering/components/skybox.hpp"
+#include "phx/rendering/render_passes/render_pass.hpp"
+
+namespace phx {
+
+class PHOENIX_EXPORT SkyboxPass : public RenderPass {
+ public:
+  explicit SkyboxPass(RenderTarget* render_target);
+  SkyboxPass(const SkyboxPass&) = default;
+  SkyboxPass(SkyboxPass&&) = default;
+
+  SkyboxPass& operator=(const SkyboxPass&) = default;
+  SkyboxPass& operator=(SkyboxPass&&) = default;
+
+  void SetSkyboxComponent(const Skybox* skybox);
+
+  void Initialize() override;
+  void Execute() override;
+
+ private:
+  void SetUpShaders();
+  void SetUpSkyboxVAO();
+  void SetUpCubeMap();
+
+  void BindResources();
+  void UnbindResources();
+  void Draw();
+
+  RenderTarget* render_target_;
+  const Skybox* skybox_;
+
+  std::unique_ptr<ShaderProgram> shader_program_;
+  std::unique_ptr<gl::cubemap_texture_array> cubemap_;
+  static std::vector<glm::vec3> skybox_vertices_;
+  static std::vector<glm::vec3> skybox_tex_coords_;
+  std::unique_ptr<gl::buffer> vertex_buffer_ = nullptr;
+  std::unique_ptr<gl::buffer> tex_coord_buffer_ = nullptr;
+  std::unique_ptr<gl::vertex_array> vertex_array_ = nullptr;
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_RENDERING_RENDER_PASSES_SKYBOX_PASS_HPP_
diff --git a/library/phx/rendering/rendering_system.cpp b/library/phx/rendering/rendering_system.cpp
index c10e4201b099320773443875e9c7496c68381612..5558d9f9434b0fd0da2ca8eea949b53e6e840d49 100644
--- a/library/phx/rendering/rendering_system.cpp
+++ b/library/phx/rendering/rendering_system.cpp
@@ -32,12 +32,16 @@
 #include "phx/core/scene.hpp"
 #include "phx/core/system.hpp"
 #include "phx/rendering/components/light.hpp"
+#include "phx/rendering/components/mesh_render_settings.hpp"
 #include "phx/rendering/components/resource_component.hpp"
+#include "phx/rendering/components/skybox.hpp"
 #include "phx/rendering/components/transform.hpp"
+#include "phx/rendering/render_passes/skybox_pass.hpp"
 #include "phx/resources/resource_pointer.hpp"
 #include "phx/resources/types/material.hpp"
 #include "phx/resources/types/mesh.hpp"
 
+#include "gl/debug.hpp"
 #include "gl/opengl.hpp"
 
 namespace phx {
@@ -53,8 +57,11 @@ void RenderingSystem::InitializeOpenGL() {
   if (!gl::initialize()) {
     error("Initializing gl failed");
   }
-  std::string prefix = "[RenderingSystem] OpenGl Error: ";
-  gl::print_error(prefix.c_str());
+
+  gl::set_debug_output_enabled(true);
+  gl::set_debug_log_callback([&](gl::debug_log log) {
+    warn("[gl::error] with message {}", log.message);
+  });
 }
 
 void RenderingSystem::SetupFramegraph() {
@@ -64,16 +71,18 @@ void RenderingSystem::SetupFramegraph() {
 }
 
 void RenderingSystem::Update(const FrameTimer::TimeInfo&) {
-  std::vector<GeometryPass::RenderingInstance> rendering_instances;
-  std::vector<std::pair<Light*, Transform*>> light_transform_pairs;
-
   if (GetEngine()->GetScene() == nullptr) {
     return;
   }
 
+  std::vector<GeometryPass::RenderingInstance> rendering_instances;
+  std::vector<std::pair<Light*, Transform*>> light_transform_pairs;
+  Skybox* skybox = nullptr;
+
   for (auto& entity : GetEngine()->GetEntities()) {
     auto light = entity->GetFirstComponent<Light>();
     auto transform = entity->GetFirstComponent<Transform>();
+<<<<<<< HEAD
     const auto material =
         entity->GetFirstComponent<ResourceComponent<Material>>();
     const auto mesh = entity->GetFirstComponent<ResourceComponent<Mesh>>();
@@ -84,32 +93,55 @@ void RenderingSystem::Update(const FrameTimer::TimeInfo&) {
              (material != nullptr ? material->GetResourcePointer().Get()
                                   : nullptr),
              transform});
-      } else if (light != nullptr) {
-        light_transform_pairs.push_back(
-            std::pair<Light*, Transform*>(light, transform));
+        == == == = auto material_handle =
+                     entity->GetFirstComponent<MaterialHandle>();
+        Material* material = nullptr;
+        if (material_handle != nullptr)
+          material = material_handle->GetMaterial().Get();
+        auto mesh_render_settings =
+            entity->GetFirstComponent<MeshRenderSettings>();
+
+        if (transform != nullptr) {
+          if (mesh_handle != nullptr) {
+            rendering_instances.push_back({mesh_handle->GetMesh().Get(),
+                                           material, transform,
+                                           mesh_render_settings});
+>>>>>>> master
+          } else if (light != nullptr) {
+            light_transform_pairs.push_back(
+                std::pair<Light*, Transform*>(light, transform));
+          }
+        }
+        if (entity->GetFirstComponent<Skybox>() != nullptr) {
+          skybox = entity->GetFirstComponent<Skybox>();
+        }
       }
-    }
-  }
 
-  auto geometry_passes = frame_graph_->GetRenderPasses<GeometryPass>();
-  for (auto geometry_pass : geometry_passes) {
-    geometry_pass->SetData(rendering_instances, light_transform_pairs);
-  }
+      auto skybox_passes = frame_graph_->GetRenderPasses<SkyboxPass>();
+      for (auto skybox_pass : skybox_passes) {
+        skybox_pass->SetSkyboxComponent(skybox);
+      }
 
-  frame_graph_->Execute();
-}
+      auto geometry_passes = frame_graph_->GetRenderPasses<GeometryPass>();
+      for (auto geometry_pass : geometry_passes) {
+        geometry_pass->SetData(rendering_instances, light_transform_pairs);
+      }
 
-FrameGraph* RenderingSystem::GetFrameGraph() const {
-  return frame_graph_.get();
-}
+      frame_graph_->Execute();
+    }
 
-void RenderingSystem::SetFrameGraph(std::unique_ptr<FrameGraph> frame_graph) {
-  frame_graph_ = std::move(frame_graph);
-}
+    FrameGraph* RenderingSystem::GetFrameGraph() const {
+      return frame_graph_.get();
+    }
 
-std::string RenderingSystem::ToString() const {
-  return "RenderingSystem with #FrameGraph-Passes: " +
-         std::to_string(frame_graph_->GetNumberOfPasses());
-}
+    void RenderingSystem::SetFrameGraph(
+        std::unique_ptr<FrameGraph> frame_graph) {
+      frame_graph_ = std::move(frame_graph);
+    }
+
+    std::string RenderingSystem::ToString() const {
+      return "RenderingSystem with #FrameGraph-Passes: " +
+             std::to_string(frame_graph_->GetNumberOfPasses());
+    }
 
-}  // namespace phx
+  }  // namespace phx
diff --git a/library/phx/rendering/rendering_system.hpp b/library/phx/rendering/rendering_system.hpp
index 6d56ee71d065e304d9482dc023b099d30a87a77a..e0fad0bd36fc21c77b715afca7502e6ad2f093c8 100644
--- a/library/phx/rendering/rendering_system.hpp
+++ b/library/phx/rendering/rendering_system.hpp
@@ -32,10 +32,10 @@
 #include "phx/core/scene.hpp"
 #include "phx/core/system.hpp"
 #include "phx/display/display_system.hpp"
+#include "phx/export.hpp"
 #include "phx/rendering/backend/render_target.hpp"
-#include "phx/rendering/render_passes/geometry_pass.hpp"
 #include "phx/rendering/frame_graph.hpp"
-#include "phx/export.hpp"
+#include "phx/rendering/render_passes/geometry_pass.hpp"
 
 namespace phx {
 
@@ -44,7 +44,7 @@ class PHOENIX_EXPORT RenderingSystem : public System {
   RenderingSystem() = delete;
   RenderingSystem(const RenderingSystem&) = delete;
   RenderingSystem(RenderingSystem&&) = default;
-  virtual ~RenderingSystem() = default;
+  ~RenderingSystem() override = default;
 
   void Update(const FrameTimer::TimeInfo& time_info) override;
 
diff --git a/library/phx/resources/generators/material_generator.cpp b/library/phx/resources/generators/material_generator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2363f1ec3e4e17be99ec31526c6e18f2556ebf2e
--- /dev/null
+++ b/library/phx/resources/generators/material_generator.cpp
@@ -0,0 +1,51 @@
+//------------------------------------------------------------------------------
+// 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 "phx/resources/generators/material_generator.hpp"
+
+#include <memory>
+#include <vector>
+
+#include "phx/resources/types/material.hpp"
+
+namespace phx {
+std::unique_ptr<Resource> MaterialGenerator::Load(
+    const ResourceDeclaration& declaration) {
+  auto resource = std::make_unique<phx::Material>();
+
+  resource->SetName(declaration["material_name"]);
+
+  std::vector<float> diffuseColor = declaration["diffuse_color"];
+  resource->SetDiffuseColor(
+      glm::vec3(diffuseColor[0], diffuseColor[1], diffuseColor[2]));
+  std::vector<float> specularColor = declaration["specular_color"];
+  resource->SetSpecularColor(
+      glm::vec3(specularColor[0], specularColor[1], specularColor[2]));
+  std::vector<float> ambientColor = declaration["ambient_color"];
+  resource->SetAmbientColor(
+      glm::vec3(ambientColor[0], ambientColor[1], ambientColor[2]));
+  float shininess = declaration["shininess"];
+  resource->SetShininess(shininess);
+
+  return resource;
+}
+}  // namespace phx
diff --git a/tests/test_utilities/dummy_material_generator.hpp b/library/phx/resources/generators/material_generator.hpp
similarity index 55%
rename from tests/test_utilities/dummy_material_generator.hpp
rename to library/phx/resources/generators/material_generator.hpp
index 746df1f1d199b3887f8f26a600eeb20dfa6b71ab..267c8824d9125587e5dfcd4ed0653821aaf94eb2 100644
--- a/tests/test_utilities/dummy_material_generator.hpp
+++ b/library/phx/resources/generators/material_generator.hpp
@@ -20,43 +20,27 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#ifndef TESTS_TEST_UTILITIES_DUMMY_MATERIAL_GENERATOR_HPP_
-#define TESTS_TEST_UTILITIES_DUMMY_MATERIAL_GENERATOR_HPP_
+#ifndef LIBRARY_PHX_RESOURCES_GENERATORS_MATERIAL_GENERATOR_HPP_
+#define LIBRARY_PHX_RESOURCES_GENERATORS_MATERIAL_GENERATOR_HPP_
 
 #include <memory>
-#include <utility>
-#include <vector>
-
-#include "phx/suppress_warnings.hpp"
-
-SUPPRESS_WARNINGS_BEGIN
-#include "glm/vec3.hpp"
-SUPPRESS_WARNINGS_END
 
 #include "phx/resources/resource_load_strategy.hpp"
 
 namespace phx {
-class Material;
-
-class DummyMaterialGenerator : public ResourceLoadStrategy {
+class MaterialGenerator final : public ResourceLoadStrategy {
  public:
-  DummyMaterialGenerator() = delete;
-  DummyMaterialGenerator(glm::vec3 diffuse_color, glm::vec3 specular_color,
-                         glm::vec3 ambient_color, float shininess);
-  DummyMaterialGenerator(const DummyMaterialGenerator &) = default;
-  DummyMaterialGenerator(DummyMaterialGenerator &&) = default;
-  ~DummyMaterialGenerator() = default;
+  MaterialGenerator() = default;
+  MaterialGenerator(const MaterialGenerator &) = delete;
+  MaterialGenerator(MaterialGenerator &&) = delete;
+  ~MaterialGenerator() = default;
 
-  DummyMaterialGenerator &operator=(const DummyMaterialGenerator &) = default;
-  DummyMaterialGenerator &operator=(DummyMaterialGenerator &&) = default;
+  MaterialGenerator &operator=(const MaterialGenerator &) = delete;
+  MaterialGenerator &operator=(MaterialGenerator &&) = delete;
 
   std::unique_ptr<Resource> Load(
       const ResourceDeclaration &declaration) override;
-
- protected:
- private:
-  std::unique_ptr<phx::Material> material_;
 };
 }  // namespace phx
 
-#endif  // TESTS_TEST_UTILITIES_DUMMY_MATERIAL_GENERATOR_HPP_
+#endif  // LIBRARY_PHX_RESOURCES_GENERATORS_MATERIAL_GENERATOR_HPP_
diff --git a/library/phx/resources/loaders/assimp_model_loader.cpp b/library/phx/resources/loaders/assimp_model_loader.cpp
index 31341d2519a1108203afed03c0652e95b97091ce..97f706fc3b371c0cc233f6218c59e48c197bb0c2 100644
--- a/library/phx/resources/loaders/assimp_model_loader.cpp
+++ b/library/phx/resources/loaders/assimp_model_loader.cpp
@@ -181,6 +181,7 @@ std::unique_ptr<phx::Mesh> AssimpModelLoader::LoadSingleMesh(
   resource->SetTangents(LoadTangents(mesh));
   resource->SetBitangents(LoadBitangents(mesh));
   resource->SetTextureCoords(LoadTextureCoords(mesh));
+  resource->SetBoundingBox(ComputeBoundingBox(resource->GetVertices()));
 
   auto mesh_name = std::string(mesh->mName.C_Str());
   // If an object is unnamed, assimp names it "defaultobject"
@@ -314,5 +315,20 @@ bool AssimpModelLoader::AssimpProgressHandler::Update(float percentage) {
 
   return true;
 }
+std::array<glm::vec3, 2> AssimpModelLoader::ComputeBoundingBox(
+    const std::vector<glm::vec3> &vertices) {
+  std::array<glm::vec3, 2> boundingbox = {
+      {glm::vec3(INFINITY), glm::vec3(-INFINITY)}};
+  for (auto &vertex : vertices) {
+    for (int i = 0; i < 3; i++) {
+      if (vertex[i] < boundingbox[0][i]) {
+        boundingbox[0][i] = vertex[i];
+      } else if (vertex[i] > boundingbox[1][i]) {
+        boundingbox[1][i] = vertex[i];
+      }
+    }
+  }
+  return boundingbox;
+}
 
 }  // namespace phx
diff --git a/library/phx/resources/loaders/assimp_model_loader.hpp b/library/phx/resources/loaders/assimp_model_loader.hpp
index 94c010f0f9423f1459f1447208fe103c4277314d..5b7777567c1c0a907194c4866ae77979b001134f 100644
--- a/library/phx/resources/loaders/assimp_model_loader.hpp
+++ b/library/phx/resources/loaders/assimp_model_loader.hpp
@@ -39,16 +39,15 @@ SUPPRESS_WARNINGS_BEGIN
 SUPPRESS_WARNINGS_END
 
 #include "phx/export.hpp"
-#include "phx/resources/types/material.hpp"
-#include "phx/resources/types/mesh.hpp"
-#include "phx/resources/types/model.hpp"
 #include "phx/resources/resource_declaration.hpp"
 #include "phx/resources/resource_load_strategy.hpp"
 #include "phx/resources/resource_pointer.hpp"
+#include "phx/resources/types/material.hpp"
+#include "phx/resources/types/mesh.hpp"
+#include "phx/resources/types/model.hpp"
 
 namespace phx {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
 class PHOENIX_EXPORT AssimpModelLoader final : public ResourceLoadStrategy {
  public:
   AssimpModelLoader();
@@ -103,6 +102,9 @@ class PHOENIX_EXPORT AssimpModelLoader final : public ResourceLoadStrategy {
 
   bool LoadFile(const std::string& filename);
 
+  std::array<glm::vec3, 2> ComputeBoundingBox(
+      const std::vector<glm::vec3>& vertices);
+
   static const int kInvalidMaterialIndex = -1;
 
   Assimp::Importer importer_;
@@ -111,7 +113,6 @@ class PHOENIX_EXPORT AssimpModelLoader final : public ResourceLoadStrategy {
   int last_material_index_ = kInvalidMaterialIndex;
   std::function<void(float)> on_progress_update_callback_;
 };
-SUPPRESS_WARNINGS_END
 
 template <typename FileSourceType>
 std::vector<glm::vec3> phx::AssimpModelLoader::LoadVectorData(
diff --git a/library/phx/resources/loaders/image_loader.hpp b/library/phx/resources/loaders/image_loader.hpp
index aaebda26146ff3e81c26ee33369a6dfe93e99961..9e0e61fb4f42d357e1689460a12100069acf2a4b 100644
--- a/library/phx/resources/loaders/image_loader.hpp
+++ b/library/phx/resources/loaders/image_loader.hpp
@@ -35,7 +35,7 @@ class ImageLoader final : public ResourceLoadStrategy {
   ImageLoader() = default;
   ImageLoader(const ImageLoader &) = delete;
   ImageLoader(ImageLoader &&) = delete;
-  ~ImageLoader() = default;
+  ~ImageLoader() override = default;
 
   ImageLoader &operator=(const ImageLoader &) = delete;
   ImageLoader &operator=(ImageLoader &&) = delete;
diff --git a/library/phx/resources/loaders/openvr_resource_loader.cpp b/library/phx/resources/loaders/openvr_resource_loader.cpp
index 3c24d99ef7846cdd8f5e5e6e9005e0bbf055c9e5..230337716f6077f804325f84ba4bc459cb6d415c 100644
--- a/library/phx/resources/loaders/openvr_resource_loader.cpp
+++ b/library/phx/resources/loaders/openvr_resource_loader.cpp
@@ -22,18 +22,25 @@
 
 #include "phx/resources/loaders/openvr_resource_loader.hpp"
 
+#include <algorithm>
 #include <memory>
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "phx/core/logger.hpp"
+#include "phx/input/device_system.hpp"
+#include "phx/input/vr_controller.hpp"
+#include "phx/resources/resource_manager.hpp"
 #include "phx/resources/types/material.hpp"
 
 namespace phx {
 
-OpenVRResourceLoader::OpenVRResourceLoader(HMD *hmd) : hmd_(hmd) {}
+OpenVRResourceLoader::OpenVRResourceLoader(DeviceSystem* device_system)
+    : device_system_(device_system) {}
 
 std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load(
-    const ResourceDeclaration &declaration) {
+    const ResourceDeclaration& declaration) {
   if (declaration.find("OpenVR_type") == declaration.end() ||
       !declaration["OpenVR_type"].is_string()) {
     warn(
@@ -43,15 +50,15 @@ std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load(
     return nullptr;
   }
   std::string type = declaration["OpenVR_type"];
-  HMD::Controller controller_side = HMD::Controller::LEFT_CONTROLLER;
+  VRController::ControllerSide controller_side = VRController::LEFT_CONTROLLER;
   if (declaration.find("side") != declaration.end() &&
       declaration["side"] == "right") {
-    controller_side = HMD::Controller::RIGHT_CONTROLLER;
+    controller_side = VRController::RIGHT_CONTROLLER;
   }
   // otherwise default left
 
   if (type == "material") {
-    return hmd_->GetControllerMaterial(controller_side);
+    return GetMaterial(controller_side);
   }
   if (type == "texture") {
     int texture_id = 0;
@@ -64,12 +71,11 @@ std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load(
     } else {
       texture_id = declaration["texture_id"];
     }
-    return hmd_->GetControllerTexture(texture_id);
+    return GetTexture(texture_id);
   }
   if (type == "mesh") {
-    return hmd_->GetControllerMesh(controller_side);
+    return GetMesh(controller_side);
   }
-
   warn(
       "OpenVRResource with OpenVR_type {}, cannot be loaded. Full declaration: "
       "{}",
@@ -77,4 +83,92 @@ std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load(
   return nullptr;
 }
 
+vr::RenderModel_t* OpenVRResourceLoader::GetModel(
+    VRController::ControllerSide side) {
+  for (auto controller : device_system_->GetDevices<VRController>()) {
+    if (controller->GetSide() == side) return controller->GetModel();
+  }
+
+  warn(
+      "[OpenVRResourceLoader::GetModel] unable to find controller with side {}",
+      side == VRController::RIGHT_CONTROLLER ? "right" : "left");
+  return nullptr;
+}
+
+std::unique_ptr<phx::Mesh> OpenVRResourceLoader::GetMesh(
+    VRController::ControllerSide side) {
+  auto model = GetModel(side);
+  if (model == nullptr) {
+    return nullptr;
+  }
+  auto mesh = std::make_unique<phx::Mesh>();
+  std::vector<glm::vec3> vertices;
+  std::vector<glm::vec3> normals;
+  std::vector<glm::vec2> texcoords;
+  for (std::size_t i = 0; i < model->unVertexCount; i++) {
+    vertices.push_back(glm::vec3(model->rVertexData[i].vPosition.v[0],
+                                 model->rVertexData[i].vPosition.v[1],
+                                 model->rVertexData[i].vPosition.v[2]));
+    normals.push_back(glm::vec3(model->rVertexData[i].vNormal.v[0],
+                                model->rVertexData[i].vNormal.v[1],
+                                model->rVertexData[i].vNormal.v[2]));
+    texcoords.push_back(glm::vec2(model->rVertexData[i].rfTextureCoord[0],
+                                  model->rVertexData[i].rfTextureCoord[1]));
+  }
+  std::vector<unsigned int> indices;
+  for (std::size_t i = 0; i < model->unTriangleCount * 3; i++) {
+    indices.push_back(model->rIndexData[i]);
+  }
+  mesh->SetVertices(std::move(vertices));
+  mesh->SetNormals(std::move(normals));
+  mesh->SetTextureCoords(std::move(texcoords));
+  mesh->SetIndices(std::move(indices));
+  return mesh;
+}
+
+std::unique_ptr<Material> OpenVRResourceLoader::GetMaterial(
+    VRController::ControllerSide side) {
+  auto model = GetModel(side);
+  if (model == nullptr) return nullptr;
+
+  auto material = std::make_unique<phx::Material>();
+  material->SetAmbientColor(glm::vec3(0.1, 0.1, 0.1));
+  material->SetSpecularColor(glm::vec3(0.3, 0.3, 0.3));
+
+  auto texture = ResourceManager::instance().DeclareResource<Image>(
+      {{"TYPE", "openVR"},
+       {"OpenVR_type", "texture"},
+       {"texture_id", model->diffuseTextureId},
+       {"side", side == VRController::LEFT_CONTROLLER ? "left" : "right"}});
+  texture.Load();
+  material->SetDiffuseImage(texture);
+
+  return material;
+}
+
+std::unique_ptr<Image> OpenVRResourceLoader::GetTexture(int id) {
+  vr::RenderModel_TextureMap_t* texture_map;
+  while (vr::VRRenderModels()->LoadTexture_Async(id, &texture_map) ==
+         vr::VRRenderModelError_Loading) {
+    std::this_thread::sleep_for(std::chrono::milliseconds(50));
+  }
+  if (texture_map == nullptr) {
+    return nullptr;
+  }
+
+  std::vector<unsigned char> image_data(texture_map->unWidth *
+                                        texture_map->unHeight * 4);
+  std::copy(texture_map->rubTextureMapData,
+            texture_map->rubTextureMapData + image_data.size(),
+            image_data.begin());
+  auto image = std::make_unique<phx::Image>(
+      &image_data[0],
+      std::array<std::size_t, 2>{
+          {static_cast<std::size_t>(texture_map->unWidth),
+           static_cast<std::size_t>(texture_map->unHeight)}},
+      32);
+
+  return image;
+}
+
 }  // namespace phx
diff --git a/library/phx/resources/loaders/openvr_resource_loader.hpp b/library/phx/resources/loaders/openvr_resource_loader.hpp
index 357ff8ad9489e2ac269f962b8a48979d884c26d9..3ccd32ea744e23e21de87a83617d63d739bec047 100644
--- a/library/phx/resources/loaders/openvr_resource_loader.hpp
+++ b/library/phx/resources/loaders/openvr_resource_loader.hpp
@@ -33,17 +33,18 @@ SUPPRESS_WARNINGS_BEGIN
 SUPPRESS_WARNINGS_END
 
 #include "phx/export.hpp"
-#include "phx/display/hmd.hpp"
-#include "phx/resources/types/mesh.hpp"
+#include "phx/input/device_system.hpp"
+#include "phx/input/vr_controller.hpp"
 #include "phx/resources/resource_declaration.hpp"
 #include "phx/resources/resource_load_strategy.hpp"
+#include "phx/resources/types/mesh.hpp"
 
 namespace phx {
 class Mesh;
 
 class PHOENIX_EXPORT OpenVRResourceLoader final : public ResourceLoadStrategy {
  public:
-  explicit OpenVRResourceLoader(HMD *hmd);
+  explicit OpenVRResourceLoader(DeviceSystem *device_system);
   OpenVRResourceLoader(const OpenVRResourceLoader &) = delete;
   OpenVRResourceLoader(OpenVRResourceLoader &&) = delete;
   ~OpenVRResourceLoader() override = default;
@@ -53,8 +54,12 @@ class PHOENIX_EXPORT OpenVRResourceLoader final : public ResourceLoadStrategy {
 
   std::unique_ptr<Resource> Load(const ResourceDeclaration &file_name) override;
 
- private:
-  HMD *hmd_;
+  vr::RenderModel_t *GetModel(VRController::ControllerSide side);
+  std::unique_ptr<Mesh> GetMesh(VRController::ControllerSide side);
+  std::unique_ptr<Material> GetMaterial(VRController::ControllerSide side);
+  std::unique_ptr<Image> GetTexture(int id);
+
+  DeviceSystem *device_system_ = nullptr;
 };
 
 }  // namespace phx
diff --git a/library/phx/resources/loaders/scene_loader.cpp b/library/phx/resources/loaders/scene_loader.cpp
index 0e353229fd657f573346676b28647cc8a851b7a9..776e321e00429ed263ca6fc1e7bbc274bcb5de96 100644
--- a/library/phx/resources/loaders/scene_loader.cpp
+++ b/library/phx/resources/loaders/scene_loader.cpp
@@ -34,11 +34,11 @@
 
 namespace phx {
 
-bool SceneLoader::InsertModelIntoScene(const std::string& file_name,
-                                       Scene* scene) {
+phx::Entity* SceneLoader::InsertModelIntoScene(const std::string& file_name,
+                                               Scene* scene) {
   auto model = ResourceUtils::LoadResourceFromFile<Model>(file_name);
   if (model == nullptr) {
-    return false;
+    return nullptr;
   }
 
   auto rootEntity = scene->CreateEntity();
@@ -55,7 +55,7 @@ bool SceneLoader::InsertModelIntoScene(const std::string& file_name,
     }
   }
 
-  return true;
+  return rootEntity;
 }
 
 std::string SceneLoader::ConstructRootName(const std::string& file_name) {
diff --git a/library/phx/resources/loaders/scene_loader.hpp b/library/phx/resources/loaders/scene_loader.hpp
index 17d9403c12422ebb2b24d9fd6f2e3b285fd9e17b..b96bd59fa3405755a963a6ecec9190f19f805704 100644
--- a/library/phx/resources/loaders/scene_loader.hpp
+++ b/library/phx/resources/loaders/scene_loader.hpp
@@ -32,7 +32,9 @@ namespace phx {
 
 class PHOENIX_EXPORT SceneLoader {
  public:
-  static bool InsertModelIntoScene(const std::string& file_name, Scene* scene);
+  // Returns the root entity of the model
+  static Entity* InsertModelIntoScene(const std::string& file_name,
+                                      Scene* scene);
 
  private:
   static std::string ConstructRootName(const std::string& file_name);
diff --git a/library/phx/resources/loaders/shader_loader.hpp b/library/phx/resources/loaders/shader_loader.hpp
index a23444fa3d7df4e823a3a31cd8fe9341c8ef8c96..3f2a816001083ab8ddd73849bd48183641fc608c 100644
--- a/library/phx/resources/loaders/shader_loader.hpp
+++ b/library/phx/resources/loaders/shader_loader.hpp
@@ -35,7 +35,7 @@ class ShaderLoader final : public ResourceLoadStrategy {
   ShaderLoader() = default;
   ShaderLoader(const ShaderLoader&) = delete;
   ShaderLoader(ShaderLoader&&) = delete;
-  ~ShaderLoader() = default;
+  ~ShaderLoader() override = default;
 
   ShaderLoader& operator=(const ShaderLoader&) = delete;
   ShaderLoader& operator=(ShaderLoader&&) = delete;
diff --git a/library/phx/resources/resource_manager.cpp b/library/phx/resources/resource_manager.cpp
index a7ac44d8b21f28a6b2a0fa473a16c1952c51ccbd..0229450d85588a367e5dd09430b4c728170bf388 100644
--- a/library/phx/resources/resource_manager.cpp
+++ b/library/phx/resources/resource_manager.cpp
@@ -26,6 +26,7 @@
 #include <string>
 #include <utility>
 
+#include "phx/resources/generators/material_generator.hpp"
 #include "phx/resources/loaders/assimp_model_loader.hpp"
 #include "phx/resources/loaders/image_loader.hpp"
 #include "phx/resources/loaders/shader_loader.hpp"
@@ -37,6 +38,7 @@ ResourceManager::ResourceManager() {
   this->RegisterMeshResourceExtensions();
   this->RegisterShaderResourceExtensions();
   this->RegisterImageResourceExtensions();
+  this->RegisterResourceGenerators();
 }
 
 void ResourceManager::RegisterResourceType(
@@ -73,6 +75,7 @@ phx::ResourceLoadStrategy *ResourceManager::GetLoaderForType(
 
 void ResourceManager::RegisterMeshResourceExtensions() {
   this->RegisterResourceType(".obj", std::make_unique<AssimpModelLoader>());
+  this->RegisterResourceType(".stl", std::make_unique<AssimpModelLoader>());
 }
 
 void ResourceManager::RegisterShaderResourceExtensions() {
@@ -88,6 +91,12 @@ void ResourceManager::RegisterImageResourceExtensions() {
   this->RegisterResourceType(".jpg", std::make_unique<ImageLoader>());
   this->RegisterResourceType(".jpeg", std::make_unique<ImageLoader>());
   this->RegisterResourceType(".png", std::make_unique<ImageLoader>());
+  this->RegisterResourceType(".tga", std::make_unique<ImageLoader>());
+}
+
+void ResourceManager::RegisterResourceGenerators() {
+  this->RegisterResourceType("GEN_MATERIAL",
+                             std::make_unique<MaterialGenerator>());
 }
 
 }  // namespace phx
diff --git a/library/phx/resources/resource_manager.hpp b/library/phx/resources/resource_manager.hpp
index 54cf258ca1a0b8cc9eb1c0c6ded2134152216a6f..a701b0673af3d49084b99ac3f27b6c6674ed61b6 100644
--- a/library/phx/resources/resource_manager.hpp
+++ b/library/phx/resources/resource_manager.hpp
@@ -28,12 +28,12 @@
 #include <string>
 
 #include "phx/core/logger.hpp"
+#include "phx/export.hpp"
 #include "phx/resources/resource_declaration.hpp"
 #include "phx/resources/resource_load_strategy.hpp"
 #include "phx/resources/resource_pointer.hpp"
 #include "phx/resources/resource_proxy.hpp"
 #include "phx/utility/aspects/singleton.hpp"
-#include "phx/export.hpp"
 
 namespace phx {
 /**
@@ -73,6 +73,8 @@ class PHOENIX_EXPORT ResourceManager final : public singleton<ResourceManager> {
   void RegisterMeshResourceExtensions();
   void RegisterImageResourceExtensions();
 
+  void RegisterResourceGenerators();
+
   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/resources/resource_utils.cpp b/library/phx/resources/resource_utils.cpp
index e8e82fcb5356defa3988ae2696281f75a7054cc9..f79aefd817b2b90614cd941e4d88dbbb7ffb6351 100644
--- a/library/phx/resources/resource_utils.cpp
+++ b/library/phx/resources/resource_utils.cpp
@@ -22,13 +22,57 @@
 #include "phx/resources/resource_utils.hpp"
 
 #include <algorithm>
+#include <cassert>
+#include <fstream>
 #include <locale>
 #include <string>
+#include <vector>
+
+#include "boost/filesystem.hpp"
 
 #include "phx/resources/resource_manager.hpp"
 #include "phx/resources_path.hpp"
 
 namespace phx {
+namespace {
+std::vector<std::string> default_resource_search_paths = {
+    {std::string("./"), resources_root}};
+}
+
+std::vector<std::string> ResourceUtils::resource_search_paths_{
+    default_resource_search_paths};
+
+ResourceDeclaration ResourceUtils::GenericMaterialDeclaration(
+    const std::string& material_name, glm::vec3 diffuse_color,
+    glm::vec3 specular_color, glm::vec3 ambient_color, float shininess) {
+  ResourceDeclaration declaration = {{"TYPE", "GEN_MATERIAL"}};
+  declaration["material_name"] = material_name;
+
+  declaration["diffuse_color"] = {diffuse_color[0], diffuse_color[1],
+                                  diffuse_color[2]};
+  declaration["specular_color"] = {specular_color[0], specular_color[1],
+                                   specular_color[2]};
+  declaration["ambient_color"] = {ambient_color[0], ambient_color[1],
+                                  ambient_color[2]};
+  declaration["shininess"] = shininess;
+  return declaration;
+}
+
+void ResourceUtils::AddResourceSearchPath(const std::string& path_to_add) {
+  assert(!path_to_add.empty());
+  auto p = (path_to_add.back() == '/' ? path_to_add
+                                      : path_to_add + std::string("/"));
+  resource_search_paths_.push_back(p);
+}
+
+void ResourceUtils::ResetResourceSearchPath() {
+  resource_search_paths_ = default_resource_search_paths;
+}
+
+std::vector<std::string> ResourceUtils::GetResourceSearchPath() {
+  return resource_search_paths_;
+}
+
 ResourceDeclaration ResourceUtils::DeclarationFromFile(
     const std::string& file_name, nlohmann::json additional_info /*= {}*/,
     bool absolute_path /*= false*/) {
@@ -36,7 +80,7 @@ ResourceDeclaration ResourceUtils::DeclarationFromFile(
   if (absolute_path) {
     declaration["file_name"] = file_name;
   } else {
-    declaration["file_name"] = resources_root + file_name;
+    declaration["file_name"] = FindFileInSearchPath(file_name);
   }
   for (nlohmann::json::iterator it = additional_info.begin();
        it != additional_info.end(); ++it) {
@@ -44,6 +88,7 @@ ResourceDeclaration ResourceUtils::DeclarationFromFile(
   }
   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(),
@@ -52,4 +97,24 @@ std::string ResourceUtils::ExtractFileExtension(const std::string& file_name) {
   return extension;
 }
 
+ResourcePointer<Material> ResourceUtils::LoadGenericMaterial(
+    const std::string& material_name, glm::vec3 diffuse_color,
+    glm::vec3 specular_color /*= Material::GetDefault()->GetSpecularColor()*/,
+    glm::vec3 ambient_color /*= Material::GetDefault()->GetAmbientColor()*/,
+    float shininess /*= Material::GetDefault()->GetShininess()*/) {
+  auto res_ptr = phx::ResourceManager::instance().DeclareResource<Material>(
+      GenericMaterialDeclaration(material_name, diffuse_color, specular_color,
+                                 ambient_color, shininess));
+  res_ptr.Load();
+  return res_ptr;
+}
+
+std::string ResourceUtils::FindFileInSearchPath(const std::string& file_name) {
+  for (auto& entry : resource_search_paths_) {
+    auto full_name = entry + file_name;
+    if (boost::filesystem::exists(full_name.c_str())) return full_name;
+  }
+  return "";
+}
+
 }  // namespace phx
diff --git a/library/phx/resources/resource_utils.hpp b/library/phx/resources/resource_utils.hpp
index 8f18bc1fa3c17ae0db1dc93a83ed4aebab8d8236..06d925854fe6c76ac45f36469e476a742b5510c8 100644
--- a/library/phx/resources/resource_utils.hpp
+++ b/library/phx/resources/resource_utils.hpp
@@ -26,29 +26,50 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "json.hpp"
 
 #include "phx/core/logger.hpp"
+#include "phx/export.hpp"
 #include "phx/resources/resource_declaration.hpp"
 #include "phx/resources/resource_load_strategy.hpp"
 #include "phx/resources/resource_manager.hpp"
 #include "phx/resources/resource_pointer.hpp"
+#include "phx/resources/types/material.hpp"
 #include "phx/utility/aspects/singleton.hpp"
-#include "phx/export.hpp"
 
 namespace phx {
 /**
  * The ResourceUtils class contains static convenience methods to simplify
- * interactions with the resource system. It should never hold any state.
+ * interactions with the resource system. It also holds a user configurable
+ * resource search path. When operating on a relative file name, it will
+ * search all entries in the search path and return/load the first file that
+ * matches the given file name. Per default, the resource search path
+ * contains the local directory and phx::resources_root.
  */
 class PHOENIX_EXPORT ResourceUtils final {
  public:
+  static ResourceDeclaration GenericMaterialDeclaration(
+      const std::string& material_name, glm::vec3 diffuse_color,
+      glm::vec3 specular_color, glm::vec3 ambient_color, float shininess);
+
+  static void AddResourceSearchPath(const std::string& path_to_add);
+  static void ResetResourceSearchPath();
+  static std::vector<std::string> GetResourceSearchPath();
+
   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 ResourcePointer<Material> LoadGenericMaterial(
+      const std::string& material_name, glm::vec3 diffuse_color,
+      glm::vec3 specular_color = Material::GetDefault()->GetSpecularColor(),
+      glm::vec3 ambient_color = Material::GetDefault()->GetAmbientColor(),
+      float shininess = Material::GetDefault()->GetShininess());
+
   template <class ResourceType>
   static ResourcePointer<ResourceType> LoadResourceFromFile(
       const std::string& file_name, nlohmann::json additional_info = {},
@@ -59,6 +80,10 @@ class PHOENIX_EXPORT ResourceUtils final {
     res_ptr.Load();
     return res_ptr;
   }
+
+ private:
+  static std::string FindFileInSearchPath(const std::string& file_name);
+  static std::vector<std::string> resource_search_paths_;
 };
 
 }  // namespace phx
diff --git a/library/phx/resources/types/image.cpp b/library/phx/resources/types/image.cpp
index f4da0845b2f216c908b3172ebc1bf92c935027c9..48f4fa2910e834a97858abadd19c89179825ec60 100644
--- a/library/phx/resources/types/image.cpp
+++ b/library/phx/resources/types/image.cpp
@@ -25,6 +25,8 @@
 #include <string>
 #include <utility>
 
+#include "phx/core/logger.hpp"
+
 namespace phx {
 std::once_flag Image::once_flag_;
 
@@ -46,7 +48,20 @@ Image::Image(const std::string& filepath, const std::int32_t native_flags) {
     format = FreeImage_GetFIFFromFilename(filepath.c_str());
 
   native_ = FreeImage_Load(format, filepath.c_str(), native_flags);
-  if (!native_) throw std::runtime_error("FreeImage_Load failed.");
+  if (!native_) {
+    error(
+        "FreeImage_Load failed for path {}. A default 1x1 fuchsia image is "
+        "used instead.",
+        filepath);
+    native_ = FreeImage_AllocateT(FIT_BITMAP, static_cast<std::int32_t>(1),
+                                  static_cast<std::int32_t>(1),
+                                  static_cast<std::int32_t>(32), 0, 0, 0);
+
+    std::array<std::uint8_t, 4> fuchsia_pixel = {{255u, 0u, 255u, 255u}};
+
+    FreeImage_SetPixelColor(native_, 0, 0,
+                            reinterpret_cast<RGBQUAD*>(fuchsia_pixel.data()));
+  }
   SwapRedBlue(native_);
 }
 Image::Image(const Image& that)
diff --git a/library/phx/resources/types/image.hpp b/library/phx/resources/types/image.hpp
index a5497948d3270f868eee852939ae1a27acdfe367..8638e7bb8f9522751fa44555848248fd05ce3cbd 100644
--- a/library/phx/resources/types/image.hpp
+++ b/library/phx/resources/types/image.hpp
@@ -81,7 +81,7 @@ class PHOENIX_EXPORT Image : public Resource, public Loggable {
                  const std::int32_t native_flags = 0);
   Image(const Image& that);
   Image(Image&& temp) noexcept;
-  virtual ~Image();
+  ~Image() override;
   Image& operator=(const Image& that);
   Image& operator=(Image&& temp) noexcept;
 
diff --git a/library/phx/resources/types/material.cpp b/library/phx/resources/types/material.cpp
index f79e5d332942b63f933ddc00235cb201fe4e8d06..bf130003f5daed018b24dd2601b26ffd5323f32c 100644
--- a/library/phx/resources/types/material.cpp
+++ b/library/phx/resources/types/material.cpp
@@ -32,6 +32,7 @@
 namespace phx {
 
 const char Material::UNNAMED[] = "UnnamedMaterial";
+const Material Material::default_material_;
 
 glm::vec3 Material::GetDiffuseColor() const { return diffuse_color_; }
 void Material::SetDiffuseColor(glm::vec3 color) { diffuse_color_ = color; }
@@ -41,11 +42,10 @@ ResourcePointer<Image> Material::GetDiffuseImage() const {
 }
 void Material::SetDiffuseImage(ResourcePointer<Image> image) {
   diffuse_image_ = image;
+  ResetTexture(&diffuse_texture_);
 }
 
-gl::texture_2d* Material::GetDiffuseTexture() {
-  if (diffuse_image_ && !diffuse_texture_)
-    SetTexture(diffuse_image_, &diffuse_texture_);
+gl::texture_2d* Material::GetDiffuseTexture() const {
   return diffuse_texture_.get();
 }
 
@@ -57,11 +57,10 @@ ResourcePointer<Image> Material::GetAmbientImage() const {
 }
 void Material::SetAmbientImage(ResourcePointer<Image> image) {
   ambient_image_ = image;
+  ResetTexture(&ambient_texture_);
 }
 
-gl::texture_2d* Material::GetAmbientTexture() {
-  if (ambient_image_ && !ambient_texture_)
-    SetTexture(ambient_image_, &ambient_texture_);
+gl::texture_2d* Material::GetAmbientTexture() const {
   return ambient_texture_.get();
 }
 
@@ -73,11 +72,10 @@ ResourcePointer<Image> Material::GetSpecularImage() const {
 }
 void Material::SetSpecularImage(ResourcePointer<Image> image) {
   specular_image_ = image;
+  ResetTexture(&specular_texture_);
 }
 
-gl::texture_2d* Material::GetSpecularTexture() {
-  if (specular_image_ && !specular_texture_)
-    SetTexture(specular_image_, &specular_texture_);
+gl::texture_2d* Material::GetSpecularTexture() const {
   return specular_texture_.get();
 }
 
@@ -93,12 +91,25 @@ void Material::SetShininess(float shininess) {
   }
 }
 
+const Material* Material::GetDefault() { return &default_material_; }
+
+void Material::UploadTextures() {
+  if (!diffuse_texture_) {
+    SetTexture(diffuse_image_, &diffuse_texture_);
+  }
+  if (!specular_texture_) {
+    SetTexture(specular_image_, &specular_texture_);
+  }
+  if (!ambient_texture_) {
+    SetTexture(ambient_image_, &ambient_texture_);
+  }
+}
+
 void Material::SetTexture(ResourcePointer<Image> image,
                           std::shared_ptr<gl::texture_2d>* texture) {
   if (image == nullptr) return;
-  if (*texture && (*texture)->is_valid()) {
-    gl::texture_handle handle(*texture->get());
-    handle.set_resident(false);
+  if (*texture) {
+    ResetTexture(texture);
   }
 
   auto dimensions = image->GetDimensions();
@@ -120,7 +131,14 @@ void Material::SetTexture(ResourcePointer<Image> image,
   (*texture)->generate_mipmap();
   gl::texture_handle handle(*texture->get());
   handle.set_resident(true);
-  gl::print_error("[Material::SetTexture] OpenGl Error code");
+}
+
+void Material::ResetTexture(std::shared_ptr<gl::texture_2d>* texture) {
+  if (*texture && (*texture)->is_valid()) {
+    gl::texture_handle handle(*texture->get());
+    handle.set_resident(false);
+  }
+  texture->reset();
 }
 
 }  // namespace phx
diff --git a/library/phx/resources/types/material.hpp b/library/phx/resources/types/material.hpp
index 557dbfc01397270ac47f926fec0be7a2d4f663ed..fc839341bbf95af5e3ec719aeaaef0caf15a56b6 100644
--- a/library/phx/resources/types/material.hpp
+++ b/library/phx/resources/types/material.hpp
@@ -35,11 +35,11 @@ SUPPRESS_WARNINGS_BEGIN
 #include "glm/vec3.hpp"
 SUPPRESS_WARNINGS_END
 
+#include "phx/export.hpp"
 #include "phx/resources/resource.hpp"
 #include "phx/resources/resource_pointer.hpp"
 #include "phx/resources/types/image.hpp"
 #include "phx/utility/aspects/nameable.hpp"
-#include "phx/export.hpp"
 
 namespace phx {
 
@@ -62,31 +62,34 @@ class PHOENIX_EXPORT Material : public Resource, public Nameable {
 
   ResourcePointer<Image> GetDiffuseImage() const;
   void SetDiffuseImage(ResourcePointer<Image> image);
-
-  gl::texture_2d* GetDiffuseTexture();
+  gl::texture_2d* GetDiffuseTexture() const;
 
   glm::vec3 GetAmbientColor() const;
   void SetAmbientColor(glm::vec3 color);
 
   ResourcePointer<Image> GetAmbientImage() const;
   void SetAmbientImage(ResourcePointer<Image> image);
-
-  gl::texture_2d* GetAmbientTexture();
+  gl::texture_2d* GetAmbientTexture() const;
 
   glm::vec3 GetSpecularColor() const;
   void SetSpecularColor(glm::vec3 color);
 
   ResourcePointer<Image> GetSpecularImage() const;
   void SetSpecularImage(ResourcePointer<Image> image);
-
-  gl::texture_2d* GetSpecularTexture();
+  gl::texture_2d* GetSpecularTexture() const;
 
   float GetShininess() const;
   void SetShininess(float shininess);
 
+  static const Material* GetDefault();
+
+  // unless this was called Get..Texture return nullptr
+  void UploadTextures();
+
  private:
   void SetTexture(ResourcePointer<Image> image,
                   std::shared_ptr<gl::texture_2d>* texture);
+  void ResetTexture(std::shared_ptr<gl::texture_2d>* texture);
 
   ResourcePointer<Image> ambient_image_{nullptr};
   ResourcePointer<Image> diffuse_image_{nullptr};
@@ -97,7 +100,9 @@ class PHOENIX_EXPORT Material : public Resource, public Nameable {
   glm::vec3 ambient_color_ = glm::vec3(0, 0, 0);
   glm::vec3 diffuse_color_ = glm::vec3(1, 0, 0);
   glm::vec3 specular_color_ = glm::vec3(1, 1, 1);
-  float shininess_ = 1.0f;
+  float shininess_ = 64.0f;
+
+  static const Material default_material_;
 };
 
 }  // namespace phx
diff --git a/library/phx/resources/types/mesh.cpp b/library/phx/resources/types/mesh.cpp
index a4e8f9438618eabed973b14b21c38eeb8b048fa0..842a130ec27f078752bc56ead8cf446b8ddf1ea9 100644
--- a/library/phx/resources/types/mesh.cpp
+++ b/library/phx/resources/types/mesh.cpp
@@ -26,8 +26,10 @@
 #include <utility>
 #include <vector>
 
+SUPPRESS_WARNINGS_BEGIN
 #include "glm/vec2.hpp"
 #include "glm/vec3.hpp"
+SUPPRESS_WARNINGS_END
 
 namespace phx {
 
@@ -74,4 +76,30 @@ void Mesh::SetIndices(std::vector<unsigned int>&& indices) {
 
 const std::vector<unsigned int>& Mesh::GetIndices() const { return indices_; }
 
+void Mesh::SetBoundingBox(const std::array<glm::vec3, 2>& boundingbox) {
+  boundingbox_ = std::move(boundingbox);
+}
+
+const std::array<glm::vec3, 2>& Mesh::GetBoundingBox() const {
+  return boundingbox_;
+}
+
+Mesh Mesh::GenerateBoundingBoxMesh() {
+  Mesh boundingbox;
+
+  boundingbox.SetVertices(
+      {glm::vec3{boundingbox_[0][0], boundingbox_[0][1], boundingbox_[0][2]},
+       glm::vec3{boundingbox_[0][0], boundingbox_[1][1], boundingbox_[0][2]},
+       glm::vec3{boundingbox_[0][0], boundingbox_[0][1], boundingbox_[1][2]},
+       glm::vec3{boundingbox_[0][0], boundingbox_[1][1], boundingbox_[1][2]},
+       glm::vec3{boundingbox_[1][0], boundingbox_[1][1], boundingbox_[1][2]},
+       glm::vec3{boundingbox_[1][0], boundingbox_[0][1], boundingbox_[0][2]},
+       glm::vec3{boundingbox_[1][0], boundingbox_[1][1], boundingbox_[0][2]},
+       glm::vec3{boundingbox_[1][0], boundingbox_[0][1], boundingbox_[1][2]}});
+  boundingbox.SetIndices({
+      0,
+  });
+  return boundingbox;
+}
+
 }  // namespace phx
diff --git a/library/phx/resources/types/mesh.hpp b/library/phx/resources/types/mesh.hpp
index 7ebd5f24e5912bd614d41218a6357f8fae719f2b..b9618fd13b5474d2d0f07034d745ade239429356 100644
--- a/library/phx/resources/types/mesh.hpp
+++ b/library/phx/resources/types/mesh.hpp
@@ -23,6 +23,7 @@
 #ifndef LIBRARY_PHX_RESOURCES_TYPES_MESH_HPP_
 #define LIBRARY_PHX_RESOURCES_TYPES_MESH_HPP_
 
+#include <array>
 #include <string>
 #include <vector>
 
@@ -70,6 +71,11 @@ class PHOENIX_EXPORT Mesh : public Resource, public Nameable {
   void SetIndices(std::vector<unsigned int> &&indices);
   const std::vector<unsigned int> &GetIndices() const;
 
+  void SetBoundingBox(const std::array<glm::vec3, 2> &boundingbox);
+  const std::array<glm::vec3, 2> &GetBoundingBox() const;
+
+  Mesh GenerateBoundingBoxMesh();
+
  private:
   std::vector<glm::vec3> vertices_;
   std::vector<glm::vec3> normals_;
@@ -77,6 +83,7 @@ class PHOENIX_EXPORT Mesh : public Resource, public Nameable {
   std::vector<glm::vec3> bitangents_;
   std::vector<glm::vec2> texture_coords_;
   std::vector<unsigned int> indices_;
+  std::array<glm::vec3, 2> boundingbox_;
 };
 
 }  // namespace phx
diff --git a/library/phx/scripting/behavior_system.hpp b/library/phx/scripting/behavior_system.hpp
index 5513e828fc5cf1e59db20b0beabc5d914b4a4fc3..e723ab04016c3b1aa9369dd78fb0c9079a050a15 100644
--- a/library/phx/scripting/behavior_system.hpp
+++ b/library/phx/scripting/behavior_system.hpp
@@ -31,14 +31,12 @@
 
 namespace phx {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 class PHOENIX_EXPORT BehaviorSystem : public System {
  public:
   BehaviorSystem() = delete;
   BehaviorSystem(const BehaviorSystem&) = delete;
   BehaviorSystem(BehaviorSystem&&) = default;
-  ~BehaviorSystem() = default;
+  ~BehaviorSystem() override = default;
 
   void Update(const FrameTimer::TimeInfo& time_info) override;
 
@@ -50,8 +48,6 @@ class PHOENIX_EXPORT BehaviorSystem : public System {
   explicit BehaviorSystem(Engine* engine);
 };
 
-SUPPRESS_WARNINGS_END
-
 }  // namespace phx
 
 #endif  // LIBRARY_PHX_SCRIPTING_BEHAVIOR_SYSTEM_HPP_
diff --git a/library/phx/scripting/generic_behavior.cpp b/library/phx/scripting/generic_behavior.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7ac991bfecc58048c6c4ea80e05e202cdba7264a
--- /dev/null
+++ b/library/phx/scripting/generic_behavior.cpp
@@ -0,0 +1,44 @@
+//------------------------------------------------------------------------------
+// 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 "generic_behavior.hpp"
+
+#include <string>
+
+namespace phx {
+
+GenericBehavior::GenericBehavior(
+    const std::function<void(Entity*)>& on_construction,
+    const std::function<void(const FrameTimer::TimeInfo* time_info, Entity*)>&
+        on_update,
+    const std::string& name /*= "unnamed"*/)
+    : on_update_(on_update), name_(name) {
+  on_construction(GetEntity());
+}
+
+void GenericBehavior::OnUpdate() { on_update_(time_info_, GetEntity()); }
+
+std::string GenericBehavior::ToString() const {
+  return "GenericBehavior: " + name_;
+}
+
+}  // namespace phx
diff --git a/library/phx/scripting/generic_behavior.hpp b/library/phx/scripting/generic_behavior.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cc86fb25b0499a453395c8a3bc0ca220655a9ac8
--- /dev/null
+++ b/library/phx/scripting/generic_behavior.hpp
@@ -0,0 +1,60 @@
+//------------------------------------------------------------------------------
+// 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_SCRIPTING_GENERIC_BEHAVIOR_HPP_
+#define LIBRARY_PHX_SCRIPTING_GENERIC_BEHAVIOR_HPP_
+
+#include <functional>
+#include <string>
+
+#include "phx/scripting/behavior.hpp"
+
+namespace phx {
+
+class PHOENIX_EXPORT GenericBehavior : public Behavior {
+ public:
+  GenericBehavior(
+      const std::function<void(Entity*)>& on_construction,
+      const std::function<void(const FrameTimer::TimeInfo* time_info, Entity*)>&
+          on_update,
+      const std::string& name = "unnamed");
+  GenericBehavior(const GenericBehavior&) = default;
+  GenericBehavior(GenericBehavior&&) = default;
+
+  GenericBehavior& operator=(const GenericBehavior&) = default;
+  GenericBehavior& operator=(GenericBehavior&&) = default;
+
+  ~GenericBehavior() = default;
+
+  void OnUpdate() override;
+  std::string ToString() const override;
+
+ protected:
+  std::function<void(Entity*)> on_construction_;
+  std::function<void(const FrameTimer::TimeInfo* time_info, Entity*)>
+      on_update_;
+  std::string name_;
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_SCRIPTING_GENERIC_BEHAVIOR_HPP_
diff --git a/library/phx/setup.cpp b/library/phx/setup.cpp
index 0cb1fb02c23183be73f05b1052ec01678d996a14..4169253e75d4abb6206ed974f45774c7ffe325a6 100644
--- a/library/phx/setup.cpp
+++ b/library/phx/setup.cpp
@@ -27,6 +27,8 @@
 #include <string>
 #include <utility>
 
+#include "input/device_system.hpp"
+#include "input/vr_controller.hpp"
 #include "phx/core/component.hpp"
 #include "phx/core/engine.hpp"
 #include "phx/core/logger.hpp"
@@ -36,10 +38,11 @@
 #include "phx/display/display_system_window.hpp"
 #include "phx/display/hmd.hpp"
 #include "phx/input/input_system.hpp"
-#include "phx/input/openvr_controller_system.hpp"
+#include "phx/input/openvr_controller_model_system.hpp"
 #include "phx/rendering/backend/render_target.hpp"
 #include "phx/rendering/render_passes/blit_pass.hpp"
 #include "phx/rendering/render_passes/clear_pass.hpp"
+#include "phx/rendering/render_passes/skybox_pass.hpp"
 #include "phx/rendering/rendering_system.hpp"
 #include "phx/scripting/behavior_system.hpp"
 #include "phx/tracking/tracking_system_openvr.hpp"
@@ -47,7 +50,7 @@
 #undef CreateWindow
 namespace phx {
 
-std::unique_ptr<Engine> Setup::CreateDefaultEngine() {
+std::unique_ptr<Engine> Setup::CreateDefaultEngine(bool use_hmd_if_available) {
   auto engine = std::make_unique<Engine>();
   auto engine_ptr = engine.get();
   engine->SetScene(std::make_shared<Scene>());
@@ -56,16 +59,21 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine() {
   engine->CreateSystem<InputSystem>()->AddQuitCallback(
       [engine_ptr]() { engine_ptr->Stop(); });
 
+  auto device_system = engine->CreateSystem<DeviceSystem>();
+
   auto displaysys_window = engine->CreateSystem<DisplaySystemWindow>();
-  DisplaySystemOpenVR* displaysys_hmd = nullptr;
+  DisplaySystemOpenVR* displaysys_openVR = nullptr;
   bool using_hmd = false;
 
-  if (HMD::IsHMDPresent()) {
+  if (HMD::IsHMDPresent() && use_hmd_if_available) {
     info("An HMD is present so we use it");
     using_hmd = true;
 
-    displaysys_hmd = engine->CreateSystem<DisplaySystemOpenVR>();
-    displaysys_hmd->CreateHMD();
+    device_system->AddDevice<HMD>();
+    device_system->AddDevice<VRController>(VRController::LEFT_CONTROLLER);
+    device_system->AddDevice<VRController>(VRController::RIGHT_CONTROLLER);
+
+    displaysys_openVR = engine->CreateSystem<DisplaySystemOpenVR>();
   }
 
   const std::string window_title{using_hmd ? "Phoenix -- HMD Companion"
@@ -79,21 +87,24 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine() {
 
   // fix update order
   engine->MoveSystemToBack(displaysys_window);
+  if (displaysys_openVR != nullptr) engine->MoveSystemToBack(displaysys_openVR);
   engine->MoveSystemBefore(rendering_system, displaysys_window);
+  engine->MoveSystemBefore(device_system, rendering_system);
 
   // setup rendering and frame graph
-  if (using_hmd) {
-    auto tracking_system =
-        engine->CreateSystem<TrackingSystemOpenVR>(displaysys_hmd);
-    auto controller_system = engine->CreateSystem<OpenVRControllerSystem>(
-        engine->GetSystem<DisplaySystem>());
-
-    displaysys_hmd->CreateRenderTargets(engine->GetScene().get());
+  if (HMD::IsHMDPresent() && use_hmd_if_available) {
+    auto controller_model_system =
+        engine->CreateSystem<OpenVRControllerModelSystem>(
+            engine->GetSystem<DeviceSystem>());
+    auto tracking_system = engine->CreateSystem<TrackingSystemOpenVR>(
+        engine->GetSystem<DeviceSystem>());
+
+    displaysys_openVR->CreateRenderTargets(engine->GetScene().get());
     SetupDefaultFrameGraphOpenVR(rendering_system, engine.get());
 
     engine->MoveSystemBefore(tracking_system, rendering_system);
     engine->MoveSystemAfter(behavior_system, tracking_system);
-    engine->MoveSystemAfter(controller_system, tracking_system);
+    engine->MoveSystemAfter(controller_model_system, tracking_system);
   } else {
     displaysys_window->CreateRenderTarget(engine->GetScene().get(), 68.0f,
                                           0.01f, 1000.0f);
@@ -115,6 +126,7 @@ void Setup::SetupDefaultFrameGraphWindow(RenderingSystem* rendering_system,
   }
 
   frame_graph->AddRenderPass(std::make_unique<ClearPass>(render_target));
+  frame_graph->AddRenderPass(std::make_unique<SkyboxPass>(render_target));
   frame_graph->AddRenderPass(std::make_unique<GeometryPass>(render_target));
   frame_graph->AddRenderPass(std::make_unique<BlitPass>(render_target));
 
@@ -143,6 +155,8 @@ void Setup::SetupDefaultFrameGraphOpenVR(RenderingSystem* rendering_system,
 
   frame_graph->AddRenderPass(std::make_unique<ClearPass>(right_render_target));
   frame_graph->AddRenderPass(std::make_unique<ClearPass>(left_render_target));
+  frame_graph->AddRenderPass(std::make_unique<SkyboxPass>(right_render_target));
+  frame_graph->AddRenderPass(std::make_unique<SkyboxPass>(left_render_target));
   frame_graph->AddRenderPass(
       std::make_unique<GeometryPass>(right_render_target));
   frame_graph->AddRenderPass(
diff --git a/library/phx/setup.hpp b/library/phx/setup.hpp
index fef65a3543c39b9a268ea14e5e9616c26b12372c..6d197c26a60051776db2617c1b8dd1446f32914a 100644
--- a/library/phx/setup.hpp
+++ b/library/phx/setup.hpp
@@ -41,7 +41,8 @@ class PHOENIX_EXPORT Setup {
   // sets up the engine with the most default setup
   // i.e.: RenderingSystem and an InputSystem
   // also creates an empty scene for the engine
-  static std::unique_ptr<Engine> CreateDefaultEngine();
+  static std::unique_ptr<Engine> CreateDefaultEngine(
+      bool use_hmd_if_available = true);
 
   static void SetupDefaultFrameGraphWindow(RenderingSystem* rendering_system,
                                            Engine* engine);
diff --git a/library/phx/tracking/tracking_system_openvr.cpp b/library/phx/tracking/tracking_system_openvr.cpp
index e101095e0ff421af0d03b59666e79bc2684abb70..8156bc3c4f16e7fb9a100611004a9b28a8f68932 100644
--- a/library/phx/tracking/tracking_system_openvr.cpp
+++ b/library/phx/tracking/tracking_system_openvr.cpp
@@ -25,23 +25,22 @@
 #include <memory>
 #include <string>
 
-#include "phx/core/logger.hpp"
 #include "phx/core/runtime_component.hpp"
 #include "phx/display/display_system_openvr.hpp"
 #include "phx/display/hmd.hpp"
+#include "phx/input/device_system.hpp"
+#include "phx/input/vr_controller.hpp"
 #include "phx/rendering/components/projection.hpp"
 #include "phx/rendering/components/transform.hpp"
 
 namespace phx {
 
 TrackingSystemOpenVR::TrackingSystemOpenVR(Engine* engine,
-                                           DisplaySystemOpenVR* display_system)
+                                           DeviceSystem* device_system)
     : System(engine) {
-  if (!display_system)
-    error("TrackingSystemOpenVR needs a valid DisplaySystemOpenVR.");
-  if (display_system->GetHMD()) {
-    CreateRuntimeEntities(engine->GetScene().get());
-    scene_changed_connection_ = engine->AddSceneChangedCallback(
+  if (device_system->GetDevices<HMD>().size() != 0) {
+    CreateRuntimeEntities(engine_->GetScene().get());
+    scene_changed_connection_ = engine_->AddSceneChangedCallback(
         [this](std::shared_ptr<Scene> old_scene,
                std::shared_ptr<Scene> new_scene) {
           OnSceneChanged(old_scene, new_scene);
@@ -54,43 +53,34 @@ TrackingSystemOpenVR::~TrackingSystemOpenVR() {
 }
 
 void TrackingSystemOpenVR::Update(const FrameTimer::TimeInfo&) {
-  const auto dispsys_openvr = engine_->GetSystem<DisplaySystemOpenVR>();
-  if (dispsys_openvr == nullptr) {
+  const auto device_system = engine_->GetSystem<DeviceSystem>();
+  if (device_system == nullptr) {
     return;
   }
-  const auto hmd = dispsys_openvr->GetHMD();
-  if (hmd == nullptr) {
-    return;
-  }
-  hmd->UpdateTrackedDevices();
-  const auto head_transformation = hmd->GetHeadTransformation();
-  if (hmd_entity_ != nullptr) {
-    hmd_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
-        head_transformation);
-    const auto left_eye_transformation = hmd->GetEyeToHeadMatrix(HMD::LEFT_EYE);
-    left_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
-        left_eye_transformation);
-    const auto right_eye_transformation =
-        hmd->GetEyeToHeadMatrix(HMD::RIGHT_EYE);
-    right_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
-        right_eye_transformation);
-
-    left_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
-        hmd->GetLeftControllerTransformation());
-
-    right_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
-        hmd->GetRightControllerTransformation());
-  }
+
+  auto hmds = device_system->GetDevices<HMD>();
+  if (hmds.size() != 1) return;
+  auto hmd = hmds[0];
+
+  hmd_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(hmd->GetPose());
+  left_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
+      hmd->GetEyeToHeadMatrix(HMD::LEFT_EYE));
+  right_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
+      hmd->GetEyeToHeadMatrix(HMD::RIGHT_EYE));
+
+  left_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
+      GetController(VRController::LEFT_CONTROLLER)->GetPose());
+
+  right_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(
+      GetController(VRController::RIGHT_CONTROLLER)->GetPose());
 }
 
 void TrackingSystemOpenVR::CreateRuntimeEntities(Scene* scene) {
-  const auto hmd = engine_->GetSystem<DisplaySystemOpenVR>()->GetHMD();
-  if (hmd == nullptr) {
-    return;
-  }
-  if (scene == nullptr) {
+  const auto device_system = engine_->GetSystem<DeviceSystem>();
+  if (device_system == nullptr || scene == nullptr) {
     return;
   }
+
   const auto virtual_platforms = scene->GetEntitiesWithComponents<
       phx::RuntimeComponent<phx::USER_PLATFORM>>();
   if (!virtual_platforms.empty()) {
@@ -102,18 +92,18 @@ void TrackingSystemOpenVR::CreateRuntimeEntities(Scene* scene) {
     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_ = 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_ = 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);
+    right_eye_entity_->AddComponent<Projection>();
 
     left_controller_entity_ = scene->CreateEntity();
     left_controller_entity_->AddComponent<RuntimeComponent<LEFT_CONTROLLER>>();
@@ -132,18 +122,34 @@ void TrackingSystemOpenVR::CreateRuntimeEntities(Scene* scene) {
 
 void TrackingSystemOpenVR::RemoveRuntimeEntities(Scene* scene) {
   scene->RemoveEntity(hmd_entity_);
+  hmd_entity_ = nullptr;
   scene->RemoveEntity(left_eye_entity_);
+  left_eye_entity_ = nullptr;
   scene->RemoveEntity(right_eye_entity_);
+  right_eye_entity_ = nullptr;
   scene->RemoveEntity(left_controller_entity_);
+  left_controller_entity_ = nullptr;
   scene->RemoveEntity(right_controller_entity_);
+  right_controller_entity_ = nullptr;
 }
 
 void TrackingSystemOpenVR::OnSceneChanged(std::shared_ptr<Scene> old_scene,
-                                    std::shared_ptr<Scene> new_scene) {
+                                          std::shared_ptr<Scene> new_scene) {
   RemoveRuntimeEntities(old_scene.get());
   CreateRuntimeEntities(new_scene.get());
 }
 
+VRController* TrackingSystemOpenVR::GetController(
+    VRController::ControllerSide side) {
+  for (auto controller :
+       engine_->GetSystem<DeviceSystem>()->GetDevices<VRController>()) {
+    if (controller->GetSide() == side) {
+      return controller;
+    }
+  }
+  return nullptr;
+}
+
 std::string TrackingSystemOpenVR::ToString() const { return "Tracking System"; }
 
 }  // namespace phx
diff --git a/library/phx/tracking/tracking_system_openvr.hpp b/library/phx/tracking/tracking_system_openvr.hpp
index ad35174c67e3bc5199e575e4dcff6ba65c9be1fb..fd76bce5588bf3018e16ea8fb33dbc9ffbf253a9 100644
--- a/library/phx/tracking/tracking_system_openvr.hpp
+++ b/library/phx/tracking/tracking_system_openvr.hpp
@@ -30,8 +30,8 @@
 
 #include "phx/core/engine.hpp"
 #include "phx/core/system.hpp"
-#include "phx/display/display_system_openvr.hpp"
 #include "phx/export.hpp"
+#include "phx/input/vr_controller.hpp"
 
 SUPPRESS_WARNINGS_BEGIN
 #define BOOST_BIND_NO_PLACEHOLDERS
@@ -39,13 +39,14 @@ SUPPRESS_WARNINGS_BEGIN
 SUPPRESS_WARNINGS_END
 
 namespace phx {
+class DeviceSystem;
 
 class PHOENIX_EXPORT TrackingSystemOpenVR : public System {
  public:
   TrackingSystemOpenVR() = delete;
   TrackingSystemOpenVR(const TrackingSystemOpenVR&) = delete;
   TrackingSystemOpenVR(TrackingSystemOpenVR&&) = default;
-  ~TrackingSystemOpenVR();
+  ~TrackingSystemOpenVR() override;
 
   TrackingSystemOpenVR& operator=(const TrackingSystemOpenVR&) = delete;
   TrackingSystemOpenVR& operator=(TrackingSystemOpenVR&&) = default;
@@ -55,7 +56,7 @@ class PHOENIX_EXPORT TrackingSystemOpenVR : public System {
   std::string ToString() const override;
 
  protected:
-  TrackingSystemOpenVR(Engine* engine, DisplaySystemOpenVR* display_system);
+  TrackingSystemOpenVR(Engine* engine, DeviceSystem* device_system);
 
  private:
   template <typename SystemType, typename... SystemArguments>
@@ -66,6 +67,8 @@ class PHOENIX_EXPORT TrackingSystemOpenVR : public System {
   void OnSceneChanged(std::shared_ptr<Scene> old_scene,
                       std::shared_ptr<Scene> new_scene);
 
+  VRController* GetController(VRController::ControllerSide side);
+
   Entity* hmd_entity_ = nullptr;
   Entity* left_eye_entity_ = nullptr;
   Entity* right_eye_entity_ = nullptr;
diff --git a/library/phx/utility/aspects/hierarchical.hpp b/library/phx/utility/aspects/hierarchical.hpp
index 45dd77d166b3908678d4e8e22ceb3ba00050bcb0..c9467b9137594b6f076cfe9ba988de142b74502f 100644
--- a/library/phx/utility/aspects/hierarchical.hpp
+++ b/library/phx/utility/aspects/hierarchical.hpp
@@ -34,15 +34,20 @@ namespace phx {
 template <typename Derived>
 class PHOENIX_EXPORT Hierarchical {
  public:
-  virtual ~Hierarchical() = default;
+  virtual ~Hierarchical() {
+    Hierarchical::SetParent(nullptr);
+    while (children_.size() != 0) {
+      children_[0]->SetParent(nullptr);
+    }
+  }
 
   virtual void SetParent(Derived* parent) {
-    if (parent_)
+    if (parent_) {
       parent_->children_.erase(
           std::remove(parent_->children_.begin(), parent_->children_.end(),
                       static_cast<Derived*>(this)),
           parent_->children_.end());
-
+    }
     parent_ = parent;
 
     if (parent_) parent_->children_.push_back(static_cast<Derived*>(this));
diff --git a/library/phx/utility/stream_helpers.hpp b/library/phx/utility/stream_helpers.hpp
index c464887c49b75208f9433a0181e50e5b2e4046db..89902b6cd7f208dc3f746915c003be3f36d55c51 100644
--- a/library/phx/utility/stream_helpers.hpp
+++ b/library/phx/utility/stream_helpers.hpp
@@ -41,7 +41,6 @@ SUPPRESS_WARNINGS_END
 // namespace like e.g. phx, leaving them in global namespace would not work
 namespace glm {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
 class PHOENIX_EXPORT PhxStreamSettings {
  public:
   explicit PhxStreamSettings(std::ostream& stream);
@@ -57,7 +56,6 @@ class PHOENIX_EXPORT PhxStreamSettings {
   std::streamsize stream_precision_;
   std::ios::fmtflags stream_flags_;
 };
-SUPPRESS_WARNINGS_END
 
 PHOENIX_EXPORT std::ostream& operator<<(std::ostream& out,
                                         const glm::vec2& vec);
diff --git a/resources/models/combustion/combustion_opt.stl b/resources/models/combustion/combustion_opt.stl
new file mode 100644
index 0000000000000000000000000000000000000000..f8bc5c571de8bdfba381df0705f7781ae2f002f8
--- /dev/null
+++ b/resources/models/combustion/combustion_opt.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:81a65764e361f9ccea2abeb959e00afcf9f096c3bf3a016bf83e7463550bc36c
+size 6725384
diff --git a/resources/models/combustion/data_box.stl b/resources/models/combustion/data_box.stl
new file mode 100644
index 0000000000000000000000000000000000000000..cecb4d3c9ac945568bb84b8bbc2e82b000e12915
--- /dev/null
+++ b/resources/models/combustion/data_box.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a2663aab1d005c25da49372e639c39c44e9ba76adf4e60659554950e77cc7b82
+size 684
diff --git a/resources/models/combustion/floor.stl b/resources/models/combustion/floor.stl
new file mode 100644
index 0000000000000000000000000000000000000000..adae63f30d474b33737634bec7fa90b8b3526455
--- /dev/null
+++ b/resources/models/combustion/floor.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e91b4c4ce14156b1addc49f1ec4fab6a5d80ef81c7b8ff0a30845ff5d8ffb6d9
+size 184
diff --git a/resources/models/combustion/mixing_layer_0.2_lowres.stl b/resources/models/combustion/mixing_layer_0.2_lowres.stl
new file mode 100644
index 0000000000000000000000000000000000000000..0400ed8b78fc9b3a0475f9a0e768a737077cf2ba
--- /dev/null
+++ b/resources/models/combustion/mixing_layer_0.2_lowres.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ef36f673ae0c59610e49354ade67213d5d6f65e4cd2088b40f8c135deb48b665
+size 2341784
diff --git a/resources/models/combustion/mixture_surface_0.2_lower.stl b/resources/models/combustion/mixture_surface_0.2_lower.stl
new file mode 100644
index 0000000000000000000000000000000000000000..cc44d3c0d984cc47ebf30ab6b74eedd2cfc9cfce
--- /dev/null
+++ b/resources/models/combustion/mixture_surface_0.2_lower.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:82c2429ef75c4b1404aad374fd8214cdb61271e50e5c03a0563919e41df87b97
+size 76440084
diff --git a/resources/models/combustion/mixture_surface_0.2_upper.stl b/resources/models/combustion/mixture_surface_0.2_upper.stl
new file mode 100644
index 0000000000000000000000000000000000000000..86e6033a340cdc5c8a86bf0a26c1652ad75d7e73
--- /dev/null
+++ b/resources/models/combustion/mixture_surface_0.2_upper.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2037d5c39fc649f7c20227443cfaa3121371da7ca73ebecd42b8b7aac589c1a9
+size 78831184
diff --git a/resources/models/cube/cube.obj b/resources/models/cube/cube.obj
new file mode 100644
index 0000000000000000000000000000000000000000..f42e55fa1448bf2aae3d3b75f5e4b35f9d2b4bad
--- /dev/null
+++ b/resources/models/cube/cube.obj
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bfcd76533c150be3296b9531272fbd08b94bfa4c8694958341f9ca960fc73bc3
+size 798
diff --git a/resources/models/cube/cube2.obj b/resources/models/cube/cube2.obj
new file mode 100644
index 0000000000000000000000000000000000000000..22784b153af7bda1fecabe0aaae386ee30d612af
--- /dev/null
+++ b/resources/models/cube/cube2.obj
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:923b22927d08fe145f02179d4451d760677891c803bfb2ede53625abcc993623
+size 800
diff --git a/resources/models/cube/default.mtl b/resources/models/cube/default.mtl
new file mode 100644
index 0000000000000000000000000000000000000000..ae8d5ab15ccb6de82245eb8b4bc12efa2bd4f2c2
--- /dev/null
+++ b/resources/models/cube/default.mtl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e26b79fdba701da25a1d3d93fc32733422ece98f3b8c3a03ca82cfca4ee3e5c
+size 277
diff --git a/resources/models/cube/default.png b/resources/models/cube/default.png
new file mode 100644
index 0000000000000000000000000000000000000000..a4e844b7c3450b0dcfd66eea23d2a290c64eb6b7
--- /dev/null
+++ b/resources/models/cube/default.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c18791857b0674ba8e19ce2446c29659bd263cd26947f5d37605ad69a41de4d1
+size 3073
diff --git a/resources/models/cube/default2.mtl b/resources/models/cube/default2.mtl
new file mode 100644
index 0000000000000000000000000000000000000000..e3a059caef93eea36c95b2b6572dbe090700313f
--- /dev/null
+++ b/resources/models/cube/default2.mtl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6ee2d873be68c67c1792ead16ed799dce477aa33fe68b0693a152e34182794c6
+size 279
diff --git a/resources/models/cube/default2.png b/resources/models/cube/default2.png
new file mode 100644
index 0000000000000000000000000000000000000000..a4e844b7c3450b0dcfd66eea23d2a290c64eb6b7
--- /dev/null
+++ b/resources/models/cube/default2.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c18791857b0674ba8e19ce2446c29659bd263cd26947f5d37605ad69a41de4d1
+size 3073
diff --git a/resources/shader/skybox.frag b/resources/shader/skybox.frag
new file mode 100644
index 0000000000000000000000000000000000000000..68e6887f96832cb85ec236a6f137532b587c7cf2
--- /dev/null
+++ b/resources/shader/skybox.frag
@@ -0,0 +1,33 @@
+//------------------------------------------------------------------------------
+// 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
+
+out vec4 frag_color;
+
+in vec3 tex_coords;
+
+uniform samplerCubeArray skybox;
+
+void main()
+{    
+    frag_color = texture(skybox, vec4(tex_coords, 0));
+}
diff --git a/resources/shader/skybox.vert b/resources/shader/skybox.vert
new file mode 100644
index 0000000000000000000000000000000000000000..aaaad20e84e013ed394e24e35fdcb42361a36ee8
--- /dev/null
+++ b/resources/shader/skybox.vert
@@ -0,0 +1,36 @@
+//------------------------------------------------------------------------------
+// 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 v_pos;
+layout (location = 1) in vec3 v_tex_coord;
+
+out vec3 tex_coords;
+
+uniform mat4 projection_matrix;
+uniform mat4 view_matrix;
+
+void main()
+{
+	tex_coords = v_tex_coord;
+    gl_Position = projection_matrix * view_matrix * vec4(v_pos, 1.0);
+}  
diff --git a/resources/skyboxes/hw_morning/back.tga b/resources/skyboxes/hw_morning/back.tga
new file mode 100644
index 0000000000000000000000000000000000000000..bdccdd8b3f56e4ffa5004e500be77902e886372d
--- /dev/null
+++ b/resources/skyboxes/hw_morning/back.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9eed6f2193fbbfe3ea6b50c7d36c2bcdc387f6805d7e2ebefd79c849af9d73a9
+size 716529
diff --git a/resources/skyboxes/hw_morning/bottom.tga b/resources/skyboxes/hw_morning/bottom.tga
new file mode 100644
index 0000000000000000000000000000000000000000..c3cb1da8bca0bb8a98667fbfea68d5bd1a09fdb8
--- /dev/null
+++ b/resources/skyboxes/hw_morning/bottom.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:554843bb20e9af1cc7236460c8ecee0ce5cc6fef12e301871ee13e8209633c0c
+size 781318
diff --git a/resources/skyboxes/hw_morning/front.tga b/resources/skyboxes/hw_morning/front.tga
new file mode 100644
index 0000000000000000000000000000000000000000..327adcfe8028f6e45dd14540a59bc63c78bec04d
--- /dev/null
+++ b/resources/skyboxes/hw_morning/front.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a3b31ed93c78a8a833ef251ea9773c00a35f324699a40a859fee14d2a17b5df
+size 706691
diff --git a/resources/skyboxes/hw_morning/left.tga b/resources/skyboxes/hw_morning/left.tga
new file mode 100644
index 0000000000000000000000000000000000000000..2da146fad1fd1859b528b71e174a57803af6b56f
--- /dev/null
+++ b/resources/skyboxes/hw_morning/left.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4219a950b02c7186c992ab2cb72940e21c9c4c9814c7c02f79a4d791ebaf0ca
+size 732806
diff --git a/resources/skyboxes/hw_morning/license.txt b/resources/skyboxes/hw_morning/license.txt
new file mode 100644
index 0000000000000000000000000000000000000000..46052cf8034aabb701da14bbc23bef79766e26d7
--- /dev/null
+++ b/resources/skyboxes/hw_morning/license.txt
@@ -0,0 +1,7 @@
+Sky box created by Hazel Whorley.
+
+This work is licensed under a Creative Commons Attribution-Noncommercial 3.0
+License.
+
+The sky box may be used for non-commercial purposes only. You're free to use
+and modify it otherwise. Please acknowledge credit in the map/mod readme file.
\ No newline at end of file
diff --git a/resources/skyboxes/hw_morning/readme.txt b/resources/skyboxes/hw_morning/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..181eaf18fb4137c327775bc506fd837aa7544a5f
--- /dev/null
+++ b/resources/skyboxes/hw_morning/readme.txt
@@ -0,0 +1,2 @@
+Downloaded from http://www.custommapmakers.org/skyboxes.php
+last-visited: 2018-06-22 
\ No newline at end of file
diff --git a/resources/skyboxes/hw_morning/right.tga b/resources/skyboxes/hw_morning/right.tga
new file mode 100644
index 0000000000000000000000000000000000000000..361c71a0e615e0e8da1d6f9198c98778a611f659
--- /dev/null
+++ b/resources/skyboxes/hw_morning/right.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:90fe9aa82c50eb44552cc44f28b371fd942c5e1e89e3e5512d060b23a3e98bbe
+size 766964
diff --git a/resources/skyboxes/hw_morning/top.tga b/resources/skyboxes/hw_morning/top.tga
new file mode 100644
index 0000000000000000000000000000000000000000..e3709ece437a7d9ace8bf9002f91e811541c0d7e
--- /dev/null
+++ b/resources/skyboxes/hw_morning/top.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11893da767dd1740a40f4242f747f2f4568f699b06e56bd00b533325a5a0be74
+size 703696
diff --git a/resources/skyboxes/phx/back.png b/resources/skyboxes/phx/back.png
new file mode 100644
index 0000000000000000000000000000000000000000..cf7077cdc6bb6d293944d4835bb80392bde0ff2c
--- /dev/null
+++ b/resources/skyboxes/phx/back.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:94328f4773c80ea40620eca53788cd4290b232f1bc8bc5a429191c6899884516
+size 70665
diff --git a/resources/skyboxes/phx/bottom.png b/resources/skyboxes/phx/bottom.png
new file mode 100644
index 0000000000000000000000000000000000000000..d8d5230f97b16dafc61b7288272e6571e0c62d19
--- /dev/null
+++ b/resources/skyboxes/phx/bottom.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:daf5c962788fc7efcbb5bce1eb8c8e53f4fe5aecd240be56ffd607edfe038daf
+size 8509
diff --git a/resources/skyboxes/phx/front.png b/resources/skyboxes/phx/front.png
new file mode 100644
index 0000000000000000000000000000000000000000..917de226d40e05d03e1b04ea3e2c260b9f7c0ba0
--- /dev/null
+++ b/resources/skyboxes/phx/front.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a5ab57ed9c1576ee3c5a473235b8003298aaf77dff8564f0e4addf342f47c025
+size 77038
diff --git a/resources/skyboxes/phx/left.png b/resources/skyboxes/phx/left.png
new file mode 100644
index 0000000000000000000000000000000000000000..992b637e76713da2799782eb75cc313b36cc6e7e
--- /dev/null
+++ b/resources/skyboxes/phx/left.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4bb790e394dbee28bf8000d4f00ff7fc675075b577612a622b9b623766a87153
+size 76878
diff --git a/resources/skyboxes/phx/right.png b/resources/skyboxes/phx/right.png
new file mode 100644
index 0000000000000000000000000000000000000000..78ae73c2992abae48a14ff0b0ee307503d4f0413
--- /dev/null
+++ b/resources/skyboxes/phx/right.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4cb5fe08c25177059c6b226233c4128e54994ea2bb9077c138d808e4ef49b193
+size 77309
diff --git a/resources/skyboxes/phx/top.png b/resources/skyboxes/phx/top.png
new file mode 100644
index 0000000000000000000000000000000000000000..eca894059165a18c0f2d7126fb163bc717dc3992
--- /dev/null
+++ b/resources/skyboxes/phx/top.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f1a2f4b839379a1fe808fd27263f0e480826a9e2657bcc61cdb0205f363daf0e
+size 6694
diff --git a/resources/skyboxes/pure_sky/back.jpg b/resources/skyboxes/pure_sky/back.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..62758bbd085f8cdbc57d7c98f24df0765b2b7a05
--- /dev/null
+++ b/resources/skyboxes/pure_sky/back.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0fdf307edcb1b30d930277b5c56f7fc5d632370c424a0503f05c2df6c7c8be08
+size 267566
diff --git a/resources/skyboxes/pure_sky/bottom.jpg b/resources/skyboxes/pure_sky/bottom.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..4d124b74ac8c205116467c4df433e38cd6090f02
--- /dev/null
+++ b/resources/skyboxes/pure_sky/bottom.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9e4dec9e6d631545a07768b3740b0027a09be156cee7012c2843afd4428f98f5
+size 12817
diff --git a/resources/skyboxes/pure_sky/front.jpg b/resources/skyboxes/pure_sky/front.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..43b69280095877d8f1f3a768a2d81516df955344
--- /dev/null
+++ b/resources/skyboxes/pure_sky/front.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b69ededdf4a43d55c9cb93449a5029e0a5afbae8fafa246348a80eee8c60a78
+size 219620
diff --git a/resources/skyboxes/pure_sky/left.jpg b/resources/skyboxes/pure_sky/left.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..348779a067c88a732da70079fca3a5c2f3294f3b
--- /dev/null
+++ b/resources/skyboxes/pure_sky/left.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:89372f0098c32750cd028120905a0a64f20d2c1f137002b9c2fb1e06ec2b1d65
+size 146198
diff --git a/resources/skyboxes/pure_sky/right.jpg b/resources/skyboxes/pure_sky/right.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..98a4eb1f29ccc7eacb7f05970996e0980d783415
--- /dev/null
+++ b/resources/skyboxes/pure_sky/right.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:208e7379b328e60e0e1855de5121bbd8467de26473bde2a1d339362e7e67f559
+size 343917
diff --git a/resources/skyboxes/pure_sky/top.jpg b/resources/skyboxes/pure_sky/top.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..0ba27a645e513b054a174a2af26ef150b9bcbcf1
--- /dev/null
+++ b/resources/skyboxes/pure_sky/top.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b2d4eae866afca5c07963b12019de83898344cd0759307b502b8ba6fd3cdb202
+size 46321
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 7a9d51630e5abc3d55fc600d163802bf5d3266ed..f63ed905d8e76ba508e35cf4c75092d0c56031d9 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -22,86 +22,43 @@
 
 #see at the bottom how to add mocked tests
 
-# configure reference image directory include string
-set(_REFIMAGE_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/reference_images/")
-set(SOURCE_REFIMAGE_INCLUDE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/test_utilities/reference_image_path.hpp.template")
-set(TARGET_REFIMAGE_INCLUDE_FILE "${CMAKE_CURRENT_BINARY_DIR}/test_utilities/reference_image_path.hpp")
-configure_file(${SOURCE_REFIMAGE_INCLUDE_FILE} ${TARGET_REFIMAGE_INCLUDE_FILE} @ONLY)
-
 file(GLOB PHOENIX_TEST_SOURCES src/*.cpp)
 file(GLOB PHOENIX_TEST_HEADERS src/*.hpp)
 file(GLOB PHOENIX_TEST_UTILITIES_TEST_SOURCES test_utilities/tests/src/*.cpp)
 file(GLOB PHOENIX_TEST_UTILITIES_HEADERS test_utilities/*.hpp)
 file(GLOB PHOENIX_TEST_UTILITIES_SOURCES test_utilities/*.cpp)
+file(GLOB TEST_MAIN_SOURCES src/tests.cpp)
+file(GLOB MOCK_MAIN_SOURCES src/mocks/mocks.cpp)
 
-#get the include directories
-get_target_property(glew_include_directories
-  ${CONAN_OR_CMAKE_glew} INTERFACE_INCLUDE_DIRECTORIES)
-get_target_property(phoenix_include_directories 
-  phoenix INCLUDE_DIRECTORIES)
-get_target_property(sdl2_include_directories
-  ${CONAN_OR_CMAKE_sdl2} INTERFACE_INCLUDE_DIRECTORIES)
-get_target_property(openvr_include_directories
-  ${CONAN_OR_CMAKE_openvr} INTERFACE_INCLUDE_DIRECTORIES)
-get_target_property(gl_include_directories
-  ${CONAN_OR_CMAKE_gl} INTERFACE_INCLUDE_DIRECTORIES)
-
-
-macro(add_mock_lib mock_name mock_src_path)
-  add_library(${mock_name} SHARED
-    ${mock_src_path}/${mock_name}.cpp
-    ${mock_src_path}/${mock_name}.hpp
+
+create_test_main(
+  SOURCES ${TEST_MAIN_SOURCES}
+  LIBRARIES ${CONAN_OR_CMAKE_catch}
   )
-  target_link_libraries(${mock_name}
-    ${CONAN_OR_CMAKE_trompeloeil})
-  #phx include dirs needed for suppress_warnings.hpp
-  target_include_directories(${mock_name}
-    PRIVATE ${phoenix_include_directories})
-  set_property(TARGET ${mock_name} PROPERTY FOLDER "Tests")
-  set_warning_levels_rwth(${mock_name})
-endmacro()
+list(REMOVE_ITEM PHOENIX_TEST_SOURCES ${TEST_MAIN_SOURCES})
 
-# generate opengl_mock
-set( OPENGL_MOCK_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/mocks)
-set( OPENGL_MOCK_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/generation/Create_openGL_mock.py)
-if( NOT EXISTS ${OPENGL_MOCK_SOURCE}/opengl_mock.cpp OR 
-    ${OPENGL_MOCK_GENERATOR} IS_NEWER_THAN 
-    ${OPENGL_MOCK_SOURCE}/opengl_mock.cpp)
-  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mocks")
-  EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} 
-    "${OPENGL_MOCK_GENERATOR}" 
-    -g "${glew_include_directories}" 
-    -m "${OPENGL_MOCK_SOURCE}/" 
-    -t "${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/generation/")
-endif()
+create_mock_main(
+  SOURCES ${MOCK_MAIN_SOURCES}
+  LIBRARIES ${CONAN_OR_CMAKE_catch} ${CONAN_OR_CMAKE_trompeloeil}
+  )
+list(REMOVE_ITEM PHOENIX_TEST_SOURCES ${MOCK_MAIN_SOURCES})
 
-add_mock_lib(opengl_mock ${OPENGL_MOCK_SOURCE})
-target_include_directories(opengl_mock
-  PUBLIC ${OPENGL_MOCK_SOURCE}
-  PUBLIC ${glew_include_directories})
-target_compile_definitions(opengl_mock
-  PRIVATE OPENGL_MOCK_BUILD)
-generate_export_header(opengl_mock
-  EXPORT_FILE_NAME ${OPENGL_MOCK_SOURCE}/opengl_mock_export.hpp
-)
 
-# generate openvr_mock
-add_mock_lib(openvr_mock ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks)
-target_include_directories(openvr_mock
-  PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src
-  PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/mocks
-  PUBLIC ${openvr_include_directories})
-target_compile_definitions(openvr_mock
-  PRIVATE OPENVR_MOCK_BUILD)
-generate_export_header(openvr_mock
-  EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/mocks/openvr_mock_export.hpp
-)
+# configure reference image directory include string
+set(_REFIMAGE_ROOT_PATH
+  "${CMAKE_CURRENT_SOURCE_DIR}/reference_images/"
+  )
+set(SOURCE_REFIMAGE_INCLUDE_FILE
+  "${CMAKE_CURRENT_SOURCE_DIR}/test_utilities/reference_image_path.hpp.template"
+  )
+set(TARGET_REFIMAGE_INCLUDE_FILE
+  "${CMAKE_CURRENT_BINARY_DIR}/test_utilities/reference_image_path.hpp"
+  )
+configure_file(${SOURCE_REFIMAGE_INCLUDE_FILE}
+  ${TARGET_REFIMAGE_INCLUDE_FILE}
+  @ONLY
+  )
 
-#the opengl mock gets quite large due to the trompleoeil templates
-if(MSVC)
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
-	set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
-endif(MSVC)
 
 # generate test_utilities
 add_library(test_utilities STATIC
@@ -120,82 +77,78 @@ target_link_libraries(test_utilities
 set_warning_levels_rwth(test_utilities)
 
 
+# generate opengl_mock
+get_target_property(glew_include_directories
+  ${CONAN_OR_CMAKE_glew} INTERFACE_INCLUDE_DIRECTORIES)
+set( OPENGL_MOCK_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/mocks)
+set( OPENGL_MOCK_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/generation/Create_openGL_mock.py)
+if( NOT EXISTS ${OPENGL_MOCK_SOURCE}/opengl_mock.cpp OR 
+    ${OPENGL_MOCK_GENERATOR} IS_NEWER_THAN 
+    ${OPENGL_MOCK_SOURCE}/opengl_mock.cpp)
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mocks")
+  EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} 
+    "${OPENGL_MOCK_GENERATOR}" 
+    -g "${glew_include_directories}" 
+    -m "${OPENGL_MOCK_SOURCE}/" 
+    -t "${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/generation/")
+endif()
+
 #get the gl link library
 if(TARGET CONAN_LIB::gl_gl)
-  get_target_property(gl_link_library CONAN_LIB::gl_gl LOCATION)
+  get_target_property(GL_LIBRARY CONAN_LIB::gl_gl LOCATION)
 elseif(TARGET CONAN_LIB::gl_glrelease)
-  get_target_property(gl_imported_location_rel CONAN_LIB::gl_glrelease LOCATION)
-  get_target_property(gl_imported_location_deb CONAN_LIB::gl_gldebug LOCATION)
-  set(gl_link_library "$<$<CONFIG:Release>:${gl_imported_location_rel}>$<$<CONFIG:Debug>:${gl_imported_location_deb}>")
-else()
-  message(FATAL_ERROR "You are using a conan version older than 0.29, please update e.g. by \"pip install conan --upgrade\"")
+  get_target_property(GL_LIBRARY_LOCATION_RELEASE
+    CONAN_LIB::gl_glrelease LOCATION)
+  get_target_property(GL_LIBRARY_LOCATION_DEBUG
+    CONAN_LIB::gl_gldebug LOCATION)
+  set(GL_LIBRARY
+    "$<$<CONFIG:Release>:${GL_LIBRARY_LOCATION_RELEASE}>$<$<CONFIG:Debug>:${GL_LIBRARY_LOCATION_DEBUG}>")
 endif()
+  
+add_mock(
+  NAME opengl_mock
+  SOURCES ${CMAKE_CURRENT_BINARY_DIR}/mocks/opengl_mock.cpp
+  HEADERS ${CMAKE_CURRENT_BINARY_DIR}/mocks/opengl_mock.hpp
+  INCLUDE_DIRECTORIES
+    ${CMAKE_CURRENT_BINARY_DIR}
+  INCLUDE_DIRECTORIES_OF
+    phoenix
+    ${CONAN_OR_CMAKE_gl}
+    ${CONAN_OR_CMAKE_glew}
+  COMPILE_OPTIONS $<$<CXX_COMPILER_ID:MSVC>:/bigobj>
+  REMOVES_LIBRARIES
+    ${CONAN_OR_CMAKE_gl}
+    ${CONAN_OR_CMAKE_glew}
+    ${CONAN_OR_CMAKE_sdl2}
+    ${OPENGL_LIBRARIES}
+  ADDS_LIBRARIES ${GL_LIBRARY}
+  )
 
-set(IS_BUILD_SERVER FALSE CACHE BOOL "Is this the build server? So we, e.g., simulate user input for tests requiring it.")
-
-
-macro(add_mocked_test cpp_file)
-  set(options MOCK_GLEW MOCK_SDL MOCK_OPENVR)
-  set(oneValueArgs )
-  set(multiValueArgs )
-  cmake_parse_arguments(ADD_MOCKED_TEST
-    "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
-  list(REMOVE_ITEM PHOENIX_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/${cpp_file}.cpp)
-
-  set(test_sources src/tests.cpp src/${cpp_file}.cpp)
-  add_executable(${cpp_file} ${test_sources})
-
-  add_dependencies(${cpp_file} phoenix test_utilities)
-
-  ADD_TEST_CATCH_INTERNAL_(${cpp_file} ${test_sources} "")
-
-  # make shure linker dependency injection works
-  target_include_directories(${cpp_file}
-    PRIVATE ${phoenix_include_directories}
-    PRIVATE ${glew_include_directories}
-	PRIVATE ${gl_include_directories}
-    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
-    PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-
-  get_target_property(phoenix_link_libraries phoenix LINK_LIBRARIES)
-
-  if(${ADD_MOCKED_TEST_MOCK_GLEW})
-    list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_gl})
-    list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_glew})
-    #SDL has also to be removed since it contains OpenGL
-    list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_sdl2})
-    list(REMOVE_ITEM phoenix_link_libraries ${OPENGL_LIBRARIES})
-    list(APPEND phoenix_link_libraries opengl_mock)
-    list(APPEND phoenix_link_libraries ${gl_link_library})
-	
-  endif(${ADD_MOCKED_TEST_MOCK_GLEW})
-
-  if(${ADD_MOCKED_TEST_MOCK_SDL})
-    target_sources(${cpp_file}
-      PRIVATE src/mocks/sdl_mock.cpp
-      PRIVATE src/mocks/sdl_mock.hpp)
-    target_compile_definitions(${cpp_file} PRIVATE TESTING)
-    target_include_directories(${cpp_file}
-      PRIVATE ${sdl2_include_directories})
-    list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_sdl2})
-  endif(${ADD_MOCKED_TEST_MOCK_SDL})
-
-  if(${ADD_MOCKED_TEST_MOCK_OPENVR})
-    list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_openvr})
-    list(APPEND phoenix_link_libraries openvr_mock)
-  endif(${ADD_MOCKED_TEST_MOCK_OPENVR})
-
-  target_link_libraries(${cpp_file}
-    ${CONAN_OR_CMAKE_trompeloeil}
-    ${CONAN_OR_CMAKE_catch}
-	$<TARGET_FILE:phoenix>
-    ${phoenix_link_libraries}
-    $<TARGET_FILE:test_utilities>
+add_mock(
+  NAME openvr_mock
+  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/openvr_mock.cpp
+  HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/openvr_mock.hpp
+  INCLUDE_DIRECTORIES
+    ${CMAKE_CURRENT_SOURCE_DIR}/src
+    ${CMAKE_CURRENT_BINARY_DIR}
+  INCLUDE_DIRECTORIES_OF
+    phoenix
+    ${CONAN_OR_CMAKE_openvr}
+  REMOVES_LIBRARIES ${CONAN_OR_CMAKE_openvr}
   )
-endmacro()
 
+add_mock(
+  NAME sdl_mock
+  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/sdl_mock.cpp
+  HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/sdl_mock.hpp
+  INCLUDE_DIRECTORIES
+    ${CMAKE_CURRENT_SOURCE_DIR}/src
+    ${CMAKE_CURRENT_BINARY_DIR}
+  INCLUDE_DIRECTORIES_OF
+    phoenix
+    ${CONAN_OR_CMAKE_sdl2}
+  REMOVES_LIBRARIES ${CONAN_OR_CMAKE_sdl2}
+  )
 
 
 # these have to go before the mocked tests.
@@ -214,37 +167,42 @@ add_test_cpplint(NAME "phoenix-tests--cpplint"
   ${PHOENIX_TEST_UTILITIES_SOURCES}
 )
 
-#-------------------------------------------------------------------------------
-#-------------------------------------------------------------------------------
 
 # Only tests that require mocking need to be specified separately here.
-# By doing so they are removed from ${PHOENIX_TEST_SOURCES} automatically.
-add_mocked_test(test_clear_pass MOCK_GLEW)
-add_mocked_test(test_input_system MOCK_SDL MOCK_OPENVR)
-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 MOCK_GLEW)
-add_mocked_test(test_tracking_system MOCK_SDL MOCK_OPENVR)
-add_mocked_test(test_openvr_controller_system MOCK_SDL MOCK_OPENVR MOCK_GLEW)
-
-add_mocked_test(integration_test_model_rendering MOCK_OPENVR)
-add_mocked_test(integration_test_opengl_buffer_data_download MOCK_OPENVR)
-add_mocked_test(integration_test_rendering MOCK_OPENVR)
-add_mocked_test(integration_test_hmd MOCK_OPENVR)
-
-#-------------------------------------------------------------------------------
-#-------------------------------------------------------------------------------
-
-
-add_test_catch(NAME "phoenix-tests"
+# By doing so they are removed from the list of sources
+# specified via AUTOREMOVE_MOCKED_TEST_SOURCE_FROM
+autoremove_mocked_test_source_from(${PHOENIX_TEST_SOURCES})
+
+add_mocked_test(test_clear_pass                     LIBRARIES phoenix                MOCKS opengl_mock)
+add_mocked_test(test_input_system                   LIBRARIES phoenix                MOCKS openvr_mock sdl_mock)
+add_mocked_test(test_geometry_pass                  LIBRARIES phoenix test_utilities MOCKS opengl_mock sdl_mock)
+add_mocked_test(test_rendering_system               LIBRARIES phoenix                MOCKS opengl_mock sdl_mock)
+add_mocked_test(test_shader                         LIBRARIES phoenix                MOCKS opengl_mock)
+add_mocked_test(test_display_system                 LIBRARIES phoenix                MOCKS sdl_mock)
+add_mocked_test(test_engine                         LIBRARIES phoenix test_utilities MOCKS opengl_mock openvr_mock sdl_mock)
+add_mocked_test(test_tracking_system                LIBRARIES phoenix test_utilities MOCKS openvr_mock sdl_mock)
+add_mocked_test(test_openvr_controller_model_system LIBRARIES phoenix                MOCKS opengl_mock openvr_mock sdl_mock)
+add_mocked_test(test_tracked_device                 LIBRARIES phoenix                MOCKS openvr_mock)
+add_mocked_test(test_vr_controller                  LIBRARIES phoenix test_utilities MOCKS openvr_mock)
+add_mocked_test(test_assimp_loader                  LIBRARIES phoenix test_utilities MOCKS opengl_mock)
+add_mocked_test(test_model                          LIBRARIES phoenix                MOCKS opengl_mock)
+add_mocked_test(test_scene_loader                   LIBRARIES phoenix                MOCKS opengl_mock)
+
+add_mocked_test(integration_test_model_rendering             LIBRARIES phoenix test_utilities MOCKS openvr_mock)
+add_mocked_test(integration_test_opengl_buffer_data_download LIBRARIES phoenix test_utilities MOCKS openvr_mock)
+add_mocked_test(integration_test_rendering                   LIBRARIES phoenix test_utilities MOCKS openvr_mock)
+add_mocked_test(integration_test_hmd                         LIBRARIES phoenix test_utilities MOCKS openvr_mock)
+
+
+get_unmocked_test_sources(PHOENIX_TEST_SOURCES)
+
+# This adds all cpp files in tests as single tests that were not used with
+# add_mocked_test() above
+add_tests(NAME "phoenix-tests"
   SOURCES ${PHOENIX_TEST_SOURCES} ${PHOENIX_TEST_UTILITIES_TEST_SOURCES}
   HEADERS ${PHOENIX_TEST_HEADERS}
-  CATCH_MAIN src/tests.cpp
-  INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} phoenix
-  LINK_LIBRARIES phoenix test_utilities
+  INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+  LIBRARIES phoenix test_utilities
   PATH_TO_ADD ${PROJECT_BINARY_DIR}/library
 )
 
-
diff --git a/tests/reference_images/default_skybox_rendering.png b/tests/reference_images/default_skybox_rendering.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c42a88c9c1e501fa15ef56156df5e4144ec2018
--- /dev/null
+++ b/tests/reference_images/default_skybox_rendering.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1fe47120c1a8d3f1c40c7439857f3fbfc4a57b3cc116e57d1c556a58ec9494bf
+size 698005
diff --git a/tests/reference_images/hmd_test_left.png b/tests/reference_images/hmd_test_left.png
index 9989f7ae4601b0eb1fcd961ccba454ddf6fdd45d..2f975671215cd6917f0d62a78ec76a8ea84191be 100644
--- a/tests/reference_images/hmd_test_left.png
+++ b/tests/reference_images/hmd_test_left.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:e4a6d5d6c1eed96056d964381617ec97dd3b6c9d26717cdc55c1e66c8343feed
-size 115516
+oid sha256:52c050bf258acaf19f3914bcc2792e4b130cc07e8c7017b8a4c5b69dcccec76f
+size 116885
diff --git a/tests/reference_images/hmd_test_right.png b/tests/reference_images/hmd_test_right.png
index 33f5fa2758442f23f4bcf04d353ea2e6661cc9a6..2519d5d6775c6b2fec9dbdf1083ac6ad7cd6fbf4 100644
--- a/tests/reference_images/hmd_test_right.png
+++ b/tests/reference_images/hmd_test_right.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:e07eb1a9c5bfea732801cbd4b6d6321c7797d815645b23fa205a14fa50d76b3d
-size 115419
+oid sha256:a9ea7817c7c1254c1765bf4a0977dd7b430d8419d1355f60b92dce461befae35
+size 116057
diff --git a/tests/reference_images/model_loading_wireframe.png b/tests/reference_images/model_loading_wireframe.png
new file mode 100644
index 0000000000000000000000000000000000000000..fba84750d93e4e9dda237e96dd0ab6004e07d5dd
--- /dev/null
+++ b/tests/reference_images/model_loading_wireframe.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:79ea2093c78aa11360295e2ee16ffa2f815ccd2e9f9e79ab5a06a29ef895564a
+size 105684
diff --git a/tests/reference_images/triangle_without_material.png b/tests/reference_images/triangle_without_material.png
index f93066742c9359d414787cbafdc31031d11f80eb..b5eb2e6a885ae9e371ef92880dfb74a9e9e0df68 100644
--- a/tests/reference_images/triangle_without_material.png
+++ b/tests/reference_images/triangle_without_material.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:cdf28b3e240057f66ad3e4323c1ffd5aa7200bd6708a1d8d0476ff9c5a6eae22
-size 3859
+oid sha256:dfcd770371589244e01ed9ac2a85fc9e113d4b96383901dcca61cb6a1038e060
+size 10984
diff --git a/tests/reference_images/user_defined_skybox_rendering.png b/tests/reference_images/user_defined_skybox_rendering.png
new file mode 100644
index 0000000000000000000000000000000000000000..c5182d79ea87e133c60400bdf3b135f3c324e008
--- /dev/null
+++ b/tests/reference_images/user_defined_skybox_rendering.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ece1f79d7ec5fb1dea41cb5e118c14dea018cb26903b9896f61432f1c13fd98a
+size 311101
diff --git a/tests/src/integration_test_hmd.cpp b/tests/src/integration_test_hmd.cpp
index 7babf1ca229f2eb11ea665b3b9ce7ba0b4e2c82d..84ea1d86c07423fe8eca6355fab162e540d6451a 100644
--- a/tests/src/integration_test_hmd.cpp
+++ b/tests/src/integration_test_hmd.cpp
@@ -27,10 +27,10 @@
 
 #include "catch/catch.hpp"
 
-#include "phx/suppress_warnings.hpp"
-
 #include "phx/core/engine.hpp"
 #include "phx/core/runtime_component.hpp"
+#include "phx/display/hmd.hpp"
+#include "phx/input/device_system.hpp"
 #include "phx/rendering/backend/opengl_image_buffer_data.hpp"
 #include "phx/rendering/components/light.hpp"
 #include "phx/rendering/components/transform.hpp"
@@ -38,12 +38,11 @@
 #include "phx/resources/resource_utils.hpp"
 #include "phx/setup.hpp"
 
-SUPPRESS_WARNINGS_BEGIN
 #include "mocks/openvr_mock.hpp"
 
 #include "gl/texture.hpp"
-SUPPRESS_WARNINGS_END
 
+#include "test_utilities/glm_mat4.hpp"
 #include "test_utilities/opengl_buffer_data_comparison.hpp"
 
 #include "trompeloeil.hpp"
@@ -158,7 +157,6 @@ SCENARIO("If a HMD is present we render for both eyes including controllers",
     ::MoveUserPlatform(engine.get());
 
     WHEN("We run the engine") {
-      // TODO(JW): controller models should also be rendered
       engine->Run();
       THEN("Submit() is called for both eyes submitting the expected images") {
         ::CheckSimilarity(::left_tex_id, "hmd_test_left.png");
@@ -167,3 +165,68 @@ SCENARIO("If a HMD is present we render for both eyes including controllers",
     }
   }
 }
+
+namespace {
+
+glm::mat4 ToMat4(float mat[12]) {
+  glm::mat4 result = glm::mat4(1.0f);
+  for (int i = 0; i < 12; ++i) {
+    result[i % 4][i / 4] = mat[i];
+  }
+  return result;
+}
+}  // namespace
+
+SCENARIO("The Eye to distance is changed via vive's eye distance knob",
+         "[phx][phx::HMD]") {
+  GIVEN("A standard engine that runs one frame") {
+    OPENVR_MOCK_ALLOW_ANY_CALL
+    phx::Engine engine;
+    phx::DeviceSystem* device_system = engine.CreateSystem<phx::DeviceSystem>();
+    auto hmd = device_system->AddDevice<phx::HMD>();
+
+    REQUIRE(hmd->GetEyeToHeadMatrix(phx::HMD::LEFT_EYE) ==
+            ToMat4(openvr_mock.eye_to_head_left_));
+    REQUIRE(hmd->GetEyeToHeadMatrix(phx::HMD::RIGHT_EYE) ==
+            ToMat4(openvr_mock.eye_to_head_right_));
+
+    WHEN("We change the eye distance while the engine is running") {
+      float eye_to_head_left[12] = {1.0f, 0.0f, 0.0f, 0.03f, 0.0f, 1.0f,
+                                    0.0f, 0.0f, 0.0f, 0.0f,  1.0f, 12.0f};
+      float eye_to_head_right[12] = {1.0f, 0.0f, 0.0f, -0.03f, 0.0f, 1.0f,
+                                     0.0f, 0.0f, 0.0f, 0.0f,   1.0f, 13.0f};
+      ALLOW_CALL(openvr_mock.GetSystem(),
+                 GetEyeToHeadTransformArray(vr::EVREye::Eye_Left))
+          .LR_RETURN(eye_to_head_left);
+      ALLOW_CALL(openvr_mock.GetSystem(),
+                 GetEyeToHeadTransformArray(vr::EVREye::Eye_Right))
+          .LR_RETURN(eye_to_head_right);
+      THEN("The eye distances are different") {
+        REQUIRE_FALSE(hmd->GetEyeToHeadMatrix(phx::HMD::LEFT_EYE) ==
+                      ToMat4(eye_to_head_left));
+        REQUIRE_FALSE(hmd->GetEyeToHeadMatrix(phx::HMD::RIGHT_EYE) ==
+                      ToMat4(eye_to_head_right));
+        WHEN("The HMD is Updated and an event is received") {
+          vr::VREvent_t event;
+          event.eventType = vr::VREvent_IpdChanged;
+          auto first_call = std::make_shared<bool>(true);
+          ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _))
+              .SIDE_EFFECT(*_1 = event)
+              .SIDE_EFFECT(*first_call = false)
+              .WITH(*first_call == true)
+              .RETURN(true);
+          ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _))
+              .WITH(*first_call == false)
+              .RETURN(false);
+          hmd->Update();
+          THEN("The eye to head matrix has changed accordingly") {
+            REQUIRE(hmd->GetEyeToHeadMatrix(phx::HMD::LEFT_EYE) ==
+                    ToMat4(eye_to_head_left));
+            REQUIRE(hmd->GetEyeToHeadMatrix(phx::HMD::RIGHT_EYE) ==
+                    ToMat4(eye_to_head_right));
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/tests/src/integration_test_model_rendering.cpp b/tests/src/integration_test_model_rendering.cpp
index 28450005f92e25bed56e9fcc8590f5788a347eb3..bd1fe2a93583f17ca2581016e7d0f9a6406d5154 100644
--- a/tests/src/integration_test_model_rendering.cpp
+++ b/tests/src/integration_test_model_rendering.cpp
@@ -32,10 +32,10 @@
 #include "phx/display/display_system_window.hpp"
 #include "phx/display/window.hpp"
 #include "phx/rendering/backend/opengl_image_buffer_data.hpp"
+#include "phx/rendering/components/mesh_render_settings.hpp"
 #include "phx/rendering/components/resource_component.hpp"
 #include "phx/rendering/rendering_system.hpp"
 #include "phx/resources/loaders/assimp_model_loader.hpp"
-#include "phx/resources/resource_manager.hpp"
 #include "phx/resources/resource_utils.hpp"
 #include "phx/resources/types/mesh.hpp"
 #include "phx/setup.hpp"
@@ -128,7 +128,7 @@ SCENARIO(
     "If no light and camera are give the model rendering takes default values",
     "[phx][phx::ModelLoading]") {
   ALLOW_CALL(openvr_mock.Get(), VR_IsHmdPresent()).RETURN(false);
-  GIVEN("A complete scene with two differently colored bunnies.") {
+  GIVEN("A complete scene with a bunny") {
     std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine();
     auto scene = engine->GetScene();
 
@@ -152,4 +152,34 @@ SCENARIO(
   }
 }
 
+SCENARIO("We can render meshes as wireframe.",
+         "[phx][phx::MeshRenderSettings]") {
+  ALLOW_CALL(openvr_mock.Get(), VR_IsHmdPresent()).RETURN(false);
+  GIVEN("A complete scene with a bunny") {
+    std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine();
+    auto scene = engine->GetScene();
+
+    auto bunny =
+        LoadBunny(glm::vec3(0.0f, -0.1f, -0.3f), 0.0f, "bunny", scene.get());
+
+    auto rendering_system = engine->GetSystem<phx::RenderingSystem>();
+    auto display_system = engine->GetSystem<phx::DisplaySystemWindow>();
+
+    WHEN("We set the rendering to be done in wireframe") {
+      auto render_settings = bunny->AddComponent<phx::MeshRenderSettings>();
+      render_settings->SetWireframeMode(true);
+      THEN("the rendering matches our reference image") {
+        rendering_system->Update(phx::FrameTimer::TimeInfo{});
+        display_system->Update(phx::FrameTimer::TimeInfo());
+        phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB> buffer(
+            1024, 768);
+        buffer.ReadColorPixels(true);
+        test_utilities::OpenGLBufferComparison::
+            REQUIRE_REFERENCE_IMAGE_SIMILARITY(
+                buffer, "model_loading_wireframe.png", 1.0);
+      }
+    }
+  }
+}
+
 SUPPRESS_WARNINGS_END
diff --git a/tests/src/integration_test_opengl_buffer_data_download.cpp b/tests/src/integration_test_opengl_buffer_data_download.cpp
index dee66eac99e3ef1b7a9692360b7fd6eb3cf2340a..70137dd96183d8cd62ff91388f54ecf3137f0668 100644
--- a/tests/src/integration_test_opengl_buffer_data_download.cpp
+++ b/tests/src/integration_test_opengl_buffer_data_download.cpp
@@ -45,7 +45,6 @@ SUPPRESS_WARNINGS_BEGIN
 #include "mocks/openvr_mock.hpp"
 SUPPRESS_WARNINGS_END
 
-#include "test_utilities/dummy_material_generator.hpp"
 #include "test_utilities/dummy_mesh_generator.hpp"
 #include "test_utilities/opengl_buffer_data_comparison.hpp"
 
@@ -90,14 +89,9 @@ class SceneSetupSimple {
     phx::Entity* triangle = scene->CreateEntity();
     triangle->AddComponent<phx::Transform>();
 
-    phx::ResourceManager::instance().RegisterResourceType(
-        "custommaterial",
-        std::make_unique<phx::DummyMaterialGenerator>(
-            glm::vec3(), glm::vec3(), glm::vec3(1.f, 0.5f, 0.25f), 500.0f));
-    auto material =
-        phx::ResourceManager::instance().DeclareResource<phx::Material>(
-            {{"TYPE", "custommaterial"}});
-    material.Load();
+    auto material = phx::ResourceUtils::LoadGenericMaterial(
+        "custommaterial", glm::vec3(), glm::vec3(), glm::vec3(1.f, 0.5f, 0.25f),
+        500.f);
 
     triangle->AddComponent<phx::ResourceComponent<phx::Material>>(material);
 
diff --git a/tests/src/integration_test_rendering.cpp b/tests/src/integration_test_rendering.cpp
index dcb7a9b076e9a1be13dd752dfd430cc8294a9869..0408ad9563589710342f134267aa0ab0bab83b6c 100644
--- a/tests/src/integration_test_rendering.cpp
+++ b/tests/src/integration_test_rendering.cpp
@@ -43,7 +43,6 @@ SUPPRESS_WARNINGS_BEGIN
 #include "mocks/openvr_mock.hpp"
 SUPPRESS_WARNINGS_END
 
-#include "test_utilities/dummy_material_generator.hpp"
 #include "test_utilities/dummy_mesh_generator.hpp"
 #include "test_utilities/opengl_buffer_data_comparison.hpp"
 
@@ -107,14 +106,9 @@ SCENARIO("We can render a simple triangle", "[phx][phx::Rendering]") {
     phx::Entity* triangle = scene->CreateEntity();
     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));
-    auto material =
-        phx::ResourceManager::instance().DeclareResource<phx::Material>(
-            {{"TYPE", "custommaterial"}});
-    material.Load();
+    auto material = phx::ResourceUtils::LoadGenericMaterial(
+        "custommaterial", glm::vec3(0.2f, 0.2f, 1.0f), glm::vec3(1, 1, 1),
+        glm::vec3(), 500.0f);
 
     triangle->AddComponent<phx::Transform>();
     triangle->AddComponent<phx::ResourceComponent<phx::Material>>(material);
diff --git a/tests/src/integration_test_skybox.cpp b/tests/src/integration_test_skybox.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2259a053edb2672371c6c2a0d0d79be98fd78e49
--- /dev/null
+++ b/tests/src/integration_test_skybox.cpp
@@ -0,0 +1,123 @@
+//------------------------------------------------------------------------------
+// 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 <memory>
+#include <string>
+#include <vector>
+
+#include "catch/catch.hpp"
+
+#include "phx/core/engine.hpp"
+#include "phx/core/runtime_component.hpp"
+#include "phx/rendering/components/skybox.hpp"
+#include "phx/setup.hpp"
+
+#include "test_utilities/opengl_buffer_data_comparison.hpp"
+
+#include "trompeloeil.hpp"
+
+extern template struct trompeloeil::reporter<trompeloeil::specialized>;
+using trompeloeil::_;
+
+namespace {
+class EngineStopTestSystem : public phx::System {
+ public:
+  explicit EngineStopTestSystem(phx::Engine* engine) : phx::System(engine) {}
+
+  void Update(const phx::FrameTimer::TimeInfo&) override {
+    GetEngine()->Stop();
+  }
+};
+}  // namespace
+
+SCENARIO("Creating and adding the default skybox to the scene.",
+         "[phx][phx::Skybox]") {
+  GIVEN("A standard engine that runs one frame") {
+    std::unique_ptr<phx::Engine> engine =
+        phx::Setup::CreateDefaultEngine(false);
+    engine->CreateSystem<EngineStopTestSystem>();
+
+    WHEN("We add a skybox entity") {
+      auto scene = engine->GetScene();
+      auto skybox_entity = scene->CreateEntity();
+      auto skybox_component = skybox_entity->AddComponent<phx::Skybox>();
+      THEN("Default skybox is loaded.") {
+        REQUIRE(skybox_component->GetSkyboxImages().size() == 6);
+
+        WHEN("We set a specific viewpoint and run the engine") {
+          phx::Entity* platform = scene->GetEntitiesWithComponents<
+              phx::RuntimeComponent<phx::USER_PLATFORM>>()[0];
+          // to see three sides of the box
+          platform->GetFirstComponent<phx::Transform>()->SetGlobalRotation(
+              glm::vec3(-0.5, 1, -1));
+
+          engine->Run();
+          THEN("The skybox is rendered.") {
+            phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>
+                buffer(1024, 768);
+            buffer.ReadColorPixels(true);
+            test_utilities::OpenGLBufferComparison::
+                REQUIRE_REFERENCE_IMAGE_SIMILARITY(
+                    buffer, "default_skybox_rendering.png", 1.0);
+          }
+        }
+      }
+    }
+  }
+}
+
+SCENARIO("Creating and adding a user-defined skybox to the scene.",
+         "[phx][phx::Skybox]") {
+  GIVEN("A standard engine that runs one frame") {
+    std::unique_ptr<phx::Engine> engine =
+        phx::Setup::CreateDefaultEngine(false);
+    engine->CreateSystem<EngineStopTestSystem>();
+
+    WHEN("We add the user-defined skybox entity") {
+      auto scene = engine->GetScene();
+      auto skybox_entity = scene->CreateEntity();
+      auto skybox_component =
+          skybox_entity->AddComponent<phx::Skybox>("skyboxes/phx", "png");
+      THEN("User-defined skybox is loaded") {
+        REQUIRE(skybox_component->GetSkyboxImages().size() == 6);
+
+        WHEN("We set a specific viewpoint and run the engine") {
+          phx::Entity* platform = scene->GetEntitiesWithComponents<
+              phx::RuntimeComponent<phx::USER_PLATFORM>>()[0];
+          // to see three sides of the box
+          platform->GetFirstComponent<phx::Transform>()->SetGlobalRotation(
+              glm::vec3(-0.5, 1, -1));
+
+          engine->Run();
+          THEN("The skybox is rendered.") {
+            phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>
+                buffer(1024, 768);
+            buffer.ReadColorPixels(true);
+            test_utilities::OpenGLBufferComparison::
+                REQUIRE_REFERENCE_IMAGE_SIMILARITY(
+                    buffer, "user_defined_skybox_rendering.png", 1.0);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/tests/src/mocks/generation/CleanUp_openGL_mock.py b/tests/src/mocks/generation/CleanUp_openGL_mock.py
new file mode 100644
index 0000000000000000000000000000000000000000..4fbd83e282c830b61488c67530bf0f6291ebe7ea
--- /dev/null
+++ b/tests/src/mocks/generation/CleanUp_openGL_mock.py
@@ -0,0 +1,114 @@
+#------------------------------------------------------------------------------
+# 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.
+#------------------------------------------------------------------------------
+import textwrap
+import os
+from collections import defaultdict
+
+params_to_be_sorted = ["functions_to_mock", "allow_calls_provided", 
+					   "gl_types"]
+
+def sortListAlphabetically(content):
+	prefix = content[0:content.find("=")]
+	print("... sorting " + prefix)
+	
+	sub_content = content[content.find("[")+1:content.find("']")]
+	params = sub_content.split("',")
+	params = sorted(params, key=lambda s: s.lower())
+	
+	if (params_to_be_sorted[1] in prefix):
+		sub_content = sortGlewBeforeGl(params)
+	elif (params_to_be_sorted[2] in prefix):
+		sub_content = sortGLBeforeRest(params)
+	else:
+		sub_content = ', '.join(str(val)+"'" for val in params)
+	
+	return (prefix+ " = [" + sub_content+"]")
+
+def sortGlewBeforeGl(params):
+	d = defaultdict(list)
+	for val in params:
+		d["glew" in val].append(val)
+	sortedParams = ','.join(str(p)+"'" for p in d[True])
+	sortedParams += ','
+	sortedParams += ','.join(str(p)+"'" for p in d[False])
+	return sortedParams
+
+def sortGLBeforeRest(params):
+	d = defaultdict(list)
+	for val in params:
+		d["GL" in val].append(val)
+	sortedParams = ', '.join(str(p)+"'" for p in d[True])
+	sortedParams += ','
+	sortedParams += ','.join(str(p)+"'" for p in d[False])
+	return sortedParams
+	
+	
+def printToFile(file, content):
+	nr_of_blanks = len(content[0:content.find("[")+1])
+	if (params_to_be_sorted[1] in content):
+		printWithLineBreakAtComma(file, content, nr_of_blanks)
+	else:
+		printWith80CharacterLimit(file, content, nr_of_blanks)
+
+def printWith80CharacterLimit(file, content, nr_of_blanks):
+	input = ('\n'+' '*nr_of_blanks).join(textwrap.wrap(content, 
+		80-nr_of_blanks))
+	input += "\n"
+	file.write(input)
+
+def printWithLineBreakAtComma(file, content, nr_of_blanks):
+	content = content.replace ("',", "',\n")
+	params = content.split("\n")
+	for val in params:
+		file.write(val+"\n" if val == params[0] else ' '*(nr_of_blanks)+val+"\n")
+
+		
+def main():
+	file_in = open("Create_openGL_mock.py","r")
+	file_out = open("tmp.py","w")
+
+	collecting_tbs_lines = False
+	lines_tbs = ''
+
+	for line in file_in:
+		if any(val+"=[" in line.replace(' ', '') for val in params_to_be_sorted):
+			collecting_tbs_lines = True
+			
+		if collecting_tbs_lines:
+			lines_tbs += line.replace('\n', '').replace('\t', '').replace(' ', '')
+			if "]" in line:
+				collecting_tbs_lines = False
+				printToFile(file_out, sortListAlphabetically(lines_tbs))
+				lines_tbs = ''
+		else:
+			file_out.write(line)
+
+	file_in.close()
+	os.remove("Create_openGL_mock.py")
+	file_out.close()
+	os.rename("tmp.py", "Create_openGL_mock.py")
+	print "Done."
+
+
+print "Cleanup openGL mock ..."
+main()
+	
diff --git a/tests/src/mocks/generation/Create_openGL_mock.py b/tests/src/mocks/generation/Create_openGL_mock.py
index e113d9005ca04a02224e258bb5c6c71d5497de26..07c1944f1a3092d356c28ca23dee79d73bf1e640 100644
--- a/tests/src/mocks/generation/Create_openGL_mock.py
+++ b/tests/src/mocks/generation/Create_openGL_mock.py
@@ -22,77 +22,185 @@
 import sys, getopt
 
 #functions you want to mock should be put in this list
-functions_to_mock =['glClear', 'glEnable', 'glClearColor', 'glCreateProgram', 'glCreateShader', 'glNamedBufferData', 'glShaderSource',
-                    'glCompileShader', 'glAttachShader', 'glBindVertexArray', 'glCreateBuffers', 'glCreateVertexArrays', 'glEnableVertexArrayAttrib',
-                    'glGetUniformLocation', 'glIsBuffer', 'glIsProgram', 'glIsVertexArray', 'glLinkProgram', 'glUniformMatrix4fv', 'glUniform3fv',
-                    'glUniform1f', 'glUseProgram', 'glVertexArrayElementBuffer', 'glVertexArrayVertexBuffer', 'glDrawElements', 'glVertexArrayAttribFormat',
-                    'glGetProgramiv', 'glGetProgramInfoLog', 'glProgramUniform3fv', 'glProgramUniformMatrix4fv', 'glClearColor',
-                    'glClearDepth', 'glClearStencil', 'glColorMask', 'glDepthMask', 'glReadPixels', '__glewStencilMaskSeparate', '__glewClampColor',
-                    '__glewColorMaski', '__glewClearDepthf', '__glewBlitNamedFramebuffer', '__glewCheckNamedFramebufferStatus', '__glewClearNamedFramebufferfi',
-                    '__glewClearNamedFramebufferfv', '__glewClearNamedFramebufferiv', '__glewClearNamedFramebufferuiv', '__glewCreateFramebuffers',
-                    '__glewGetNamedFramebufferAttachmentParameteriv', '__glewGetNamedFramebufferParameteriv', '__glewInvalidateNamedFramebufferData',
-                    '__glewInvalidateNamedFramebufferSubData', '__glewNamedFramebufferDrawBuffer', '__glewNamedFramebufferDrawBuffers', '__glewNamedFramebufferParameteri',
-                    '__glewNamedFramebufferReadBuffer', '__glewNamedFramebufferRenderbuffer', '__glewDeleteFramebuffers', '__glewIsFramebuffer', '__glewCopyImageSubData',
-                    '__glewCreateRenderbuffers', '__glewGetNamedRenderbufferParameteriv', '__glewNamedRenderbufferStorage', '__glewNamedRenderbufferStorageMultisample',
-                    '__glewBindRenderbuffer', '__glewDeleteRenderbuffers', '__glewIsRenderbuffer', '__glewGetInternalformativ', '__glewGetInternalformati64v',
-                    'glFinish', 'glFlush', 'glGetError', 'glGetIntegerv', '__glewGetGraphicsResetStatus', '__glewBindAttribLocation', '__glewDeleteProgram', '__glewDetachShader',
-                    '__glewGetActiveAttrib', '__glewGetActiveUniform', '__glewGetAttachedShaders', '__glewGetAttribLocation', '__glewGetUniformfv', '__glewGetUniformiv',
-                    '__glewValidateProgram', '__glewBindFragDataLocation', '__glewGetFragDataLocation', '__glewGetTransformFeedbackVarying', '__glewGetUniformuiv',
-                    '__glewTransformFeedbackVaryings', '__glewProgramUniformHandleui64ARB', '__glewProgramUniformHandleui64vARB', '__glewBindFragDataLocationIndexed',
-                    '__glewGetFragDataIndex', '__glewProgramParameteri', '__glewGetUniformdv', '__glewGetProgramInterfaceiv', '__glewGetProgramResourceIndex',
-                    '__glewGetProgramResourceLocation', '__glewGetProgramResourceLocationIndex', '__glewGetProgramResourceName', '__glewGetProgramResourceiv',
-                    '__glewProgramUniform1d', '__glewProgramUniform1dv', '__glewProgramUniform1f', '__glewProgramUniform1fv', '__glewProgramUniform1i', '__glewProgramUniform1iv',
-                    '__glewProgramUniform1ui', '__glewProgramUniform1uiv', '__glewProgramUniform2d', '__glewProgramUniform2dv', '__glewProgramUniform2f', '__glewProgramUniform2fv',
-                    '__glewProgramUniform2i', '__glewProgramUniform2iv', '__glewProgramUniform2ui', '__glewProgramUniform2uiv', '__glewProgramUniform3d', '__glewProgramUniform3dv',
-                    '__glewProgramUniform3f', '__glewProgramUniform3i', '__glewProgramUniform3iv', '__glewProgramUniform3ui', '__glewProgramUniform3uiv', '__glewProgramUniform4d',
-                    '__glewProgramUniform4dv', '__glewProgramUniform4f', '__glewProgramUniform4fv', '__glewProgramUniform4i', '__glewProgramUniform4iv', '__glewProgramUniform4ui',
-                    '__glewProgramUniform4uiv', '__glewProgramUniformMatrix2dv', '__glewProgramUniformMatrix2fv', '__glewProgramUniformMatrix2x3dv', '__glewProgramUniformMatrix2x3fv',
-                    '__glewProgramUniformMatrix2x4dv', '__glewProgramUniformMatrix2x4fv', '__glewProgramUniformMatrix3dv', '__glewProgramUniformMatrix3fv', '__glewProgramUniformMatrix3x2dv',
-                    '__glewProgramUniformMatrix3x2fv', '__glewProgramUniformMatrix3x4dv', '__glewProgramUniformMatrix3x4fv', '__glewProgramUniformMatrix4dv', '__glewProgramUniformMatrix4x2dv',
-                    '__glewProgramUniformMatrix4x2fv', '__glewProgramUniformMatrix4x3dv', '__glewProgramUniformMatrix4x3fv', '__glewGetActiveAtomicCounterBufferiv', '__glewShaderStorageBlockBinding',
-                    '__glewGetActiveSubroutineName', '__glewGetActiveSubroutineUniformName', '__glewGetActiveSubroutineUniformiv', '__glewGetProgramStageiv', '__glewGetSubroutineIndex',
-                    '__glewGetSubroutineUniformLocation', '__glewGetUniformSubroutineuiv', '__glewUniformSubroutinesuiv', '__glewGetActiveUniformBlockName', '__glewGetActiveUniformBlockiv',
-                    '__glewGetActiveUniformName', '__glewGetActiveUniformsiv', '__glewGetUniformBlockIndex', '__glewGetUniformIndices', '__glewUniformBlockBinding', '__glewDeleteShader',
-                    '__glewGetShaderInfoLog', '__glewGetShaderSource', '__glewGetShaderiv', '__glewIsShader', '__glewSpecializeShader', '__glewReleaseShaderCompiler', '__glewShaderBinary',
-                    '__glewIsImageHandleResidentARB', '__glewMakeImageHandleNonResidentARB', '__glewMakeImageHandleResidentARB', '__glewIsTextureHandleResidentARB', '__glewMakeTextureHandleNonResidentARB',
-                    '__glewMakeTextureHandleResidentARB', '__glewDeleteBuffers', '__glewClearNamedBufferData', '__glewClearNamedBufferSubData','__glewCopyNamedBufferSubData',
-                    '__glewFlushMappedNamedBufferRange', '__glewGetNamedBufferParameteri64v', '__glewGetNamedBufferParameteriv', '__glewGetNamedBufferPointerv', 'glViewport',
-                    '__glewGetNamedBufferSubData', '__glewMapNamedBuffer', '__glewMapNamedBufferRange', '__glewNamedBufferStorage', '__glewNamedBufferSubData',
-                    '__glewUnmapNamedBuffer', '__glewInvalidateBufferData', '__glewInvalidateBufferSubData', '__glewDisableVertexArrayAttrib', '__glewGetVertexArrayIndexed64iv',
-                    '__glewGetVertexArrayIndexediv', '__glewGetVertexArrayiv', '__glewVertexArrayAttribBinding', '__glewVertexArrayAttribIFormat', '__glewVertexArrayAttribLFormat',
-                    '__glewVertexArrayBindingDivisor', '__glewDeleteVertexArrays', 'glBindTexture', 'glDeleteTextures', '__glewCreateTextures', '__glewNamedFramebufferTexture',
-                    '__glewTextureStorage2D', '__glewBindFramebuffer', 'glIsTexture', '__glewGetTextureHandleARB', '__glewGenerateTextureMipmap', '__glewTextureParameteri', '__glewTextureSubImage2D',
-                    '__glewBindBufferBase', '__glewBindBuffer', '__glewBindBufferRange', '__glewCreateShaderProgramv', 'glDepthRange', '__glewDepthRangef', '__glewDepthRangeIndexed', '__glewViewportIndexedf']
+functions_to_mock = ['__glewBindAttribLocation',
+                     '__glewBindBuffer', '__glewBindBufferBase',
+                     '__glewBindBufferRange', '__glewBindFragDataLocation',
+                     '__glewBindFragDataLocationIndexed',
+                     '__glewBindFramebuffer', '__glewBindRenderbuffer',
+                     '__glewBlitNamedFramebuffer',
+                     '__glewCheckNamedFramebufferStatus', '__glewClampColor',
+                     '__glewClearDepthf', '__glewClearNamedBufferData',
+                     '__glewClearNamedBufferSubData',
+                     '__glewClearNamedFramebufferfi',
+                     '__glewClearNamedFramebufferfv',
+                     '__glewClearNamedFramebufferiv',
+                     '__glewClearNamedFramebufferuiv', '__glewClientWaitSync',
+                     '__glewColorMaski', '__glewCopyImageSubData',
+                     '__glewCopyNamedBufferSubData', '__glewCreateFramebuffers',
+                     '__glewCreateRenderbuffers', '__glewCreateShaderProgramv',
+                     '__glewCreateTextures', '__glewDebugMessageControl',
+                     '__glewDebugMessageInsert', '__glewDeleteBuffers',
+                     '__glewDeleteFramebuffers', '__glewDeleteProgram',
+                     '__glewDeleteRenderbuffers', '__glewDeleteShader',
+                     '__glewDeleteSync', '__glewDeleteVertexArrays',
+                     '__glewDepthRangef', '__glewDepthRangeIndexed',
+                     '__glewDetachShader', '__glewDisableVertexArrayAttrib',
+                     '__glewFenceSync', '__glewFlushMappedNamedBufferRange',
+                     '__glewGenerateTextureMipmap',
+                     '__glewGetActiveAtomicCounterBufferiv',
+                     '__glewGetActiveAttrib', '__glewGetActiveSubroutineName',
+                     '__glewGetActiveSubroutineUniformiv',
+                     '__glewGetActiveSubroutineUniformName',
+                     '__glewGetActiveUniform', '__glewGetActiveUniformBlockiv',
+                     '__glewGetActiveUniformBlockName',
+                     '__glewGetActiveUniformName', '__glewGetActiveUniformsiv',
+                     '__glewGetAttachedShaders', '__glewGetAttribLocation',
+                     '__glewGetDebugMessageLog', '__glewGetFragDataIndex',
+                     '__glewGetFragDataLocation',
+                     '__glewGetGraphicsResetStatus',
+                     '__glewGetInternalformati64v', '__glewGetInternalformativ',
+                     '__glewGetNamedBufferParameteri64v',
+                     '__glewGetNamedBufferParameteriv',
+                     '__glewGetNamedBufferPointerv',
+                     '__glewGetNamedBufferSubData',
+                     '__glewGetNamedFramebufferAttachmentParameteriv',
+                     '__glewGetNamedFramebufferParameteriv',
+                     '__glewGetNamedRenderbufferParameteriv',
+                     '__glewGetObjectPtrLabel', '__glewGetProgramInterfaceiv',
+                     '__glewGetProgramResourceIndex',
+                     '__glewGetProgramResourceiv',
+                     '__glewGetProgramResourceLocation',
+                     '__glewGetProgramResourceLocationIndex',
+                     '__glewGetProgramResourceName', '__glewGetProgramStageiv',
+                     '__glewGetShaderInfoLog', '__glewGetShaderiv',
+                     '__glewGetShaderSource', '__glewGetSubroutineIndex',
+                     '__glewGetSubroutineUniformLocation', '__glewGetSynciv',
+                     '__glewGetTextureHandleARB',
+                     '__glewGetTransformFeedbackVarying',
+                     '__glewGetUniformBlockIndex', '__glewGetUniformdv',
+                     '__glewGetUniformfv', '__glewGetUniformIndices',
+                     '__glewGetUniformiv', '__glewGetUniformSubroutineuiv',
+                     '__glewGetUniformuiv', '__glewGetVertexArrayIndexed64iv',
+                     '__glewGetVertexArrayIndexediv', '__glewGetVertexArrayiv',
+                     '__glewInvalidateBufferData',
+                     '__glewInvalidateBufferSubData',
+                     '__glewInvalidateNamedFramebufferData',
+                     '__glewInvalidateNamedFramebufferSubData',
+                     '__glewIsFramebuffer', '__glewIsImageHandleResidentARB',
+                     '__glewIsRenderbuffer', '__glewIsShader', '__glewIsSync',
+                     '__glewIsTextureHandleResidentARB',
+                     '__glewMakeImageHandleNonResidentARB',
+                     '__glewMakeImageHandleResidentARB',
+                     '__glewMakeTextureHandleNonResidentARB',
+                     '__glewMakeTextureHandleResidentARB',
+                     '__glewMapNamedBuffer', '__glewMapNamedBufferRange',
+                     '__glewNamedBufferStorage', '__glewNamedBufferSubData',
+                     '__glewNamedFramebufferDrawBuffer',
+                     '__glewNamedFramebufferDrawBuffers',
+                     '__glewNamedFramebufferParameteri',
+                     '__glewNamedFramebufferReadBuffer',
+                     '__glewNamedFramebufferRenderbuffer',
+                     '__glewNamedFramebufferTexture',
+                     '__glewNamedRenderbufferStorage',
+                     '__glewNamedRenderbufferStorageMultisample',
+                     '__glewObjectPtrLabel', '__glewPopDebugGroup',
+                     '__glewProgramParameteri', '__glewProgramUniform1d',
+                     '__glewProgramUniform1dv', '__glewProgramUniform1f',
+                     '__glewProgramUniform1fv', '__glewProgramUniform1i',
+                     '__glewProgramUniform1iv', '__glewProgramUniform1ui',
+                     '__glewProgramUniform1uiv', '__glewProgramUniform2d',
+                     '__glewProgramUniform2dv', '__glewProgramUniform2f',
+                     '__glewProgramUniform2fv', '__glewProgramUniform2i',
+                     '__glewProgramUniform2iv', '__glewProgramUniform2ui',
+                     '__glewProgramUniform2uiv', '__glewProgramUniform3d',
+                     '__glewProgramUniform3dv', '__glewProgramUniform3f',
+                     '__glewProgramUniform3i', '__glewProgramUniform3iv',
+                     '__glewProgramUniform3ui', '__glewProgramUniform3uiv',
+                     '__glewProgramUniform4d', '__glewProgramUniform4dv',
+                     '__glewProgramUniform4f', '__glewProgramUniform4fv',
+                     '__glewProgramUniform4i', '__glewProgramUniform4iv',
+                     '__glewProgramUniform4ui', '__glewProgramUniform4uiv',
+                     '__glewProgramUniformHandleui64ARB',
+                     '__glewProgramUniformHandleui64vARB',
+                     '__glewProgramUniformMatrix2dv',
+                     '__glewProgramUniformMatrix2fv',
+                     '__glewProgramUniformMatrix2x3dv',
+                     '__glewProgramUniformMatrix2x3fv',
+                     '__glewProgramUniformMatrix2x4dv',
+                     '__glewProgramUniformMatrix2x4fv',
+                     '__glewProgramUniformMatrix3dv',
+                     '__glewProgramUniformMatrix3fv',
+                     '__glewProgramUniformMatrix3x2dv',
+                     '__glewProgramUniformMatrix3x2fv',
+                     '__glewProgramUniformMatrix3x4dv',
+                     '__glewProgramUniformMatrix3x4fv',
+                     '__glewProgramUniformMatrix4dv',
+                     '__glewProgramUniformMatrix4x2dv',
+                     '__glewProgramUniformMatrix4x2fv',
+                     '__glewProgramUniformMatrix4x3dv',
+                     '__glewProgramUniformMatrix4x3fv', '__glewPushDebugGroup',
+                     '__glewReleaseShaderCompiler', '__glewShaderBinary',
+                     '__glewShaderStorageBlockBinding',
+                     '__glewSpecializeShader', '__glewStencilMaskSeparate',
+                     '__glewTextureParameteri', '__glewTextureStorage2D',
+                     '__glewTextureStorage3D', '__glewTextureSubImage2D',
+                     '__glewTextureSubImage3D',
+                     '__glewTransformFeedbackVaryings',
+                     '__glewUniformBlockBinding', '__glewUniformSubroutinesuiv',
+                     '__glewUnmapNamedBuffer', '__glewValidateProgram',
+                     '__glewVertexArrayAttribBinding',
+                     '__glewVertexArrayAttribIFormat',
+                     '__glewVertexArrayAttribLFormat',
+                     '__glewVertexArrayBindingDivisor',
+                     '__glewViewportIndexedf', '__glewWaitSync',
+                     'glAttachShader', 'glBindTexture', 'glBindVertexArray',
+                     'glClear', 'glClearColor', 'glClearDepth',
+                     'glClearStencil', 'glColorMask', 'glCompileShader',
+                     'glCreateBuffers', 'glCreateProgram', 'glCreateShader',
+                     'glCreateVertexArrays', 'glDebugMessageCallback',
+                     'glDeleteTextures', 'glDepthMask', 'glDepthRange',
+                     'glDisable', 'glDrawArrays', 'glDrawElements', 'glEnable',
+                     'glEnableVertexArrayAttrib', 'glFinish', 'glFlush',
+                     'glGetError', 'glGetIntegerv', 'glGetPointerv',
+                     'glGetProgramInfoLog', 'glGetProgramiv',
+                     'glGetUniformLocation', 'glIsBuffer', 'glIsEnabled',
+                     'glIsProgram', 'glIsTexture', 'glIsVertexArray',
+                     'glLinkProgram', 'glNamedBufferData', 'glPolygonMode',
+                     'glProgramUniform3fv', 'glProgramUniformMatrix4fv',
+                     'glReadPixels', 'glShaderSource', 'glUniform1f',
+                     'glUniform3fv', 'glUniformMatrix4fv', 'glUseProgram',
+                     'glVertexArrayAttribFormat', 'glVertexArrayElementBuffer',
+                     'glVertexArrayVertexBuffer', 'glViewport']
 
 #allow calls you want to provide and not be auto generated
-allow_calls_provided = ['ALLOW_CALL(open_gl_mock, glewInit()).RETURN(GLEW_OK);',
-                        'ALLOW_CALL(open_gl_mock, glCompileShader(gt(0u)));',
-                        'ALLOW_CALL(open_gl_mock, glCreateProgram()).RETURN(1u);',
-                        'ALLOW_CALL(open_gl_mock, glCreateShader(_)).RETURN(1u);'
-                        'ALLOW_CALL(open_gl_mock, glAttachShader(gt(0u), gt(0u)));',
-                        'ALLOW_CALL(open_gl_mock, glCreateBuffers(1, _)).SIDE_EFFECT(*_2 = 1);',
-                        'ALLOW_CALL(open_gl_mock, glCreateVertexArrays(0, _));',
-                        'ALLOW_CALL(open_gl_mock, glCreateVertexArrays(1, _)).SIDE_EFFECT(*_2 = 1);',
-                        'ALLOW_CALL(open_gl_mock, glIsBuffer(0u)).RETURN(false);',
-                        'ALLOW_CALL(open_gl_mock, glIsBuffer(gt(0u))).RETURN(true);',
-                        'ALLOW_CALL(open_gl_mock, glIsProgram(0u)).RETURN(false);',
-                        'ALLOW_CALL(open_gl_mock, glIsProgram(gt(0u))).RETURN(true);',
-                        'ALLOW_CALL(open_gl_mock, glIsVertexArray(0u)).RETURN(false);',
-                        'ALLOW_CALL(open_gl_mock, glIsVertexArray(gt(0u))).RETURN(true);',
-                        'ALLOW_CALL(open_gl_mock, glLinkProgram(gt(0u)));',
-                        'ALLOW_CALL(open_gl_mock, glGetProgramInfoLog(_, _, _, _)).SIDE_EFFECT(*_3 = 0);',
-                        'ALLOW_CALL(open_gl_mock, glGetProgramiv(_, _, _)).SIDE_EFFECT(*_3 = GL_TRUE);',
-						'ALLOW_CALL(open_gl_mock, glIsFramebuffer(gt(0u))).RETURN(true);',
-						'ALLOW_CALL(open_gl_mock, glCheckNamedFramebufferStatus(gt(0u),_)).RETURN(GL_FRAMEBUFFER_COMPLETE);']
-
-#order does matter here, so prefixes of other types should come later
-gl_types = ['GLuint64EXT', 'GLint64EXT', 'GLintptr', 'GLsizeiptr', 'GLint64', 'GLuint64', 'GLboolean', 'GLbyte', 'GLubyte', 
-            'GLint', 'GLuint', 'GLsizei', 'GLfixed', 'GLenum', 'GLsync', 'GLbitfield', 'GLhalf', 'GLfloat', 'GLclampf',
-            'GLdouble', 'GLclampd', 'GLvoid', 'void', 'GLchar', 'GLshort', 'GLushort', 'GLclampx',
-            'GLhandleARB', 'GLcharARB', 'GLintptrARB', 'GLsizeiptrARB',
-            'GLvdpauSurfaceNV', 'GLeglClientBufferEXT',
-            'GLDEBUGPROCAMD', 'GLDEBUGPROCARB', 'GLDEBUGPROC', 'GLLOGPROCREGAL',
-            'cl_context', 'cl_event']
+allow_calls_provided = ['ALLOW_CALL(open_gl_mock,glewInit()).RETURN(GLEW_OK);',
+                        'ALLOW_CALL(open_gl_mock,glCheckNamedFramebufferStatus(gt(0u),_)).RETURN(GL_FRAMEBUFFER_COMPLETE);',
+                        'ALLOW_CALL(open_gl_mock,glCompileShader(gt(0u)));',
+                        'ALLOW_CALL(open_gl_mock,glCreateBuffers(1,_)).SIDE_EFFECT(*_2=1);',
+                        'ALLOW_CALL(open_gl_mock,glCreateProgram()).RETURN(1u);',
+                        'ALLOW_CALL(open_gl_mock,glCreateShader(_)).RETURN(1u);''ALLOW_CALL(open_gl_mock,glAttachShader(gt(0u),gt(0u)));',
+                        'ALLOW_CALL(open_gl_mock,glCreateVertexArrays(0,_));',
+                        'ALLOW_CALL(open_gl_mock,glCreateVertexArrays(1,_)).SIDE_EFFECT(*_2=1);',
+                        'ALLOW_CALL(open_gl_mock,glGetProgramInfoLog(_,_,_,_)).SIDE_EFFECT(*_3=0);',
+                        'ALLOW_CALL(open_gl_mock,glGetProgramiv(_,_,_)).SIDE_EFFECT(*_3=GL_TRUE);',
+                        'ALLOW_CALL(open_gl_mock,glIsBuffer(0u)).RETURN(false);',
+                        'ALLOW_CALL(open_gl_mock,glIsBuffer(gt(0u))).RETURN(true);',
+                        'ALLOW_CALL(open_gl_mock,glIsFramebuffer(gt(0u))).RETURN(true);',
+                        'ALLOW_CALL(open_gl_mock,glIsProgram(0u)).RETURN(false);',
+                        'ALLOW_CALL(open_gl_mock,glIsProgram(gt(0u))).RETURN(true);',
+                        'ALLOW_CALL(open_gl_mock,glIsVertexArray(0u)).RETURN(false);',
+                        'ALLOW_CALL(open_gl_mock,glIsVertexArray(gt(0u))).RETURN(true);',
+                        'ALLOW_CALL(open_gl_mock,glLinkProgram(gt(0u)));']
+
+gl_types = ['GLbitfield', 'GLboolean', 'GLbyte', 'GLchar',
+            'GLcharARB', 'GLclampd', 'GLclampf', 'GLclampx', 'GLDEBUGPROC',
+            'GLDEBUGPROCAMD', 'GLDEBUGPROCARB', 'GLdouble',
+            'GLeglClientBufferEXT', 'GLenum', 'GLfixed', 'GLfloat', 'GLhalf',
+            'GLhandleARB', 'GLint', 'GLint64', 'GLint64EXT', 'GLintptr',
+            'GLintptrARB', 'GLLOGPROCREGAL', 'GLshort', 'GLsizei', 'GLsizeiptr',
+            'GLsizeiptrARB', 'GLsync', 'GLubyte', 'GLuint', 'GLuint64',
+            'GLuint64EXT', 'GLushort', 'GLvdpauSurfaceNV',
+            'GLvoid','cl_context','cl_event','void']
 
 
 functions = []
diff --git a/tests/src/mocks/mocks.cpp b/tests/src/mocks/mocks.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..73334bc45712e87a4b20bbf33cee8eff29543345
--- /dev/null
+++ b/tests/src/mocks/mocks.cpp
@@ -0,0 +1,43 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 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 "catch/catch.hpp"
+
+#include "trompeloeil.hpp"
+
+namespace trompeloeil {
+template <>
+void reporter<specialized>::send(severity s, const char* file,  // NOLINT
+                                 unsigned long line,            // NOLINT
+                                 const char* msg) {             // NOLINT
+  std::ostringstream os;
+  if (line) os << file << ':' << line << '\n';
+  os << msg;
+  auto failure = os.str();
+  if (s == severity::fatal) {
+    FAIL(failure);
+  } else {
+    CAPTURE(failure);
+    CHECK(failure.empty());
+  }
+}
+}  // namespace trompeloeil
diff --git a/tests/src/mocks/openvr_mock.hpp b/tests/src/mocks/openvr_mock.hpp
index 83ec13827c371fd43eb7406175ab4393cb5867bd..38b02409278d868caf237b55a002dff137249a3e 100644
--- a/tests/src/mocks/openvr_mock.hpp
+++ b/tests/src/mocks/openvr_mock.hpp
@@ -36,7 +36,7 @@ SUPPRESS_WARNINGS_BEGIN
 #include "trompeloeil.hpp"
 SUPPRESS_WARNINGS_END
 
-#include "openvr_mock_export.hpp"
+#include "mocks/openvr_mock_export.hpp"
 
 using trompeloeil::_;
 
@@ -114,8 +114,9 @@ class OPENVR_MOCK_EXPORT IVRSystemMock : public IVRSystem {
   MAKE_MOCK3(GetControllerState, bool(vr::TrackedDeviceIndex_t,
                                       vr::VRControllerState_t*, uint32_t));
   MAKE_MOCK5(GetControllerStateWithPose,
-             bool(ETrackingUniverseOrigin, vr::TrackedDeviceIndex_t,
-                  vr::VRControllerState_t*, uint32_t, TrackedDevicePose_t*));
+             bool(vr::ETrackingUniverseOrigin, vr::TrackedDeviceIndex_t,
+                  vr::VRControllerState_t*, uint32_t,
+                  vr::TrackedDevicePose_t*));
   MAKE_MOCK3(TriggerHapticPulse,
              void(vr::TrackedDeviceIndex_t, uint32_t, unsigned short));
   MAKE_MOCK1(GetButtonIdNameFromEnum, const char*(EVRButtonId));
@@ -251,8 +252,6 @@ class OPENVR_MOCK_EXPORT IVRRenderModelsMock : public IVRRenderModels {
 
 }  // namespace vr
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 class OPENVR_MOCK_EXPORT OpenVRMock {
  public:
   OpenVRMock()
@@ -270,7 +269,7 @@ class OPENVR_MOCK_EXPORT OpenVRMock {
   }
 
   OpenVRMock& operator=(const OpenVRMock&) = delete;
-  OpenVRMock& operator=(OpenVRMock&&) = default;
+  OpenVRMock& operator=(OpenVRMock&&) = delete;
 
   OpenVRMockInternal& Get() { return *mock_; }
   vr::IVRSystemMock& GetSystem() const { return *ivr_system_mock_; }
@@ -357,8 +356,6 @@ class OPENVR_MOCK_EXPORT OpenVRMock {
   vr::IVRRenderModelsMock* ivr_render_models_mock_;
 };
 
-SUPPRESS_WARNINGS_END
-
 extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock;
 
 #define OPENVR_MOCK_ALLOW_ANY_CALL                                             \
@@ -405,6 +402,24 @@ extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock;
               OpenVRMock::toHMDMatrix34_t(openvr_mock.l_cont_transformation_)) \
       .SIDE_EFFECT(_1[openvr_mock.index_left_controller_].bPoseIsValid =       \
                        true);                                                  \
+  ALLOW_CALL(openvr_mock.GetSystem(),                                          \
+             GetControllerStateWithPose(vr::TrackingUniverseStanding,          \
+                                        openvr_mock.index_right_controller_,   \
+                                        _, _, _))                              \
+      .RETURN(true)                                                            \
+      .SIDE_EFFECT(                                                            \
+          _5->mDeviceToAbsoluteTracking =                                      \
+              OpenVRMock::toHMDMatrix34_t(openvr_mock.r_cont_transformation_)) \
+      .SIDE_EFFECT(_5->bPoseIsValid = true);                                   \
+  ALLOW_CALL(                                                                  \
+      openvr_mock.GetSystem(),                                                 \
+      GetControllerStateWithPose(vr::TrackingUniverseStanding,                 \
+                                 openvr_mock.index_left_controller_, _, _, _)) \
+      .RETURN(true)                                                            \
+      .SIDE_EFFECT(                                                            \
+          _5->mDeviceToAbsoluteTracking =                                      \
+              OpenVRMock::toHMDMatrix34_t(openvr_mock.l_cont_transformation_)) \
+      .SIDE_EFFECT(_5->bPoseIsValid = true);                                   \
   ALLOW_CALL(openvr_mock.GetCompositor(), Submit(_, _, _, _))                  \
       .RETURN(vr::EVRCompositorError::VRCompositorError_None);                 \
   ALLOW_CALL(openvr_mock.Get(), VR_GetGenericInterface(_, _))                  \
@@ -451,6 +466,22 @@ extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock;
   ALLOW_CALL(openvr_mock.GetRenderModels(), LoadTexture_Async(_, _))           \
       .RETURN(vr::VRRenderModelError_None)                                     \
       .SIDE_EFFECT(*_2 = openvr_mock.CreateDummyTexture(                       \
-                       static_cast<unsigned int>(_1)));
+                       static_cast<unsigned int>(_1)));                        \
+  ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)).RETURN(false);      \
+  ALLOW_CALL(openvr_mock.GetSystem(),                                          \
+             GetInt32TrackedDeviceProperty(_, vr::Prop_Axis0Type_Int32, _))    \
+      .RETURN(vr::k_eControllerAxis_None);                                     \
+  ALLOW_CALL(openvr_mock.GetSystem(),                                          \
+             GetInt32TrackedDeviceProperty(_, vr::Prop_Axis1Type_Int32, _))    \
+      .RETURN(vr::k_eControllerAxis_None);                                     \
+  ALLOW_CALL(openvr_mock.GetSystem(),                                          \
+             GetInt32TrackedDeviceProperty(_, vr::Prop_Axis2Type_Int32, _))    \
+      .RETURN(vr::k_eControllerAxis_None);                                     \
+  ALLOW_CALL(openvr_mock.GetSystem(),                                          \
+             GetInt32TrackedDeviceProperty(_, vr::Prop_Axis3Type_Int32, _))    \
+      .RETURN(vr::k_eControllerAxis_None);                                     \
+  ALLOW_CALL(openvr_mock.GetSystem(),                                          \
+             GetInt32TrackedDeviceProperty(_, vr::Prop_Axis4Type_Int32, _))    \
+      .RETURN(vr::k_eControllerAxis_None);
 
 #endif  // TESTS_SRC_MOCKS_OPENVR_MOCK_HPP_
diff --git a/tests/src/mocks/sdl_mock.cpp b/tests/src/mocks/sdl_mock.cpp
index 919ae46a7ece937b40f6c97ad5ccd936b21b35cb..3adf85769cb0e6a72a6723f7fbc1fce9e0bfc72f 100644
--- a/tests/src/mocks/sdl_mock.cpp
+++ b/tests/src/mocks/sdl_mock.cpp
@@ -33,8 +33,6 @@ SdlMock sdl_mock;
 #pragma clang diagnostic pop
 #endif
 
-extern template struct trompeloeil::reporter<trompeloeil::specialized>;
-
 extern "C" {
 int SDL_Init(Uint32 flags) { return sdl_mock.Get().SDL_Init(flags); }
 int SDL_InitSubSystem(Uint32 flags) {
diff --git a/tests/src/mocks/sdl_mock.hpp b/tests/src/mocks/sdl_mock.hpp
index c9146b5dda4c69339f0b10366da6f80b37acba15..1d00d1a68ce5b4aaf211871e66cb84680315e69b 100644
--- a/tests/src/mocks/sdl_mock.hpp
+++ b/tests/src/mocks/sdl_mock.hpp
@@ -31,7 +31,9 @@ SUPPRESS_WARNINGS_END
 
 #include "trompeloeil.hpp"
 
-class SdlMockInternal {
+#include "mocks/sdl_mock_export.hpp"
+
+class SDL_MOCK_EXPORT SdlMockInternal {
  public:
   MAKE_MOCK1(SDL_Init, int(Uint32 flags));  // NOLINT(readability/casting)
   MAKE_MOCK1(SDL_InitSubSystem,
@@ -55,7 +57,7 @@ class SdlMockInternal {
   MAKE_MOCK1(SDL_HideWindow, void(SDL_Window*));
 };
 
-class SdlMock {
+class SDL_MOCK_EXPORT SdlMock {
  public:
   SdlMock() : mock_(new SdlMockInternal) {}
   SdlMock(const SdlMock&) = delete;
@@ -64,7 +66,7 @@ class SdlMock {
   ~SdlMock() { delete mock_; }
 
   SdlMock& operator=(const SdlMock&) = delete;
-  SdlMock& operator=(SdlMock&&) = default;
+  SdlMock& operator=(SdlMock&&) = delete;
 
   SdlMockInternal& Get() { return *mock_; }
 
@@ -72,7 +74,7 @@ class SdlMock {
   SdlMockInternal* mock_{nullptr};
 };
 
-extern SdlMock sdl_mock;
+extern SDL_MOCK_EXPORT SdlMock sdl_mock;
 
 #define SDL_MOCK_ALLOW_ANY_CALL                                              \
   ALLOW_CALL(sdl_mock.Get(), SDL_Init(SDL_INIT_VIDEO)).RETURN(0);            \
diff --git a/tests/src/test_assimp_loader.cpp b/tests/src/test_assimp_loader.cpp
index f1cbfcc6b9cdfa9f32657b561b1a3cde09330443..9711390663d673f35d6679ec15e424b86642a565 100644
--- a/tests/src/test_assimp_loader.cpp
+++ b/tests/src/test_assimp_loader.cpp
@@ -26,14 +26,18 @@
 
 #include "catch/catch.hpp"
 
+#include "mocks/opengl_mock.hpp"
+
 #include "phx/core/logger.hpp"
-#include "phx/resources/types/mesh.hpp"
 #include "phx/resources/loaders/assimp_model_loader.hpp"
 #include "phx/resources/resource_utils.hpp"
+#include "phx/resources/types/mesh.hpp"
 #include "phx/utility/aspects/nameable.hpp"
 
 #include "test_utilities/log_capture.hpp"
 
+extern template struct trompeloeil::reporter<trompeloeil::specialized>;
+
 class TestLogger {
  public:
   TestLogger() : log_capture_{std::make_shared<test_utilities::LogCapture>()} {
@@ -49,6 +53,7 @@ class TestLogger {
 
 SCENARIO("The assimp loader loads models using the Assimp library.",
          "[phx][phx::AssimpLoader]") {
+  OPENGL_MOCK_ALLOW_ANY_CALL
   GIVEN("A plain loader") {
     phx::CreateDefaultLogger();
     TestLogger test_logger;
@@ -94,6 +99,18 @@ SCENARIO("The assimp loader loads models using the Assimp library.",
         }
       }
     }
+    WHEN("We load a unit cube.") {
+      auto resource = phx::ResourceUtils::LoadResourceFromFile<phx::Model>(
+          "models/cube/cube.obj");
+      auto mesh = resource->GetMesh();
+      auto bounding_box = mesh->GetBoundingBox();
+      THEN("The lower bound should be at (-0.5,-0.5,-0.5)") {
+        REQUIRE(bounding_box[0] == glm::vec3(-0.5f));
+      }
+      THEN("The upper bound should be at (0.5,0.5,0.5)") {
+        REQUIRE(bounding_box[1] == glm::vec3(0.5f));
+      }
+    }
     WHEN("We load a specific material.") {
       const std::string material_name = "Material.001";
       auto resource = phx::ResourceUtils::LoadResourceFromFile<phx::Material>(
diff --git a/tests/src/test_behavior_system.cpp b/tests/src/test_behavior_system.cpp
index d7ce2e41fd95c38264588fbf57f7614c612dd97a..3584faecc25b600396331f84b53e838953f55c77 100644
--- a/tests/src/test_behavior_system.cpp
+++ b/tests/src/test_behavior_system.cpp
@@ -21,15 +21,17 @@
 //------------------------------------------------------------------------------
 
 #include <memory>
-#include <vector>
 
 #include "catch/catch.hpp"
 #include "trompeloeil.hpp"
 
 #include "phx/core/engine.hpp"
+#include "phx/core/logger.hpp"
 #include "phx/core/scene.hpp"
 #include "phx/scripting/behavior.hpp"
 #include "phx/scripting/behavior_system.hpp"
+#include "phx/scripting/generic_behavior.hpp"
+#include "test_utilities/log_capture.hpp"
 
 extern template struct trompeloeil::reporter<trompeloeil::specialized>;
 
@@ -50,10 +52,11 @@ SCENARIO(
     auto scene = std::make_shared<phx::Scene>();
     engine.SetScene(scene);
     auto entity = scene->CreateEntity();
-    auto behavior = entity->AddComponent<BehaviorMock>();
 
-    WHEN("We create a behavior system.") {
-      auto behavior_system = engine.CreateSystem<phx::BehaviorSystem>();
+    auto behavior_system = engine.CreateSystem<phx::BehaviorSystem>();
+
+    WHEN("We create a mocked behavior") {
+      auto behavior = entity->AddComponent<BehaviorMock>();
       WHEN("We update the behavior system 10 times.") {
         REQUIRE_CALL(*behavior, OnUpdate()).TIMES(10);
         for (auto i = 0; i < 10; ++i)
@@ -62,3 +65,49 @@ SCENARIO(
     }
   }
 }
+SCENARIO("we can have generic behaviors", "[phx][phx::GenericBehavior]") {
+  GIVEN("An engine with a behavior system and an entity") {
+    phx::Engine engine;
+    auto scene = std::make_shared<phx::Scene>();
+    engine.SetScene(scene);
+    auto entity = scene->CreateEntity();
+    auto behavior_system = engine.CreateSystem<phx::BehaviorSystem>();
+
+    WHEN("We create a generic behavior") {
+      bool onConstructionCalled = false;
+      bool onUpdateCalled = false;
+      entity->AddComponent<phx::GenericBehavior>(
+          [&onConstructionCalled](phx::Entity*) {
+            onConstructionCalled = true;
+          },
+          [&onUpdateCalled](const phx::FrameTimer::TimeInfo*, phx::Entity*) {
+            onUpdateCalled = true;
+          });
+      THEN("onConstruction should be called, but no onUpdate yet") {
+        REQUIRE(onConstructionCalled);
+        REQUIRE_FALSE(onUpdateCalled);
+        WHEN("The behavior system is updated") {
+          behavior_system->Update(phx::FrameTimer::TimeInfo());
+          THEN("onUpdate is also called") {
+            REQUIRE(onConstructionCalled);
+            REQUIRE(onUpdateCalled);
+          }
+        }
+      }
+    }
+    WHEN("We we name a generic behavior") {
+      auto behavior = entity->AddComponent<phx::GenericBehavior>(
+          [](phx::Entity*) {},
+          [](const phx::FrameTimer::TimeInfo*, phx::Entity*) {}, "GenericName");
+      THEN("it is loggable") {
+        auto log_capture = std::make_shared<test_utilities::LogCapture>();
+        phx::logger =
+            std::make_shared<spdlog::logger>("logcapture", log_capture);
+        phx::logger->set_pattern("%v");
+
+        phx::info("{}", *behavior);
+        REQUIRE(*log_capture == "GenericBehavior: GenericName");
+      }
+    }
+  }
+}
diff --git a/tests/src/test_clear_pass.cpp b/tests/src/test_clear_pass.cpp
index 2a8322c1a6361b681b86398e5a18b4d0df0f2a40..dce48d4c421761b0d77d5e3b5f087f061f54d827 100644
--- a/tests/src/test_clear_pass.cpp
+++ b/tests/src/test_clear_pass.cpp
@@ -20,8 +20,6 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#include "catch/catch.hpp"
-
 #include "phx/suppress_warnings.hpp"
 
 SUPPRESS_WARNINGS_BEGIN
@@ -31,11 +29,13 @@ SUPPRESS_WARNINGS_END
 
 #include "trompeloeil.hpp"
 
-#include "mocks/opengl_mock.hpp"
+#include "catch/catch.hpp"
 
 #include "phx/rendering/backend/render_target.hpp"
 #include "phx/rendering/render_passes/clear_pass.hpp"
 
+#include "mocks/opengl_mock.hpp"
+
 extern template struct trompeloeil::reporter<trompeloeil::specialized>;
 
 SCENARIO("The clear pass clears the active color and depth buffers.",
diff --git a/tests/src/test_device_system.cpp b/tests/src/test_device_system.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc5b0401fcbaf186a710783e4bc007b8187bb31f
--- /dev/null
+++ b/tests/src/test_device_system.cpp
@@ -0,0 +1,83 @@
+//------------------------------------------------------------------------------
+// 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 <memory>
+
+#include "catch/catch.hpp"
+
+#include "trompeloeil.hpp"
+
+#include "phx/core/engine.hpp"
+#include "phx/input/device.hpp"
+#include "phx/input/device_system.hpp"
+
+using trompeloeil::_;
+using trompeloeil::ne;
+
+extern template struct trompeloeil::reporter<trompeloeil::specialized>;
+
+class MockDevice : public phx::Device {
+ public:
+  MAKE_MOCK0(Update, void());
+};
+
+class MockDevice2 : public MockDevice {
+ public:
+  MAKE_MOCK0(Update, void());
+};
+
+SCENARIO("The device system manages and updates its devices",
+         "[phx][phx::DeviceSystem]") {
+  GIVEN("an device system") {
+    phx::Engine engine;
+    auto device_system = engine.CreateSystem<phx::DeviceSystem>();
+
+    WHEN("We add devices") {
+      MockDevice* device1 = device_system->AddDevice<MockDevice>();
+      MockDevice2* device2 = device_system->AddDevice<MockDevice2>();
+
+      THEN("their update method is called when the system is updated") {
+        REQUIRE_CALL(*device1, Update()).TIMES(1);
+        REQUIRE_CALL(*device2, Update()).TIMES(1);
+        device_system->Update(phx::FrameTimer::TimeInfo());
+      }
+
+      THEN("we can get these devices by type") {
+        auto devices = device_system->GetDevices<MockDevice>();
+        REQUIRE(devices.size() == 2);
+        REQUIRE(std::find(devices.begin(), devices.end(), device1) !=
+                devices.end());
+        REQUIRE(std::find(devices.begin(), devices.end(), device2) !=
+                devices.end());
+
+        auto devices2 = device_system->GetDevices<MockDevice2>();
+        REQUIRE(devices2.size() == 1);
+        REQUIRE(devices2[0] == device2);
+      }
+
+      THEN("devices can be removed again") {
+        device_system->RemoveDevice(device1);
+        REQUIRE(device_system->GetDevices<MockDevice>().size() == 1);
+      }
+    }
+  }
+}
diff --git a/tests/src/test_generic_material_generator.cpp b/tests/src/test_generic_material_generator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d26fa874c578ff318fb10d539af6182eacbf84e7
--- /dev/null
+++ b/tests/src/test_generic_material_generator.cpp
@@ -0,0 +1,90 @@
+//------------------------------------------------------------------------------
+// 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 "catch/catch.hpp"
+
+#include "phx/resources/resource_utils.hpp"
+#include "phx/resources/types/material.hpp"
+
+#include "test_utilities/glm_vec.hpp"
+
+SCENARIO("The Generic Material Loader can create Generic materials.",
+         "[phx][phx::MaterialGenerator]") {
+  GIVEN("A plain Generic Material Generator") {
+    WHEN("We load a red material") {
+      auto materialResource = phx::ResourceUtils::LoadGenericMaterial(
+          "generic_material", glm::vec3(1.f, 0.f, 0.f));
+
+      THEN("We have a material with the given name") {
+        REQUIRE(materialResource != nullptr);
+        REQUIRE(materialResource->GetName() == "generic_material");
+      }
+
+      THEN(
+          "The material is red, and specular color, ambient color and "
+          "shininess are set to default values.") {
+        auto diffuseColor = materialResource->GetDiffuseColor();
+        REQUIRE(diffuseColor == glm::vec3(1.0, 0.0, 0.0));
+
+        auto specularColor = materialResource->GetSpecularColor();
+        auto specularColorDefault =
+            phx::Material::GetDefault()->GetSpecularColor();
+        REQUIRE(specularColor == specularColorDefault);
+
+        auto ambientColor = materialResource->GetAmbientColor();
+        auto ambientColorDefault =
+            phx::Material::GetDefault()->GetAmbientColor();
+        REQUIRE(ambientColor == ambientColorDefault);
+
+        float shininess = materialResource->GetShininess();
+        REQUIRE(shininess == 64.0f);  //  default shininess value
+      }
+    }
+
+    WHEN(
+        "We load a different, more exotic material with some arbitrary "
+        "values") {
+      auto secondMaterial = phx::ResourceUtils::LoadGenericMaterial(
+          "generic_material_2", glm::vec3(0.4f, 0.3f, 0.55f),
+          glm::vec3(0.48f, 0.42f, 0.88f), glm::vec3(0.11f, 0.94f, 0.73f), 40.f);
+
+      THEN("We still get a material with the given name") {
+        REQUIRE(secondMaterial != nullptr);
+        REQUIRE(secondMaterial->GetName() == "generic_material_2");
+      }
+
+      THEN("All the values in the material are set to the right value.") {
+        auto diffuseColor = secondMaterial->GetDiffuseColor();
+        REQUIRE(diffuseColor == glm::vec3(0.4f, 0.3f, 0.55f));
+
+        auto specularColor = secondMaterial->GetSpecularColor();
+        REQUIRE(specularColor == glm::vec3(0.48f, 0.42f, 0.88f));
+
+        auto ambientColor = secondMaterial->GetAmbientColor();
+        REQUIRE(ambientColor == glm::vec3(0.11f, 0.94f, 0.73f));
+
+        float shininess = secondMaterial->GetShininess();
+        REQUIRE(shininess == 40.f);
+      }
+    }
+  }
+}
diff --git a/tests/src/test_hierarchical.cpp b/tests/src/test_hierarchical.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..30a8437b52c98be02d0898aa12c141d7df807aff
--- /dev/null
+++ b/tests/src/test_hierarchical.cpp
@@ -0,0 +1,97 @@
+//------------------------------------------------------------------------------
+// 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 <cstddef>
+
+#include <memory>
+
+#include "catch/catch.hpp"
+
+#include "phx/utility/aspects/hierarchical.hpp"
+
+class SomeHierarchical : public phx::Hierarchical<SomeHierarchical> {};
+
+SCENARIO("The hierarchical has parents and childs.",
+         "[phx][phx::Hierarchical]") {
+  GIVEN("Two hierarchical objects.") {
+    auto child = std::make_unique<SomeHierarchical>();
+    auto parent = std::make_unique<SomeHierarchical>();
+    WHEN("We query the first transform's parent.") {
+      auto parent_ptr = child->GetParent();
+      THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); }
+    }
+
+    WHEN("We set the first hierarchical's parent to the second.") {
+      child->SetParent(parent.get());
+
+      WHEN("We query the first hierarchical's parent.") {
+        auto parent_ptr = child->GetParent();
+        THEN("It should equal the second hierarchical.") {
+          REQUIRE(parent_ptr == parent.get());
+        }
+      }
+
+      WHEN("We query the second hierarchical's child count.") {
+        auto child_count = parent->GetChildCount();
+        THEN("Its size should equal 1.") { REQUIRE(child_count == 1); }
+      }
+
+      WHEN("We query the second hierarchical's first child.") {
+        auto child_ptr = parent->GetChild(0);
+        THEN("It should equal the first hierarchical.") {
+          REQUIRE(child_ptr == child.get());
+        }
+      }
+
+      WHEN("We iterate the second hierarchical's children.") {
+        auto iteration_count = 0;
+        for (auto it = parent->begin(); it != parent->end(); ++it)
+          iteration_count++;
+        THEN("It should consist of a single iteration.") {
+          REQUIRE(iteration_count == 1);
+        }
+      }
+
+      WHEN("We set the first hierarchical's parent to nullptr.") {
+        child->SetParent(nullptr);
+        WHEN("We query the hierarchical's parent.") {
+          auto parent_ptr = child->GetParent();
+          THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); }
+        }
+      }
+
+      WHEN("We destroy the child.") {
+        child.reset();
+        THEN("The parent has no more children.") {
+          REQUIRE(parent->GetChildCount() == 0);
+        }
+      }
+
+      WHEN("We destroy the parent.") {
+        parent.reset();
+        THEN("The child has no more parent.") {
+          REQUIRE(child->GetParent() == nullptr);
+        }
+      }
+    }
+  }
+}
diff --git a/tests/src/test_image.cpp b/tests/src/test_image.cpp
index bc6f702a54d3096148e61d1c9c2a6aeb7d9a5a9a..8c52dd7d58e520cb444cd8d407a21ea8e25ec248 100644
--- a/tests/src/test_image.cpp
+++ b/tests/src/test_image.cpp
@@ -173,3 +173,22 @@ SCENARIO("Images can be loaded from and saved to disk.", "[phx][phx::Image]") {
     }
   }
 }
+
+SCENARIO(
+    "When the filepath of an image does not exist, a default image is "
+    "generated.",
+    "[phx][phx::Image]") {
+  WHEN("We load an Image from an invalid filepath.") {
+    auto invalid_image = std::make_unique<phx::Image>("This is nonsense");
+
+    THEN("The image is a 1x1 fuchsia image.") {
+      auto dimensions = invalid_image->GetDimensions();
+      auto pixel = invalid_image->GetPixelColor({{0, 0}});
+      std::array<std::uint8_t, 4> fuchsia_pixel = {{255u, 0u, 255u, 255u}};
+
+      REQUIRE(dimensions[0] == 1);
+      REQUIRE(dimensions[1] == 1);
+      REQUIRE(pixel == fuchsia_pixel);
+    }
+  }
+}
diff --git a/tests/src/test_light.cpp b/tests/src/test_light.cpp
index 6506ab2cdfa096a474b6518518795482aaac7a8d..6d219cdcaf4cf3ecb3be6eb3642360b2c7b97e12 100644
--- a/tests/src/test_light.cpp
+++ b/tests/src/test_light.cpp
@@ -27,7 +27,9 @@
 #include "phx/core/entity.hpp"
 #include "phx/rendering/components/light.hpp"
 
+SUPPRESS_WARNINGS_BEGIN
 #include "glm/glm.hpp"
+SUPPRESS_WARNINGS_END
 
 SCENARIO("The light component describes a light source in space.",
          "[phx][phx::Light]") {
diff --git a/tests/src/test_material.cpp b/tests/src/test_material.cpp
index 761cc950a541e96bd4fbda2ddfd340851e4546dd..47815119165884e47a0c59c962ba019171dad68d 100644
--- a/tests/src/test_material.cpp
+++ b/tests/src/test_material.cpp
@@ -29,7 +29,7 @@
 #include "phx/core/logger.hpp"
 #include "phx/resources/types/material.hpp"
 
-#include "test_utilities/glm_vec3.hpp"
+#include "test_utilities/glm_vec.hpp"
 
 SCENARIO("The material component keeps track of its attributes",
          "[phx][phx::Material]") {
@@ -42,7 +42,7 @@ SCENARIO("The material component keeps track of its attributes",
       REQUIRE(material.GetAmbientColor() == glm::vec3(0, 0, 0));
       REQUIRE(material.GetSpecularColor() == glm::vec3(1, 1, 1));
       REQUIRE(material.GetDiffuseColor() == glm::vec3(1, 0, 0));
-      REQUIRE(material.GetShininess() == 1.0f);
+      REQUIRE(material.GetShininess() == 64.0f);
     }
     WHEN("we assign it to be some other material") {
       glm::vec3 blue(0, 0, 1), white(1, 1, 1), green(0, 1, 0);
diff --git a/tests/src/test_model.cpp b/tests/src/test_model.cpp
index 7098d7ca52f01c7dec6872d7a815ff80aee976b3..883d5e3b09fff45fb85c2f552b266b76073d4cdd 100644
--- a/tests/src/test_model.cpp
+++ b/tests/src/test_model.cpp
@@ -25,12 +25,18 @@
 #include "catch/catch.hpp"
 
 #include "phx/resources/loaders/assimp_model_loader.hpp"
-#include "phx/resources/types/model.hpp"
 #include "phx/resources/resource_manager.hpp"
 #include "phx/resources/resource_utils.hpp"
+#include "phx/resources/types/model.hpp"
+
+#include "mocks/opengl_mock.hpp"
+#include "trompeloeil.hpp"
+
+extern template struct trompeloeil::reporter<trompeloeil::specialized>;
 
 SCENARIO("A Model can be loaded that contains multiple meshes",
          "[phx][phx::Model]") {
+  OPENGL_MOCK_ALLOW_ANY_CALL
   GIVEN("A fresh resource manager...") {
     GIVEN("A file name of an .obj file...") {
       std::string model_file_name{"models/2MeshTest/2meshTest.obj"};
diff --git a/tests/src/test_openvr_controller_system.cpp b/tests/src/test_openvr_controller_model_system.cpp
similarity index 86%
rename from tests/src/test_openvr_controller_system.cpp
rename to tests/src/test_openvr_controller_model_system.cpp
index 5a8d237a384ffaf72e1687bb3b59c1ed939d7299..aae3caa286ab807a7fe2870f95319946da7f83b2 100644
--- a/tests/src/test_openvr_controller_system.cpp
+++ b/tests/src/test_openvr_controller_model_system.cpp
@@ -35,8 +35,10 @@ SUPPRESS_WARNINGS_END
 #include "phx/core/entity.hpp"
 #include "phx/core/scene.hpp"
 #include "phx/display/display_system_openvr.hpp"
+#include "phx/input/device_system.hpp"
 #include "phx/input/openvr_controller_behavior.hpp"
-#include "phx/input/openvr_controller_system.hpp"
+#include "phx/input/openvr_controller_model_system.hpp"
+#include "phx/input/vr_controller.hpp"
 #include "phx/rendering/components/resource_component.hpp"
 #include "phx/rendering/rendering_system.hpp"
 
@@ -72,23 +74,23 @@ class EngineStopTestSystem : public phx::System {
 SCENARIO(
     "The OpenVRControllerSystem automatically inserts controller entities into "
     "a scene if controllers are connected",
-    "[phx][phx::OpenVRControllerSystem]") {
+    "[phx][phx::OpenVRControllerModelSystem]") {
   OPENGL_MOCK_ALLOW_ANY_CALL;
   OPENVR_MOCK_ALLOW_ANY_CALL;
   SDL_MOCK_ALLOW_ANY_CALL;
 
   phx::Engine engine;
-  auto display_system = engine.CreateSystem<phx::DisplaySystemOpenVR>();
+  auto device_system = engine.CreateSystem<phx::DeviceSystem>();
 
-  GIVEN(
-      "The display system has an HMD and the engine has a scene. Also, there's "
-      "an OpenVRControllerSystem.") {
-    display_system->CreateHMD();
+  GIVEN("a device system with two vr controllers") {
+    device_system->AddDevice<phx::VRController>(
+        phx::VRController::LEFT_CONTROLLER);
+    device_system->AddDevice<phx::VRController>(
+        phx::VRController::RIGHT_CONTROLLER);
     auto scene = std::make_shared<phx::Scene>();
     engine.SetScene(scene);
-    engine.CreateSystem<phx::OpenVRControllerSystem>(display_system);
+    engine.CreateSystem<phx::OpenVRControllerModelSystem>(device_system);
     engine.CreateSystem<EngineStopTestSystem>();
-    engine.CreateSystem<phx::RenderingSystem>(display_system);
 
     WHEN("We run the engine for once frame (updating each system once)") {
       engine.Run();
diff --git a/tests/src/test-phoenix.cpp b/tests/src/test_phoenix.cpp
similarity index 100%
rename from tests/src/test-phoenix.cpp
rename to tests/src/test_phoenix.cpp
diff --git a/tests/src/test_resource_utils.cpp b/tests/src/test_resource_utils.cpp
index d2b0a9bb82b807735219581322600fec1e1c6e26..1da8fcab9e8789cba8a6a11d1a0261b45dc8aa36 100644
--- a/tests/src/test_resource_utils.cpp
+++ b/tests/src/test_resource_utils.cpp
@@ -20,15 +20,27 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
+#include <fstream>
 #include <string>
 
 #include "catch/catch.hpp"
 
-#include "phx/resources/types/image.hpp"
 #include "phx/resources/resource_declaration.hpp"
 #include "phx/resources/resource_utils.hpp"
+#include "phx/resources/types/image.hpp"
 #include "phx/resources_path.hpp"
 
+namespace {
+void CreateDummyFile(const std::string &file_name) {
+  std::fstream out_file(file_name.c_str(), std::ios::out);
+  // force fail test if file could not be created
+  REQUIRE(out_file.is_open());
+}
+void DeleteDummyFile(const std::string &file_name) {
+  std::remove(file_name.c_str());
+}
+}  // namespace
+
 SCENARIO("Resource utils can extract file extensions.",
          "[phx][phx::ResourceUtils]") {
   GIVEN("A file name") {
@@ -56,17 +68,19 @@ SCENARIO("Resource utils can declarare a file resource.",
   GIVEN("A file name with absolute path") {
     std::string file_name{"C:/some_path/to/file.34.ext"};
 
-    THEN("It can declare it") {
+    THEN("we get a declaration with a valid (unmodified) file name") {
       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"};
+  GIVEN("A file name and a search path") {
+    std::string file_name{"textures/splash_progress.png"};
 
-    THEN("It can declare it") {
+    THEN(
+        "we get a declaration with a valid file name that has been pre-pended "
+        "by the first hit in the search path") {
       phx::ResourceDeclaration declaration =
           phx::ResourceUtils::DeclarationFromFile(file_name);
       REQUIRE(declaration["TYPE"] == ".png");
@@ -74,11 +88,11 @@ SCENARIO("Resource utils can declarare a file resource.",
     }
   }
   GIVEN("Additional key, value pairs are provided") {
-    std::string file_name{"file.png"};
+    std::string file_name{"textures/splash_progress.png"};
     std::string key = "some_key";
     int value = 42;
 
-    THEN("these are in the resulting declaration") {
+    THEN("the resulting declaration contains them") {
       phx::ResourceDeclaration declaration =
           phx::ResourceUtils::DeclarationFromFile(file_name, {{key, value}},
                                                   false);
@@ -87,9 +101,26 @@ SCENARIO("Resource utils can declarare a file resource.",
   }
 }
 
+SCENARIO("Resource utils find file resource outside the original search path",
+         "[phx][phx::ResourceUtils]") {
+  GIVEN("A file outside the original resource tree") {
+    std::string dummy_file = "dummy.txt";
+    std::string offset_search_path = "../";
+    ::CreateDummyFile(offset_search_path + dummy_file);
+
+    phx::ResourceUtils::AddResourceSearchPath(offset_search_path);
+    THEN("the file is found and a valid declaration is returned") {
+      phx::ResourceDeclaration declaration =
+          phx::ResourceUtils::DeclarationFromFile(dummy_file, {}, false);
+      CHECK(declaration["file_name"] == offset_search_path + dummy_file);
+      ::DeleteDummyFile(offset_search_path + dummy_file);
+    }
+  }
+}
+
 SCENARIO("Resource utils can load a file resource.",
          "[phx][phx::ResourceUtils]") {
-  GIVEN("A file name") {
+  GIVEN("A file name within the original resource tree") {
     std::string file_name{"textures/splash_progress.png"};
 
     THEN("It can load it") {
diff --git a/tests/src/test_scene_loader.cpp b/tests/src/test_scene_loader.cpp
index 807a22be5f1cad2e088943c480b48e5b4631411f..8256a2037e2ff25ba7bc9c28e3b8865232f31aa8 100644
--- a/tests/src/test_scene_loader.cpp
+++ b/tests/src/test_scene_loader.cpp
@@ -32,7 +32,13 @@
 #include "phx/resources/resource_pointer.hpp"
 #include "phx/resources/types/mesh.hpp"
 
+#include "mocks/opengl_mock.hpp"
+#include "trompeloeil.hpp"
+
+extern template struct trompeloeil::reporter<trompeloeil::specialized>;
+
 SCENARIO("The scene loader can load a model", "[phx][phx::SceneLoader]") {
+  OPENGL_MOCK_ALLOW_ANY_CALL
   GIVEN("An empty scene") {
     phx::Scene scene;
     WHEN("We load a model into the scene") {
diff --git a/tests/src/test_tracked_device.cpp b/tests/src/test_tracked_device.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3166d86284093d5bb116345a8e69eeabdc5d2731
--- /dev/null
+++ b/tests/src/test_tracked_device.cpp
@@ -0,0 +1,135 @@
+//------------------------------------------------------------------------------
+// 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 <memory>
+
+#include "catch/catch.hpp"
+
+#include "phx/display/hmd.hpp"
+#include "phx/input/tracked_device.hpp"
+#include "phx/input/vr_controller.hpp"
+
+#include "mocks/openvr_mock.hpp"
+
+#include "trompeloeil.hpp"
+
+#undef _
+
+extern template struct trompeloeil::reporter<trompeloeil::specialized>;
+using trompeloeil::_;
+
+SCENARIO("OpenVR is only initialized and shutdown once",
+         "[phx][phx::TrackedDevice]") {
+  OPENVR_MOCK_ALLOW_ANY_CALL
+
+  WHEN("we initialize 2 derrived tracked devices") {
+    THEN("openVR is initialized exactly once") {
+      REQUIRE_CALL(openvr_mock.Get(),
+                   VR_InitInternal2(_, vr::VRApplication_Scene, _))
+          .TIMES(1)
+          .RETURN(static_cast<uint32_t>(1337))
+          .SIDE_EFFECT(*_1 = vr::VRInitError_None);
+      auto device1 = std::make_unique<phx::HMD>();
+      auto device2 = std::make_unique<phx::VRController>(
+          phx::VRController::LEFT_CONTROLLER);
+
+      WHEN("we destroy one") {
+        THEN("openVR shutdown is not called") {
+          FORBID_CALL(openvr_mock.Get(), VR_ShutdownInternal());
+          device1.reset();
+          WHEN("we destroy the second one") {
+            THEN("openVR shutdown is called") {
+              REQUIRE_CALL(openvr_mock.Get(), VR_ShutdownInternal()).TIMES(1);
+              device2.reset();
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+#define ALLOW_EVENT_POLL_CALLS                             \
+  ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)) \
+      .SIDE_EFFECT(*_1 = event)                            \
+      .SIDE_EFFECT(*first_call = false)                    \
+      .WITH(*first_call == true)                           \
+      .RETURN(true);                                       \
+  ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)) \
+      .WITH(*first_call == false)                          \
+      .RETURN(false);
+
+class ActualDevice : public phx::TrackedDevice {
+ public:
+  ActualDevice() : TrackedDevice() { id_ = 1; }
+  void Update() override { TrackedDevice::Update(); }
+  void UpdateDeviceIndex() override {}
+  MAKE_MOCK1(OnOpenVREvent, void(const vr::VREvent_t&));
+};
+
+SCENARIO("OpenVR events are forwarded as signals to all tracked devices",
+         "[phx][phx::TrackedDevice]") {
+  OPENVR_MOCK_ALLOW_ANY_CALL
+  GIVEN("a derrived tracked device and some OpenVR event") {
+    auto device = std::make_unique<ActualDevice>();
+
+    auto first_call = std::make_shared<bool>(true);
+    vr::VREvent_t event;
+    event.eventType = vr::EVREventType::VREvent_ButtonPress;
+    WHEN("Update is called") {
+      THEN("OnOpenVREvent is called") {
+        *first_call = true;
+        ALLOW_EVENT_POLL_CALLS
+        REQUIRE_CALL(*device, OnOpenVREvent(_))
+            .WITH(_1.eventType == vr::EVREventType::VREvent_ButtonPress);
+
+        device->Update();
+      }
+    }
+    WHEN("we create a second device") {
+      auto device2 = std::make_unique<ActualDevice>();
+      THEN("the onOpenVREvent is called for both devices") {
+        *first_call = true;
+        ALLOW_EVENT_POLL_CALLS
+        REQUIRE_CALL(*device, OnOpenVREvent(_))
+            .WITH(_1.eventType == vr::EVREventType::VREvent_ButtonPress);
+        REQUIRE_CALL(*device2, OnOpenVREvent(_))
+            .WITH(_1.eventType == vr::EVREventType::VREvent_ButtonPress);
+        device->Update();
+        device2->Update();
+
+        WHEN("the first device is deleted") {
+          device.reset();
+          THEN("the second device still gets the event") {
+            *first_call = true;
+            ALLOW_EVENT_POLL_CALLS
+            REQUIRE_CALL(*device2, OnOpenVREvent(_))
+                .WITH(_1.eventType == vr::EVREventType::VREvent_ButtonPress);
+            device2->Update();
+          }
+        }
+      }
+    }
+  }
+}
+
+// the pose etc. is tested in integration_test_hmd
diff --git a/tests/src/test_tracking_system.cpp b/tests/src/test_tracking_system.cpp
index 44124c76815edf17b45827619708cb257c698973..40de8072af27d14242bdd5fca156463bedf50692 100644
--- a/tests/src/test_tracking_system.cpp
+++ b/tests/src/test_tracking_system.cpp
@@ -27,6 +27,7 @@
 
 #include "trompeloeil.hpp"
 
+#include "phx/display/hmd.hpp"
 #include "phx/suppress_warnings.hpp"
 
 SUPPRESS_WARNINGS_BEGIN
@@ -37,7 +38,7 @@ SUPPRESS_WARNINGS_END
 #include "phx/core/entity.hpp"
 #include "phx/core/runtime_component.hpp"
 #include "phx/core/scene.hpp"
-#include "phx/display/display_system_openvr.hpp"
+#include "phx/input/device_system.hpp"
 #include "phx/rendering/components/projection.hpp"
 #include "phx/rendering/components/transform.hpp"
 #include "phx/tracking/tracking_system_openvr.hpp"
@@ -325,12 +326,17 @@ SCENARIO(
   SDL_MOCK_ALLOW_ANY_CALL;
 
   phx::Engine engine;
-  auto display_system = engine.CreateSystem<phx::DisplaySystemOpenVR>();
+  auto device_system = engine.CreateSystem<phx::DeviceSystem>();
 
   GIVEN(
       "The display system has an HMD and the engine has a scene with a "
       "user platform.") {
-    display_system->CreateHMD();
+    device_system->AddDevice<phx::HMD>();
+    device_system->AddDevice<phx::VRController>(
+        phx::VRController::LEFT_CONTROLLER);
+    device_system->AddDevice<phx::VRController>(
+        phx::VRController::RIGHT_CONTROLLER);
+    device_system->Update(phx::FrameTimer::TimeInfo());
     auto first_scene = std::make_shared<phx::Scene>();
     engine.SetScene(first_scene);
     auto platform_trans_mat =
@@ -339,7 +345,7 @@ SCENARIO(
     ::SetPlatformMatrix(first_scene.get(), platform_trans_mat);
     WHEN("A tracking system is created and initialized.") {
       auto tracking_system =
-          engine.CreateSystem<phx::TrackingSystemOpenVR>(display_system);
+          engine.CreateSystem<phx::TrackingSystemOpenVR>(device_system);
 
       ::TestRuntimeEntityStructure(first_scene);
       ::TestRuntimeEntityUpdate(first_scene, tracking_system);
diff --git a/tests/src/test-transform.cpp b/tests/src/test_transform.cpp
similarity index 83%
rename from tests/src/test-transform.cpp
rename to tests/src/test_transform.cpp
index f0f8c7662b45126e0e9ccacc0091ba235a56d8fd..53f98a7a244e55569439a6905dc560048f15658a 100644
--- a/tests/src/test-transform.cpp
+++ b/tests/src/test_transform.cpp
@@ -37,9 +37,10 @@ SUPPRESS_WARNINGS_END
 #include "phx/core/scene.hpp"
 #include "phx/rendering/components/transform.hpp"
 
+#include "test_utilities/approx.hpp"
 #include "test_utilities/glm_mat4.hpp"
 #include "test_utilities/glm_quat.hpp"
-#include "test_utilities/glm_vec3.hpp"
+#include "test_utilities/glm_vec.hpp"
 #include "test_utilities/log_capture.hpp"
 
 SCENARIO(
@@ -59,14 +60,19 @@ SCENARIO(
     }
     WHEN("We query its local rotation.") {
       auto rotation = transform->GetLocalRotation();
-      THEN("It should be equal to the unit quaternion.") {
-        REQUIRE(rotation == glm::quat());
+
+      THEN("It should be equal to the identity quaternion.") {
+        // REQUIRE(rotation == glm::quat());
+        REQUIRE(rotation[0] == 0.0f);
+        REQUIRE(rotation[1] == 0.0f);
+        REQUIRE(rotation[2] == 0.0f);
+        REQUIRE(rotation[3] == 1.0f);
       }
     }
     WHEN("We query its local rotation in Euler angles.") {
       auto rotation = transform->GetLocalRotationEuler();
       THEN("It should be equal to the zero vector.") {
-        REQUIRE(rotation == glm::vec3());
+        REQUIRE(test_utilities::Approx<glm::vec3>(glm::vec3()) == rotation);
       }
     }
     WHEN("We query its local scale.") {
@@ -78,7 +84,7 @@ SCENARIO(
     WHEN("We query its local transform matrix.") {
       auto matrix = transform->GetLocalMatrix();
       THEN("It should be equal to the identity matrix.") {
-        REQUIRE(matrix == glm::mat4());
+        REQUIRE(matrix == glm::mat4(1.0f));
       }
     }
 
@@ -121,7 +127,7 @@ SCENARIO(
         THEN(
             "The local transform matrix should be equal to the identity "
             "matrix.") {
-          REQUIRE(transform->GetLocalMatrix() == glm::mat4());
+          REQUIRE(transform->GetLocalMatrix() == glm::mat4(1.0f));
         }
       }
     }
@@ -132,8 +138,12 @@ SCENARIO(
           "The rotation vector be equal to [0.43967, 0.36042, 0.82236, "
           "0.02225].") {
         auto rotation = transform->GetLocalRotation();
-        REQUIRE(test_utilities::Approx<glm::quat>(rotation) ==
-                glm::quat(0.43967f, 0.36042f, 0.82236f, 0.02225f));
+        //  REQUIRE(test_utilities::Approx<glm::quat>(rotation) ==
+        //          glm::quat(0.43967f, 0.36042f, 0.82236f, 0.02225f));
+        REQUIRE(rotation[0] == Approx(0.36042f));
+        REQUIRE(rotation[1] == Approx(0.82236f));
+        REQUIRE(rotation[2] == Approx(0.02225f));
+        REQUIRE(rotation[3] == Approx(0.43967f));
       }
       THEN(
           "The transform matrix should be equal to the corresponding "
@@ -147,12 +157,16 @@ SCENARIO(
       WHEN("We reset it.") {
         transform->Reset();
         THEN("The local rotation should be equal to the identity quaternion.") {
-          REQUIRE(transform->GetLocalRotation() == glm::quat());
+          //  REQUIRE(transform->GetLocalRotation() == glm::quat());
+          REQUIRE(transform->GetLocalRotation()[0] == 0.0f);
+          REQUIRE(transform->GetLocalRotation()[1] == 0.0f);
+          REQUIRE(transform->GetLocalRotation()[2] == 0.0f);
+          REQUIRE(transform->GetLocalRotation()[3] == 1.0f);
         }
         THEN(
             "The local transform matrix should be equal to the identity "
             "matrix.") {
-          REQUIRE(transform->GetLocalMatrix() == glm::mat4());
+          REQUIRE(transform->GetLocalMatrix() == glm::mat4(1.0f));
         }
       }
     }
@@ -177,18 +191,18 @@ SCENARIO(
       WHEN("We rotate it in Euler angles by [5.0, 10.0, 15.0].") {
         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].") {
+            "The local rotation vector should be equal to [51.351, 43.540, "
+            "85.105].") {
           auto rotation = transform->GetLocalRotationEuler();
           REQUIRE(test_utilities::Approx<glm::vec3>(rotation) ==
-                  glm::vec3(45.7610f, 44.6317f, 85.7065f));
+                  glm::vec3(51.351f, 43.540f, 85.105f));
         }
         THEN(
             "The local transform matrix should be equal to the corresponding "
             "rotation matrix.") {
-          const glm::mat4 rotation_matrix(0.05327f, 0.70964f, -0.70254f, 0.0f,
-                                          -0.65801f, 0.55414f, 0.50984f, 0.0f,
-                                          0.75112f, 0.43512f, 0.49647f, 0.0f,
+          const glm::mat4 rotation_matrix(0.06185f, 0.72225f, -0.68886f, 0.0f,
+                                          -0.57637f, 0.58931f, 0.56613f, 0.0f,
+                                          0.81484f, 0.36202f, 0.45274f, 0.0f,
                                           0.0f, 0.0f, 0.0f, 1.0f);
           REQUIRE(test_utilities::Approx<glm::mat4>(
                       transform->GetLocalMatrix()) == rotation_matrix);
@@ -197,12 +211,13 @@ SCENARIO(
       WHEN("We reset it.") {
         transform->Reset();
         THEN("The local rotation vector should be equal to the zero vector.") {
-          REQUIRE(transform->GetLocalRotationEuler() == glm::vec3());
+          REQUIRE(test_utilities::Approx<glm::vec3>(glm::vec3()) ==
+                  transform->GetLocalRotationEuler());
         }
         THEN(
             "The local transform matrix should be equal to the identity "
             "matrix.") {
-          REQUIRE(transform->GetLocalMatrix() == glm::mat4());
+          REQUIRE(transform->GetLocalMatrix() == glm::mat4(1.0f));
         }
       }
     }
@@ -243,7 +258,7 @@ SCENARIO(
         THEN(
             "The local transform matrix should be equal to the identity "
             "matrix.") {
-          REQUIRE(transform->GetLocalMatrix() == glm::mat4());
+          REQUIRE(transform->GetLocalMatrix() == glm::mat4(1.0f));
         }
       }
     }
@@ -252,12 +267,16 @@ SCENARIO(
       THEN(
           "The local rotation quaternion should be equal to the identity "
           "quaternion.") {
-        REQUIRE(transform->GetLocalRotation() == glm::quat());
+        //  REQUIRE(transform->GetLocalRotation() == glm::quat());
+        REQUIRE(transform->GetLocalRotation()[0] == 0.0f);
+        REQUIRE(transform->GetLocalRotation()[1] == 0.0f);
+        REQUIRE(transform->GetLocalRotation()[2] == 0.0f);
+        REQUIRE(transform->GetLocalRotation()[3] == 1.0f);
       }
       THEN(
           "The local transform matrix should be equal to the identity "
           "matrix.") {
-        REQUIRE(transform->GetLocalMatrix() == glm::mat4());
+        REQUIRE(transform->GetLocalMatrix() == glm::mat4(1.0f));
       }
     }
   }
@@ -266,58 +285,6 @@ SCENARIO(
 SCENARIO(
     "The transform component enables hierarchical arrangement of transforms.",
     "[phx][phx::Transform]") {
-  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();
-      THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); }
-    }
-
-    WHEN("We set the first transform's parent to the second.") {
-      child->SetParent(parent);
-
-      WHEN("We query the first transform's parent.") {
-        auto parent_ptr = child->GetParent();
-        THEN("It should equal the second transform.") {
-          REQUIRE(parent_ptr == parent);
-        }
-      }
-
-      WHEN("We query the second transform's child count.") {
-        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);
-        THEN("It should equal the first transform.") {
-          REQUIRE(child_ptr == child);
-        }
-      }
-
-      WHEN("We iterate the second transform's children.") {
-        auto iteration_count = 0;
-        for (auto it = parent->begin(); it != parent->end(); ++it)
-          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);
-        WHEN("We query the transform's parent.") {
-          auto parent_ptr = child->GetParent();
-          THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); }
-        }
-      }
-    }
-  }
   GIVEN("A hierarchy of transforms: A( B, C( D ) )") {
     auto scene = std::make_shared<phx::Scene>();
     auto entity1 = scene->CreateEntity();
@@ -371,9 +338,12 @@ SCENARIO(
     WHEN("We set A's local rotation to the (20, 30, 40) euler angles.") {
       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(test_utilities::Approx<glm::vec3>(glm::vec3()) ==
+                B->GetLocalRotationEuler());
+        REQUIRE(test_utilities::Approx<glm::vec3>(glm::vec3()) ==
+                C->GetLocalRotationEuler());
+        REQUIRE(test_utilities::Approx<glm::vec3>(glm::vec3()) ==
+                D->GetLocalRotationEuler());
       }
       THEN("The global rotation of B, C and D is (20, 30, 40).") {
         REQUIRE(
@@ -454,9 +424,9 @@ SCENARIO(
       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(1.0f));
+        REQUIRE(C->GetLocalMatrix() == glm::mat4(1.0f));
+        REQUIRE(D->GetLocalMatrix() == glm::mat4(1.0f));
       }
       THEN("The global matrix of B, C and D is the same as A's.") {
         REQUIRE(B->GetGlobalMatrix() == A->GetGlobalMatrix());
@@ -464,16 +434,17 @@ SCENARIO(
         REQUIRE(D->GetGlobalMatrix() == A->GetGlobalMatrix());
       }
       WHEN("We set C's global matrix to identity.") {
-        C->SetGlobalMatrix(glm::mat4());
+        C->SetGlobalMatrix(glm::mat4(1.0f));
         THEN("It's local matrix is the inverse of A's.") {
-          REQUIRE(C->GetLocalMatrix() == glm::inverse(A->GetLocalMatrix()));
+          REQUIRE(test_utilities::Approx<glm::mat4>(C->GetLocalMatrix()) ==
+                  glm::inverse(A->GetLocalMatrix()));
         }
         THEN("B's global matrix is still the same as A's.") {
           REQUIRE(B->GetGlobalMatrix() == A->GetGlobalMatrix());
         }
         THEN("D's global matrix is identity.") {
           REQUIRE(test_utilities::Approx<glm::mat4>(D->GetGlobalMatrix()) ==
-                  glm::mat4());
+                  glm::mat4(1.0f));
         }
 
         WHEN(
@@ -482,15 +453,15 @@ SCENARIO(
           C->SetParent(nullptr);
           THEN("C's global and local matrices are identity.") {
             REQUIRE(test_utilities::Approx<glm::mat4>(C->GetLocalMatrix()) ==
-                    glm::mat4());
+                    glm::mat4(1.0f));
             REQUIRE(test_utilities::Approx<glm::mat4>(C->GetGlobalMatrix()) ==
-                    glm::mat4());
+                    glm::mat4(1.0f));
           }
           THEN("D's global and local matrices are identity.") {
             REQUIRE(test_utilities::Approx<glm::mat4>(D->GetLocalMatrix()) ==
-                    glm::mat4());
+                    glm::mat4(1.0f));
             REQUIRE(test_utilities::Approx<glm::mat4>(D->GetGlobalMatrix()) ==
-                    glm::mat4());
+                    glm::mat4(1.0f));
           }
         }
 
diff --git a/tests/src/test_vr_controller.cpp b/tests/src/test_vr_controller.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..052a8ddd02495dd5a2902655c8b75a8a8ab5b182
--- /dev/null
+++ b/tests/src/test_vr_controller.cpp
@@ -0,0 +1,166 @@
+//------------------------------------------------------------------------------
+// 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 <memory>
+#include <string>
+
+#include "catch/catch.hpp"
+
+#include "phx/core/logger.hpp"
+#include "phx/input/vr_controller.hpp"
+
+#include "mocks/openvr_mock.hpp"
+
+#include "test_utilities/glm_vec.hpp"
+#include "test_utilities/log_capture.hpp"
+
+#include "trompeloeil.hpp"
+
+extern template struct trompeloeil::reporter<trompeloeil::specialized>;
+using trompeloeil::_;
+
+namespace {
+
+auto first_call = std::make_shared<bool>(true);
+
+void CheckForButtonEvent(uint32_t openVR_event_type,
+                         phx::VRController::ButtonEvent vr_cont_event,
+                         uint32_t button_id, const std::string& side,
+                         bool expect_to_fire,
+                         phx::VRController* vr_controller) {
+  WHEN("a " + side +
+       " openVR event (type: " + std::to_string(openVR_event_type) +
+       " button id: " + std::to_string(button_id) + " is fired") {
+    vr::VREvent_t event;
+    event.eventType = openVR_event_type;
+    event.data.controller.button = button_id;
+    event.trackedDeviceIndex =
+        (side == "left" ? 2    // in the mock 2 is given to the left controller
+                        : 1);  // in the mock 1 is given to the right controller
+    *first_call = true;
+    ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _))
+        .SIDE_EFFECT(*_1 = event)
+        .SIDE_EFFECT(*first_call = false)
+        .WITH(*first_call == true)
+        .RETURN(true);
+    ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _))
+        .WITH(*first_call == false)
+        .RETURN(false);
+    THEN("we expect the signal to " + (expect_to_fire ? "" : "not") + " fire") {
+      bool signal_fired = false;
+      vr_controller->RegisterButtonSignal(
+          [&signal_fired, vr_cont_event, button_id](
+              phx::VRController::ButtonId id,
+              phx::VRController::ButtonEvent input_event) {
+            if (input_event == vr_cont_event &&
+                id == static_cast<phx::VRController::ButtonId>(button_id))
+              signal_fired = true;
+          });
+      vr_controller->Update();
+      REQUIRE(signal_fired == expect_to_fire);
+    }
+  }
+}
+}  // namespace
+
+SCENARIO("Button click events of openVR get forwarded as signals",
+         "[phx][phx::VRController]") {
+  OPENVR_MOCK_ALLOW_ANY_CALL
+  GIVEN("a VRController") {
+    auto vr_controller =
+        std::make_unique<phx::VRController>(phx::VRController::LEFT_CONTROLLER);
+    THEN("all button events are mapped correctly") {
+      CheckForButtonEvent(vr::VREvent_ButtonPress,
+                          phx::VRController::BUTTON_PRESSED, vr::k_EButton_Grip,
+                          "left", true, vr_controller.get());
+      CheckForButtonEvent(
+          vr::VREvent_ButtonUnpress, phx::VRController::BUTTON_RELEASED,
+          vr::k_EButton_Grip, "left", true, vr_controller.get());
+      CheckForButtonEvent(vr::VREvent_ButtonTouch,
+                          phx::VRController::BUTTON_TOUCH, vr::k_EButton_Grip,
+                          "left", true, vr_controller.get());
+      CheckForButtonEvent(vr::VREvent_ButtonUntouch,
+                          phx::VRController::BUTTON_UNTOUCH, vr::k_EButton_Grip,
+                          "left", true, vr_controller.get());
+    }
+    THEN("wrong sided events are ignored") {
+      CheckForButtonEvent(vr::VREvent_ButtonPress,
+                          phx::VRController::BUTTON_PRESSED, vr::k_EButton_Grip,
+                          "right", false, vr_controller.get());
+    }
+
+    THEN(
+        "events on other buttons are ignored, respectively the correct button "
+        "id is forwarded") {
+      CheckForButtonEvent(
+          vr::VREvent_ButtonPress, phx::VRController::BUTTON_PRESSED,
+          vr::k_EButton_SteamVR_Trigger, "right", false, vr_controller.get());
+    }
+  }
+}
+
+SCENARIO("we can get the actual position on the analog axes, like the trackpad",
+         "[phx][phx::VRController]") {
+  OPENVR_MOCK_ALLOW_ANY_CALL
+  GIVEN("a VRController") {
+    auto vr_controller =
+        std::make_unique<phx::VRController>(phx::VRController::LEFT_CONTROLLER);
+
+    WHEN("for an axis is asked that does not exist for this mocked device") {
+      auto log_capture = std::make_shared<test_utilities::LogCapture>();
+      phx::logger = std::make_shared<spdlog::logger>("logcapture", log_capture);
+      phx::logger->set_pattern("%w");
+      ALLOW_CALL(openvr_mock.GetSystem(), GetControllerAxisTypeNameFromEnum(
+                                              vr::k_eControllerAxis_Trigger))
+          .RETURN("k_eControllerAxis_Trigger");
+
+      vr_controller->GetAxesValue(phx::VRController::AXES_TRIGGER);
+
+      THEN("we expect a warning") {
+        REQUIRE(*log_capture ==
+                "VRController device  does not provide the requested axes: "
+                "k_eControllerAxis_Trigger ");
+      }
+    }
+
+    WHEN("we ask for an existing axis the values are returned") {
+      ALLOW_CALL(
+          openvr_mock.GetSystem(),
+          GetControllerStateWithPose(vr::TrackingUniverseStanding, 2, _, _, _))
+          .RETURN(true)
+          .SIDE_EFFECT(_3->rAxis[0].x = 0.5f)
+          .SIDE_EFFECT(_3->rAxis[0].y = 0.7f);
+      ALLOW_CALL(openvr_mock.GetSystem(),
+                 GetInt32TrackedDeviceProperty(_, vr::Prop_Axis0Type_Int32, _))
+          .RETURN(vr::k_eControllerAxis_Trigger);
+
+      glm::vec2 value =
+          vr_controller->GetAxesValue(phx::VRController::AXES_TRIGGER);
+
+      THEN("we get the expected values") {
+        REQUIRE(value == glm::vec2(0.5f, 0.7f));
+      }
+    }
+  }
+}
+
+// TODO(JW) try also axes
diff --git a/tests/src/tests.cpp b/tests/src/tests.cpp
index 470c22fbbdaf5a1a78617debe061cf17ac89d6bb..ca1bc6a8bef06092ff36028073f3e64a8022b451 100644
--- a/tests/src/tests.cpp
+++ b/tests/src/tests.cpp
@@ -22,23 +22,3 @@
 
 #define CATCH_CONFIG_MAIN
 #include "catch/catch.hpp"
-
-#include "trompeloeil.hpp"
-
-namespace trompeloeil {
-template <>
-void reporter<specialized>::send(severity s, const char* file,  // NOLINT
-                                 unsigned long line,            // NOLINT
-                                 const char* msg) {             // NOLINT
-  std::ostringstream os;
-  if (line) os << file << ':' << line << '\n';
-  os << msg;
-  auto failure = os.str();
-  if (s == severity::fatal) {
-    FAIL(failure);
-  } else {
-    CAPTURE(failure);
-    CHECK(failure.empty());
-  }
-}
-}  // namespace trompeloeil
diff --git a/tests/test_utilities/approx.hpp b/tests/test_utilities/approx.hpp
index f09bbcaaba026bf7bad7ccfeaf8468dfe4ba3e1b..bfd3956d4824c5a64936d39a9ceb4c35922519c6 100644
--- a/tests/test_utilities/approx.hpp
+++ b/tests/test_utilities/approx.hpp
@@ -38,8 +38,6 @@ SUPPRESS_WARNINGS_END
 
 namespace test_utilities {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 template <typename T>
 class Approx {
  public:
@@ -76,8 +74,6 @@ class Approx {
   const T& value_;
 };
 
-SUPPRESS_WARNINGS_END
-
 }  // namespace test_utilities
 
 namespace Catch {
diff --git a/tests/test_utilities/dummy_mesh_generator.hpp b/tests/test_utilities/dummy_mesh_generator.hpp
index 49c73df2c9849330a259cd0883c07de04e87826c..5febf12190e0be8621034ef4bdb7cefeaab617a7 100644
--- a/tests/test_utilities/dummy_mesh_generator.hpp
+++ b/tests/test_utilities/dummy_mesh_generator.hpp
@@ -46,7 +46,7 @@ class DummyMeshLoader : public ResourceLoadStrategy {
                   std::vector<unsigned int> &&indices);
   DummyMeshLoader(const DummyMeshLoader &) = default;
   DummyMeshLoader(DummyMeshLoader &&) = default;
-  ~DummyMeshLoader() = default;
+  ~DummyMeshLoader() override = default;
 
   DummyMeshLoader &operator=(const DummyMeshLoader &) = default;
   DummyMeshLoader &operator=(DummyMeshLoader &&) = default;
diff --git a/tests/test_utilities/glm_vec3.hpp b/tests/test_utilities/glm_vec.hpp
similarity index 58%
rename from tests/test_utilities/glm_vec3.hpp
rename to tests/test_utilities/glm_vec.hpp
index d33ee272edb8b709e0fdb86e2d6f3f443d5a7f8f..2c56f39eae5c91ba404fcedf5269ada536f32e7f 100644
--- a/tests/test_utilities/glm_vec3.hpp
+++ b/tests/test_utilities/glm_vec.hpp
@@ -20,8 +20,8 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
-#ifndef TESTS_TEST_UTILITIES_GLM_VEC3_HPP_
-#define TESTS_TEST_UTILITIES_GLM_VEC3_HPP_
+#ifndef TESTS_TEST_UTILITIES_GLM_VEC_HPP_
+#define TESTS_TEST_UTILITIES_GLM_VEC_HPP_
 
 #include <algorithm>
 #include <limits>
@@ -42,6 +42,15 @@ SUPPRESS_WARNINGS_END
 
 namespace Catch {
 
+template <>
+struct StringMaker<glm::vec2> {
+  static std::string convert(const glm::vec2& vector) {
+    std::ostringstream sstr;
+    sstr << vector;
+    return sstr.str();
+  }
+};
+
 template <>
 struct StringMaker<glm::vec3> {
   static std::string convert(const glm::vec3& vector) {
@@ -51,25 +60,50 @@ struct StringMaker<glm::vec3> {
   }
 };
 
+template <>
+struct StringMaker<glm::vec4> {
+  static std::string convert(const glm::vec4& vector) {
+    std::ostringstream sstr;
+    sstr << vector;
+    return sstr.str();
+  }
+};
+
 }  // namespace Catch
 
 namespace test_utilities {
 
-template <>
-inline bool Approx<glm::vec3>::operator==(const glm::vec3& rhs) {
+template <class VectorType>
+bool CompareVectors(VectorType a, VectorType b, int rows, float eps,
+                    float margin, float scale) {
   bool result = true;
-  for (int row = 0; row < 3; ++row) {
-    const auto this_element = value_[row];
+  for (int row = 0; row < rows; ++row) {
+    const auto this_element = a[row];
     auto this_approx_element = Catch::Detail::Approx(this_element);
-    this_approx_element.epsilon(epsilon_);
-    this_approx_element.margin(margin_);
-    this_approx_element.scale(scale_);
-    const auto other_element = rhs[row];
+    this_approx_element.epsilon(eps);
+    this_approx_element.margin(margin);
+    this_approx_element.scale(scale);
+    const auto other_element = b[row];
     result &= (other_element == this_approx_element);
   }
   return result;
 }
 
+template <>
+inline bool Approx<glm::vec2>::operator==(const glm::vec2& rhs) {
+  return CompareVectors<glm::vec2>(value_, rhs, 2, epsilon_, margin_, scale_);
+}
+
+template <>
+inline bool Approx<glm::vec3>::operator==(const glm::vec3& rhs) {
+  return CompareVectors<glm::vec3>(value_, rhs, 3, epsilon_, margin_, scale_);
+}
+
+template <>
+inline bool Approx<glm::vec4>::operator==(const glm::vec4& rhs) {
+  return CompareVectors<glm::vec4>(value_, rhs, 4, epsilon_, margin_, scale_);
+}
+
 }  // namespace test_utilities
 
-#endif  // TESTS_TEST_UTILITIES_GLM_VEC3_HPP_
+#endif  // TESTS_TEST_UTILITIES_GLM_VEC_HPP_
diff --git a/tests/test_utilities/log_capture.hpp b/tests/test_utilities/log_capture.hpp
index ba70a17190eb1d0c9f1ba7da9b9cc2cea32457cf..aa0e5dddec6f8ee823877b6e032eafe98b81cf5b 100644
--- a/tests/test_utilities/log_capture.hpp
+++ b/tests/test_utilities/log_capture.hpp
@@ -34,8 +34,6 @@
 
 namespace test_utilities {
 
-SUPPRESS_WARNINGS_BEGIN_PADDED
-
 class LogCapture : public spdlog::sinks::sink {
  public:
   void log(const spdlog::details::log_msg& msg) override;
@@ -49,8 +47,6 @@ class LogCapture : public spdlog::sinks::sink {
   std::stringstream log_stream_;
 };
 
-SUPPRESS_WARNINGS_END
-
 }  // namespace test_utilities
 
 namespace Catch {
diff --git a/tests/test_utilities/opengl_buffer_data_comparison.hpp b/tests/test_utilities/opengl_buffer_data_comparison.hpp
index 5f86bb1bb79253277cd60ca51606447cfd5997d4..47c28f0cce855ac18f8f8fee696fe79518424ee6 100644
--- a/tests/test_utilities/opengl_buffer_data_comparison.hpp
+++ b/tests/test_utilities/opengl_buffer_data_comparison.hpp
@@ -35,11 +35,15 @@
 #include <tuple>
 #include <utility>
 
+#include "boost/filesystem/operations.hpp"
+#include "boost/filesystem/path.hpp"
+
 #include "catch/catch.hpp"
 
+#include "phx/core/logger.hpp"
 #include "phx/rendering/backend/opengl_image_buffer_data.hpp"
-#include "phx/resources/types/image.hpp"
 #include "phx/resources/resource_manager.hpp"
+#include "phx/resources/types/image.hpp"
 
 #include "test_utilities/reference_image_path.hpp"
 
@@ -217,9 +221,14 @@ void OpenGLBufferComparison::REQUIRE_REFERENCE_IMAGE_SIMILARITY(
       phx::ResourceUtils::DeclarationFromFile(
           filename_with_path, {{"bit_format", bit_format}}, true));
 
-  try {
-    image.Load();
-  } catch (const std::exception&) {
+  if (boost::filesystem::exists(boost::filesystem::path(filename_with_path))) {
+    try {
+      image.Load();
+    } catch (const std::exception&) {
+      phx::error(
+          "Unexpected exception while loading the reference image. File exists "
+          "but may be corrupted.");
+    }
   }
 
   similarity = ComputeSimilarity(buffer_test, image.Get());