diff --git a/library/phx/engine.cpp b/library/phx/engine.cpp index 3daf43d9f975319def2db5e1cf9e4c338d8e4c72..cf624f5649bf511076bd400694a41354051da84e 100644 --- a/library/phx/engine.cpp +++ b/library/phx/engine.cpp @@ -25,8 +25,8 @@ #include <iostream> #include <memory> #include <string> -#include <vector> #include <utility> +#include <vector> #include "phx/logger.hpp" #include "phx/scene.hpp" @@ -91,11 +91,18 @@ void Engine::SetScene(const std::shared_ptr<Scene>& new_scene) { if (scene_ != nullptr) { scene_->engine_ = nullptr; } + scene_changed_signal_(scene_, new_scene); // attach to new scene scene_ = new_scene; scene_->engine_ = this; } +boost::signals2::connection Engine::AddSceneChangedCallback( + const std::function<void(std::shared_ptr<Scene>, std::shared_ptr<Scene>)>& + callback) { + return scene_changed_signal_.connect(callback); +} + void Engine::UpdateOrder::MoveToFront(System* system) const { auto it = FindSystem(system); if (it == engine_->systems_.end()) { diff --git a/library/phx/engine.hpp b/library/phx/engine.hpp index 8144525394a1676faaa48e7d9730e64766961355..41565afaca0fff102115e2307bd6f656faf52285 100644 --- a/library/phx/engine.hpp +++ b/library/phx/engine.hpp @@ -32,6 +32,12 @@ #include <utility> #include <vector> +SUPPRESS_WARNINGS_BEGIN +#define BOOST_BIND_NO_PLACEHOLDERS +#include "boost/signals2/connection.hpp" +#include "boost/signals2/signal.hpp" +SUPPRESS_WARNINGS_END + #include "phx/behavior.hpp" #include "phx/export.hpp" #include "phx/frame_timer.hpp" @@ -89,6 +95,10 @@ class PHOENIX_EXPORT Engine final : public Loggable { void SetScene(const std::shared_ptr<Scene>& new_scene); std::shared_ptr<Scene> GetScene() const; + // Parameters to the callback are: (old scene, new scene) + boost::signals2::connection AddSceneChangedCallback( + const std::function<void(std::shared_ptr<Scene>, std::shared_ptr<Scene>)>& + callback); const FrameTimer& GetFrameTimer(); @@ -133,6 +143,8 @@ class PHOENIX_EXPORT Engine final : public Loggable { bool is_running_ = false; std::shared_ptr<Scene> scene_; + boost::signals2::signal<void(std::shared_ptr<Scene>, std::shared_ptr<Scene>)> + scene_changed_signal_; FrameTimer frame_timer_; UpdateOrder update_order_ = UpdateOrder(this); diff --git a/library/phx/tracking_system.cpp b/library/phx/tracking_system.cpp index ae836ae207255f3163e21681f2e0b1f9b5963a07..e5f74faf1bc802be1a97223fa7bbcc72ac100ca6 100644 --- a/library/phx/tracking_system.cpp +++ b/library/phx/tracking_system.cpp @@ -22,6 +22,7 @@ #include "tracking_system.hpp" +#include <memory> #include <string> #include "display_system.hpp" @@ -36,36 +37,47 @@ namespace phx { TrackingSystem::TrackingSystem(Engine* engine, DisplaySystem* display_system) : System(engine) { if (!display_system) error("TrackingSystem needs a valid DisplaySystem."); - CreateRuntimeEntities(); + if (display_system->GetHMD()) { + 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); + }); + } } -void TrackingSystem::Update(const FrameTimer::TimeInfo&) { - const auto hmd = engine_->GetSystem<DisplaySystem>()->GetHMD(); - if (hmd == nullptr) return; - hmd->UpdateTrackedDevices(); - const auto head_transformation = hmd->GetHeadTransformation(); - 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()); +TrackingSystem::~TrackingSystem() { + scene_changed_connection_.disconnect(); } -void TrackingSystem::CreateRuntimeEntities() { +void TrackingSystem::Update(const FrameTimer::TimeInfo&) { const auto hmd = engine_->GetSystem<DisplaySystem>()->GetHMD(); if (hmd == nullptr) { return; } - auto scene = engine_->GetScene(); + 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()); + } +} + +void TrackingSystem::CreateRuntimeEntities(Scene* scene) { if (scene == nullptr) { return; } @@ -108,6 +120,20 @@ void TrackingSystem::CreateRuntimeEntities() { } } +void TrackingSystem::RemoveRuntimeEntities(Scene* scene) { + scene->RemoveEntity(hmd_entity_); + scene->RemoveEntity(left_eye_entity_); + scene->RemoveEntity(right_eye_entity_); + scene->RemoveEntity(left_controller_entity_); + scene->RemoveEntity(right_controller_entity_); +} + +void TrackingSystem::OnSceneChanged(std::shared_ptr<Scene> old_scene, + std::shared_ptr<Scene> new_scene) { + RemoveRuntimeEntities(old_scene.get()); + CreateRuntimeEntities(new_scene.get()); +} + std::string TrackingSystem::ToString() const { return "Tracking System"; } } // namespace phx diff --git a/library/phx/tracking_system.hpp b/library/phx/tracking_system.hpp index 4022d7e7f2c28358e62a4d665862f84ea1228751..c0bab8a27d5f400001ca2c0d8bac204f98e61f61 100644 --- a/library/phx/tracking_system.hpp +++ b/library/phx/tracking_system.hpp @@ -23,6 +23,7 @@ #ifndef LIBRARY_PHX_TRACKING_SYSTEM_HPP_ #define LIBRARY_PHX_TRACKING_SYSTEM_HPP_ +#include <memory> #include <string> #include "phx/display_system.hpp" @@ -30,6 +31,11 @@ #include "phx/export.hpp" #include "phx/system.hpp" +SUPPRESS_WARNINGS_BEGIN +#define BOOST_BIND_NO_PLACEHOLDERS +#include "boost/signals2/connection.hpp" +SUPPRESS_WARNINGS_END + namespace phx { class PHOENIX_EXPORT TrackingSystem : public System { @@ -37,7 +43,7 @@ class PHOENIX_EXPORT TrackingSystem : public System { TrackingSystem() = delete; TrackingSystem(const TrackingSystem&) = delete; TrackingSystem(TrackingSystem&&) = default; - ~TrackingSystem() = default; + ~TrackingSystem(); TrackingSystem& operator=(const TrackingSystem&) = delete; TrackingSystem& operator=(TrackingSystem&&) = default; @@ -50,17 +56,22 @@ class PHOENIX_EXPORT TrackingSystem : public System { TrackingSystem(Engine* engine, DisplaySystem* display_system); private: - void CreateRuntimeEntities(); - template <typename SystemType, typename... SystemArguments> friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments); explicit TrackingSystem(Engine* engine); + void CreateRuntimeEntities(Scene* scene); + void RemoveRuntimeEntities(Scene* scene); + void OnSceneChanged(std::shared_ptr<Scene> old_scene, + std::shared_ptr<Scene> new_scene); + Entity* hmd_entity_ = nullptr; Entity* left_eye_entity_ = nullptr; Entity* right_eye_entity_ = nullptr; Entity* left_controller_entity_ = nullptr; Entity* right_controller_entity_ = nullptr; + + boost::signals2::connection scene_changed_connection_; }; } // namespace phx diff --git a/tests/src/test_engine.cpp b/tests/src/test_engine.cpp index c7a97c9bc72ef5c6385656e98901ca69d4a0f39c..c7a614e34b941f7b288f0ae84f392efc89da5f25 100644 --- a/tests/src/test_engine.cpp +++ b/tests/src/test_engine.cpp @@ -177,11 +177,26 @@ SCENARIO("The active scene in an engine can be switched", GIVEN("An engine") { phx::Engine engine; auto first_scene = std::make_shared<phx::Scene>(); + + // setup a way to test the scene changed signal + phx::Scene* s1; + phx::Scene* s2; + engine.AddSceneChangedCallback( + [&s1, &s2](std::shared_ptr<phx::Scene> param_s1, + std::shared_ptr<phx::Scene> param_s2) { + s1 = param_s1.get(); + s2 = param_s2.get(); + }); + engine.SetScene(first_scene); THEN("It has a scene") { REQUIRE(engine.GetScene() != nullptr); REQUIRE(engine.GetScene()->GetEngine() == &engine); } + THEN("The old scene is nullptr.") { REQUIRE(s1 == nullptr); } + THEN("The new scene is the one we just set.") { + REQUIRE(s2 == first_scene.get()); + } WHEN("The scene is exchanged for a new scene") { auto new_scene = std::make_shared<phx::Scene>(); engine.SetScene(new_scene); @@ -194,6 +209,10 @@ SCENARIO("The active scene in an engine can be switched", REQUIRE(first_scene->GetEngine() == nullptr); } + THEN("The signal is triggered with the appropriate values.") { + REQUIRE(s1 == first_scene.get()); + REQUIRE(s2 == new_scene.get()); + } } } } diff --git a/tests/src/test_tracking_system.cpp b/tests/src/test_tracking_system.cpp index ea6f58a16e8e944ec443a99d475d82d88192aeff..86fde97792a1afc3ed413da25e322de0f731a728 100644 --- a/tests/src/test_tracking_system.cpp +++ b/tests/src/test_tracking_system.cpp @@ -46,8 +46,8 @@ using trompeloeil::ne; extern template struct trompeloeil::reporter<trompeloeil::specialized>; -void SetGlmToArrayMatrix_3_4(glm::mat4 m, float* array); -void SetGlmToArrayMatrix_3_4(glm::mat4 m, float* array) { +void SetGlmToArrayMatrix_3_4(glm::mat4 m, float *array); +void SetGlmToArrayMatrix_3_4(glm::mat4 m, float *array) { std::size_t index = 0; for (int x = 0; x < 3; x++) { for (int y = 0; y < 4; y++) { @@ -56,6 +56,341 @@ void SetGlmToArrayMatrix_3_4(glm::mat4 m, float* array) { } } +template <typename ComponentType> +bool HasComponent(phx::Entity *entity) { + return entity->GetFirstComponent<ComponentType>() != nullptr; +} + +bool HasUserPlatform(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>(entity); +} + +bool HasHMD(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::HEAD>>(entity); +} + +bool HasTransform(phx::Entity *entity) { + return HasComponent<phx::Transform>(entity); +} + +bool HasLeftEye(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::LEFT_EYE>>(entity); +} + +bool HasRightEye(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::RIGHT_EYE>>(entity); +} + +bool HasProjection(phx::Entity *entity) { + return HasComponent<phx::Projection>(entity); +} + +bool HasLeftController(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::LEFT_CONTROLLER>>(entity); +} + +bool HasRightController(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>(entity); +} + +bool ValidateUserPlatform(phx::Entity *entity, bool platform_present) { + if (HasUserPlatform(entity)) { + THEN("There is only one virtual platform.") { + REQUIRE(platform_present == false); + } + return true; + } + return false; +} + +bool ValidateHMD(phx::Entity *entity, bool hmd_present) { + if (HasHMD(entity)) { + THEN("There is only one HMD.") { REQUIRE(hmd_present == false); } + THEN("The HMD has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN( + "The parent of the HMD's transform is the platform's " + "transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE( + entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>()); + } + return true; + } + return false; +} + +bool ValidateLeftEye(phx::Entity *entity, bool left_eye_present) { + if (HasLeftEye(entity)) { + THEN("There is only one left eye.") { REQUIRE(left_eye_present == false); } + THEN("The left eye has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN("The left eye has a projection component.") { + REQUIRE(HasProjection(entity)); + } + THEN( + "The parent of the left eye's transform is the HMD's " + "transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE(entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()); + } + return true; + } + return false; +} + +bool ValidateRightEye(phx::Entity *entity, bool right_eye_present) { + if (HasRightEye(entity)) { + THEN("There is only one right eye.") { + REQUIRE(right_eye_present == false); + } + THEN("The right eye has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN("The right eye has a projection component.") { + REQUIRE(HasProjection(entity)); + } + THEN( + "The parent of the right eye's transform is the HMD's " + "transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE(entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()); + } + return true; + } + return false; +} + +bool ValidateLeftController(phx::Entity *entity, bool left_controller_present) { + if (HasLeftController(entity)) { + THEN("There is only one left controller.") { + REQUIRE(left_controller_present == false); + } + THEN("The left controller has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN( + "The parent of the left controller's transform is the " + "platform's transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE( + entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>()); + } + return true; + } + return false; +} + +bool ValidateRightController(phx::Entity *entity, + bool right_controller_present) { + if (HasRightController(entity)) { + THEN("There is only one right controller.") { + REQUIRE(right_controller_present == false); + } + THEN("The right controller has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN( + "The parent of the right controller's transform is the " + "platform's transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE( + entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>()); + } + return true; + } + return false; +} + +bool ValidatePlatformTransform(phx::Entity *entity, + glm::mat4 platform_trans_mat) { + if (HasUserPlatform(entity)) { + THEN("The platform transformation did not change.") { + REQUIRE( + test_utilities::Approx<glm::mat4>( + entity->GetFirstComponent<phx::Transform>()->GetLocalMatrix()) == + platform_trans_mat); + } + return true; + } + return false; +} + +bool ValidateHMDTransform(phx::Entity *entity, glm::mat4 hmd_trans_mat) { + if (HasHMD(entity)) { + THEN( + "The hmd transformation changes to the one provided by " + "OpenVR.") { + REQUIRE( + test_utilities::Approx<glm::mat4>( + entity->GetFirstComponent<phx::Transform>()->GetLocalMatrix()) == + hmd_trans_mat); + } + return true; + } + return false; +} + +bool ValidateLeftEyeTransform(phx::Entity *entity, + glm::mat4 left_eye_trans_mat) { + if (HasLeftEye(entity)) { + THEN( + "The left eye transformation changes to the one provided " + "by OpenVR.") { + REQUIRE( + test_utilities::Approx<glm::mat4>( + entity->GetFirstComponent<phx::Transform>()->GetLocalMatrix()) == + left_eye_trans_mat); + } + return true; + } + return false; +} + +bool ValidateRightEyeTransform(phx::Entity *entity, + glm::mat4 right_eye_trans_mat) { + if (HasRightEye(entity)) { + THEN( + "The right eye transformation changes to the one provided " + "by OpenVR.") { + REQUIRE( + test_utilities::Approx<glm::mat4>( + entity->GetFirstComponent<phx::Transform>()->GetLocalMatrix()) == + right_eye_trans_mat); + } + return true; + } + return false; +} + +void TestRuntimeEntityStructure(std::shared_ptr<phx::Scene> scene) { + THEN("The HMD and the user's eyes are represented in the scene.") { + auto entities = scene->GetEntitiesWithComponents<phx::Transform>(); + bool platform_present = false; + bool hmd_present = false; + bool left_eye_present = false; + bool right_eye_present = false; + bool left_controller_present = false; + bool right_controller_present = false; + + for (auto entity : entities) { + platform_present |= ValidateUserPlatform(entity, platform_present); + + hmd_present |= ValidateHMD(entity, hmd_present); + + left_eye_present |= ValidateLeftEye(entity, left_eye_present); + right_eye_present |= ValidateRightEye(entity, right_eye_present); + + left_controller_present |= + ValidateLeftController(entity, left_controller_present); + right_controller_present |= + ValidateLeftController(entity, right_controller_present); + } + + THEN( + "There are six entities: The platform, the HMD, the left and " + "the right eyes, the left and right controllers.") { + REQUIRE(entities.size() == 6); + REQUIRE(platform_present); + REQUIRE(hmd_present); + REQUIRE(left_eye_present); + REQUIRE(right_eye_present); + REQUIRE(left_controller_present); + REQUIRE(right_controller_present); + } + } +} + +void InitMatrices(glm::mat4 *platform_trans_mat, glm::mat4 *hmd_trans_mat, + glm::mat4 *left_eye_trans_mat, + glm::mat4 *right_eye_trans_mat) { + *platform_trans_mat = + glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 1.0f, -1.0f, 0.5f, 1.0f); + *hmd_trans_mat = glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.8f, 0.2f, 1.0f); + *left_eye_trans_mat = + glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, -0.04f, 0.0f, 0.0f, 1.0f); + *right_eye_trans_mat = + glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.04f, 0.0f, 0.0f, 1.0f); + SetGlmToArrayMatrix_3_4(*hmd_trans_mat, openvr_mock.head_transformation_); + SetGlmToArrayMatrix_3_4(*left_eye_trans_mat, openvr_mock.eye_to_head_left_); + SetGlmToArrayMatrix_3_4(*right_eye_trans_mat, openvr_mock.eye_to_head_right_); +} + +void TestRuntimeEntityUpdate(std::shared_ptr<phx::Scene> scene, + phx::TrackingSystem *tracking_system) { + glm::mat4 platform_trans_mat, hmd_trans_mat, left_eye_trans_mat, + right_eye_trans_mat; + InitMatrices(&platform_trans_mat, &hmd_trans_mat, &left_eye_trans_mat, + &right_eye_trans_mat); + + WHEN("The tracking system is updated.") { + tracking_system->Update(phx::FrameTimer::TimeInfo()); + THEN( + "It sets the transforms of all the runtime entities to the " + "correct values.") { + auto entities = scene->GetEntitiesWithComponents<phx::Transform>(); + bool platform_present = false; + bool hmd_present = false; + bool left_eye_present = false; + bool right_eye_present = false; + bool left_controller_present = false; + bool right_controller_present = false; + + for (auto entity : entities) { + platform_present |= + ValidatePlatformTransform(entity, platform_trans_mat); + + hmd_present |= ValidateHMDTransform(entity, hmd_trans_mat); + + left_eye_present |= + ValidateLeftEyeTransform(entity, left_eye_trans_mat); + right_eye_present |= + ValidateRightEyeTransform(entity, right_eye_trans_mat); + + left_controller_present |= HasLeftController(entity); + right_controller_present |= HasRightController(entity); + } + THEN( + "There are still six entities: The platform, the HMD, the " + "left and the right eyes, the left and the right controllers.") { + REQUIRE(entities.size() == 6); + REQUIRE(platform_present); + REQUIRE(hmd_present); + REQUIRE(left_eye_present); + REQUIRE(right_eye_present); + REQUIRE(left_controller_present); + REQUIRE(right_controller_present); + } + } + } +} + SCENARIO( "The tracking system tracks hardware and updates their software " "counterparts.", @@ -70,12 +405,12 @@ SCENARIO( "The display system has an HMD and the engine has a scene with a " "user platform.") { display_system->CreateHMD(); - auto scene = std::make_shared<phx::Scene>(); - engine.SetScene(scene); + auto first_scene = std::make_shared<phx::Scene>(); + engine.SetScene(first_scene); auto platform_trans_mat = glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.5f, 1.0f); - scene + first_scene ->GetEntitiesWithComponents< phx::RuntimeComponent<phx::USER_PLATFORM>>()[0] ->GetFirstComponent<phx::Transform>() @@ -83,239 +418,50 @@ SCENARIO( WHEN("A tracking system is created and initialized.") { auto tracking_system = engine.CreateSystem<phx::TrackingSystem>(display_system); - THEN("The HMD and the user's eyes are represented in the scene.") { - auto entities = scene->GetEntitiesWithComponents<phx::Transform>(); - bool platform_present = false; - bool hmd_present = false; - bool left_eye_present = false; - bool right_eye_present = false; - bool left_controller_present = false; - bool right_controller_present = false; - for (auto entity : entities) { - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()) { - THEN("There is only one virtual platform.") { - REQUIRE(platform_present == false); - } - platform_present = true; - } - if (entity->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()) { - THEN("There is only one HMD.") { REQUIRE(hmd_present == false); } - THEN("The HMD has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN( - "The parent of the HMD's transform is the platform's " - "transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE(entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()); - } - hmd_present = true; - } - if (entity - ->GetFirstComponent<phx::RuntimeComponent<phx::LEFT_EYE>>()) { - THEN("There is only one left eye.") { - REQUIRE(left_eye_present == false); - } - THEN("The left eye has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN("The left eye has a projection component.") { - REQUIRE(entity->GetFirstComponent<phx::Projection>()); - } - THEN( - "The parent of the left eye's transform is the HMD's " - "transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE( - entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()); - } - left_eye_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::RIGHT_EYE>>()) { - THEN("There is only one right eye.") { - REQUIRE(right_eye_present == false); - } - THEN("The right eye has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN("The right eye has a projection component.") { - REQUIRE(entity->GetFirstComponent<phx::Projection>()); - } - THEN( - "The parent of the right eye's transform is the HMD's " - "transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE( - entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()); - } - right_eye_present = true; - } - - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::LEFT_CONTROLLER>>()) { - THEN("There is only one left controller.") { - REQUIRE(left_controller_present == false); - } - THEN("The left controller has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN( - "The parent of the left controller's transform is the " - "platform's transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE(entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()); - } - left_controller_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()) { - THEN("There is only one right controller.") { - REQUIRE(right_controller_present == false); - } - THEN("The right controller has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN( - "The parent of the right controller's transform is the " - "platform's transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE(entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()); - } - right_controller_present = true; - } - } - THEN( - "There are six entities: The platform, the HMD, the left and " - "the right eyes, the left and right controllers.") { - REQUIRE(entities.size() == 6); - REQUIRE(platform_present); - REQUIRE(hmd_present); - REQUIRE(left_eye_present); - REQUIRE(right_eye_present); - REQUIRE(left_controller_present); - REQUIRE(right_controller_present); - } - } - WHEN("The tracking system is then updated.") { - auto hmd_trans_mat = - glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.5f, 1.8f, 0.2f, 1.0f); - auto left_eye_trans_mat = - glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, -0.04f, 0.0f, 0.0f, 1.0f); - auto right_eye_trans_mat = - glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.04f, 0.0f, 0.0f, 1.0f); - SetGlmToArrayMatrix_3_4(hmd_trans_mat, - openvr_mock.head_transformation_); - SetGlmToArrayMatrix_3_4(left_eye_trans_mat, - openvr_mock.eye_to_head_left_); - SetGlmToArrayMatrix_3_4(right_eye_trans_mat, - openvr_mock.eye_to_head_right_); - tracking_system->Update(phx::FrameTimer::TimeInfo()); + + TestRuntimeEntityStructure(first_scene); + TestRuntimeEntityUpdate(first_scene, tracking_system); + + WHEN("The scene is then changed.") { + auto new_scene = std::make_shared<phx::Scene>(); + new_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::USER_PLATFORM>>()[0] + ->GetFirstComponent<phx::Transform>() + ->SetLocalMatrix(platform_trans_mat); + engine.SetScene(new_scene); + THEN( - "It sets the transforms of all the runtime entities to the " - "correct values.") { - auto entities = scene->GetEntitiesWithComponents<phx::Transform>(); - bool platform_present = false; - bool hmd_present = false; - bool left_eye_present = false; - bool right_eye_present = false; - bool left_controller_present = false; - bool right_controller_present = false; - for (auto entity : entities) { - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()) { - THEN("The platform transformation did not change.") { - REQUIRE(test_utilities::Approx<glm::mat4>( - entity->GetFirstComponent<phx::Transform>() - ->GetLocalMatrix()) == platform_trans_mat); - } - platform_present = true; - } - if (entity->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()) { - THEN( - "The hmd transformation changes to the one provided by " - "OpenVR.") { - REQUIRE(test_utilities::Approx<glm::mat4>( - entity->GetFirstComponent<phx::Transform>() - ->GetLocalMatrix()) == hmd_trans_mat); - } - hmd_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::LEFT_EYE>>()) { - THEN( - "The left eye transformation changes to the one provided " - "by OpenVR.") { - REQUIRE(test_utilities::Approx<glm::mat4>( - entity->GetFirstComponent<phx::Transform>() - ->GetLocalMatrix()) == left_eye_trans_mat); - } - left_eye_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::RIGHT_EYE>>()) { - THEN( - "The right eye transformation changes to the one provided " - "by OpenVR.") { - REQUIRE(test_utilities::Approx<glm::mat4>( - entity->GetFirstComponent<phx::Transform>() - ->GetLocalMatrix()) == right_eye_trans_mat); - } - right_eye_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::LEFT_CONTROLLER>>()) { - left_controller_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()) { - right_controller_present = true; - } - } - THEN( - "There are still six entities: The platform, the HMD, the " - "left and the right eyes, the left and the right controllers.") { - REQUIRE(entities.size() == 6); - REQUIRE(platform_present); - REQUIRE(hmd_present); - REQUIRE(left_eye_present); - REQUIRE(right_eye_present); - REQUIRE(left_controller_present); - REQUIRE(right_controller_present); - } + "The first scene does not contain runtime entities except for " + "exactly one user platform.") { + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::USER_PLATFORM>>() + .size() == 1); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::HEAD>>() + .size() == 0); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::LEFT_EYE>>() + .size() == 0); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::RIGHT_EYE>>() + .size() == 0); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::LEFT_CONTROLLER>>() + .size() == 0); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>() + .size() == 0); } + + TestRuntimeEntityStructure(new_scene); + TestRuntimeEntityUpdate(new_scene, tracking_system); } } }