diff --git a/demos/combustion_demo/src/combustion_demo.cpp b/demos/combustion_demo/src/combustion_demo.cpp
index 4aaf9f7a6ef0fe802a4e99c67e717eaa47b429de..ab7bedcef95122ba366b7797bb0f60e947c649c9 100644
--- a/demos/combustion_demo/src/combustion_demo.cpp
+++ b/demos/combustion_demo/src/combustion_demo.cpp
@@ -33,6 +33,7 @@
#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"
@@ -47,11 +48,12 @@
#endif
int main(int, char**) {
- std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(false);
+ 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>();
phx::SplashScreen* splash = engine->CreateSystem<phx::SplashScreen>(
engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow());
@@ -70,15 +72,17 @@ int main(int, char**) {
if (key == 'q') engine->Stop();
});
- auto virtual_platform = scene->GetEntitiesWithComponents<
- phx::RuntimeComponent<phx::USER_PLATFORM>>()[0];
- // virtual_platform->AddComponent<DesktopNavigationBehavior>(
- // engine->GetSystem<phx::InputSystem>());
- auto controller_navigation_behavior =
- virtual_platform->AddComponent<VRControllerInteractionBehavior>(
- openvr_system);
+ 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([&controller_navigation_behavior, &scene,
+ auto handle = std::async([&right_interaction_behavior, &scene,
rendering_system, splash, input_system,
openvr_system]() {
auto model_surface_entity = phx::SceneLoader::InsertModelIntoScene(
@@ -133,7 +137,8 @@ int main(int, char**) {
glm::vec3(1.0f, 1.0f, 1.0f));
floor_material->GetMaterial()->SetAmbientColor(glm::vec3(0.1f, 0.1f, 0.1f));
- controller_navigation_behavior->SetTarget(vis_root_transform);
+ if (right_interaction_behavior)
+ right_interaction_behavior->SetTarget(vis_root_transform);
auto desk_entity = phx::SceneLoader::InsertModelIntoScene(
"models/cube/cube2.obj", scene.get());
@@ -199,6 +204,8 @@ int main(int, char**) {
if (!openvr_system) {
camera->AddComponent<DesktopNavigationBehavior>(input_system);
+ camera->GetFirstComponent<phx::Transform>()->Translate(
+ glm::vec3(0.0f, 1.0f, 1.0f));
}
engine->Run();
diff --git a/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp b/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp
index a7d7e2b0fe6357b0c3db50b1d45a35ab56143723..6d113a15013a3ef318979fae5e1fbf739d8451ae 100644
--- a/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp
+++ b/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp
@@ -41,47 +41,31 @@ SUPPRESS_WARNINGS_END
#include "phx/rendering/components/transform.hpp"
VRControllerInteractionBehavior::VRControllerInteractionBehavior(
- phx::DisplaySystemOpenVR* display_system_openvr)
- : display_system_openvr_(display_system_openvr) {}
+ phx::DeviceSystem* device_system)
+ : device_system_(device_system) {}
-void VRControllerInteractionBehavior::OnUpdate() {
- // If there exists an HMD and a transform.
- phx::HMD* hmd = nullptr;
- if (display_system_openvr_ != nullptr) {
- hmd = display_system_openvr_->GetHMD();
- }
+void VRControllerInteractionBehavior::OnButtonSignal(
+ phx::VRController::ButtonId id, phx::VRController::ButtonEvent event) {
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);
-
- if (target_) {
- if (controller_state.ulButtonPressed &
- vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_SteamVR_Trigger)) {
- target_->SetParent(
- GetEntity()
- ->GetScene()
- ->GetEntitiesWithComponents<
- phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()[0]
- ->GetFirstComponent<phx::Transform>());
- is_grabbed_ = true;
- release_delay_ = 100;
- } else if (is_grabbed_) {
- release_delay_--;
- }
- if (release_delay_ == 0) {
- target_->SetParent(nullptr);
- is_grabbed_ = false;
- }
+ 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;
}
@@ -89,3 +73,33 @@ void VRControllerInteractionBehavior::SetTarget(phx::Transform* 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
index 6ed6f309f1cb452e1e330c06ea634ae5ed5c516b..c66c364edaf9e77ec3f5dd6938facee53789c175 100644
--- a/demos/combustion_demo/src/vr_controller_interaction_behavior.hpp
+++ b/demos/combustion_demo/src/vr_controller_interaction_behavior.hpp
@@ -23,14 +23,14 @@
#ifndef DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_INTERACTION_BEHAVIOR_HPP_
#define DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_INTERACTION_BEHAVIOR_HPP_
-#include "phx/display/display_system_openvr.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::DisplaySystemOpenVR* display_system_openvr);
+ explicit VRControllerInteractionBehavior(phx::DeviceSystem* device_system);
VRControllerInteractionBehavior(const VRControllerInteractionBehavior& that) =
default;
VRControllerInteractionBehavior(VRControllerInteractionBehavior&& temp) =
@@ -42,17 +42,21 @@ class VRControllerInteractionBehavior : public phx::Behavior {
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::DisplaySystemOpenVR* display_system_openvr_;
+ phx::DeviceSystem* device_system_;
private:
+ void RegisterOnDeviceSignal();
+
phx::Transform* target_ = nullptr;
- bool is_grabbed_ = false;
- int release_delay_ = 0;
+ 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 d8d6fe5a575496de37b140e615c349bb0ee62fa0..a43834aba9cadeb24c0425e9c2373a7b4026f78d 100644
--- a/demos/viewer/src/viewer.cpp
+++ b/demos/viewer/src/viewer.cpp
@@ -22,10 +22,7 @@
#include <chrono>
#include <future>
-#include <iostream>
#include <memory>
-#include <string>
-#include <utility>
#include <vector>
#include "phx/core/engine.hpp"
@@ -36,47 +33,40 @@
#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/material_handle.hpp"
-#include "phx/rendering/components/mesh_handle.hpp"
#include "phx/rendering/components/transform.hpp"
#include "phx/rendering/rendering_system.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/types/mesh.hpp"
-#include "phx/resources/resource_declaration.hpp"
#include "phx/resources/resource_manager.hpp"
-#include "phx/resources/resource_pointer.hpp"
-#include "phx/phoenix.hpp"
#include "phx/setup.hpp"
-#include "navigation_behavior.hpp"
-#include "rotation_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(false);
+ 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 = engine->CreateSystem<phx::SplashScreen>(
- engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow());
+ engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow());
engine->MoveSystemBefore(splash,
- engine->GetSystem<phx::DisplaySystemWindow>());
+ engine->GetSystem<phx::DisplaySystemWindow>());
auto assimp_loader = static_cast<phx::AssimpModelLoader*>(
- phx::ResourceManager::instance().GetLoaderForType(".obj"));
+ phx::ResourceManager::instance().GetLoaderForType(".obj"));
assimp_loader->SetProgressUpdateCallback(
- [splash](float progress) { splash->SetLoadProgress(progress); });
+ [splash](float progress) { splash->SetLoadProgress(progress); });
phx::InputSystem* input_system = engine->GetSystem<phx::InputSystem>();
ViewerSystem* viewer_system = engine->CreateSystem<ViewerSystem>();
@@ -88,8 +78,8 @@ int main(int, char**) {
});
auto handle = std::async([&scene, rendering_system, splash]() {
- phx::SceneLoader::InsertModelIntoScene(
- "models/UniversityScene/Univers20171013.obj", scene.get());
+ phx::SceneLoader::InsertModelIntoScene("models/bunny.obj", scene.get());
+ // "models/UniversityScene/Univers20171013.obj"
rendering_system->SetEnabled(true);
splash->SetEnabled(false);
@@ -124,8 +114,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/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/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 eb1c50dace48a0a776c12e78e6e823f779393237..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 {
@@ -45,17 +45,12 @@ class PHOENIX_EXPORT DisplaySystemOpenVR : public DisplaySystem {
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/hmd.cpp b/library/phx/display/hmd.cpp
index 437a090372151dc070cfd88c423cd44d4fd8407c..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,145 +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, nullptr, 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();
+ TrackedDevice::Update();
}
-glm::mat4 HMD::GetLeftControllerTransformation() {
- return GetTransformationForRole(vr::TrackedControllerRole_LeftHand);
-}
-
-glm::mat4 HMD::GetRightControllerTransformation() {
- return GetTransformationForRole(vr::TrackedControllerRole_RightHand);
-}
-
-std::unique_ptr<phx::Mesh> HMD::GetControllerMesh(Controller controller) {
- auto model = GetControllerModel(controller);
- 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]);
+void HMD::OnOpenVREvent(const vr::VREvent_t& event) {
+ if (event.eventType == vr::VREvent_IpdChanged) {
+ UpdateEyeToHeadMatrices();
}
- 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));
- material->SetShininess(1.0f);
-
- 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) {
@@ -259,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..be7f5f0f6398c3a2d03ad65af177873e5e9c9628 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,45 +60,25 @@ 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_;
@@ -106,9 +86,6 @@ class PHOENIX_EXPORT HMD {
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_;
};
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/demos/viewer/src/navigation_behavior.hpp b/library/phx/input/device.hpp
similarity index 57%
rename from demos/viewer/src/navigation_behavior.hpp
rename to library/phx/input/device.hpp
index aee9accca37a44597cd536ae96edbcb5d6471ab4..c477639764a905efdc712809d5a74034be7aaf98 100644
--- a/demos/viewer/src/navigation_behavior.hpp
+++ b/library/phx/input/device.hpp
@@ -20,25 +20,19 @@
// limitations under the License.
//------------------------------------------------------------------------------
-#ifndef DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_
-#define DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_
+#ifndef LIBRARY_PHX_INPUT_DEVICE_HPP_
+#define LIBRARY_PHX_INPUT_DEVICE_HPP_
-#include "phx/display/display_system_openvr.hpp"
-#include "phx/scripting/behavior.hpp"
+#include "phx/export.hpp"
-class NavigationBehavior : public phx::Behavior {
- public:
- explicit NavigationBehavior(phx::DisplaySystemOpenVR* display_system_openvr);
- NavigationBehavior(const NavigationBehavior& that) = default;
- NavigationBehavior(NavigationBehavior&& temp) = default;
- ~NavigationBehavior() override = default;
- NavigationBehavior& operator=(const NavigationBehavior& that) = default;
- NavigationBehavior& operator=(NavigationBehavior&& temp) = default;
-
- void OnUpdate() override;
+namespace phx {
- protected:
- phx::DisplaySystemOpenVR* display_system_openvr_;
+class PHOENIX_EXPORT Device {
+ public:
+ virtual ~Device() = default;
+ virtual void Update() = 0;
};
-#endif // DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_
+} // namespace phx
+
+#endif // LIBRARY_PHX_INPUT_DEVICE_HPP_
diff --git a/library/phx/input/device_system.cpp b/library/phx/input/device_system.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c19d7947ef0a3d69cfecb728c33ddf9de591405a
--- /dev/null
+++ b/library/phx/input/device_system.cpp
@@ -0,0 +1,53 @@
+//------------------------------------------------------------------------------
+// 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_system.hpp"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "phx/core/logger.hpp"
+
+namespace phx {
+DeviceSystem::DeviceSystem(Engine* engine) : System(engine) {}
+
+DeviceSystem::~DeviceSystem() {}
+
+void DeviceSystem::Update(const FrameTimer::TimeInfo&) {
+ for (auto& device : devices_) {
+ device->Update();
+ }
+}
+
+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/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 99483e41811c55be517c7eb80fde13b3d20d5153..9f8ce400b7a42d04937543ca2daf4df505361814 100644
--- a/library/phx/input/openvr_controller_behavior.hpp
+++ b/library/phx/input/openvr_controller_behavior.hpp
@@ -26,20 +26,19 @@
#include "phx/suppress_warnings.hpp"
#include "phx/export.hpp"
+#include "phx/input/vr_controller.hpp"
#include "phx/scripting/behavior.hpp"
namespace phx {
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;
};
} // namespace phx
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 eecf2be7c87c8437f68888ac9670a81bb7123009..643328fc5d8d7169f2b312a6ea9573d3dda354b2 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/material_handle.hpp"
#include "phx/rendering/components/mesh_handle.hpp"
#include "phx/rendering/components/transform.hpp"
@@ -38,18 +39,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));
}
@@ -58,11 +52,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}};
@@ -82,9 +76,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<MeshHandle>()->SetMesh(mesh);
controller->AddComponent<Transform>();
@@ -98,10 +92,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;
@@ -112,31 +105,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 607f72a46f02a0db1a33f1457ada5583f27f3e97..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;
- ~OpenVRControllerSystem() override = 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/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/setup.cpp b/library/phx/setup.cpp
index 0c0a862450954383f9621a8cc40f27a5473e5449..895d4eb3251b1257004b50e5222aeb558c984487 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,7 +38,7 @@
#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"
@@ -56,16 +58,21 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine(bool use_hmd_if_available) {
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() && 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 +86,24 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine(bool use_hmd_if_available) {
// 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>());
+ 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_hmd->CreateRenderTargets(engine->GetScene().get());
+ 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);
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 d2cd2172a18097ebe5c7d6d48c7720993663a9b1..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,6 +39,7 @@ SUPPRESS_WARNINGS_BEGIN
SUPPRESS_WARNINGS_END
namespace phx {
+class DeviceSystem;
class PHOENIX_EXPORT TrackingSystemOpenVR : public System {
public:
@@ -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/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b1ed5f2b63fde5bf248365046d9842d0bcb17323..f63ed905d8e76ba508e35cf4c75092d0c56031d9 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -173,73 +173,25 @@ add_test_cpplint(NAME "phoenix-tests--cpplint"
# 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_system
- LIBRARIES phoenix
- MOCKS opengl_mock openvr_mock sdl_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
-)
-
-
+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)
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/src/integration_test_hmd.cpp b/tests/src/integration_test_hmd.cpp
index 7babf1ca229f2eb11ea665b3b9ce7ba0b4e2c82d..ee8600182ee2cb73f9084dcdd4d389e9e660ebc0 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;
+ 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/mocks/openvr_mock.hpp b/tests/src/mocks/openvr_mock.hpp
index 855e2b48c5bf4951cc359382ca7a2d55e374535a..2b0d15f92f91a2acb820db4f3f1fd871ba923aeb 100644
--- a/tests/src/mocks/openvr_mock.hpp
+++ b/tests/src/mocks/openvr_mock.hpp
@@ -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));
@@ -401,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(_, _)) \
@@ -447,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/test-transform.cpp b/tests/src/test-transform.cpp
index 38e66cf05e85e2e07bd72e61c4eb7c4a865e166f..a35aea6909dd71327875cae8dd57c77a3cf11f0f 100644
--- a/tests/src/test-transform.cpp
+++ b/tests/src/test-transform.cpp
@@ -39,7 +39,7 @@ SUPPRESS_WARNINGS_END
#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(
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_material.cpp b/tests/src/test_material.cpp
index 843bd61e2fde6e39861566ecc0735b02ce29e633..93eb5ccb3eb8442d7417c346af9bea174c996904 100644
--- a/tests/src/test_material.cpp
+++ b/tests/src/test_material.cpp
@@ -29,7 +29,7 @@
#include "phx/core/logger.hpp"
#include "phx/rendering/components/material_handle.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]") {
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 0b2987fe9e6899e0ed6e1434137415228c94a054..1a90a5d68a079ee0d76f4d212626d0083e456326 100644
--- a/tests/src/test_openvr_controller_system.cpp
+++ b/tests/src/test_openvr_controller_model_system.cpp
@@ -34,9 +34,9 @@ SUPPRESS_WARNINGS_END
#include "phx/core/entity.hpp"
#include "phx/core/scene.hpp"
-#include "phx/display/display_system_openvr.hpp"
-#include "phx/input/openvr_controller_behavior.hpp"
-#include "phx/input/openvr_controller_system.hpp"
+#include "phx/input/device_system.hpp"
+#include "phx/input/openvr_controller_model_system.hpp"
+#include "phx/input/vr_controller.hpp"
#include "phx/rendering/components/material_handle.hpp"
#include "phx/rendering/components/mesh_handle.hpp"
#include "phx/rendering/rendering_system.hpp"
@@ -73,23 +73,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_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_vr_controller.cpp b/tests/src/test_vr_controller.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..628db4de69c0c41f45edd95c60b701181d830823
--- /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, 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/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_