diff --git a/conanfile.py b/conanfile.py index 550e2e251f56223e7aa58ef3dd65019070df498f..8b074ea48bfe90284c9dd7a660005b0bd7e82d28 100644 --- a/conanfile.py +++ b/conanfile.py @@ -24,7 +24,7 @@ from conans import ConanFile, CMake class ProjectPhoenix(ConanFile): name = "phx" - version = "18.01.0" + version = "18.02.0" license = "3-Clause BSD License" description = """Project Phoenix""" settings = "os", "compiler", "build_type", "arch" diff --git a/demos/viewer/src/navigation_behavior.cpp b/demos/viewer/src/navigation_behavior.cpp index 71d6a2e2b04a717693d9a5f77e4068480e1ea39e..1dd78c59f213a0a2c67cc4830b821d8d0dee89f7 100644 --- a/demos/viewer/src/navigation_behavior.cpp +++ b/demos/viewer/src/navigation_behavior.cpp @@ -28,7 +28,7 @@ #include "glm/glm.hpp" #include "glm/gtc/matrix_access.hpp" -#include "phx/display_system.hpp" +#include "phx/display_system_openvr.hpp" #include "phx/engine.hpp" #include "phx/entity.hpp" #include "phx/hmd.hpp" @@ -37,13 +37,16 @@ void NavigationBehavior::OnUpdate() { // If there exists an HMD and a transform. - const auto hmd = GetEntity() - ->GetScene() - ->GetEngine() - ->GetSystem<phx::DisplaySystem>() - ->GetHMD(); + const auto display_system_hmd = GetEntity() + ->GetScene() + ->GetEngine() + ->GetSystem<phx::DisplaySystemOpenVR>(); + phx::HMD* hmd = nullptr; + if (display_system_hmd != nullptr) { + hmd = display_system_hmd->GetHMD(); + } const auto transform = GetEntity()->GetFirstComponent<phx::Transform>(); - if (!hmd || !transform) return; + if (hmd == nullptr || transform == nullptr) return; auto indices = hmd->GetControllerIndices(); diff --git a/demos/viewer/src/viewer.cpp b/demos/viewer/src/viewer.cpp index 3992fcfa5c26a70c3a7b140ff2d333e5823236a0..bb293a384a638272b18eca2dd3b178a20fd135c8 100644 --- a/demos/viewer/src/viewer.cpp +++ b/demos/viewer/src/viewer.cpp @@ -28,7 +28,7 @@ #include <vector> #include "phx/assimp_model_loader.hpp" -#include "phx/display_system.hpp" +#include "phx/display_system_window.hpp" #include "phx/engine.hpp" #include "phx/entity.hpp" #include "phx/input_system.hpp" @@ -42,6 +42,7 @@ #include "phx/rendering_system.hpp" #include "phx/resource_declaration.hpp" #include "phx/resource_manager.hpp" +#include "phx/resource_pointer.hpp" #include "phx/resource_proxy.hpp" #include "phx/runtime_component.hpp" #include "phx/scene.hpp" @@ -51,63 +52,20 @@ #include "phx/transform.hpp" #include "phx/window.hpp" -#include "controller_behavior.hpp" #include "navigation_behavior.hpp" +#include "phx/display_system_openvr.hpp" #include "rotation_behavior.hpp" #include "viewer_system.hpp" -void AddControllerEntity(const std::shared_ptr<phx::Scene>& scene, - phx::ResourceProxy* mesh_proxy, - phx::ResourceProxy* material_proxy, - ControllerBehavior::Side side) { - phx::Entity* controller = scene->CreateEntity(); - controller->AddComponent<phx::MeshHandle>()->SetMeshProxy(mesh_proxy); - controller->AddComponent<phx::Transform>(); - controller->AddComponent<phx::MaterialHandle>()->SetMaterialProxy( - material_proxy); - controller->AddComponent<ControllerBehavior>()->SetSide(side); -} - -void AddController(const std::shared_ptr<phx::Scene>& scene, - ControllerBehavior::Side side) { - auto& resource_manager = phx::ResourceManager::instance(); - - phx::ResourceDeclaration mesh_declaration{ - {"TYPE", "openVR"}, - {"OpenVR_type", "mesh"}, - {"side", (side == ControllerBehavior::RIGHT ? "right" : "left")}}; - auto mesh_proxy = - phx::ResourceManager::instance().DeclareResource(mesh_declaration); - mesh_proxy->Load(); - - phx::ResourceDeclaration material_declaration{{"TYPE", "openVR"}, - {"OpenVR_type", "material"}}; - auto material_proxy = resource_manager.DeclareResource(material_declaration); - material_proxy->Load(); - - if (mesh_proxy->GetAs<phx::Mesh>() != nullptr) { - AddControllerEntity(scene, mesh_proxy, material_proxy, side); - } -} - -void SetupOpenVRController(const std::shared_ptr<phx::Scene>& scene, - phx::HMD* hmd) { - auto& resource_manager = phx::ResourceManager::instance(); - auto openvr_loader = std::make_unique<phx::OpenVRResourceLoader>(hmd); - resource_manager.RegisterResourceType("openVR", std::move(openvr_loader)); - - AddController(scene, ControllerBehavior::RIGHT); - AddController(scene, ControllerBehavior::LEFT); -} - int main(int, char**) { std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(); auto scene = engine->GetScene(); phx::SplashScreen splash( - engine->GetSystem<phx::DisplaySystem>()->GetWindow()); + engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow()); splash.Draw(); - auto assimp_loader = std::make_unique<phx::AssimpModelLoader>(); + auto assimp_loader = static_cast<phx::AssimpModelLoader*>( + phx::ResourceManager::instance().GetLoaderForType(".obj")); assimp_loader->SetProgressUpdateCallback([&splash](float progress) { splash.SetLoadProgress(progress); splash.Draw(); @@ -122,11 +80,6 @@ int main(int, char**) { viewer_system->SetShowFramerate(!viewer_system->GetShowFramerate()); }); - auto hmd = engine->GetSystem<phx::DisplaySystem>()->GetHMD(); - if (hmd != nullptr) { - SetupOpenVRController(scene, hmd); - } - phx::SceneLoader::InsertModelIntoScene( "models/UniversityScene/Univers20171013.obj", scene.get()); @@ -157,7 +110,7 @@ int main(int, char**) { phx::RuntimeComponent<phx::USER_PLATFORM>>()[0] ->GetFirstComponent<phx::Transform>(); - virtual_platform_transform->SetLocalTranslation(glm::vec3(0, 1.0, 5.0)); + virtual_platform_transform->SetLocalTranslation(glm::vec3(0.f, -1.f, -2.f)); phx::Entity* camera = scene->CreateEntity(); auto camera_transform = camera->AddComponent<phx::Transform>(); diff --git a/library/phx/assimp_model_loader.cpp b/library/phx/assimp_model_loader.cpp index 74b525a957e21d6fe4b506c98c4381d90d0a0284..0882122dfde8d46b32060ef1211600f9feed06e1 100644 --- a/library/phx/assimp_model_loader.cpp +++ b/library/phx/assimp_model_loader.cpp @@ -77,13 +77,13 @@ std::unique_ptr<phx::Resource> AssimpModelLoader::Load( auto resource = std::make_unique<phx::Model>(); for (unsigned int i = 0; i < scene->mNumMaterials; ++i) { - auto material_proxy = phx::ResourceUtils::LoadResourceFromFile( + auto material_proxy = phx::ResourceUtils::LoadResourceFromFile<Material>( file_name, {{"material_index", i}}, true); resource->AddMaterial(material_proxy); } for (unsigned int i = 0; i < scene->mNumMeshes; i++) { - auto mesh_proxy = phx::ResourceUtils::LoadResourceFromFile( + auto mesh_proxy = phx::ResourceUtils::LoadResourceFromFile<Mesh>( file_name, {{"mesh_index", i}}, true); resource->AddMesh(mesh_proxy); if (last_material_index_ != -1) { @@ -147,19 +147,19 @@ std::unique_ptr<phx::Material> AssimpModelLoader::LoadSingleMaterial( aiString relative_path; if (AI_SUCCESS == material->GetTexture(aiTextureType_AMBIENT, 0, &relative_path)) { - auto image_proxy = ResourceUtils::LoadResourceFromFile( + auto image_proxy = ResourceUtils::LoadResourceFromFile<Image>( filepath + "/" + std::string(relative_path.C_Str()), {}, true); resource->SetAmbientImage(image_proxy); } if (AI_SUCCESS == material->GetTexture(aiTextureType_DIFFUSE, 0, &relative_path)) { - auto image_proxy = ResourceUtils::LoadResourceFromFile( + auto image_proxy = ResourceUtils::LoadResourceFromFile<Image>( filepath + "/" + std::string(relative_path.C_Str()), {}, true); resource->SetDiffuseImage(image_proxy); } if (AI_SUCCESS == material->GetTexture(aiTextureType_SPECULAR, 0, &relative_path)) { - auto image_proxy = ResourceUtils::LoadResourceFromFile( + auto image_proxy = ResourceUtils::LoadResourceFromFile<Image>( filepath + "/" + std::string(relative_path.C_Str()), {}, true); resource->SetSpecularImage(image_proxy); } diff --git a/library/phx/behavior.hpp b/library/phx/behavior.hpp index 6de30dc82b4f860f979f65f876cde355fc36b3cb..4ec2fa75798671f089d942c469464825e45b9f2d 100644 --- a/library/phx/behavior.hpp +++ b/library/phx/behavior.hpp @@ -37,7 +37,7 @@ class PHOENIX_EXPORT Behavior : public Component { friend BehaviorSystem; public: - virtual void OnUpdate() {} + virtual void OnUpdate() = 0; std::string ToString() const override; diff --git a/library/phx/component.hpp b/library/phx/component.hpp index e47c01d7e4a963200db8780b6e2fdf8c84498280..ccd93f7d9c0e91371bd88a8a26a4df46e33e9f02 100644 --- a/library/phx/component.hpp +++ b/library/phx/component.hpp @@ -34,19 +34,21 @@ namespace phx { class Entity; class PHOENIX_EXPORT Component : public Loggable { - friend class Entity; - public: + // Entity should be the only one to construct Components + // has to be befriended to all derived Components, since friendship is not + // inheritable + friend class Entity; virtual ~Component() = default; Entity* GetEntity() const; protected: Component() = default; - Component(const Component&) = default; + Component(const Component&) = delete; Component(Component&&) = default; - Component& operator=(const Component&) = default; + Component& operator=(const Component&) = delete; Component& operator=(Component&&) = default; private: diff --git a/library/phx/display_system.cpp b/library/phx/display_system.cpp index 80e7a9a440e1c3f81bed4cc6c627909598397af1..675f964211c9f8d7d852aa198436487b267a04ae 100644 --- a/library/phx/display_system.cpp +++ b/library/phx/display_system.cpp @@ -22,72 +22,8 @@ #include "display_system.hpp" -#include <algorithm> -#include <memory> -#include <stdexcept> -#include <string> -#include <vector> - -#include "SDL2/SDL_video.h" - -#include "logger.hpp" -#include "rendering_system.hpp" - -#undef CreateWindow - namespace phx { -DisplaySystem::DisplaySystem(Engine* engine) : System(engine) { - if (SDL_VideoInit(nullptr) != 0) - throw std::runtime_error( - "Unable to initialize SDL video subsystem. Error: " + - std::string(SDL_GetError())); -} -DisplaySystem::~DisplaySystem() { SDL_VideoQuit(); } - -void DisplaySystem::DestroyWindow() { - window_.reset(); - /*windows_.erase( - std::remove_if(windows_.begin(), windows_.end(), - [window](const std::unique_ptr<Window>& iteratee) { - return window == iteratee.get(); - }));*/ -} - -Window* DisplaySystem::GetWindow() { - /*std::vector<Window*> windows(windows_.size()); - std::transform( - windows_.begin(), windows_.end(), windows.begin(), - [](const std::unique_ptr<Window>& iteratee) { return iteratee.get(); });*/ - return window_.get(); -} - -phx::HMD* DisplaySystem::CreateHMD() { - if (hmd_ == nullptr) { - hmd_ = std::make_unique<HMD>(); - CreateWindow("Phoenix HMD Companion", glm::uvec2(10, 10), - hmd_->GetViewportSize()); - } else { - warn("[DisplaySystem] HMD already created, so far only one is supported."); - } - return hmd_.get(); -} - -void DisplaySystem::DestroyHMD() { hmd_.reset(); } - -phx::HMD* DisplaySystem::GetHMD() { return hmd_.get(); } +DisplaySystem::DisplaySystem(Engine* engine) : System(engine) {} +DisplaySystem::~DisplaySystem() {} -void DisplaySystem::Update(const FrameTimer::TimeInfo&) { - if (window_ != nullptr) { - window_->Swap(); - } - if (hmd_ != nullptr) { - auto rendering_system = engine_->GetSystem<RenderingSystem>(); - auto left_texture = - rendering_system->GetLeftRenderTarget()->GetColorTexture(); - hmd_->Submit(HMD::LEFT_EYE, left_texture); - auto right_texture = - rendering_system->GetRightRenderTarget()->GetColorTexture(); - hmd_->Submit(HMD::RIGHT_EYE, right_texture); - } -} } // namespace phx diff --git a/library/phx/display_system.hpp b/library/phx/display_system.hpp index ffcc68492dda98db366bf20f8bf9cd684590e1e8..98b84f3ceb0a433000582e059730885a7e00bb47 100644 --- a/library/phx/display_system.hpp +++ b/library/phx/display_system.hpp @@ -28,11 +28,8 @@ #include "phx/engine.hpp" #include "phx/export.hpp" -#include "phx/hmd.hpp" +#include "phx/render_target.hpp" #include "phx/system.hpp" -#include "phx/window.hpp" - -#undef CreateWindow namespace phx { @@ -45,31 +42,15 @@ class PHOENIX_EXPORT DisplaySystem : public System { DisplaySystem& operator=(const DisplaySystem&) = delete; DisplaySystem& operator=(DisplaySystem&&) = default; - template <typename... Arguments> - Window* CreateWindow(Arguments&&... arguments); - void DestroyWindow(); - Window* GetWindow(); - - HMD* CreateHMD(); - void DestroyHMD(); - HMD* GetHMD(); + void Update(const FrameTimer::TimeInfo&) override = 0; - void Update(const FrameTimer::TimeInfo&) override; + virtual std::vector<std::unique_ptr<RenderTarget>> CreateRenderTargets() = 0; protected: explicit DisplaySystem(Engine* engine); friend DisplaySystem* Engine::CreateSystem<DisplaySystem>(); - - std::unique_ptr<Window> window_; - std::unique_ptr<HMD> hmd_; }; -template <typename... Arguments> -Window* DisplaySystem::CreateWindow(Arguments&&... arguments) { - // Do not use make_unique due to the friendship to Window. - window_ = std::unique_ptr<Window>(new Window(arguments...)); - return window_.get(); -} } // namespace phx #endif // LIBRARY_PHX_DISPLAY_SYSTEM_HPP_ diff --git a/library/phx/display_system_openvr.cpp b/library/phx/display_system_openvr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..909eeb69c0334a2bfac89ab77c0e68c70baaf526 --- /dev/null +++ b/library/phx/display_system_openvr.cpp @@ -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. +//------------------------------------------------------------------------------ + +#include "display_system_openvr.hpp" + +#include <algorithm> +#include <memory> +#include <stdexcept> +#include <string> +#include <utility> +#include <vector> + +#include "logger.hpp" +#include "rendering_system.hpp" + +#undef CreateWindow + +namespace phx { +DisplaySystemOpenVR::DisplaySystemOpenVR(Engine* engine) + : DisplaySystem(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(); +} + +void DisplaySystemOpenVR::DestroyHMD() { hmd_.reset(); } + +phx::HMD* DisplaySystemOpenVR::GetHMD() { return hmd_.get(); } + +void DisplaySystemOpenVR::Update(const FrameTimer::TimeInfo&) { + if (hmd_ != nullptr) { + auto rendering_system = engine_->GetSystem<RenderingSystem>(); + if (rendering_system != nullptr) { + auto render_targets = rendering_system->GetRenderTargets(); + if (render_targets.size() >= 2) { + auto right_texture = render_targets[0]->GetColorTexture(); + hmd_->Submit(HMD::RIGHT_EYE, right_texture); + auto left_texture = render_targets[1]->GetColorTexture(); + hmd_->Submit(HMD::LEFT_EYE, left_texture); + } + } + } +} + +std::vector<std::unique_ptr<RenderTarget>> +DisplaySystemOpenVR::CreateRenderTargets() { + std::vector<std::unique_ptr<RenderTarget>> render_targets; + if (GetHMD() == nullptr) { + error("Cannot create render targets: no HMD."); + return std::move(render_targets); + } + + render_targets.push_back( + std::make_unique<RenderTarget>(GetHMD()->GetViewportSize())); + render_targets.push_back( + std::make_unique<RenderTarget>(GetHMD()->GetViewportSize())); + return std::move(render_targets); +} + +} // namespace phx diff --git a/library/phx/display_system_openvr.hpp b/library/phx/display_system_openvr.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8e0962cc327aa78984354bc48b5630f078723cbb --- /dev/null +++ b/library/phx/display_system_openvr.hpp @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef LIBRARY_PHX_DISPLAY_SYSTEM_OPENVR_HPP_ +#define LIBRARY_PHX_DISPLAY_SYSTEM_OPENVR_HPP_ + +#include <memory> +#include <vector> + +#include "phx/display_system.hpp" +#include "phx/export.hpp" +#include "phx/hmd.hpp" +#include "phx/window.hpp" + +namespace phx { + +class PHOENIX_EXPORT DisplaySystemOpenVR : public DisplaySystem { + public: + explicit DisplaySystemOpenVR(Engine* engine); + DisplaySystemOpenVR(const DisplaySystemOpenVR&) = delete; + DisplaySystemOpenVR(DisplaySystemOpenVR&&) = default; + ~DisplaySystemOpenVR(); + + DisplaySystemOpenVR& operator=(const DisplaySystemOpenVR&) = delete; + DisplaySystemOpenVR& operator=(DisplaySystemOpenVR&&) = default; + + HMD* CreateHMD(); + void DestroyHMD(); + HMD* GetHMD(); + + void Update(const FrameTimer::TimeInfo&) override; + + std::vector<std::unique_ptr<RenderTarget>> CreateRenderTargets() override; + + protected: + std::unique_ptr<HMD> hmd_; +}; + +} // namespace phx + +#endif // LIBRARY_PHX_DISPLAY_SYSTEM_OPENVR_HPP_ diff --git a/library/phx/display_system_window.cpp b/library/phx/display_system_window.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f87978aa75274c4396227f799d406b573a68773 --- /dev/null +++ b/library/phx/display_system_window.cpp @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// 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 "display_system_window.hpp" + +#include <algorithm> +#include <memory> +#include <stdexcept> +#include <string> +#include <utility> +#include <vector> + +#include "SDL2/SDL_video.h" + +#include "logger.hpp" +#include "rendering_system.hpp" + +#undef CreateWindow + +namespace phx { +DisplaySystemWindow::DisplaySystemWindow(Engine* engine) + : DisplaySystem(engine) { + if (SDL_VideoInit(nullptr) != 0) + throw std::runtime_error( + "Unable to initialize SDL video subsystem. Error: " + + std::string(SDL_GetError())); +} +DisplaySystemWindow::~DisplaySystemWindow() { SDL_VideoQuit(); } + +void DisplaySystemWindow::DestroyWindow() { + window_.reset(); + /*windows_.erase( + std::remove_if(windows_.begin(), windows_.end(), + [window](const std::unique_ptr<Window>& iteratee) { + return window == iteratee.get(); + }));*/ +} + +Window* DisplaySystemWindow::GetWindow() { + /*std::vector<Window*> windows(windows_.size()); + std::transform( + windows_.begin(), windows_.end(), windows.begin(), + [](const std::unique_ptr<Window>& iteratee) { return iteratee.get(); });*/ + return window_.get(); +} + +void DisplaySystemWindow::Update(const FrameTimer::TimeInfo&) { + if (window_ != nullptr) { + window_->Swap(); + } +} + +std::vector<std::unique_ptr<RenderTarget>> +DisplaySystemWindow::CreateRenderTargets() { + std::vector<std::unique_ptr<RenderTarget>> render_targets; + if (GetWindow() == nullptr) { + error("Cannot create render target: no window."); + return std::move(render_targets); + } + + render_targets.push_back( + std::make_unique<RenderTarget>(GetWindow()->GetSize())); + return std::move(render_targets); +} + +} // namespace phx diff --git a/library/phx/display_system_window.hpp b/library/phx/display_system_window.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8abfcee03022c9af1d00355b788690bfbf9ffd55 --- /dev/null +++ b/library/phx/display_system_window.hpp @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// 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_DISPLAY_SYSTEM_WINDOW_HPP_ +#define LIBRARY_PHX_DISPLAY_SYSTEM_WINDOW_HPP_ + +#include <memory> +#include <vector> + +#include "phx/display_system.hpp" +#include "phx/export.hpp" +#include "phx/render_target.hpp" +#include "phx/window.hpp" + +#undef CreateWindow + +namespace phx { + +class PHOENIX_EXPORT DisplaySystemWindow : public DisplaySystem { + public: + explicit DisplaySystemWindow(Engine* engine); + DisplaySystemWindow(const DisplaySystemWindow&) = delete; + DisplaySystemWindow(DisplaySystemWindow&&) = default; + ~DisplaySystemWindow(); + + DisplaySystemWindow& operator=(const DisplaySystemWindow&) = delete; + DisplaySystemWindow& operator=(DisplaySystemWindow&&) = default; + + template <typename... Arguments> + Window* CreateWindow(Arguments&&... arguments); + void DestroyWindow(); + Window* GetWindow(); + + void Update(const FrameTimer::TimeInfo&) override; + + std::vector<std::unique_ptr<RenderTarget>> CreateRenderTargets() override; + + protected: + std::unique_ptr<Window> window_; +}; + +template <typename... Arguments> +Window* DisplaySystemWindow::CreateWindow(Arguments&&... arguments) { + // Do not use make_unique due to the friendship to Window. + window_ = std::unique_ptr<Window>(new Window(arguments...)); + return window_.get(); +} +} // namespace phx + +#endif // LIBRARY_PHX_DISPLAY_SYSTEM_WINDOW_HPP_ diff --git a/library/phx/engine.cpp b/library/phx/engine.cpp index 315ac6ae015c0562bb1c78c3a0461c275943469b..cf624f5649bf511076bd400694a41354051da84e 100644 --- a/library/phx/engine.cpp +++ b/library/phx/engine.cpp @@ -25,6 +25,7 @@ #include <iostream> #include <memory> #include <string> +#include <utility> #include <vector> #include "phx/logger.hpp" @@ -90,9 +91,75 @@ void Engine::SetScene(const std::shared_ptr<Scene>& new_scene) { if (scene_ != nullptr) { scene_->engine_ = nullptr; } + scene_changed_signal_(scene_, new_scene); // attach to new scene scene_ = new_scene; scene_->engine_ = this; } +boost::signals2::connection Engine::AddSceneChangedCallback( + const std::function<void(std::shared_ptr<Scene>, std::shared_ptr<Scene>)>& + callback) { + return scene_changed_signal_.connect(callback); +} + +void Engine::UpdateOrder::MoveToFront(System* system) const { + auto it = FindSystem(system); + if (it == engine_->systems_.end()) { + error("Cannot move system to front of update order: system not found"); + return; + } + auto system_temp = std::move(*it); + engine_->systems_.erase(it); + engine_->systems_.insert(engine_->systems_.begin(), std::move(system_temp)); +} + +void Engine::UpdateOrder::MoveToBack(System* system) const { + auto it = FindSystem(system); + if (it == engine_->systems_.end()) { + error("Cannot move system to back of update order: system not found"); + return; + } + auto system_temp = std::move(*it); + engine_->systems_.erase(it); + engine_->systems_.insert(engine_->systems_.end(), std::move(system_temp)); +} + +void Engine::UpdateOrder::MoveAfter(System* system, System* after) const { + MoveBeforeRelative(system, after, 1); +} + +void Engine::UpdateOrder::MoveBefore(System* system, System* before) const { + MoveBeforeRelative(system, before, 0); +} + +std::vector<std::unique_ptr<phx::System>>::iterator +Engine::UpdateOrder::FindSystem(System* system) const { + auto find_func = [system](const std::unique_ptr<System>& sys) { + return sys.get() == system; + }; + return std::find_if(engine_->systems_.begin(), engine_->systems_.end(), + find_func); +} + +void Engine::UpdateOrder::MoveBeforeRelative(System* system, + System* relative_to, + int distance) const { + if (system == relative_to) return; + auto it_sys = FindSystem(system); + if (it_sys == engine_->systems_.end()) { + error("Cannot change update order: system not found"); + return; + } + if (FindSystem(relative_to) == engine_->systems_.end()) { + error("Cannot change update order: target system not found"); + return; + } + auto system_temp = std::move(*it_sys); + engine_->systems_.erase(it_sys); + auto it_target = FindSystem(relative_to); + it_target += distance; + engine_->systems_.insert(it_target, std::move(system_temp)); +} + } // namespace phx diff --git a/library/phx/engine.hpp b/library/phx/engine.hpp index 481afbe06eac5f947da28688362e5256427d1ad5..99898cfeb8760fa6665d5a385b1001e6cd658bea 100644 --- a/library/phx/engine.hpp +++ b/library/phx/engine.hpp @@ -32,6 +32,12 @@ #include <utility> #include <vector> +SUPPRESS_WARNINGS_BEGIN +#define BOOST_BIND_NO_PLACEHOLDERS +#include "boost/signals2/connection.hpp" +#include "boost/signals2/signal.hpp" +SUPPRESS_WARNINGS_END + #include "phx/behavior.hpp" #include "phx/export.hpp" #include "phx/frame_timer.hpp" @@ -56,7 +62,7 @@ class PHOENIX_EXPORT Engine final : public Loggable { template <typename SystemType, typename... SystemArguments> SystemType* CreateSystem(SystemArguments&&... arguments) { static_assert(std::is_base_of<System, SystemType>::value, - "The type does not inherit from system."); + "The type does not inherit from System."); auto system = std::unique_ptr<SystemType>(new SystemType(this, arguments...)); systems_.push_back(std::move(system)); @@ -65,7 +71,7 @@ class PHOENIX_EXPORT Engine final : public Loggable { template <typename SystemType> SystemType* GetSystem() { static_assert(std::is_base_of<System, SystemType>::value, - "The type does not inherit from system."); + "The type does not inherit from System."); auto iterator = std::find_if(systems_.begin(), systems_.end(), SystemMatchPredicate<SystemType>); return iterator != systems_.end() @@ -73,9 +79,22 @@ class PHOENIX_EXPORT Engine final : public Loggable { : nullptr; } template <typename SystemType> + std::vector<SystemType*> GetSystems() { + static_assert(std::is_base_of<System, SystemType>::value, + "The type does not inherit from System."); + + std::vector<SystemType*> systems; + for (const auto& system : systems_) { + if (SystemMatchPredicate<SystemType>(system)) { + systems.push_back(static_cast<SystemType*>(system.get())); + } + } + return systems; + } + template <typename SystemType> void RemoveSystem() { static_assert(std::is_base_of<System, SystemType>::value, - "The type does not inherit from system."); + "The type does not inherit from System."); systems_.erase(std::remove_if(systems_.begin(), systems_.end(), SystemMatchPredicate<SystemType>), systems_.end()); @@ -89,6 +108,10 @@ class PHOENIX_EXPORT Engine final : public Loggable { void SetScene(const std::shared_ptr<Scene>& new_scene); std::shared_ptr<Scene> GetScene() const; + // Parameters to the callback are: (old scene, new scene) + boost::signals2::connection AddSceneChangedCallback( + const std::function<void(std::shared_ptr<Scene>, std::shared_ptr<Scene>)>& + callback); const FrameTimer& GetFrameTimer(); @@ -101,11 +124,29 @@ class PHOENIX_EXPORT Engine final : public Loggable { template <typename... Components> std::vector<std::tuple<Components*...>> GetFirstComponentsMany() const; + // Update order + class UpdateOrder { + public: + explicit UpdateOrder(Engine* engine) : engine_(engine) {} + void MoveToFront(System* system) const; + void MoveToBack(System* system) const; + void MoveAfter(System* system, System* after) const; + void MoveBefore(System* system, System* before) const; + + private: + std::vector<std::unique_ptr<System>>::iterator FindSystem( + System* system) const; + void MoveBeforeRelative(System* system, System* relative_to, + int distance) const; + Engine* engine_; + }; + + const UpdateOrder& GetUpdateOrder() const { return update_order_; } + private: template <typename SystemType> static bool SystemMatchPredicate(const std::unique_ptr<System>& iteratee) { - const auto& system = *iteratee.get(); - return typeid(SystemType) == typeid(system); + return (dynamic_cast<SystemType*>(iteratee.get()) != nullptr); } void UpdateSystems(); @@ -114,8 +155,11 @@ class PHOENIX_EXPORT Engine final : public Loggable { bool is_running_ = false; std::shared_ptr<Scene> scene_; + boost::signals2::signal<void(std::shared_ptr<Scene>, std::shared_ptr<Scene>)> + scene_changed_signal_; FrameTimer frame_timer_; + UpdateOrder update_order_ = UpdateOrder(this); }; template <typename... Components> diff --git a/library/phx/entity.hpp b/library/phx/entity.hpp index 5cfb7e533d5d953c71c8091b4f21a2cac8611194..d902b8da6cb494ecfaa9f8ec218508f0bce5d6ca 100644 --- a/library/phx/entity.hpp +++ b/library/phx/entity.hpp @@ -56,8 +56,8 @@ class PHOENIX_EXPORT Entity : public Nameable, public Loggable { ComponentType* AddComponent() { static_assert(std::is_base_of<phx::Component, ComponentType>::value, "ComponentType is not derived from phx::Component."); - components_.push_back(std::make_unique<ComponentType>()); - auto component_ptr = static_cast<ComponentType*>(components_.back().get()); + auto component_ptr = new ComponentType(); + components_.push_back(std::unique_ptr<ComponentType>(component_ptr)); component_ptr->entity_ = this; return component_ptr; } diff --git a/library/phx/frame_graph.hpp b/library/phx/frame_graph.hpp index c8d83a8051172fa58c06d15a8007674787927977..09079b5ead43296d5791c92d895756d114840e10 100644 --- a/library/phx/frame_graph.hpp +++ b/library/phx/frame_graph.hpp @@ -26,6 +26,7 @@ #include <list> #include <memory> #include <utility> +#include <vector> #include "phx/export.hpp" #include "phx/render_pass.hpp" @@ -47,10 +48,27 @@ class PHOENIX_EXPORT FrameGraph { std::size_t GetNumberOfPasses(); RenderPass* AddRenderPass(std::unique_ptr<RenderPass> render_pass); + template <typename RenderPassType> + std::vector<RenderPassType*> GetRenderPasses() const; + private: std::list<std::unique_ptr<RenderPass>> render_passes_; }; +template <typename RenderPassType> +std::vector<RenderPassType*> phx::FrameGraph::GetRenderPasses() const { + static_assert(std::is_base_of<RenderPass, RenderPassType>::value, + "The type does not inherit from RenderPass."); + std::vector<RenderPassType*> render_passes; + for (const auto& render_pass : render_passes_) { + auto pass_pointer = dynamic_cast<RenderPassType*>(render_pass.get()); + if (pass_pointer != nullptr) { + render_passes.push_back(pass_pointer); + } + } + return render_passes; +} + } // namespace phx #endif // LIBRARY_PHX_FRAME_GRAPH_HPP_ diff --git a/library/phx/geometry_pass.cpp b/library/phx/geometry_pass.cpp index 01fd54e7d25011d18f4b237252237df445adcdf3..3f464d4bb203e979cdc723b8ea6c2c8ed4ff78b7 100644 --- a/library/phx/geometry_pass.cpp +++ b/library/phx/geometry_pass.cpp @@ -38,6 +38,7 @@ #include "phx/resource_proxy.hpp" #include "phx/resource_utils.hpp" #include "phx/transform.hpp" +#include "phx/shader_source.hpp" namespace phx { @@ -139,15 +140,15 @@ void GeometryPass::Initialize() { } void GeometryPass::SetUpShaders() { - auto vertex_shader_proxy = - ResourceUtils::LoadResourceFromFile("shader/phong.vert"); - auto fragment_shader_proxy = - ResourceUtils::LoadResourceFromFile("shader/phong.frag"); + auto vertex_shader = + ResourceUtils::LoadResourceFromFile<ShaderSource>("shader/phong.vert"); + auto fragment_shader = + ResourceUtils::LoadResourceFromFile<ShaderSource>("shader/phong.frag"); shader_program_ = std::make_unique<ShaderProgram>(); - shader_program_->SetShaderProxy(ShaderProgram::VERTEX, vertex_shader_proxy); - shader_program_->SetShaderProxy(ShaderProgram::FRAGMENT, - fragment_shader_proxy); + shader_program_->SetShader(ShaderProgram::VERTEX, vertex_shader); + shader_program_->SetShader(ShaderProgram::FRAGMENT, + fragment_shader); shader_program_->Link(); } diff --git a/library/phx/hmd.cpp b/library/phx/hmd.cpp index 847f58f078f0aa03293d97fbd52729f2e387b6ba..4f8c5d3e7edd18133fe39a504e9924a9548f1e20 100644 --- a/library/phx/hmd.cpp +++ b/library/phx/hmd.cpp @@ -39,6 +39,7 @@ SUPPRESS_WARNINGS_END #include "gl/texture.hpp" #include "phx/logger.hpp" #include "phx/resource_manager.hpp" +#include "phx/resource_pointer.hpp" namespace phx { HMD::HMD() { @@ -79,6 +80,11 @@ std::vector<vr::TrackedDeviceIndex_t> HMD::GetControllerIndices() { return std::move(indices); } +vr::ETrackedControllerRole HMD::GetControllerRoleForTrackedDeviceIndex( + vr::TrackedDeviceIndex_t device_index) const { + return vr_system_->GetControllerRoleForTrackedDeviceIndex(device_index); +} + const glm::uvec2& HMD::GetViewportSize() const { return viewport_size_; } const glm::mat4& HMD::GetProjectionMatrix(Side side) const { @@ -136,7 +142,9 @@ glm::mat4 HMD::GetRightControllerTransformation() { std::unique_ptr<phx::Mesh> HMD::GetControllerMesh(Controller controller) { auto model = GetControllerModel(controller); - if (model == nullptr) return nullptr; + if (model == nullptr) { + return nullptr; + } auto mesh = std::make_unique<phx::Mesh>(); std::vector<glm::vec3> vertices; std::vector<glm::vec3> normals; @@ -176,7 +184,7 @@ vr::RenderModel_t* HMD::GetControllerModel(Controller controller) { } } - vr::RenderModel_t* model; + 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)); @@ -203,7 +211,7 @@ std::unique_ptr<Material> HMD::GetControllerMaterial(Controller controller) { {"side", controller == HMD::Controller::LEFT_CONTROLLER ? "left" : "right"}}); texture_proxy->Load(); - material->SetDiffuseImage(texture_proxy); + material->SetDiffuseImage(ResourcePointer<Image>(texture_proxy)); return material; } @@ -224,9 +232,9 @@ std::unique_ptr<Image> HMD::GetControllerTexture(int id) { texture_map->rubTextureMapData + image_data.size(), image_data.begin()); auto image = std::make_unique<phx::Image>( - image_data, std::array<std::size_t, 2>{ + &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)}}); + static_cast<std::size_t>(texture_map->unHeight)}}, 32); return image; } diff --git a/library/phx/hmd.hpp b/library/phx/hmd.hpp index a3e0b7cfb165fc91f4e9622a6261cf803889fd59..82bedf2cfaf9e305a32b7126ec7415fd6635d45b 100644 --- a/library/phx/hmd.hpp +++ b/library/phx/hmd.hpp @@ -78,6 +78,8 @@ class PHOENIX_EXPORT HMD { void Submit(Side side, gl::texture_2d* texture); std::vector<vr::TrackedDeviceIndex_t> GetControllerIndices(); + vr::ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex( + vr::TrackedDeviceIndex_t device_index) const; private: glm::mat4 GetTransformationForRole(vr::ETrackedControllerRole role); diff --git a/library/phx/image.hpp b/library/phx/image.hpp index 5f686d332ed717a515565219c031645531368082..b9ccc8bcf7a0cb9f6cec1b2c406a9a6f15f1f941 100644 --- a/library/phx/image.hpp +++ b/library/phx/image.hpp @@ -71,7 +71,8 @@ class PHOENIX_EXPORT Image : public Resource, public Loggable { static_cast<std::int32_t>(dimensions[1]), static_cast<std::int32_t>((bits_per_pixel * dimensions[0] + 31) / 32 * 4), - static_cast<std::int32_t>(bits_per_pixel), 0, 0, 0, false); + static_cast<std::int32_t>(bits_per_pixel), FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, false); if (!native_) { throw std::runtime_error("FreeImage_ConvertFromRawBitsEx failed."); } diff --git a/library/phx/light.hpp b/library/phx/light.hpp index 5a11219010eca50a2698680a66c4e5b9cbb8f4b3..53e590f1ef6303c9cb3bf79d7c1d3ccbd0adffd6 100644 --- a/library/phx/light.hpp +++ b/library/phx/light.hpp @@ -36,7 +36,7 @@ namespace phx { SUPPRESS_WARNINGS_BEGIN_PADDED -class PHOENIX_EXPORT Light : public Component { +class PHOENIX_EXPORT Light final : public Component { public: enum class Type { kPoint, @@ -61,6 +61,15 @@ class PHOENIX_EXPORT Light : public Component { std::string ToString() const override; + protected: + friend Entity; + Light() = default; + Light(const Light&) = delete; + Light(Light&&) = default; + + Light& operator=(const Light&) = delete; + Light& operator=(Light&&) = default; + private: Type type_ = Type::kDirectional; glm::vec3 color_ = glm::vec3(1.0f, 1.0f, 1.0f); diff --git a/library/phx/material.cpp b/library/phx/material.cpp index 63810ec40e612451032117ef1a52c8ce57faa7c8..59f16c5abedb7d7e765abf358b97a6c1a8b71545 100644 --- a/library/phx/material.cpp +++ b/library/phx/material.cpp @@ -32,38 +32,48 @@ namespace phx { glm::vec3 Material::GetDiffuseColor() const { return diffuse_color_; } void Material::SetDiffuseColor(glm::vec3 color) { diffuse_color_ = color; } -ResourceProxy* Material::GetDiffuseImage() const { return diffuse_image_; } -void Material::SetDiffuseImage(ResourceProxy* image) { diffuse_image_ = image; } +ResourcePointer<Image> Material::GetDiffuseImage() const { + return diffuse_image_; +} +void Material::SetDiffuseImage(ResourcePointer<Image> image) { + diffuse_image_ = image; +} gl::texture_2d* Material::GetDiffuseTexture() { if (diffuse_image_ && !diffuse_texture_) - SetTexture(diffuse_image_->GetAs<phx::Image>(), &diffuse_texture_); + SetTexture(diffuse_image_, &diffuse_texture_); return diffuse_texture_.get(); } glm::vec3 Material::GetAmbientColor() const { return ambient_color_; } void Material::SetAmbientColor(glm::vec3 color) { ambient_color_ = color; } -ResourceProxy* Material::GetAmbientImage() const { return ambient_image_; } -void Material::SetAmbientImage(ResourceProxy* image) { ambient_image_ = image; } +ResourcePointer<Image> Material::GetAmbientImage() const { + return ambient_image_; +} +void Material::SetAmbientImage(ResourcePointer<Image> image) { + ambient_image_ = image; +} gl::texture_2d* Material::GetAmbientTexture() { if (ambient_image_ && !ambient_texture_) - SetTexture(ambient_image_->GetAs<phx::Image>(), &ambient_texture_); + SetTexture(ambient_image_, &ambient_texture_); return ambient_texture_.get(); } glm::vec3 Material::GetSpecularColor() const { return specular_color_; } void Material::SetSpecularColor(glm::vec3 color) { specular_color_ = color; } -ResourceProxy* Material::GetSpecularImage() const { return specular_image_; } -void Material::SetSpecularImage(ResourceProxy* image) { +ResourcePointer<Image> Material::GetSpecularImage() const { + return specular_image_; +} +void Material::SetSpecularImage(ResourcePointer<Image> image) { specular_image_ = image; } gl::texture_2d* Material::GetSpecularTexture() { if (specular_image_ && !specular_texture_) - SetTexture(specular_image_->GetAs<phx::Image>(), &specular_texture_); + SetTexture(specular_image_, &specular_texture_); return specular_texture_.get(); } @@ -82,9 +92,9 @@ void Material::SetShininess(float shininess) { const std::string& Material::GetName() const { return name_; } void Material::SetName(const std::string& name) { name_ = name; } -void Material::SetTexture(Image* image, +void Material::SetTexture(ResourcePointer<Image> image, std::shared_ptr<gl::texture_2d>* texture) { - if (!image) return; + if (!image.GetProxy()->IsOnline()) return; if (*texture && (*texture)->is_valid()) { gl::texture_handle handle(*texture->get()); handle.set_resident(false); diff --git a/library/phx/material.hpp b/library/phx/material.hpp index 4a51fff148501d114972656629bd1439ff76dea3..3de50a4d1f860ba3292faef1aa29e6d9178c7ae8 100644 --- a/library/phx/material.hpp +++ b/library/phx/material.hpp @@ -36,6 +36,7 @@ SUPPRESS_WARNINGS_END #include "phx/export.hpp" #include "phx/image.hpp" #include "phx/resource.hpp" +#include "phx/resource_pointer.hpp" #include "phx/resource_proxy.hpp" namespace phx { @@ -46,24 +47,24 @@ class PHOENIX_EXPORT Material : public Resource { glm::vec3 GetDiffuseColor() const; void SetDiffuseColor(glm::vec3 color); - ResourceProxy* GetDiffuseImage() const; - void SetDiffuseImage(ResourceProxy* proxy); + ResourcePointer<Image> GetDiffuseImage() const; + void SetDiffuseImage(ResourcePointer<Image> proxy); gl::texture_2d* GetDiffuseTexture(); glm::vec3 GetAmbientColor() const; void SetAmbientColor(glm::vec3 color); - ResourceProxy* GetAmbientImage() const; - void SetAmbientImage(ResourceProxy* proxy); + ResourcePointer<Image> GetAmbientImage() const; + void SetAmbientImage(ResourcePointer<Image> proxy); gl::texture_2d* GetAmbientTexture(); glm::vec3 GetSpecularColor() const; void SetSpecularColor(glm::vec3 color); - ResourceProxy* GetSpecularImage() const; - void SetSpecularImage(ResourceProxy* image); + ResourcePointer<Image> GetSpecularImage() const; + void SetSpecularImage(ResourcePointer<Image> image); gl::texture_2d* GetSpecularTexture(); @@ -74,14 +75,15 @@ class PHOENIX_EXPORT Material : public Resource { void SetName(const std::string& name); private: - void SetTexture(Image* image, std::shared_ptr<gl::texture_2d>* texture); + void SetTexture(ResourcePointer<Image> image, + std::shared_ptr<gl::texture_2d>* texture); glm::vec3 ambient_color_ = glm::vec3(0, 0, 0); glm::vec3 diffuse_color_ = glm::vec3(1, 0, 0); glm::vec3 specular_color_ = glm::vec3(1, 1, 1); - ResourceProxy* ambient_image_ = nullptr; - ResourceProxy* diffuse_image_ = nullptr; - ResourceProxy* specular_image_ = nullptr; + ResourcePointer<Image> ambient_image_{nullptr}; + ResourcePointer<Image> diffuse_image_{nullptr}; + ResourcePointer<Image> specular_image_{nullptr}; std::shared_ptr<gl::texture_2d> ambient_texture_ = nullptr; std::shared_ptr<gl::texture_2d> diffuse_texture_ = nullptr; std::shared_ptr<gl::texture_2d> specular_texture_ = nullptr; diff --git a/library/phx/material_handle.cpp b/library/phx/material_handle.cpp index 67059f6cac8f098c38c705ce999d22286b17fca6..ecafabb6b67ee45ee2c2fd99868b29712b00b2fd 100644 --- a/library/phx/material_handle.cpp +++ b/library/phx/material_handle.cpp @@ -29,16 +29,12 @@ namespace phx { -void MaterialHandle::SetMaterialProxy(ResourceProxy* proxy) { - material_proxy_ = proxy; +void MaterialHandle::SetMaterial(ResourcePointer<Material> material) { + material_ = material; } -phx::ResourceProxy* MaterialHandle::GetMaterialProxy() const { - return material_proxy_; -} - -phx::Material* MaterialHandle::GetMaterial() const { - return material_proxy_->GetAs<phx::Material>(); +ResourcePointer<Material> MaterialHandle::GetMaterial() const { + return material_; } std::string MaterialHandle::ToString() const { diff --git a/library/phx/material_handle.hpp b/library/phx/material_handle.hpp index 5f9a71380d5941a6cef266b26d71370f2a9daa4f..0a5e866be3725faa7f3ea74678070f5fc0a449cb 100644 --- a/library/phx/material_handle.hpp +++ b/library/phx/material_handle.hpp @@ -34,21 +34,29 @@ SUPPRESS_WARNINGS_END #include "phx/export.hpp" #include "phx/material.hpp" #include "phx/nameable.hpp" +#include "phx/resource_pointer.hpp" #include "phx/resource_proxy.hpp" namespace phx { -class PHOENIX_EXPORT MaterialHandle : public Component, public Nameable { +class PHOENIX_EXPORT MaterialHandle final : public Component, public Nameable { public: - void SetMaterialProxy(ResourceProxy* proxy); - ResourceProxy* GetMaterialProxy() const; - - Material* GetMaterial() const; + void SetMaterial(ResourcePointer<Material> material); + ResourcePointer<Material> GetMaterial() const; std::string ToString() const override; + protected: + friend Entity; + MaterialHandle() = default; + MaterialHandle(const MaterialHandle&) = delete; + MaterialHandle(MaterialHandle&&) = default; + + MaterialHandle& operator=(const MaterialHandle&) = delete; + MaterialHandle& operator=(MaterialHandle&&) = default; + private: - ResourceProxy* material_proxy_ = nullptr; + ResourcePointer<Material> material_{nullptr}; }; } // namespace phx diff --git a/library/phx/mesh_handle.cpp b/library/phx/mesh_handle.cpp index cae2c5ddd8dc8a3016d3a6159621913e2cb418e9..abaaac234aaaf3497634dc9d8c234737c96ee6b9 100644 --- a/library/phx/mesh_handle.cpp +++ b/library/phx/mesh_handle.cpp @@ -28,16 +28,12 @@ namespace phx { -void MeshHandle::SetMeshProxy(ResourceProxy* proxy) { mesh_proxy_ = proxy; } +void MeshHandle::SetMesh(ResourcePointer<Mesh> mesh) { mesh_ = mesh; } -phx::ResourceProxy* MeshHandle::GetMeshProxy() const { return mesh_proxy_; } - -phx::Mesh* MeshHandle::GetMesh() const { - return mesh_proxy_->GetAs<phx::Mesh>(); -} +ResourcePointer<Mesh> MeshHandle::GetMesh() const { return mesh_; } std::string MeshHandle::ToString() const { - if (mesh_proxy_ == nullptr) return GetName() + " (MeshHandle <empty>)"; + if (mesh_ == nullptr) return GetName() + " (MeshHandle <empty>)"; auto mesh = this->GetMesh(); return GetName() + " (MeshHandle" + diff --git a/library/phx/mesh_handle.hpp b/library/phx/mesh_handle.hpp index a9a01cd181bd5419810eebfdaf894e8ca6aade6d..4b83c9f87d76a5c59a5f2c93217db90f13611985 100644 --- a/library/phx/mesh_handle.hpp +++ b/library/phx/mesh_handle.hpp @@ -25,34 +25,37 @@ #include <string> #include "phx/component.hpp" +#include "phx/export.hpp" #include "phx/mesh.hpp" #include "phx/nameable.hpp" +#include "phx/resource_pointer.hpp" #include "phx/resource_proxy.hpp" namespace phx { /** * A component holding a simple pointer to a mesh */ -class MeshHandle : public Component, public Nameable { +class PHOENIX_EXPORT MeshHandle final : public Component, public Nameable { public: - MeshHandle() = default; - MeshHandle(const MeshHandle&) = default; - MeshHandle(MeshHandle&&) = default; - ~MeshHandle() = default; + virtual ~MeshHandle() = default; - MeshHandle& operator=(const MeshHandle&) = default; - MeshHandle& operator=(MeshHandle&&) = default; + void SetMesh(ResourcePointer<Mesh> mesh); + ResourcePointer<Mesh> GetMesh() const; - void SetMeshProxy(ResourceProxy* proxy); - ResourceProxy* GetMeshProxy() const; + std::string ToString() const override; - Mesh* GetMesh() const; + protected: + friend Entity; + MeshHandle() = default; + MeshHandle(const MeshHandle&) = delete; + MeshHandle(MeshHandle&&) = default; - std::string ToString() const override; + MeshHandle& operator=(const MeshHandle&) = delete; + MeshHandle& operator=(MeshHandle&&) = default; protected: private: - ResourceProxy* mesh_proxy_{nullptr}; + ResourcePointer<Mesh> mesh_{nullptr}; }; } // namespace phx diff --git a/library/phx/model.cpp b/library/phx/model.cpp index a97604037e6f72888cb786023f37c3a2b4bb0966..164b63f8b447d5e7c570d1dab35f4f0a7f3bb3ff 100644 --- a/library/phx/model.cpp +++ b/library/phx/model.cpp @@ -24,29 +24,35 @@ #include <vector> +#include "phx/material.hpp" + namespace phx { -const std::vector<phx::ResourceProxy*>& Model::GetMeshes() const { +const std::vector<ResourcePointer<Mesh>>& Model::GetMeshes() const { return mesh_proxies_; } -void Model::AddMesh(ResourceProxy* mesh) { mesh_proxies_.push_back(mesh); } +void Model::AddMesh(ResourcePointer<Mesh> mesh) { + mesh_proxies_.push_back(mesh); +} -const std::vector<phx::ResourceProxy*>& Model::GetMaterials() const { +const std::vector<ResourcePointer<Material>>& Model::GetMaterials() const { return material_proxies_; } -void Model::AddMaterial(ResourceProxy* material) { +void Model::AddMaterial(ResourcePointer<Material> material) { material_proxies_.push_back(material); } -phx::ResourceProxy* Model::GetMaterialForMesh(ResourceProxy* mesh) const { +ResourcePointer<Material> Model::GetMaterialForMesh( + ResourcePointer<Mesh> mesh) const { auto it = material_by_mesh_.find(mesh); - if (it == material_by_mesh_.end()) return nullptr; + if (it == material_by_mesh_.end()) return ResourcePointer<Material>(nullptr); return it->second; } -void Model::SetMaterialForMesh(ResourceProxy* mesh, ResourceProxy* material) { +void Model::SetMaterialForMesh(ResourcePointer<Mesh> mesh, + ResourcePointer<Material> material) { material_by_mesh_[mesh] = material; } diff --git a/library/phx/model.hpp b/library/phx/model.hpp index 5a26f3a0befaf5d514dcbc7f9f670a0a38383a8a..16b569d9be23be17abafeca3e35a6c3e1cb3a57c 100644 --- a/library/phx/model.hpp +++ b/library/phx/model.hpp @@ -26,15 +26,16 @@ #include <map> #include <string> #include <vector> -#include "resource_proxy.hpp" SUPPRESS_WARNINGS_BEGIN #include "glm/glm.hpp" SUPPRESS_WARNINGS_END #include "phx/export.hpp" +#include "phx/material.hpp" #include "phx/mesh.hpp" #include "phx/resource.hpp" +#include "phx/resource_pointer.hpp" namespace phx { @@ -49,19 +50,21 @@ class PHOENIX_EXPORT Model : public Resource { Model& operator=(const Model&) = default; Model& operator=(Model&&) = default; - const std::vector<phx::ResourceProxy*>& GetMeshes() const; - void AddMesh(ResourceProxy* mesh); + const std::vector<ResourcePointer<Mesh>>& GetMeshes() const; + void AddMesh(ResourcePointer<Mesh> mesh); - const std::vector<phx::ResourceProxy*>& GetMaterials() const; - void AddMaterial(ResourceProxy* material); + const std::vector<ResourcePointer<Material>>& GetMaterials() const; + void AddMaterial(ResourcePointer<Material> material); - ResourceProxy* GetMaterialForMesh(ResourceProxy* mesh) const; - void SetMaterialForMesh(ResourceProxy* mesh, ResourceProxy* material); + ResourcePointer<Material> GetMaterialForMesh( + ResourcePointer<Mesh> mesh) const; + void SetMaterialForMesh(ResourcePointer<Mesh> mesh, + ResourcePointer<Material> material); private: - std::vector<ResourceProxy*> mesh_proxies_; - std::vector<ResourceProxy*> material_proxies_; - std::map<ResourceProxy*, ResourceProxy*> material_by_mesh_; + std::vector<ResourcePointer<Mesh>> mesh_proxies_; + std::vector<ResourcePointer<Material>> material_proxies_; + std::map<ResourcePointer<Mesh>, ResourcePointer<Material>> material_by_mesh_; }; } // namespace phx diff --git a/library/phx/opengl_image_buffer_data.hpp b/library/phx/opengl_image_buffer_data.hpp index f697e4468592ae0219a5d722bf51a6047dfed3b6..ec22c55097df8da5951da12b871f77c7edb5bf86 100644 --- a/library/phx/opengl_image_buffer_data.hpp +++ b/library/phx/opengl_image_buffer_data.hpp @@ -353,9 +353,9 @@ void phx::OpenGLImageBufferData<T>::SaveToFilePNG( template <typename T> phx::OpenGLImageBufferData<T> phx::OpenGLImageBufferData<T>::ReadFromFilePNG( const std::string& filename) { - auto img_proxy = phx::ResourceUtils::LoadResourceFromFile(filename, {}, true); - auto image = img_proxy->GetAs<phx::Image>(); - auto buffer = OpenGLImageBufferData<T>::CreateFromImage(image); + auto image = + phx::ResourceUtils::LoadResourceFromFile<Image>(filename, {}, true); + auto buffer = OpenGLImageBufferData<T>::CreateFromImage(image.Get()); auto* buffer_pointer = buffer.release(); return *buffer_pointer; } diff --git a/demos/viewer/src/controller_behavior.cpp b/library/phx/openvr_controller_behavior.cpp similarity index 84% rename from demos/viewer/src/controller_behavior.cpp rename to library/phx/openvr_controller_behavior.cpp index f4fc9a45a6bcdc867ff836d6a039a9039a924f09..51620fe1f74a172e3890a0207fa47f23c91a4383 100644 --- a/demos/viewer/src/controller_behavior.cpp +++ b/library/phx/openvr_controller_behavior.cpp @@ -20,14 +20,15 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "controller_behavior.hpp" +#include "openvr_controller_behavior.hpp" #include "phx/entity.hpp" #include "phx/runtime_component.hpp" #include "phx/scene.hpp" #include "phx/transform.hpp" -void ControllerBehavior::OnUpdate() { +namespace phx { +void OpenVRControllerBehavior::OnUpdate() { phx::Scene* scene = GetEntity()->GetScene(); phx::Entity* runtime_entity = nullptr; if (side_ == Side::LEFT) { @@ -48,8 +49,14 @@ void ControllerBehavior::OnUpdate() { !(GetEntity()->GetFirstComponent<phx::Transform>()->GetParent() == runtime_entity->GetFirstComponent<phx::Transform>())) { GetEntity()->GetFirstComponent<phx::Transform>()->SetParent( - runtime_entity->GetFirstComponent<phx::Transform>()); + runtime_entity->GetFirstComponent<phx::Transform>(), false); } } -void ControllerBehavior::SetSide(Side side) { side_ = side; } +void OpenVRControllerBehavior::SetSide(Side side) { side_ = side; } + +OpenVRControllerBehavior::Side OpenVRControllerBehavior::GetSide() const { + return side_; +} + +} // namespace phx diff --git a/demos/viewer/src/controller_behavior.hpp b/library/phx/openvr_controller_behavior.hpp similarity index 79% rename from demos/viewer/src/controller_behavior.hpp rename to library/phx/openvr_controller_behavior.hpp index 5f6855c2736df123f19220e24df2f6ccbd571a1a..c8fb9898d17f86bb238273837acb31dcc97df088 100644 --- a/demos/viewer/src/controller_behavior.hpp +++ b/library/phx/openvr_controller_behavior.hpp @@ -20,21 +20,26 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef DEMOS_VIEWER_SRC_CONTROLLER_BEHAVIOR_HPP_ -#define DEMOS_VIEWER_SRC_CONTROLLER_BEHAVIOR_HPP_ +#ifndef LIBRARY_PHX_OPENVR_CONTROLLER_BEHAVIOR_HPP_ +#define LIBRARY_PHX_OPENVR_CONTROLLER_BEHAVIOR_HPP_ #include "phx/behavior.hpp" +#include "phx/export.hpp" -class ControllerBehavior : public phx::Behavior { +namespace phx { + +class PHOENIX_EXPORT OpenVRControllerBehavior : public Behavior { public: enum Side { LEFT, RIGHT }; void OnUpdate() override; void SetSide(Side side); + Side GetSide() const; private: Side side_ = Side::LEFT; }; +} // namespace phx -#endif // DEMOS_VIEWER_SRC_CONTROLLER_BEHAVIOR_HPP_ +#endif // LIBRARY_PHX_OPENVR_CONTROLLER_BEHAVIOR_HPP_ diff --git a/library/phx/openvr_controller_system.cpp b/library/phx/openvr_controller_system.cpp new file mode 100644 index 0000000000000000000000000000000000000000..737f3be65a0201f4929455f29bf46e43456cccc9 --- /dev/null +++ b/library/phx/openvr_controller_system.cpp @@ -0,0 +1,146 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "openvr_controller_system.hpp" + +#include <memory> +#include <string> +#include <utility> + +#include "display_system_openvr.hpp" +#include "logger.hpp" +#include "material_handle.hpp" +#include "mesh_handle.hpp" +#include "openvr_controller_behavior.hpp" +#include "openvr_resource_loader.hpp" +#include "resource_manager.hpp" +#include "resource_pointer.hpp" +#include "transform.hpp" + +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; + } + + auto& resource_manager = ResourceManager::instance(); + auto openvr_loader = std::make_unique<OpenVRResourceLoader>(hmd_); + resource_manager.RegisterResourceType("openVR", std::move(openvr_loader)); +} + +Entity* AddControllerEntity(const std::shared_ptr<phx::Scene>& scene, + ResourcePointer<Mesh> mesh, + ResourcePointer<Material> material, + OpenVRControllerBehavior::Side side) { + Entity* controller = scene->CreateEntity(); + controller->AddComponent<MeshHandle>()->SetMesh(mesh); + controller->AddComponent<Transform>(); + controller->AddComponent<MaterialHandle>()->SetMaterial(material); + controller->AddComponent<OpenVRControllerBehavior>()->SetSide(side); + + return controller; +} + +Entity* AddController(const std::shared_ptr<phx::Scene>& scene, + OpenVRControllerBehavior::Side side) { + auto& resource_manager = ResourceManager::instance(); + std::string side_string = + side == OpenVRControllerBehavior::LEFT ? "left" : "right"; + + ResourceDeclaration mesh_declaration{ + {"TYPE", "openVR"}, {"OpenVR_type", "mesh"}, {"side", side_string}}; + auto mesh_proxy = + ResourceManager::instance().DeclareResource(mesh_declaration); + mesh_proxy->Load(); + + ResourceDeclaration material_declaration{ + {"TYPE", "openVR"}, {"OpenVR_type", "material"}, {"side", side_string}}; + auto material_proxy = resource_manager.DeclareResource(material_declaration); + material_proxy->Load(); + + if (mesh_proxy->GetAs<Mesh>() != nullptr) { + return AddControllerEntity(scene, ResourcePointer<Mesh>(mesh_proxy), + ResourcePointer<Material>(material_proxy), side); + } + return nullptr; +} + +void OpenVRControllerSystem::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; + + // get controller entities in the scene + Entity* left_controller_entity = nullptr; + Entity* right_controller_entity = nullptr; + auto controller_entities = + GetEngine()->GetEntitiesWithComponents<OpenVRControllerBehavior>(); + for (auto entity : controller_entities) { + if (entity->GetFirstComponent<OpenVRControllerBehavior>()->GetSide() == + OpenVRControllerBehavior::LEFT) { + 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) { + // do we have a left controller in the scene? + if (left_controller_entity == nullptr) { + // create that controller + left_controller_entity = + AddController(scene, OpenVRControllerBehavior::LEFT); + } + left_controller_active = true; + } else if (role == vr::TrackedControllerRole_RightHand) { + if (right_controller_entity == nullptr) { + right_controller_entity = + AddController(scene, OpenVRControllerBehavior::RIGHT); + } + right_controller_active = true; + } + } + + // remove unnecessary entities + if (!left_controller_active && left_controller_entity != nullptr) { + scene->RemoveEntity(left_controller_entity); + } + if (!right_controller_active && right_controller_entity != nullptr) { + scene->RemoveEntity(right_controller_entity); + } +} + +} // namespace phx diff --git a/library/phx/tracking_system.hpp b/library/phx/openvr_controller_system.hpp similarity index 62% rename from library/phx/tracking_system.hpp rename to library/phx/openvr_controller_system.hpp index 4022d7e7f2c28358e62a4d665862f84ea1228751..21a3f8b91e243cd6643f34ed9921a31980561ed2 100644 --- a/library/phx/tracking_system.hpp +++ b/library/phx/openvr_controller_system.hpp @@ -20,49 +20,44 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef LIBRARY_PHX_TRACKING_SYSTEM_HPP_ -#define LIBRARY_PHX_TRACKING_SYSTEM_HPP_ +#ifndef LIBRARY_PHX_OPENVR_CONTROLLER_SYSTEM_HPP_ +#define LIBRARY_PHX_OPENVR_CONTROLLER_SYSTEM_HPP_ -#include <string> +#include <memory> +#include <vector> + +SUPPRESS_WARNINGS_BEGIN +#include "glm/glm.hpp" +SUPPRESS_WARNINGS_END #include "phx/display_system.hpp" #include "phx/engine.hpp" #include "phx/export.hpp" +#include "phx/hmd.hpp" #include "phx/system.hpp" namespace phx { - -class PHOENIX_EXPORT TrackingSystem : public System { +class PHOENIX_EXPORT OpenVRControllerSystem : public System { public: - TrackingSystem() = delete; - TrackingSystem(const TrackingSystem&) = delete; - TrackingSystem(TrackingSystem&&) = default; - ~TrackingSystem() = default; + OpenVRControllerSystem() = delete; + OpenVRControllerSystem(const OpenVRControllerSystem&) = delete; + OpenVRControllerSystem(OpenVRControllerSystem&&) = default; + virtual ~OpenVRControllerSystem() = default; - TrackingSystem& operator=(const TrackingSystem&) = delete; - TrackingSystem& operator=(TrackingSystem&&) = default; + OpenVRControllerSystem& operator=(const OpenVRControllerSystem&) = delete; + OpenVRControllerSystem& operator=(OpenVRControllerSystem&&) = default; void Update(const FrameTimer::TimeInfo&) override; - std::string ToString() const override; - protected: - TrackingSystem(Engine* engine, DisplaySystem* display_system); - - private: - void CreateRuntimeEntities(); - template <typename SystemType, typename... SystemArguments> friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments); - explicit TrackingSystem(Engine* engine); + OpenVRControllerSystem(Engine* engine, DisplaySystem* display_system); - Entity* hmd_entity_ = nullptr; - Entity* left_eye_entity_ = nullptr; - Entity* right_eye_entity_ = nullptr; - Entity* left_controller_entity_ = nullptr; - Entity* right_controller_entity_ = nullptr; + private: + HMD* hmd_ = nullptr; }; } // namespace phx -#endif // LIBRARY_PHX_TRACKING_SYSTEM_HPP_ +#endif // LIBRARY_PHX_OPENVR_CONTROLLER_SYSTEM_HPP_ diff --git a/library/phx/projection.hpp b/library/phx/projection.hpp index 3acec40ce95d14ac23957110bf0255567481eb59..5f882493183b25de522b786c91ed9e1d564a56a4 100644 --- a/library/phx/projection.hpp +++ b/library/phx/projection.hpp @@ -34,7 +34,7 @@ SUPPRESS_WARNINGS_END namespace phx { -class PHOENIX_EXPORT Projection : public Component { +class PHOENIX_EXPORT Projection final : public Component { public: const glm::mat4& GetMatrix() const; @@ -45,6 +45,15 @@ class PHOENIX_EXPORT Projection : public Component { std::string ToString() const override; + protected: + friend Entity; + Projection() = default; + Projection(const Projection&) = delete; + Projection(Projection&&) = default; + + Projection& operator=(const Projection&) = delete; + Projection& operator=(Projection&&) = default; + private: glm::mat4 matrix_; }; diff --git a/library/phx/render_target.hpp b/library/phx/render_target.hpp index 4f9b65a69ebb1a780c0c6ac9176523a7afa78236..b87da3212c45b003218067c09ff3d5199976c41b 100644 --- a/library/phx/render_target.hpp +++ b/library/phx/render_target.hpp @@ -37,7 +37,6 @@ SUPPRESS_WARNINGS_END #include "gl/texture.hpp" #include "phx/export.hpp" -#include "phx/hmd.hpp" namespace phx { diff --git a/library/phx/rendering_system.cpp b/library/phx/rendering_system.cpp index 15a17eb3ef60a938bec74ec15002c0bc98ff0310..45d55f276d7a08076cb91a6274c69c4fb5819544 100644 --- a/library/phx/rendering_system.cpp +++ b/library/phx/rendering_system.cpp @@ -31,7 +31,8 @@ #include "blit_pass.hpp" #include "clear_pass.hpp" -#include "display_system.hpp" +#include "display_system_openvr.hpp" +#include "display_system_window.hpp" #include "engine.hpp" #include "light.hpp" #include "logger.hpp" @@ -39,6 +40,7 @@ #include "mesh.hpp" #include "mesh_handle.hpp" #include "projection.hpp" +#include "runtime_component.hpp" #include "scene.hpp" #include "system.hpp" #include "transform.hpp" @@ -51,7 +53,6 @@ RenderingSystem::RenderingSystem(Engine* engine, DisplaySystem* display_system) : System(engine) { if (!display_system) error("RenderingSystem needs a valid DisplaySystem."); InitializeOpenGL(); - InitializeRenderTargets(); SetupFramegraph(); } @@ -63,59 +64,9 @@ void RenderingSystem::InitializeOpenGL() { gl::print_error(prefix.c_str()); } -void RenderingSystem::InitializeRenderTargets() { - const auto displaySystem = engine_->GetSystem<DisplaySystem>(); - if (displaySystem == nullptr) { - error( - "The Rendering System cannot be initialized without a display system"); - return; - } - - // TODO(all) Support multiple windows and HMD? - if (displaySystem->GetHMD() != nullptr) { - HMD* hmd = displaySystem->GetHMD(); - right_render_target_ = - std::make_unique<RenderTarget>(hmd->GetViewportSize()); - left_render_target_ = - std::make_unique<RenderTarget>(hmd->GetViewportSize()); - } else if (displaySystem->GetWindow() != nullptr) { - auto window = displaySystem->GetWindow(); - window_render_target_ = std::make_unique<RenderTarget>(window->GetSize()); - } -} - void RenderingSystem::SetupFramegraph() { + // set up an empty frame graph, just so we always have one... frame_graph_ = std::make_unique<FrameGraph>(); - - if (right_render_target_ != nullptr && left_render_target_ != nullptr) { - frame_graph_->AddRenderPass( - std::make_unique<ClearPass>(right_render_target_.get())); - frame_graph_->AddRenderPass( - std::make_unique<ClearPass>(left_render_target_.get())); - auto geometry_pass = static_cast<GeometryPass*>(frame_graph_->AddRenderPass( - std::make_unique<GeometryPass>(right_render_target_.get()))); - geometry_passes_.push_back(geometry_pass); - geometry_pass = static_cast<GeometryPass*>(frame_graph_->AddRenderPass( - std::make_unique<GeometryPass>(left_render_target_.get()))); - geometry_passes_.push_back(geometry_pass); - - frame_graph_->AddRenderPass( - std::make_unique<BlitPass>(right_render_target_.get())); - } else if (window_render_target_ != nullptr) { - frame_graph_->AddRenderPass( - std::make_unique<ClearPass>(window_render_target_.get())); - auto geometry_pass = static_cast<GeometryPass*>(frame_graph_->AddRenderPass( - std::make_unique<GeometryPass>(window_render_target_.get()))); - geometry_passes_.push_back(geometry_pass); - frame_graph_->AddRenderPass( - std::make_unique<BlitPass>(window_render_target_.get())); - } else { - error( - "[RenderingSystem] No Render Targets are defined, so no framegraph " - "will be created."); - return; - } - frame_graph_->Initialize(); } @@ -127,16 +78,30 @@ void RenderingSystem::Update(const FrameTimer::TimeInfo&) { if (GetEngine()->GetScene() == nullptr) { return; } + if (render_targets_.empty()) { + return; + } - // TODO(@all) refactor this part, once there is a way to get entities by name - for (auto& entity : GetEngine()->GetEntities()) { - if (entity->GetName() == "RuntimeEntityEyeLeft") { - left_render_target_->SetView( - inverse(entity->GetFirstComponent<Transform>()->GetGlobalMatrix())); + const auto display_system_window = engine_->GetSystem<DisplaySystemWindow>(); + const auto display_system_hmd = engine_->GetSystem<DisplaySystemOpenVR>(); + + // @TODO(anyone): these render parameters should be extracted from the scene + // somehow, the RenderingSystem shouldn't have to know about this. + if (display_system_hmd != nullptr && render_targets_.size() >= 2) { + auto right_eye_entities = + GetEngine()->GetEntitiesWithComponents<RuntimeComponent<RIGHT_EYE>>(); + if (!right_eye_entities.empty()) { + render_targets_[0]->SetView(inverse(right_eye_entities[0] + ->GetFirstComponent<Transform>() + ->GetGlobalMatrix())); } - if (entity->GetName() == "RuntimeEntityEyeRight") { - right_render_target_->SetView( - inverse(entity->GetFirstComponent<Transform>()->GetGlobalMatrix())); + + auto left_eye_entities = + GetEngine()->GetEntitiesWithComponents<RuntimeComponent<LEFT_EYE>>(); + if (!left_eye_entities.empty()) { + render_targets_[1]->SetView(inverse(left_eye_entities[0] + ->GetFirstComponent<Transform>() + ->GetGlobalMatrix())); } } @@ -150,9 +115,9 @@ void RenderingSystem::Update(const FrameTimer::TimeInfo&) { if (mesh_handle != nullptr) { Material* material = nullptr; if (material_handle != nullptr) - material = material_handle->GetMaterial(); + material = material_handle->GetMaterial().Get(); rendering_instances.push_back( - {mesh_handle->GetMesh(), material, transform}); + {mesh_handle->GetMesh().Get(), material, transform}); } else if (light != nullptr) { light_transform_pairs.push_back( std::pair<Light*, Transform*>(light, transform)); @@ -162,22 +127,25 @@ void RenderingSystem::Update(const FrameTimer::TimeInfo&) { } } } - for (auto geometry_pass : geometry_passes_) { + auto geometry_passes = frame_graph_->GetRenderPasses<GeometryPass>(); + for (auto geometry_pass : geometry_passes) { geometry_pass->SetData(rendering_instances, light_transform_pairs); } - HMD* hmd = engine_->GetSystem<DisplaySystem>()->GetHMD(); - if (left_render_target_ != nullptr) { - left_render_target_->SetProjection(hmd->GetProjectionMatrix(HMD::LEFT_EYE)); + // @TODO(anyone) see above, should also not be in the RenderingSystem, but + // the scene (Camera?) should store the connection between render target + // and Camera + if (display_system_hmd != nullptr && render_targets_.size() >= 2) { + HMD* hmd = display_system_hmd->GetHMD(); + render_targets_[0]->SetProjection(hmd->GetProjectionMatrix(HMD::RIGHT_EYE)); + render_targets_[1]->SetProjection(hmd->GetProjectionMatrix(HMD::LEFT_EYE)); } - if (right_render_target_ != nullptr) { - right_render_target_->SetProjection( - hmd->GetProjectionMatrix(HMD::RIGHT_EYE)); - } - if (window_render_target_ != nullptr && !projection_transform_pairs.empty()) { - window_render_target_->SetProjection( + + if (display_system_hmd == nullptr && display_system_window != nullptr && + !render_targets_.empty() && !projection_transform_pairs.empty()) { + render_targets_[0]->SetProjection( projection_transform_pairs[0].first->GetMatrix()); - window_render_target_->SetView( + render_targets_[0]->SetView( glm::inverse(projection_transform_pairs[0].second->GetGlobalMatrix())); } @@ -188,17 +156,27 @@ FrameGraph* RenderingSystem::GetFrameGraph() const { return frame_graph_.get(); } +void RenderingSystem::SetFrameGraph(std::unique_ptr<FrameGraph> frame_graph) { + frame_graph_ = std::move(frame_graph); +} + std::string RenderingSystem::ToString() const { return "RenderingSystem with #FrameGraph-Passes: " + std::to_string(frame_graph_->GetNumberOfPasses()); } -phx::RenderTarget* RenderingSystem::GetRightRenderTarget() const { - return right_render_target_.get(); +void RenderingSystem::SetRenderTargets( + std::vector<std::unique_ptr<RenderTarget>>* render_targets) { + render_targets_ = std::move(*render_targets); } -phx::RenderTarget* RenderingSystem::GetLeftRenderTarget() const { - return left_render_target_.get(); +std::vector<RenderTarget*> RenderingSystem::GetRenderTargets() const { + std::vector<RenderTarget*> render_targets; + std::transform( + render_targets_.begin(), render_targets_.end(), + std::back_inserter(render_targets), + [](const std::unique_ptr<RenderTarget>& ptr) { return ptr.get(); }); + return render_targets; } } // namespace phx diff --git a/library/phx/rendering_system.hpp b/library/phx/rendering_system.hpp index 43ca9b6df6fd09ce363ab4c7e2ee76a73fec3ff7..886f321b624c15f9b0859086ff2e96c7898fea23 100644 --- a/library/phx/rendering_system.hpp +++ b/library/phx/rendering_system.hpp @@ -49,35 +49,29 @@ class PHOENIX_EXPORT RenderingSystem : public System { void Update(const FrameTimer::TimeInfo& time_info) override; FrameGraph* GetFrameGraph() const; + void SetFrameGraph(std::unique_ptr<FrameGraph> frame_graph); std::string ToString() const override; RenderingSystem& operator=(const RenderingSystem&) = delete; RenderingSystem& operator=(RenderingSystem&&) = default; - RenderTarget* GetRightRenderTarget() const; - RenderTarget* GetLeftRenderTarget() const; + void SetRenderTargets( + std::vector<std::unique_ptr<RenderTarget>>* render_targets); + std::vector<RenderTarget*> GetRenderTargets() const; protected: + template <typename SystemType, typename... SystemArguments> + friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments); RenderingSystem(Engine* engine, DisplaySystem* display_system); private: void InitializeOpenGL(); - void InitializeRenderTargets(); void SetupFramegraph(); - template <typename SystemType, typename... SystemArguments> - friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments); - explicit RenderingSystem(Engine* engine); - std::unique_ptr<FrameGraph> frame_graph_; - // either window_render_target or the other two are nullptr - std::unique_ptr<RenderTarget> right_render_target_; - std::unique_ptr<RenderTarget> left_render_target_; - std::unique_ptr<RenderTarget> window_render_target_; - - std::vector<GeometryPass*> geometry_passes_; + std::vector<std::unique_ptr<RenderTarget>> render_targets_; }; } // namespace phx diff --git a/library/phx/resource_manager.cpp b/library/phx/resource_manager.cpp index 158befcea4a783d3111d9d3e1f18b3008e62495c..2d566f968c9e5be02d7bd71eb0f1017d849d83aa 100644 --- a/library/phx/resource_manager.cpp +++ b/library/phx/resource_manager.cpp @@ -64,7 +64,7 @@ std::unique_ptr<phx::Resource> ResourceManager::Load( const ResourceDeclaration &declaration) { ResourceLoadStrategy *loader = nullptr; if (declaration.find("TYPE") != declaration.end()) { - loader = DetermineLoader(declaration["TYPE"]); + loader = GetLoaderForType(declaration["TYPE"]); } else { warn( "No Loader for Resource Declaration {}, can be determined, since it " @@ -75,7 +75,7 @@ std::unique_ptr<phx::Resource> ResourceManager::Load( return loader->Load(declaration); } -phx::ResourceLoadStrategy *ResourceManager::DetermineLoader( +phx::ResourceLoadStrategy *ResourceManager::GetLoaderForType( const std::string &type) const { auto loader_iterator = loaders_by_type_.find(type); if (loader_iterator == loaders_by_type_.end()) { diff --git a/library/phx/resource_manager.hpp b/library/phx/resource_manager.hpp index 926c47832edc9a5d039b4ab95560f9b1e7e67f55..25e9bc943e903033fcf876e8a4b1a55e43c36797 100644 --- a/library/phx/resource_manager.hpp +++ b/library/phx/resource_manager.hpp @@ -52,6 +52,8 @@ class PHOENIX_EXPORT ResourceManager final : public singleton<ResourceManager> { ResourceProxy* DeclareResource(const ResourceDeclaration& declaration); + ResourceLoadStrategy* GetLoaderForType(const std::string& type) const; + protected: friend ResourceManager& phx::singleton<ResourceManager>::instance<>(); ResourceManager(); @@ -63,8 +65,6 @@ class PHOENIX_EXPORT ResourceManager final : public singleton<ResourceManager> { std::unique_ptr<Resource> Load(const ResourceDeclaration& declaration); - ResourceLoadStrategy* DetermineLoader(const std::string& type) const; - private: void RegisterShaderResourceExtensions(); void RegisterMeshResourceExtensions(); diff --git a/library/phx/resource_pointer.hpp b/library/phx/resource_pointer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f2cb9391373e770bcf040f78424061c920453f15 --- /dev/null +++ b/library/phx/resource_pointer.hpp @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef LIBRARY_PHX_RESOURCE_POINTER_HPP_ +#define LIBRARY_PHX_RESOURCE_POINTER_HPP_ + +#include "phx/export.hpp" +#include "resource_proxy.hpp" + +namespace phx { + +template <class ResourceType> +class PHOENIX_EXPORT ResourcePointer { + public: + ResourcePointer() : proxy_(nullptr) {} + explicit ResourcePointer(nullptr_t) : proxy_(nullptr) {} + explicit ResourcePointer(ResourceProxy *proxy) : proxy_(proxy) {} + ResourcePointer(const ResourcePointer &) = default; + ResourcePointer(ResourcePointer &&) = default; + ResourcePointer &operator=(const ResourcePointer &) = default; + ResourcePointer &operator=(ResourcePointer &&) = default; + ~ResourcePointer() = default; + + ResourceType *operator->() { return proxy_->GetAs<ResourceType>(); } + bool operator==(const ResourcePointer<ResourceType> &a) const { + return a.GetProxy() == proxy_; + } + bool operator==(nullptr_t) const { + return proxy_ == nullptr || !proxy_->IsOnline(); + } + bool operator!=(const ResourcePointer<ResourceType> &a) const { + return a.GetProxy() != proxy_; + } + bool operator!=(nullptr_t) const { + return proxy_ != nullptr && proxy_->IsOnline(); + } + bool operator<(const ResourcePointer<ResourceType> &a) const { + return a.GetProxy() < proxy_; + } + operator bool() const { return proxy_ != nullptr && proxy_->IsOnline(); } + + ResourceProxy *GetProxy() const { return proxy_; } + ResourceType *Get() const { return proxy_->GetAs<ResourceType>(); } + + private: + ResourceProxy *proxy_; +}; + +} // namespace phx + +#endif // LIBRARY_PHX_RESOURCE_POINTER_HPP_ diff --git a/library/phx/resource_utils.cpp b/library/phx/resource_utils.cpp index b6f88334f32210acfc6e6bc1d17460808e0235e5..e4e7f6990c71a92f781618f91ee2418e89f66f26 100644 --- a/library/phx/resource_utils.cpp +++ b/library/phx/resource_utils.cpp @@ -51,13 +51,4 @@ std::string ResourceUtils::ExtractFileExtension(const std::string& file_name) { return extension; } -phx::ResourceProxy* ResourceUtils::LoadResourceFromFile( - const std::string& file_name, nlohmann::json additional_info /*= {}*/, - bool absolute_path /*= false*/) { - auto proxy = phx::ResourceManager::instance().DeclareResource( - DeclarationFromFile(file_name, additional_info, absolute_path)); - proxy->Load(); - return proxy; -} - } // namespace phx diff --git a/library/phx/resource_utils.hpp b/library/phx/resource_utils.hpp index 8a7546f4b94a86bb9f0b72d456d5512fc485d0cc..b599385d96f4231bce773898872d0db9b6fb7341 100644 --- a/library/phx/resource_utils.hpp +++ b/library/phx/resource_utils.hpp @@ -33,14 +33,15 @@ #include "phx/logger.hpp" #include "phx/resource_declaration.hpp" #include "phx/resource_load_strategy.hpp" +#include "phx/resource_manager.hpp" +#include "phx/resource_pointer.hpp" #include "phx/resource_proxy.hpp" #include "phx/singleton.hpp" namespace phx { /** - * The ResourceManager is the central instance for the management of all - * externally provided content, aka resources. Resources have to be declared - * first before being available in the system. + * The ResourceUtils class contains static convenience methods to simplify + * interactions with the resource system. It should never hold any state. */ class PHOENIX_EXPORT ResourceUtils final { public: @@ -48,9 +49,16 @@ class PHOENIX_EXPORT ResourceUtils final { static ResourceDeclaration DeclarationFromFile( const std::string& file_name, nlohmann::json additional_info = {}, bool absolute_path = false); - static ResourceProxy* LoadResourceFromFile( + + template <class ResourceType> + static ResourcePointer<ResourceType> LoadResourceFromFile( const std::string& file_name, nlohmann::json additional_info = {}, - bool absolute_path = false); + bool absolute_path = false) { + auto proxy = phx::ResourceManager::instance().DeclareResource( + DeclarationFromFile(file_name, additional_info, absolute_path)); + proxy->Load(); + return ResourcePointer<ResourceType>(proxy); + } }; } // namespace phx diff --git a/library/phx/scene_loader.cpp b/library/phx/scene_loader.cpp index 0c02dc743854508df0e4a20290b9806287c08800..a112b8884c0fe1aa8f192d5fcb820c7f72073867 100644 --- a/library/phx/scene_loader.cpp +++ b/library/phx/scene_loader.cpp @@ -35,20 +35,18 @@ namespace phx { bool SceneLoader::InsertModelIntoScene(const std::string& file_name, Scene* scene) { - auto model_proxy = ResourceUtils::LoadResourceFromFile(file_name); - if (!model_proxy->IsOnline()) { + auto model = ResourceUtils::LoadResourceFromFile<Model>(file_name); + if (!model.GetProxy()->IsOnline()) { return false; } - auto model = model_proxy->GetAs<Model>(); - - for (auto mesh_proxy : model->GetMeshes()) { + for (auto mesh : model->GetMeshes()) { auto entity = scene->CreateEntity(); entity->AddComponent<Transform>(); - entity->AddComponent<MeshHandle>()->SetMeshProxy(mesh_proxy); - phx::ResourceProxy* material_proxy = model->GetMaterialForMesh(mesh_proxy); - if (material_proxy != nullptr) { - entity->AddComponent<MaterialHandle>()->SetMaterialProxy(material_proxy); + entity->AddComponent<MeshHandle>()->SetMesh(mesh); + ResourcePointer<Material> material = model->GetMaterialForMesh(mesh); + if (material != nullptr && material.GetProxy()->IsOnline()) { + entity->AddComponent<MaterialHandle>()->SetMaterial(material); } } diff --git a/library/phx/setup.cpp b/library/phx/setup.cpp index 28773ff4351a4bf94d37bc9ff23b667c470d208c..2b97bad44ba18b34ec81f4a458f3869c44af6318 100644 --- a/library/phx/setup.cpp +++ b/library/phx/setup.cpp @@ -24,15 +24,21 @@ #include <cassert> #include <memory> +#include <utility> #include "behavior_system.hpp" -#include "display_system.hpp" +#include "blit_pass.hpp" +#include "clear_pass.hpp" +#include "display_system_openvr.hpp" +#include "display_system_window.hpp" #include "engine.hpp" #include "hmd.hpp" #include "input_system.hpp" #include "logger.hpp" +#include "openvr_controller_system.hpp" +#include "render_target.hpp" #include "rendering_system.hpp" -#include "tracking_system.hpp" +#include "tracking_system_openvr.hpp" #undef CreateWindow namespace phx { @@ -42,21 +48,94 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine() { auto engine_ptr = engine.get(); engine->SetScene(std::make_shared<Scene>()); - engine->CreateSystem<BehaviorSystem>(); + auto behavior_system = engine->CreateSystem<BehaviorSystem>(); engine->CreateSystem<InputSystem>()->AddQuitCallback( [engine_ptr]() { engine_ptr->Stop(); }); - auto displaysys = engine->CreateSystem<DisplaySystem>(); + auto displaysys_window = engine->CreateSystem<DisplaySystemWindow>(); + DisplaySystemOpenVR* displaysys_hmd = nullptr; + bool using_hmd = false; if (HMD::IsHMDPresent()) { info("An HMD is present so we use it"); - displaysys->CreateHMD(); + using_hmd = true; + + displaysys_hmd = engine->CreateSystem<DisplaySystemOpenVR>(); + displaysys_hmd->CreateHMD(); + displaysys_window->CreateWindow( + "Phoenix HMD Companion", glm::uvec2(10, 10), + displaysys_hmd->GetHMD()->GetViewportSize()); + } else { + displaysys_window->CreateWindow("Phoenix", glm::uvec2(100, 100)); + } + + auto rendering_system = + engine->CreateSystem<RenderingSystem>(engine->GetSystem<DisplaySystem>()); + + // fix update order + engine->GetUpdateOrder().MoveToBack(displaysys_window); + engine->GetUpdateOrder().MoveBefore(rendering_system, displaysys_window); + + // setup rendering and frame graph + if (using_hmd) { + // we need the render targets of the DisplaySystemOpenVR, but the + // DisplaySystemWindow only exists for blitting the result of the left eye + // into the window and does not create a render target themselves + auto render_targets = displaysys_hmd->CreateRenderTargets(); + rendering_system->SetRenderTargets(&render_targets); + SetupDefaultFrameGraphOpenVR(rendering_system); + auto tracking_system = + engine->CreateSystem<TrackingSystemOpenVR>(displaysys_hmd); + auto controller_system = engine->CreateSystem<OpenVRControllerSystem>( + engine->GetSystem<DisplaySystem>()); + engine->GetUpdateOrder().MoveBefore(tracking_system, rendering_system); + engine->GetUpdateOrder().MoveAfter(behavior_system, tracking_system); + engine->GetUpdateOrder().MoveAfter(tracking_system, controller_system); } else { - displaysys->CreateWindow("Phoenix", glm::uvec2(100, 100)); + auto render_targets = displaysys_window->CreateRenderTargets(); + rendering_system->SetRenderTargets(&render_targets); + SetupDefaultFrameGraphWindow(rendering_system); } - engine->CreateSystem<RenderingSystem>(engine->GetSystem<DisplaySystem>()); - engine->CreateSystem<TrackingSystem>(engine->GetSystem<DisplaySystem>()); return engine; } +void Setup::SetupDefaultFrameGraphWindow(RenderingSystem* rendering_system) { + auto frame_graph = std::make_unique<FrameGraph>(); + auto render_targets = rendering_system->GetRenderTargets(); + if (render_targets.empty()) { + error("Cannot setup default frame graph (window): no render targets."); + return; + } + + frame_graph->AddRenderPass(std::make_unique<ClearPass>(render_targets[0])); + frame_graph->AddRenderPass(std::make_unique<GeometryPass>(render_targets[0])); + frame_graph->AddRenderPass(std::make_unique<BlitPass>(render_targets[0])); + + frame_graph->Initialize(); + + rendering_system->SetFrameGraph(std::move(frame_graph)); +} + +void Setup::SetupDefaultFrameGraphOpenVR(RenderingSystem* rendering_system) { + auto frame_graph = std::make_unique<FrameGraph>(); + auto render_targets = rendering_system->GetRenderTargets(); + if (render_targets.size() < 2) { + error( + "Cannot setup default frame graph (OpenVR): not enough render " + "targets."); + return; + } + + frame_graph->AddRenderPass(std::make_unique<ClearPass>(render_targets[0])); + frame_graph->AddRenderPass(std::make_unique<ClearPass>(render_targets[1])); + frame_graph->AddRenderPass(std::make_unique<GeometryPass>(render_targets[0])); + frame_graph->AddRenderPass(std::make_unique<GeometryPass>(render_targets[1])); + + frame_graph->AddRenderPass(std::make_unique<BlitPass>(render_targets[0])); + + frame_graph->Initialize(); + + rendering_system->SetFrameGraph(std::move(frame_graph)); +} + } // namespace phx diff --git a/library/phx/setup.hpp b/library/phx/setup.hpp index d96286b93bd00da09675b298b3c2c5508acfbeae..c5fb4a7496c2990dc871484eb020e0c22c0b437b 100644 --- a/library/phx/setup.hpp +++ b/library/phx/setup.hpp @@ -27,6 +27,8 @@ #include "phx/engine.hpp" #include "phx/export.hpp" +#include "phx/frame_graph.hpp" +#include "phx/rendering_system.hpp" #include "phx/scene.hpp" #include "phx/window.hpp" @@ -40,6 +42,9 @@ class PHOENIX_EXPORT Setup { // i.e.: RenderingSystem and an InputSystem // also creates an empty scene for the engine static std::unique_ptr<Engine> CreateDefaultEngine(); + + static void SetupDefaultFrameGraphWindow(RenderingSystem* rendering_system); + static void SetupDefaultFrameGraphOpenVR(RenderingSystem* rendering_system); }; } // namespace phx diff --git a/library/phx/shader_program.cpp b/library/phx/shader_program.cpp index 3532ffaa356cefa15e15be00daa3df13ce2b3e9d..445f001ef4171700c652313dc480c1e557673caa 100644 --- a/library/phx/shader_program.cpp +++ b/library/phx/shader_program.cpp @@ -33,14 +33,14 @@ namespace phx { -bool ShaderProgram::SetShaderProxy(ShaderChannel channel, - ResourceProxy* shader_source) { +bool ShaderProgram::SetShader( + ShaderChannel channel, ResourcePointer<ShaderSource> shader_source) { auto& entry = shaders_[channel]; entry.first = shader_source; entry.second = std::make_unique<gl::shader>(this->ConvertChannelToGL(channel)); gl::shader* shader = entry.second.get(); - shader->set_source(this->GetShaderSource(shader_source)); + shader->set_source(shader_source->GetSource()); return this->CompileAndAttachShader(shader); } @@ -85,9 +85,4 @@ GLenum ShaderProgram::ConvertChannelToGL(ShaderChannel channel) const { } } -std::string ShaderProgram::GetShaderSource(ResourceProxy* shader_proxy) const { - auto shader_source = shader_proxy->GetAs<phx::ShaderSource>(); - return shader_source->GetSource(); -} - } // namespace phx diff --git a/library/phx/shader_program.hpp b/library/phx/shader_program.hpp index 2c6d92dc7b0be2d6c6d23a509257dd9d659a2e3b..3244f15ee0a6174b50ccfd3301026bdfca7178e4 100644 --- a/library/phx/shader_program.hpp +++ b/library/phx/shader_program.hpp @@ -38,7 +38,8 @@ SUPPRESS_WARNINGS_END #include "phx/export.hpp" #include "phx/mesh.hpp" #include "phx/render_pass.hpp" -#include "phx/resource_proxy.hpp" +#include "phx/resource_pointer.hpp" +#include "phx/shader_source.hpp" #include "phx/transform.hpp" namespace phx { @@ -67,7 +68,8 @@ class PHOENIX_EXPORT ShaderProgram : public gl::program { ShaderProgram& operator=(const ShaderProgram&) = delete; ShaderProgram& operator=(ShaderProgram&&) = default; - bool SetShaderProxy(ShaderChannel channel, ResourceProxy* shader_source); + bool SetShader(ShaderChannel channel, + ResourcePointer<ShaderSource> shader_source); template <typename T> void SetUniform(const std::string& name, T value) { @@ -82,9 +84,8 @@ class PHOENIX_EXPORT ShaderProgram : public gl::program { GLenum ConvertChannelToGL(ShaderChannel channel) const; - std::string GetShaderSource(ResourceProxy* shader_proxy) const; - - using ShaderHandle = std::pair<ResourceProxy*, std::unique_ptr<gl::shader>>; + using ShaderHandle = + std::pair<ResourcePointer<ShaderSource>, std::unique_ptr<gl::shader>>; std::array<ShaderHandle, MAX_SHADER_CHANNEL> shaders_; }; diff --git a/library/phx/splash_screen.cpp b/library/phx/splash_screen.cpp index fa0afd0b127671c40fa939a4dde90e82390d6c0c..3a36247ff754345f13c6ad1b564496a68e8f1b9a 100644 --- a/library/phx/splash_screen.cpp +++ b/library/phx/splash_screen.cpp @@ -36,8 +36,8 @@ namespace phx { phx::SplashScreen::SplashScreen(Window* window) : window_(window) { - splash_image_proxy_ = - ResourceUtils::LoadResourceFromFile("textures/splash_progress.png"); + splash_image_ = ResourceUtils::LoadResourceFromFile<Image>( + "textures/splash_progress.png"); progressbar_image_ = std::make_unique<Image>(std::array<std::size_t, 2>{{1ull, 1ull}}, 32); @@ -47,8 +47,7 @@ phx::SplashScreen::SplashScreen(Window* window) : window_(window) { } void SplashScreen::Draw() { - Image* splash_image = splash_image_proxy_->GetAs<Image>(); - auto splash_dimensions = splash_image->GetDimensions(); + auto splash_dimensions = splash_image_->GetDimensions(); auto progress_dimensions = progressbar_image_->GetDimensions(); gl::initialize(); gl::framebuffer splash_buff; @@ -62,7 +61,7 @@ void SplashScreen::Draw() { 0); splash_tex.set_sub_image(0, 0, 0, static_cast<GLsizei>(splash_dimensions[0]), static_cast<GLsizei>(splash_dimensions[1]), GL_RGBA, - GL_UNSIGNED_BYTE, splash_image->GetPixels().first); + GL_UNSIGNED_BYTE, splash_image_->GetPixels().first); gl::print_error("OpenGl Error creating Splash Screen texture: "); splash_buff.unbind(); splash_tex.unbind(); diff --git a/library/phx/splash_screen.hpp b/library/phx/splash_screen.hpp index d1fc1044df205acae2d36c28d8e23ed92a37f989..71492e57ab2e767359e97016fda3868c9d7e7d10 100644 --- a/library/phx/splash_screen.hpp +++ b/library/phx/splash_screen.hpp @@ -27,6 +27,7 @@ #include "phx/export.hpp" #include "phx/image.hpp" +#include "phx/resource_pointer.hpp" #include "phx/resource_proxy.hpp" #include "phx/window.hpp" @@ -49,7 +50,7 @@ class PHOENIX_EXPORT SplashScreen { private: std::unique_ptr<gl::framebuffer> default_framebuffer_; - ResourceProxy* splash_image_proxy_; + ResourcePointer<Image> splash_image_; std::unique_ptr<Image> progressbar_image_; Window* window_; diff --git a/library/phx/tracking_system.cpp b/library/phx/tracking_system_openvr.cpp similarity index 55% rename from library/phx/tracking_system.cpp rename to library/phx/tracking_system_openvr.cpp index ae836ae207255f3163e21681f2e0b1f9b5963a07..e78c6ebade0c9b4dec3453b79a37ac0bb8da64a1 100644 --- a/library/phx/tracking_system.cpp +++ b/library/phx/tracking_system_openvr.cpp @@ -20,11 +20,12 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "tracking_system.hpp" +#include "tracking_system_openvr.hpp" +#include <memory> #include <string> -#include "display_system.hpp" +#include "display_system_openvr.hpp" #include "hmd.hpp" #include "logger.hpp" #include "projection.hpp" @@ -33,39 +34,60 @@ namespace phx { -TrackingSystem::TrackingSystem(Engine* engine, DisplaySystem* display_system) +TrackingSystemOpenVR::TrackingSystemOpenVR(Engine* engine, + DisplaySystemOpenVR* display_system) : System(engine) { - if (!display_system) error("TrackingSystem needs a valid DisplaySystem."); - CreateRuntimeEntities(); + if (!display_system) + error("TrackingSystemOpenVR needs a valid DisplaySystemOpenVR."); + if (display_system->GetHMD()) { + CreateRuntimeEntities(engine->GetScene().get()); + scene_changed_connection_ = engine->AddSceneChangedCallback( + [this](std::shared_ptr<Scene> old_scene, + std::shared_ptr<Scene> new_scene) { + OnSceneChanged(old_scene, new_scene); + }); + } +} + +TrackingSystemOpenVR::~TrackingSystemOpenVR() { + scene_changed_connection_.disconnect(); } -void TrackingSystem::Update(const FrameTimer::TimeInfo&) { - const auto hmd = engine_->GetSystem<DisplaySystem>()->GetHMD(); - if (hmd == nullptr) return; +void TrackingSystemOpenVR::Update(const FrameTimer::TimeInfo&) { + const auto dispsys_openvr = engine_->GetSystem<DisplaySystemOpenVR>(); + if (dispsys_openvr == nullptr) { + return; + } + const auto hmd = dispsys_openvr->GetHMD(); + if (hmd == nullptr) { + return; + } hmd->UpdateTrackedDevices(); const auto head_transformation = hmd->GetHeadTransformation(); - hmd_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - head_transformation); - const auto left_eye_transformation = hmd->GetEyeToHeadMatrix(HMD::LEFT_EYE); - left_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - left_eye_transformation); - const auto right_eye_transformation = hmd->GetEyeToHeadMatrix(HMD::RIGHT_EYE); - right_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - right_eye_transformation); - - left_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - hmd->GetLeftControllerTransformation()); - - right_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - hmd->GetRightControllerTransformation()); + if (hmd_entity_ != nullptr) { + hmd_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + head_transformation); + const auto left_eye_transformation = hmd->GetEyeToHeadMatrix(HMD::LEFT_EYE); + left_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + left_eye_transformation); + const auto right_eye_transformation = + hmd->GetEyeToHeadMatrix(HMD::RIGHT_EYE); + right_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + right_eye_transformation); + + left_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + hmd->GetLeftControllerTransformation()); + + right_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + hmd->GetRightControllerTransformation()); + } } -void TrackingSystem::CreateRuntimeEntities() { - const auto hmd = engine_->GetSystem<DisplaySystem>()->GetHMD(); +void TrackingSystemOpenVR::CreateRuntimeEntities(Scene* scene) { + const auto hmd = engine_->GetSystem<DisplaySystemOpenVR>()->GetHMD(); if (hmd == nullptr) { return; } - auto scene = engine_->GetScene(); if (scene == nullptr) { return; } @@ -108,6 +130,20 @@ void TrackingSystem::CreateRuntimeEntities() { } } -std::string TrackingSystem::ToString() const { return "Tracking System"; } +void TrackingSystemOpenVR::RemoveRuntimeEntities(Scene* scene) { + scene->RemoveEntity(hmd_entity_); + scene->RemoveEntity(left_eye_entity_); + scene->RemoveEntity(right_eye_entity_); + scene->RemoveEntity(left_controller_entity_); + scene->RemoveEntity(right_controller_entity_); +} + +void TrackingSystemOpenVR::OnSceneChanged(std::shared_ptr<Scene> old_scene, + std::shared_ptr<Scene> new_scene) { + RemoveRuntimeEntities(old_scene.get()); + CreateRuntimeEntities(new_scene.get()); +} + +std::string TrackingSystemOpenVR::ToString() const { return "Tracking System"; } } // namespace phx diff --git a/library/phx/tracking_system_openvr.hpp b/library/phx/tracking_system_openvr.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c11f301c89d77052db966367b82834ac6c6e92b --- /dev/null +++ b/library/phx/tracking_system_openvr.hpp @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +// 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_TRACKING_SYSTEM_OPENVR_HPP_ +#define LIBRARY_PHX_TRACKING_SYSTEM_OPENVR_HPP_ + +#include <memory> +#include <string> + +#include "phx/display_system_openvr.hpp" +#include "phx/engine.hpp" +#include "phx/export.hpp" +#include "phx/system.hpp" + +SUPPRESS_WARNINGS_BEGIN +#define BOOST_BIND_NO_PLACEHOLDERS +#include "boost/signals2/connection.hpp" +SUPPRESS_WARNINGS_END + +namespace phx { + +class PHOENIX_EXPORT TrackingSystemOpenVR : public System { + public: + TrackingSystemOpenVR() = delete; + TrackingSystemOpenVR(const TrackingSystemOpenVR&) = delete; + TrackingSystemOpenVR(TrackingSystemOpenVR&&) = default; + ~TrackingSystemOpenVR(); + + TrackingSystemOpenVR& operator=(const TrackingSystemOpenVR&) = delete; + TrackingSystemOpenVR& operator=(TrackingSystemOpenVR&&) = default; + + void Update(const FrameTimer::TimeInfo&) override; + + std::string ToString() const override; + + protected: + TrackingSystemOpenVR(Engine* engine, DisplaySystemOpenVR* display_system); + + private: + template <typename SystemType, typename... SystemArguments> + friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments); + + void CreateRuntimeEntities(Scene* scene); + void RemoveRuntimeEntities(Scene* scene); + void OnSceneChanged(std::shared_ptr<Scene> old_scene, + std::shared_ptr<Scene> new_scene); + + Entity* hmd_entity_ = nullptr; + Entity* left_eye_entity_ = nullptr; + Entity* right_eye_entity_ = nullptr; + Entity* left_controller_entity_ = nullptr; + Entity* right_controller_entity_ = nullptr; + + boost::signals2::connection scene_changed_connection_; +}; + +} // namespace phx + +#endif // LIBRARY_PHX_TRACKING_SYSTEM_OPENVR_HPP_ diff --git a/library/phx/transform.hpp b/library/phx/transform.hpp index ed5e8d25b9e69908f07f53429aaf79ea2d07af62..787c9df69a1c1055b3021fd58bc658ce349c0431 100644 --- a/library/phx/transform.hpp +++ b/library/phx/transform.hpp @@ -42,18 +42,8 @@ namespace phx { class PHOENIX_EXPORT Transform : public Component, public Hierarchical<Transform> { public: - Transform(const glm::vec3& translation = glm::vec3(), - const glm::quat& rotation = glm::quat(), - const glm::vec3& scale = glm::vec3(1.0f)); - Transform(const glm::vec3& translation, const glm::vec3& rotation_euler, - const glm::vec3& scale = glm::vec3(1.0f)); - Transform(const Transform&) = default; - Transform(Transform&&) = default; virtual ~Transform() = default; - Transform& operator=(const Transform&) = default; - Transform& operator=(Transform&&) = default; - // Getting/Setting Local transform const glm::vec3& GetLocalTranslation() const; Transform& SetLocalTranslation(const glm::vec3& translation); @@ -104,6 +94,19 @@ class PHOENIX_EXPORT Transform : public Component, std::string ToString() const override; + protected: + friend Entity; + Transform(const glm::vec3& translation = glm::vec3(), + const glm::quat& rotation = glm::quat(), + const glm::vec3& scale = glm::vec3(1.0f)); + Transform(const glm::vec3& translation, const glm::vec3& rotation_euler, + const glm::vec3& scale = glm::vec3(1.0f)); + Transform(const Transform&) = delete; + Transform(Transform&&) = default; + + Transform& operator=(const Transform&) = delete; + Transform& operator=(Transform&&) = default; + private: bool CheckIfParentIsValid(Transform* parent); void UpdateLocalMatrix(); diff --git a/library/phx/window.cpp b/library/phx/window.cpp index 2c61f93c5c80657361ccb00814be1c910bbc2425..edc41151a95ac2d2785ed975b657dbcdc8cdfedb 100644 --- a/library/phx/window.cpp +++ b/library/phx/window.cpp @@ -35,11 +35,12 @@ Window::Window(const std::string& title) Validate(); } Window::Window(const std::string& title, const glm::uvec2& position, - const glm::uvec2& size) + const glm::uvec2& size, bool hidden) : native_(SDL_CreateWindow( title.c_str(), static_cast<int>(position.x), static_cast<int>(position.y), static_cast<int>(size.x), - static_cast<int>(size.y), SetupOpenGLContextFlags())), + static_cast<int>(size.y), + SetupOpenGLContextFlags() | (hidden ? SDL_WINDOW_HIDDEN : 0))), gl_context_(SDL_GL_CreateContext(native_)) { Validate(); } @@ -57,6 +58,14 @@ glm::uvec2 Window::GetSize() const { return glm::uvec2(static_cast<unsigned>(w), static_cast<unsigned>(h)); } +void Window::SetVisible(bool visible) { + if (visible) { + SDL_ShowWindow(native_); + } else { + SDL_HideWindow(native_); + } +} + std::uint32_t Window::SetupOpenGLContextFlags() const { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); diff --git a/library/phx/window.hpp b/library/phx/window.hpp index edd41bd4cf3715036775b30de75ba836b955c6f5..ea581f66159bc2051f4a0f5c04a3eefb46ea9dfd 100644 --- a/library/phx/window.hpp +++ b/library/phx/window.hpp @@ -34,7 +34,7 @@ SUPPRESS_WARNINGS_END namespace phx { -class DisplaySystem; +class DisplaySystemWindow; class PHOENIX_EXPORT Window { public: @@ -49,12 +49,14 @@ class PHOENIX_EXPORT Window { glm::uvec2 GetSize() const; + void SetVisible(bool visible); + protected: - friend DisplaySystem; + friend DisplaySystemWindow; explicit Window(const std::string& title); Window(const std::string& title, const glm::uvec2& position, - const glm::uvec2& size = glm::uvec2(1024, 768)); + const glm::uvec2& size = glm::uvec2(1024, 768), bool hidden = false); std::uint32_t SetupOpenGLContextFlags() const; void Validate() const; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0c516001fb8a2bb48d0353a54fac5d8469af3628..14e3a3d43d80b7ada144e5e8a3f81e07489abb90 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -226,6 +226,7 @@ add_mocked_test(test_shader MOCK_GLEW) add_mocked_test(test_display_system MOCK_SDL) add_mocked_test(test_engine MOCK_SDL MOCK_OPENVR MOCK_GLEW) add_mocked_test(test_tracking_system MOCK_SDL MOCK_OPENVR) +add_mocked_test(test_openvr_controller_system MOCK_SDL MOCK_OPENVR MOCK_GLEW) add_mocked_test(integration_test_model_rendering MOCK_OPENVR) add_mocked_test(integration_test_opengl_buffer_data_download MOCK_OPENVR) diff --git a/tests/src/integration_test_model_rendering.cpp b/tests/src/integration_test_model_rendering.cpp index f4d4a601534826ecb357afdc8f5619d314b65221..dc107451e97160504b6886a2d518bf801e229bd5 100644 --- a/tests/src/integration_test_model_rendering.cpp +++ b/tests/src/integration_test_model_rendering.cpp @@ -25,7 +25,7 @@ #include "catch/catch.hpp" #include "phx/assimp_model_loader.hpp" -#include "phx/display_system.hpp" +#include "phx/display_system_window.hpp" #include "phx/frame_timer.hpp" #include "phx/mesh.hpp" #include "phx/mesh_handle.hpp" @@ -51,20 +51,20 @@ extern template struct trompeloeil::reporter<trompeloeil::specialized>; phx::Entity* LoadBunny(glm::vec3 pos, float angleDeg, unsigned int material_index, phx::Scene* scene) { - auto bunny_proxy = phx::ResourceUtils::LoadResourceFromFile( + auto bunny_mesh = phx::ResourceUtils::LoadResourceFromFile<phx::Mesh>( "models/bunny.obj", {{"mesh_index", 0}}); phx::Entity* bunny = scene->CreateEntity(); phx::MeshHandle* bunny_handle = bunny->AddComponent<phx::MeshHandle>(); - bunny_handle->SetMeshProxy(bunny_proxy); + bunny_handle->SetMesh(bunny_mesh); phx::MaterialHandle* bunny_material_handle = bunny->AddComponent<phx::MaterialHandle>(); - auto bunny_material_proxy = phx::ResourceUtils::LoadResourceFromFile( + auto bunny_material = phx::ResourceUtils::LoadResourceFromFile<phx::Material>( "models/bunny.obj", {{"material_index", material_index}}); - bunny_material_handle->SetMaterialProxy(bunny_material_proxy); + bunny_material_handle->SetMaterial(bunny_material); phx::Transform* bunny_transform = bunny->AddComponent<phx::Transform>(); bunny_transform->SetLocalTranslation(pos); @@ -111,7 +111,7 @@ SCENARIO( LoadBunny(glm::vec3(-0.15f, -0.1f, 0.0f), 180.0f, 2, scene.get()); auto rendering_system = engine->GetSystem<phx::RenderingSystem>(); - auto display_system = engine->GetSystem<phx::DisplaySystem>(); + auto display_system = engine->GetSystem<phx::DisplaySystemWindow>(); WHEN("We render the scene") { rendering_system->Update(phx::FrameTimer::TimeInfo()); @@ -140,7 +140,7 @@ SCENARIO( LoadBunny(glm::vec3(0.0f, -0.1f, -0.3f), 0.0f, 1, scene.get()); auto rendering_system = engine->GetSystem<phx::RenderingSystem>(); - auto display_system = engine->GetSystem<phx::DisplaySystem>(); + auto display_system = engine->GetSystem<phx::DisplaySystemWindow>(); WHEN("We render the scene") { rendering_system->Update(phx::FrameTimer::TimeInfo{}); diff --git a/tests/src/integration_test_opengl_buffer_data_download.cpp b/tests/src/integration_test_opengl_buffer_data_download.cpp index cad6ca25a7335d3f9725011eece775800fd9a960..16ec61edecc70111a11ca1ce5e3ee78a2bccdb64 100644 --- a/tests/src/integration_test_opengl_buffer_data_download.cpp +++ b/tests/src/integration_test_opengl_buffer_data_download.cpp @@ -35,6 +35,7 @@ #include "phx/rendering_system.hpp" #include "phx/resource_declaration.hpp" #include "phx/resource_manager.hpp" +#include "phx/resource_pointer.hpp" #include "phx/resource_proxy.hpp" #include "phx/scene.hpp" #include "phx/setup.hpp" @@ -99,7 +100,8 @@ class SceneSetupSimple { phx::MaterialHandle* material_handle = triangle->AddComponent<phx::MaterialHandle>(); - material_handle->SetMaterialProxy(material_proxy); + material_handle->SetMaterial( + phx::ResourcePointer<phx::Material>(material_proxy)); std::vector<glm::vec3> vertices = { {-1.0f, -1.0f, 0.5f}, {1.0f, -1.0f, 0.5f}, {1.0f, 1.0f, 0.5f}}; @@ -116,7 +118,7 @@ class SceneSetupSimple { mesh_proxy->Load(); auto mesh_handle = triangle->AddComponent<phx::MeshHandle>(); - mesh_handle->SetMeshProxy(mesh_proxy); + mesh_handle->SetMesh(phx::ResourcePointer<phx::Mesh>(mesh_proxy)); phx::Entity* main_light = scene->CreateEntity(); main_light->AddComponent<phx::Transform>(); diff --git a/tests/src/integration_test_rendering.cpp b/tests/src/integration_test_rendering.cpp index a4f6257c7c158ce19978f7480c0aa13d0062f89d..4dc91836cd69184f7098628ee3c63fbc2e8ad285 100644 --- a/tests/src/integration_test_rendering.cpp +++ b/tests/src/integration_test_rendering.cpp @@ -26,7 +26,7 @@ #include "catch/catch.hpp" -#include "phx/display_system.hpp" +#include "phx/display_system_window.hpp" #include "phx/frame_timer.hpp" #include "phx/mesh.hpp" #include "phx/mesh_handle.hpp" @@ -87,7 +87,7 @@ void CreateTestTriangleComponent(phx::Entity* triangle) { mesh_proxy->Load(); phx::MeshHandle* mesh_handle = triangle->AddComponent<phx::MeshHandle>(); - mesh_handle->SetMeshProxy(mesh_proxy); + mesh_handle->SetMesh(phx::ResourcePointer<phx::Mesh>(mesh_proxy)); } SCENARIO("We can render a simple triangle", "[phx][phx::Rendering]") { @@ -95,7 +95,7 @@ SCENARIO("We can render a simple triangle", "[phx][phx::Rendering]") { GIVEN("A complete triangle") { std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(); auto rendering_system = engine->GetSystem<phx::RenderingSystem>(); - auto display_system = engine->GetSystem<phx::DisplaySystem>(); + auto display_system = engine->GetSystem<phx::DisplaySystemWindow>(); auto scene = engine->GetScene(); SetupLightAndCamera(scene.get()); @@ -114,7 +114,8 @@ SCENARIO("We can render a simple triangle", "[phx][phx::Rendering]") { phx::Transform* transform = triangle->AddComponent<phx::Transform>(); phx::MaterialHandle* material_handle = triangle->AddComponent<phx::MaterialHandle>(); - material_handle->SetMaterialProxy(material_proxy); + material_handle->SetMaterial( + phx::ResourcePointer<phx::Material>(material_proxy)); WHEN("We render the scene") { rendering_system->Update(phx::FrameTimer::TimeInfo{}); @@ -137,7 +138,7 @@ SCENARIO("If no material given a default red one is taken.", GIVEN("A triangle without material") { std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(); auto rendering_system = engine->GetSystem<phx::RenderingSystem>(); - auto display_system = engine->GetSystem<phx::DisplaySystem>(); + auto display_system = engine->GetSystem<phx::DisplaySystemWindow>(); auto scene = engine->GetScene(); SetupLightAndCamera(scene.get()); diff --git a/tests/src/mocks/openvr_mock.hpp b/tests/src/mocks/openvr_mock.hpp index 89ecec4ee202166ee92d3cf35ac315b825a7f496..77881d7054b6d4a82e1777140ec2b34724e70a8c 100644 --- a/tests/src/mocks/openvr_mock.hpp +++ b/tests/src/mocks/openvr_mock.hpp @@ -39,11 +39,11 @@ using trompeloeil::_; class OPENVR_MOCK_EXPORT OpenVRMockInternal { public: MAKE_MOCK0(VR_IsHmdPresent, bool()); - MAKE_MOCK2(VR_GetGenericInterface, void*(const char*, vr::EVRInitError*)); - MAKE_MOCK3(VR_InitInternal2, - uint32_t(vr::EVRInitError*, vr::EVRApplicationType, const char*)); + MAKE_MOCK2(VR_GetGenericInterface, void *(const char *, vr::EVRInitError *)); + MAKE_MOCK3(VR_InitInternal2, uint32_t(vr::EVRInitError *, + vr::EVRApplicationType, const char *)); MAKE_MOCK0(VR_ShutdownInternal, void()); - MAKE_MOCK1(VR_IsInterfaceVersionValid, bool(const char*)); + MAKE_MOCK1(VR_IsInterfaceVersionValid, bool(const char *)); MAKE_MOCK0(VR_GetInitToken, uint32_t()); }; @@ -52,30 +52,31 @@ namespace vr { class OPENVR_MOCK_EXPORT IVRSystemMock : public IVRSystem { public: virtual ~IVRSystemMock() = default; - MAKE_MOCK2(GetRecommendedRenderTargetSize, void(uint32_t*, uint32_t*)); - MAKE_MOCK5(GetProjectionRaw, void(EVREye, float*, float*, float*, float*)); + MAKE_MOCK2(GetRecommendedRenderTargetSize, void(uint32_t *, uint32_t *)); + MAKE_MOCK5(GetProjectionRaw, + void(EVREye, float *, float *, float *, float *)); MAKE_MOCK4(ComputeDistortion, - bool(EVREye, float, float, DistortionCoordinates_t*)); - MAKE_MOCK2(GetTimeSinceLastVsync, bool(float*, uint64_t*)); + bool(EVREye, float, float, DistortionCoordinates_t *)); + MAKE_MOCK2(GetTimeSinceLastVsync, bool(float *, uint64_t *)); MAKE_MOCK0(GetD3D9AdapterIndex, int32_t()); - MAKE_MOCK1(GetDXGIOutputInfo, void(int32_t*)); - MAKE_MOCK3(GetOutputDevice, void(uint64_t*, ETextureType, VkInstance_T*)); + MAKE_MOCK1(GetDXGIOutputInfo, void(int32_t *)); + MAKE_MOCK3(GetOutputDevice, void(uint64_t *, ETextureType, VkInstance_T *)); MAKE_MOCK0(IsDisplayOnDesktop, bool()); MAKE_MOCK1(SetDisplayVisibility, bool(bool)); MAKE_MOCK4(GetDeviceToAbsoluteTrackingPose, - void(ETrackingUniverseOrigin, float, TrackedDevicePose_t*, + void(ETrackingUniverseOrigin, float, TrackedDevicePose_t *, uint32_t)); MAKE_MOCK0(ResetSeatedZeroPose, void()); MAKE_MOCK0(GetSeatedZeroPoseToStandingAbsoluteTrackingPose, HmdMatrix34_t()); MAKE_MOCK0(GetRawZeroPoseToStandingAbsoluteTrackingPose, HmdMatrix34_t()); MAKE_MOCK4(GetSortedTrackedDeviceIndicesOfClass, - uint32_t(ETrackedDeviceClass, TrackedDeviceIndex_t*, uint32_t, + uint32_t(ETrackedDeviceClass, TrackedDeviceIndex_t *, uint32_t, TrackedDeviceIndex_t)); MAKE_MOCK1(GetTrackedDeviceActivityLevel, EDeviceActivityLevel(TrackedDeviceIndex_t)); MAKE_MOCK3(ApplyTransform, - void(TrackedDevicePose_t*, const TrackedDevicePose_t*, - const HmdMatrix34_t*)); + void(TrackedDevicePose_t *, const TrackedDevicePose_t *, + const HmdMatrix34_t *)); MAKE_MOCK1(GetTrackedDeviceIndexForControllerRole, TrackedDeviceIndex_t(vr::ETrackedControllerRole)); MAKE_MOCK1(GetControllerRoleForTrackedDeviceIndex, @@ -85,43 +86,43 @@ class OPENVR_MOCK_EXPORT IVRSystemMock : public IVRSystem { MAKE_MOCK1(IsTrackedDeviceConnected, bool(vr::TrackedDeviceIndex_t)); MAKE_MOCK3(GetBoolTrackedDeviceProperty, bool(vr::TrackedDeviceIndex_t, ETrackedDeviceProperty, - ETrackedPropertyError*)); + ETrackedPropertyError *)); MAKE_MOCK3(GetFloatTrackedDeviceProperty, float(vr::TrackedDeviceIndex_t, ETrackedDeviceProperty, - ETrackedPropertyError*)); + ETrackedPropertyError *)); MAKE_MOCK3(GetInt32TrackedDeviceProperty, int32_t(vr::TrackedDeviceIndex_t, ETrackedDeviceProperty, - ETrackedPropertyError*)); + ETrackedPropertyError *)); MAKE_MOCK3(GetUint64TrackedDeviceProperty, uint64_t(vr::TrackedDeviceIndex_t, ETrackedDeviceProperty, - ETrackedPropertyError*)); + ETrackedPropertyError *)); MAKE_MOCK3(GetMatrix34TrackedDeviceProperty, HmdMatrix34_t(vr::TrackedDeviceIndex_t, ETrackedDeviceProperty, - ETrackedPropertyError*)); + ETrackedPropertyError *)); MAKE_MOCK5(GetStringTrackedDeviceProperty, - uint32_t(vr::TrackedDeviceIndex_t, ETrackedDeviceProperty, char*, - uint32_t, ETrackedPropertyError*)); - MAKE_MOCK1(GetPropErrorNameFromEnum, const char*(ETrackedPropertyError)); - MAKE_MOCK2(PollNextEvent, bool(VREvent_t*, uint32_t)); - MAKE_MOCK4(PollNextEventWithPose, bool(ETrackingUniverseOrigin, VREvent_t*, - uint32_t, vr::TrackedDevicePose_t*)); - MAKE_MOCK1(GetEventTypeNameFromEnum, const char*(EVREventType)); + uint32_t(vr::TrackedDeviceIndex_t, ETrackedDeviceProperty, char *, + uint32_t, ETrackedPropertyError *)); + MAKE_MOCK1(GetPropErrorNameFromEnum, const char *(ETrackedPropertyError)); + MAKE_MOCK2(PollNextEvent, bool(VREvent_t *, uint32_t)); + MAKE_MOCK4(PollNextEventWithPose, bool(ETrackingUniverseOrigin, VREvent_t *, + uint32_t, vr::TrackedDevicePose_t *)); + MAKE_MOCK1(GetEventTypeNameFromEnum, const char *(EVREventType)); MAKE_MOCK2(GetHiddenAreaMesh, HiddenAreaMesh_t(EVREye, EHiddenAreaMeshType)); MAKE_MOCK3(GetControllerState, bool(vr::TrackedDeviceIndex_t, - vr::VRControllerState_t*, uint32_t)); + vr::VRControllerState_t *, uint32_t)); MAKE_MOCK5(GetControllerStateWithPose, bool(ETrackingUniverseOrigin, vr::TrackedDeviceIndex_t, - vr::VRControllerState_t*, uint32_t, TrackedDevicePose_t*)); + vr::VRControllerState_t *, uint32_t, TrackedDevicePose_t *)); MAKE_MOCK3(TriggerHapticPulse, void(vr::TrackedDeviceIndex_t, uint32_t, unsigned short)); - MAKE_MOCK1(GetButtonIdNameFromEnum, const char*(EVRButtonId)); + MAKE_MOCK1(GetButtonIdNameFromEnum, const char *(EVRButtonId)); MAKE_MOCK1(GetControllerAxisTypeNameFromEnum, - const char*(EVRControllerAxisType)); + const char *(EVRControllerAxisType)); MAKE_MOCK0(CaptureInputFocus, bool()); MAKE_MOCK0(ReleaseInputFocus, void()); MAKE_MOCK0(IsInputFocusCapturedByAnotherProcess, bool()); - MAKE_MOCK4(DriverDebugRequest, - uint32_t(vr::TrackedDeviceIndex_t, const char*, char*, uint32_t)); + MAKE_MOCK4(DriverDebugRequest, uint32_t(vr::TrackedDeviceIndex_t, + const char *, char *, uint32_t)); MAKE_MOCK1(PerformFirmwareUpdate, vr::EVRFirmwareError(vr::TrackedDeviceIndex_t)); MAKE_MOCK0(AcknowledgeQuit_Exiting, void()); @@ -130,9 +131,9 @@ class OPENVR_MOCK_EXPORT IVRSystemMock : public IVRSystem { // somehow returning structs is a problem, so this workaround // TODO(WJ): should be reworked once this is solved in trompeloeil // https://github.com/rollbear/trompeloeil/issues/69 - MAKE_MOCK3(GetProjectionMatrixArray, float*(vr::EVREye, float, float)); + MAKE_MOCK3(GetProjectionMatrixArray, float *(vr::EVREye, float, float)); vr::HmdMatrix44_t GetProjectionMatrix(vr::EVREye, float, float); - MAKE_MOCK1(GetEyeToHeadTransformArray, float*(EVREye)); + MAKE_MOCK1(GetEyeToHeadTransformArray, float *(EVREye)); HmdMatrix34_t GetEyeToHeadTransform(EVREye); }; @@ -140,27 +141,28 @@ class OPENVR_MOCK_EXPORT IVRCompositorMock : public IVRCompositor { public: MAKE_MOCK1(SetTrackingSpace, void(ETrackingUniverseOrigin)); MAKE_MOCK0(GetTrackingSpace, ETrackingUniverseOrigin()); - MAKE_MOCK4(WaitGetPoses, EVRCompositorError(TrackedDevicePose_t*, uint32_t, - TrackedDevicePose_t*, uint32_t)); - MAKE_MOCK4(GetLastPoses, EVRCompositorError(TrackedDevicePose_t*, uint32_t, - TrackedDevicePose_t*, uint32_t)); + MAKE_MOCK4(WaitGetPoses, EVRCompositorError(TrackedDevicePose_t *, uint32_t, + TrackedDevicePose_t *, uint32_t)); + MAKE_MOCK4(GetLastPoses, EVRCompositorError(TrackedDevicePose_t *, uint32_t, + TrackedDevicePose_t *, uint32_t)); MAKE_MOCK3(GetLastPoseForTrackedDeviceIndex, - EVRCompositorError(TrackedDeviceIndex_t, TrackedDevicePose_t*, - TrackedDevicePose_t*)); + EVRCompositorError(TrackedDeviceIndex_t, TrackedDevicePose_t *, + TrackedDevicePose_t *)); MAKE_MOCK4(Submit, - EVRCompositorError(EVREye, const Texture_t*, - const VRTextureBounds_t*, EVRSubmitFlags)); + EVRCompositorError(EVREye, const Texture_t *, + const VRTextureBounds_t *, EVRSubmitFlags)); MAKE_MOCK0(ClearLastSubmittedFrame, void()); MAKE_MOCK0(PostPresentHandoff, void()); - MAKE_MOCK2(GetFrameTiming, bool(Compositor_FrameTiming*, uint32_t)); - MAKE_MOCK2(GetFrameTimings, uint32_t(Compositor_FrameTiming*, uint32_t)); + MAKE_MOCK2(GetFrameTiming, bool(Compositor_FrameTiming *, uint32_t)); + MAKE_MOCK2(GetFrameTimings, uint32_t(Compositor_FrameTiming *, uint32_t)); MAKE_MOCK0(GetFrameTimeRemaining, float()); - MAKE_MOCK2(GetCumulativeStats, void(Compositor_CumulativeStats*, uint32_t)); + MAKE_MOCK2(GetCumulativeStats, void(Compositor_CumulativeStats *, uint32_t)); MAKE_MOCK6(FadeToColor, void(float, float, float, float, float, bool)); MAKE_MOCK1(GetCurrentFadeColor, HmdColor_t(bool)); MAKE_MOCK2(FadeGrid, void(float, bool)); MAKE_MOCK0(GetCurrentGridAlpha, float()); - MAKE_MOCK2(SetSkyboxOverride, EVRCompositorError(const Texture_t*, uint32_t)); + MAKE_MOCK2(SetSkyboxOverride, + EVRCompositorError(const Texture_t *, uint32_t)); MAKE_MOCK0(ClearSkyboxOverride, void()); MAKE_MOCK0(CompositorBringToFront, void()); MAKE_MOCK0(CompositorGoToBack, void()); @@ -178,22 +180,62 @@ class OPENVR_MOCK_EXPORT IVRCompositorMock : public IVRCompositor { MAKE_MOCK0(ForceReconnectProcess, void()); MAKE_MOCK1(SuspendRendering, void(bool)); MAKE_MOCK3(GetMirrorTextureD3D11, - EVRCompositorError(vr::EVREye, void*, void**)); - MAKE_MOCK1(ReleaseMirrorTextureD3D11, void(void*)); + EVRCompositorError(vr::EVREye, void *, void **)); + MAKE_MOCK1(ReleaseMirrorTextureD3D11, void(void *)); MAKE_MOCK3(GetMirrorTextureGL, - vr::EVRCompositorError(vr::EVREye, vr::glUInt_t*, - vr::glSharedTextureHandle_t*)); + vr::EVRCompositorError(vr::EVREye, vr::glUInt_t *, + vr::glSharedTextureHandle_t *)); MAKE_MOCK2(ReleaseSharedGLTexture, bool(vr::glUInt_t, vr::glSharedTextureHandle_t)); MAKE_MOCK1(LockGLSharedTextureForAccess, void(vr::glSharedTextureHandle_t)); MAKE_MOCK1(UnlockGLSharedTextureForAccess, void(vr::glSharedTextureHandle_t)); - MAKE_MOCK2(GetVulkanInstanceExtensionsRequired, uint32_t(char*, uint32_t)); + MAKE_MOCK2(GetVulkanInstanceExtensionsRequired, uint32_t(char *, uint32_t)); MAKE_MOCK3(GetVulkanDeviceExtensionsRequired, - uint32_t(VkPhysicalDevice_T*, char*, uint32_t)); + uint32_t(VkPhysicalDevice_T *, char *, uint32_t)); MAKE_MOCK1(SetExplicitTimingMode, void(bool)); MAKE_MOCK0(SubmitExplicitTimingData, EVRCompositorError()); }; +class OPENVR_MOCK_EXPORT IVRRenderModelsMock : public IVRRenderModels { + public: + MAKE_MOCK2(LoadRenderModel_Async, + EVRRenderModelError(const char *, RenderModel_t **)); + MAKE_MOCK1(FreeRenderModel, void(RenderModel_t *)); + MAKE_MOCK2(LoadTexture_Async, + EVRRenderModelError(TextureID_t, RenderModel_TextureMap_t **)); + MAKE_MOCK1(FreeTexture, void(RenderModel_TextureMap_t *)); + MAKE_MOCK3(LoadTextureD3D11_Async, + EVRRenderModelError(TextureID_t, void *, void **)); + MAKE_MOCK2(LoadIntoTextureD3D11_Async, + EVRRenderModelError(TextureID_t, void *)); + MAKE_MOCK1(FreeTextureD3D11, void(void *)); + MAKE_MOCK3(GetRenderModelName, + uint32_t(uint32_t, VR_OUT_STRING() char *, uint32_t)); + MAKE_MOCK0(GetRenderModelCount, uint32_t()); + MAKE_MOCK1(GetComponentCount, uint32_t(const char *)); + MAKE_MOCK4(GetComponentName, uint32_t(const char *, uint32_t, + VR_OUT_STRING() char *, uint32_t)); + + MAKE_MOCK2(GetComponentButtonMask, uint64_t(const char *, const char *)); + MAKE_MOCK4(GetComponentRenderModelName, + uint32_t(const char *, const char *, VR_OUT_STRING() char *, + uint32_t)); + MAKE_MOCK5(GetComponentState, + bool(const char *, const char *, const vr::VRControllerState_t *, + const RenderModel_ControllerMode_State_t *, + RenderModel_ComponentState_t *)); + + MAKE_MOCK2(RenderModelHasComponent, bool(const char *, const char *)); + MAKE_MOCK4(GetRenderModelThumbnailURL, + uint32_t(const char *, VR_OUT_STRING() char *, uint32_t, + vr::EVRRenderModelError *)); + MAKE_MOCK4(GetRenderModelOriginalPath, + uint32_t(const char *, VR_OUT_STRING() char *, uint32_t, + vr::EVRRenderModelError *)); + MAKE_MOCK1(GetRenderModelErrorNameFromEnum, + const char *(vr::EVRRenderModelError)); +}; + } // namespace vr class OPENVR_MOCK_EXPORT OpenVRMock { @@ -201,7 +243,8 @@ class OPENVR_MOCK_EXPORT OpenVRMock { OpenVRMock() : mock_(new OpenVRMockInternal), ivr_system_mock_(new vr::IVRSystemMock), - ivr_compositor_mock_(new vr::IVRCompositorMock) {} + ivr_compositor_mock_(new vr::IVRCompositorMock), + ivr_render_models_mock_(new vr::IVRRenderModelsMock) {} ~OpenVRMock() { // We have to leak the mock_ pointer for the time being. // see @@ -209,9 +252,35 @@ class OPENVR_MOCK_EXPORT OpenVRMock { // TODO(@tvierjahn) regularly check progress on this issue } - OpenVRMockInternal& Get() { return *mock_; } - vr::IVRSystemMock& GetSystem() const { return *ivr_system_mock_; } - vr::IVRCompositorMock& GetCompositor() { return *ivr_compositor_mock_; } + OpenVRMockInternal &Get() { return *mock_; } + vr::IVRSystemMock &GetSystem() const { return *ivr_system_mock_; } + vr::IVRCompositorMock &GetCompositor() { return *ivr_compositor_mock_; } + vr::IVRRenderModelsMock &GetRenderModels() { + return *ivr_render_models_mock_; + } + + static vr::RenderModel_t *CreateDummyTriangleModel() { + vr::RenderModel_t *model = new vr::RenderModel_t(); + model->rIndexData = new uint16_t[3]{0, 1, 2}; + model->unVertexCount = 3; + model->unTriangleCount = 1; + model->diffuseTextureId = -1; + + vr::RenderModel_Vertex_t vertex1; + vertex1.vPosition = {{-0.1f, -0.1f, 0.f}}; + vertex1.vNormal = {{0.f, 0.f, 1.f}}; + vertex1.rfTextureCoord[0] = 0.f; + vertex1.rfTextureCoord[1] = 0.f; + vr::RenderModel_Vertex_t vertex2 = vertex1; + vertex2.vPosition = {{+0.1f, -0.1f, 0.f}}; + vr::RenderModel_Vertex_t vertex3 = vertex1; + vertex2.vPosition = {{0.f, +0.1f, 0.f}}; + + model->rVertexData = + new vr::RenderModel_Vertex_t[3]{vertex1, vertex2, vertex3}; + + return model; + } static vr::HmdMatrix44_t toHMDMatrix44_t(float mat[16]); static vr::HmdMatrix34_t toHMDMatrix34_t(float mat[12]); @@ -229,9 +298,10 @@ class OPENVR_MOCK_EXPORT OpenVRMock { 0.0f, 1.8f, 0.0f, 0.0f, 1.0f, 0.0f}; private: - OpenVRMockInternal* mock_; - vr::IVRSystemMock* ivr_system_mock_; - vr::IVRCompositorMock* ivr_compositor_mock_; + OpenVRMockInternal *mock_; + vr::IVRSystemMock *ivr_system_mock_; + vr::IVRCompositorMock *ivr_compositor_mock_; + vr::IVRRenderModelsMock *ivr_render_models_mock_; }; extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock; @@ -251,12 +321,14 @@ extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock; .RETURN(openvr_mock.eye_to_head_right_); \ ALLOW_CALL(openvr_mock.GetSystem(), \ GetSortedTrackedDeviceIndicesOfClass(_, _, _, _)) \ - .RETURN(0u); \ + .RETURN(2u) \ + .SIDE_EFFECT(*_2 = 0u) \ + .SIDE_EFFECT(*(_2 + sizeof(vr::TrackedDeviceIndex_t)) = 1u); \ ALLOW_CALL(openvr_mock.GetSystem(), \ - GetControllerRoleForTrackedDeviceIndex(_)) \ + GetControllerRoleForTrackedDeviceIndex(0u)) \ .RETURN(vr::TrackedControllerRole_LeftHand); \ ALLOW_CALL(openvr_mock.GetSystem(), \ - GetControllerRoleForTrackedDeviceIndex(_)) \ + GetControllerRoleForTrackedDeviceIndex(1u)) \ .RETURN(vr::TrackedControllerRole_RightHand); \ ALLOW_CALL(openvr_mock.GetCompositor(), WaitGetPoses(_, _, _, _)) \ .RETURN(vr::EVRCompositorError::VRCompositorError_None) \ @@ -268,16 +340,28 @@ extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock; .RETURN(vr::EVRCompositorError::VRCompositorError_None); \ ALLOW_CALL(openvr_mock.Get(), VR_GetGenericInterface(_, _)) \ .WITH(std::string(vr::IVRSystem_Version) == std::string(_1)) \ - .RETURN(static_cast<void*>(&openvr_mock.GetSystem())); \ + .RETURN(static_cast<void *>(&openvr_mock.GetSystem())); \ + ALLOW_CALL(openvr_mock.Get(), VR_GetGenericInterface(_, _)) \ + .WITH(std::string(vr::IVRRenderModels_Version) == std::string(_1)) \ + .RETURN(static_cast<void *>(&openvr_mock.GetRenderModels())); \ ALLOW_CALL(openvr_mock.Get(), VR_GetGenericInterface(_, _)) \ .WITH(std::string(vr::IVRCompositor_Version) == std::string(_1)) \ - .RETURN(static_cast<void*>(&openvr_mock.GetCompositor())); \ + .RETURN(static_cast<void *>(&openvr_mock.GetCompositor())); \ ALLOW_CALL(openvr_mock.Get(), \ VR_InitInternal2(_, vr::VRApplication_Scene, _)) \ .RETURN(static_cast<uint32_t>(1337)) \ .SIDE_EFFECT(*_1 = vr::VRInitError_None); \ ALLOW_CALL(openvr_mock.Get(), VR_ShutdownInternal()); \ ALLOW_CALL(openvr_mock.Get(), VR_IsInterfaceVersionValid(_)).RETURN(true); \ - ALLOW_CALL(openvr_mock.Get(), VR_GetInitToken()).RETURN(1337); + ALLOW_CALL(openvr_mock.Get(), VR_GetInitToken()).RETURN(1337); \ + ALLOW_CALL(openvr_mock.GetRenderModels(), LoadRenderModel_Async(_, _)) \ + .RETURN(vr::VRRenderModelError_None) \ + .SIDE_EFFECT(*_2 = openvr_mock.CreateDummyTriangleModel()); \ + ALLOW_CALL(openvr_mock.GetSystem(), \ + GetStringTrackedDeviceProperty(_, _, _, _, _)) \ + .RETURN(0); \ + ALLOW_CALL(openvr_mock.GetRenderModels(), LoadTexture_Async(_, _)) \ + .RETURN(vr::VRRenderModelError_None) \ + .SIDE_EFFECT(*_2 = nullptr); #endif // TESTS_SRC_MOCKS_OPENVR_MOCK_HPP_ diff --git a/tests/src/mocks/sdl_mock.cpp b/tests/src/mocks/sdl_mock.cpp index 702b3d6bd8eadbc6d6510b3905a0df1e749db600..919ae46a7ece937b40f6c97ad5ccd936b21b35cb 100644 --- a/tests/src/mocks/sdl_mock.cpp +++ b/tests/src/mocks/sdl_mock.cpp @@ -76,4 +76,10 @@ void SDL_GetWindowSize(SDL_Window* window, int* w, int* h) { int SDL_GL_SetSwapInterval(int interval) { return sdl_mock.Get().SDL_GL_SetSwapInterval(interval); } +void SDL_ShowWindow(SDL_Window* window) { + sdl_mock.Get().SDL_ShowWindow(window); +} +void SDL_HideWindow(SDL_Window* window) { + sdl_mock.Get().SDL_HideWindow(window); +} } diff --git a/tests/src/mocks/sdl_mock.hpp b/tests/src/mocks/sdl_mock.hpp index ee35b1f2ac01a8832277899f4b23a88fffca6d71..3faddfd628ae9facd6a4096e641a865ff89dd94d 100644 --- a/tests/src/mocks/sdl_mock.hpp +++ b/tests/src/mocks/sdl_mock.hpp @@ -49,6 +49,8 @@ class SdlMockInternal { MAKE_MOCK1(SDL_PollEvent, int(SDL_Event*)); // NOLINT(readability/casting) MAKE_MOCK3(SDL_GetWindowSize, void(SDL_Window*, int*, int*)); MAKE_MOCK1(SDL_GL_SetSwapInterval, int(int)); + MAKE_MOCK1(SDL_ShowWindow, void(SDL_Window*)); + MAKE_MOCK1(SDL_HideWindow, void(SDL_Window*)); }; class SdlMock { @@ -88,6 +90,8 @@ extern SdlMock sdl_mock; ALLOW_CALL(sdl_mock.Get(), SDL_GL_SwapWindow(ne(nullptr))); \ ALLOW_CALL(sdl_mock.Get(), SDL_GetWindowSize(ne(nullptr), _, _)); \ ALLOW_CALL(sdl_mock.Get(), SDL_GL_SetSwapInterval(_)).RETURN(0); \ - ALLOW_CALL(sdl_mock.Get(), SDL_PollEvent(_)).RETURN(0); + ALLOW_CALL(sdl_mock.Get(), SDL_PollEvent(_)).RETURN(0); \ + ALLOW_CALL(sdl_mock.Get(), SDL_ShowWindow(_)); \ + ALLOW_CALL(sdl_mock.Get(), SDL_HideWindow(_)); #endif // TESTS_SRC_MOCKS_SDL_MOCK_HPP_ diff --git a/tests/src/test-transform.cpp b/tests/src/test-transform.cpp index 24d01a7eb2b411d5956267f2415e8275ef4ac2af..f9cc4cd768ca728876390516071144d790209032 100644 --- a/tests/src/test-transform.cpp +++ b/tests/src/test-transform.cpp @@ -554,27 +554,5 @@ SCENARIO("The transform component checks if its parent is in the same scene.", } } } - - GIVEN("A transform t4 without a scene.") { - phx::Transform t4; - - WHEN("We set t4 to be the parent of t1.") { - auto log_capture = std::make_shared<test_utilities::LogCapture>(); - phx::logger = - std::make_shared<spdlog::logger>("logcapture", log_capture); - phx::logger->set_pattern("%v"); - - t1->SetParent(&t4); - THEN("An error is logged.") { - REQUIRE(*log_capture == - "The transform to be set as parent is not within the same " - "scene."); - } - THEN("t4 is not the parent of t1.") { - REQUIRE(t1->GetParent() != &t4); - REQUIRE(t4.GetChildCount() == 0); - } - } - } } } diff --git a/tests/src/test_assimp_loader.cpp b/tests/src/test_assimp_loader.cpp index e143adb324cb38808c92fa9d68f35eabac6d5173..1855a8b7361992e9c8e559e232e8e263b12960b7 100644 --- a/tests/src/test_assimp_loader.cpp +++ b/tests/src/test_assimp_loader.cpp @@ -54,7 +54,7 @@ SCENARIO("The assimp loader loads models using the Assimp library.", TestLogger test_logger; WHEN("We load an invalid file") { - phx::ResourceUtils::LoadResourceFromFile("invalid.obj"); + phx::ResourceUtils::LoadResourceFromFile<phx::Mesh>("invalid.obj"); THEN("We get an error message printed to the console.") { std::string captured_message = test_logger.GetCapture()->ToString(); @@ -65,9 +65,9 @@ SCENARIO("The assimp loader loads models using the Assimp library.", } } WHEN("We load the stanford bunny.") { - auto resource_proxy = phx::ResourceUtils::LoadResourceFromFile( + auto resource = phx::ResourceUtils::LoadResourceFromFile<phx::Mesh>( "models/bunny.obj", {{"mesh_index", 0}}); - auto mesh = dynamic_cast<phx::Mesh*>(resource_proxy->GetAs<phx::Mesh>()); + auto mesh = resource.Get(); THEN("there is a mesh") { REQUIRE(mesh != nullptr); diff --git a/tests/src/test_display_system.cpp b/tests/src/test_display_system.cpp index 5c7e6b07219fc8858c4c319cd2e456f06c833d2a..97135366aa074c6ed2dcc0d869125bbe2e3eb3b4 100644 --- a/tests/src/test_display_system.cpp +++ b/tests/src/test_display_system.cpp @@ -29,7 +29,7 @@ #include "mocks/sdl_mock.hpp" -#include "phx/display_system.hpp" +#include "phx/display_system_window.hpp" #include "phx/logger.hpp" #include "phx/window.hpp" @@ -53,8 +53,8 @@ SCENARIO( REQUIRE_CALL(sdl_mock.Get(), SDL_VideoInit(nullptr)).RETURN(0); REQUIRE_CALL(sdl_mock.Get(), SDL_VideoQuit()); phx::Engine engine; - phx::DisplaySystem* displaySystem = - engine.CreateSystem<phx::DisplaySystem>(); + phx::DisplaySystemWindow* displaySystem = + engine.CreateSystem<phx::DisplaySystemWindow>(); WHEN("We create a window.") { THEN("The window is created.") { diff --git a/tests/src/test_engine.cpp b/tests/src/test_engine.cpp index 3256e671385c5be1d8f79da1df7c43b876786ea3..f8a7baf5c5e313fd405ed9ca22895433af8bfaad 100644 --- a/tests/src/test_engine.cpp +++ b/tests/src/test_engine.cpp @@ -22,12 +22,14 @@ #include <chrono> #include <memory> +#include <string> #include <thread> +#include <vector> #include "catch/catch.hpp" #include "phx/behavior.hpp" -#include "phx/display_system.hpp" +#include "phx/display_system_window.hpp" #include "phx/engine.hpp" #include "phx/input_system.hpp" #include "phx/logger.hpp" @@ -39,11 +41,9 @@ #include "trompeloeil.hpp" -SUPPRESS_WARNINGS_BEGIN #include "mocks/openvr_mock.hpp" -SUPPRESS_WARNINGS_END -#include "mocks/sdl_mock.hpp" #include "mocks/opengl_mock.hpp" +#include "mocks/sdl_mock.hpp" using trompeloeil::_; using trompeloeil::ne; @@ -52,7 +52,7 @@ extern template struct trompeloeil::reporter<trompeloeil::specialized>; class TimeTrackerSystem : public phx::System { public: - explicit TimeTrackerSystem(phx::Engine* engine) : System(engine) {} + explicit TimeTrackerSystem(phx::Engine* engine) : phx::System(engine) {} void Update(const phx::FrameTimer::TimeInfo& time_info) override { time_info_ = &time_info; @@ -64,7 +64,7 @@ class TimeTrackerSystem : public phx::System { class EngineStopTestSystem : public phx::System { public: explicit EngineStopTestSystem(phx::Engine* engine, int stop_in_frame = 1) - : System(engine), stop_on_update_(stop_in_frame) {} + : phx::System(engine), stop_on_update_(stop_in_frame) {} void Update(const phx::FrameTimer::TimeInfo& time_info) override { update_counter_++; @@ -83,13 +83,31 @@ class EngineStopTestSystem : public phx::System { class WaitSystem : public phx::System { public: - explicit WaitSystem(phx::Engine* engine) : System(engine) {} + explicit WaitSystem(phx::Engine* engine) : phx::System(engine) {} void Update(const phx::FrameTimer::TimeInfo&) override { std::this_thread::sleep_for(std::chrono::milliseconds(5)); } }; -class AnyBehavior : public phx::Behavior {}; +template <typename T> +class TestOrderSystem : public phx::System { + public: + explicit TestOrderSystem(phx::Engine* engine) : phx::System(engine) {} + void Update(const phx::FrameTimer::TimeInfo&) override { + if (call_on_update_ != nullptr) call_on_update_(); + } + + void SetCallbackOnUpdate(const std::function<void(void)>& callback) { + call_on_update_ = callback; + } + + private: + std::function<void(void)> call_on_update_; +}; + +class AnyBehavior : public phx::Behavior { + void OnUpdate() override{}; +}; SCENARIO("Only one engine should be created", "[phx][phx::Engine]") { auto log_capture = std::make_shared<test_utilities::LogCapture>(); @@ -131,6 +149,15 @@ SCENARIO("An engine is a container of systems.", "[phx][phx::Engine]") { REQUIRE(engine.GetSystem<EngineStopTestSystem>() == nullptr); } } + + WHEN("We add another test system") { + engine.CreateSystem<EngineStopTestSystem>(); + THEN("It contains two test systems") { + std::vector<EngineStopTestSystem*> systems = + engine.GetSystems<EngineStopTestSystem>(); + REQUIRE(systems.size() == 2); + } + } } } @@ -158,11 +185,26 @@ SCENARIO("The active scene in an engine can be switched", GIVEN("An engine") { phx::Engine engine; auto first_scene = std::make_shared<phx::Scene>(); + + // setup a way to test the scene changed signal + phx::Scene* s1; + phx::Scene* s2; + engine.AddSceneChangedCallback( + [&s1, &s2](std::shared_ptr<phx::Scene> param_s1, + std::shared_ptr<phx::Scene> param_s2) { + s1 = param_s1.get(); + s2 = param_s2.get(); + }); + engine.SetScene(first_scene); THEN("It has a scene") { REQUIRE(engine.GetScene() != nullptr); REQUIRE(engine.GetScene()->GetEngine() == &engine); } + THEN("The old scene is nullptr.") { REQUIRE(s1 == nullptr); } + THEN("The new scene is the one we just set.") { + REQUIRE(s2 == first_scene.get()); + } WHEN("The scene is exchanged for a new scene") { auto new_scene = std::make_shared<phx::Scene>(); engine.SetScene(new_scene); @@ -175,6 +217,10 @@ SCENARIO("The active scene in an engine can be switched", REQUIRE(first_scene->GetEngine() == nullptr); } + THEN("The signal is triggered with the appropriate values.") { + REQUIRE(s1 == first_scene.get()); + REQUIRE(s2 == new_scene.get()); + } } } } @@ -288,7 +334,8 @@ SCENARIO("An engine can be setup by the default setup", "[phx][phx::Engine]") { THEN("It is not null") { REQUIRE(engine != nullptr); THEN("It has a window") { - auto window = engine->GetSystem<phx::DisplaySystem>()->GetWindow(); + auto window = + engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow(); REQUIRE(window != nullptr); } THEN("It has a RenderingSystem") { @@ -310,3 +357,67 @@ SCENARIO("An engine can be setup by the default setup", "[phx][phx::Engine]") { } } } + +SCENARIO("The order in which systems are updated by the engine can be changed", + "[phx][phx::Engine]") { + SDL_MOCK_ALLOW_ANY_CALL + OPENGL_MOCK_ALLOW_ANY_CALL + GIVEN("An engine set up with a number of test systems") { + auto engine = std::make_unique<phx::Engine>(); + auto system1 = engine->CreateSystem<TestOrderSystem<int>>(); + auto system2 = engine->CreateSystem<TestOrderSystem<float>>(); + auto system3 = engine->CreateSystem<TestOrderSystem<unsigned int>>(); + auto system4 = engine->CreateSystem<TestOrderSystem<double>>(); + engine->CreateSystem<EngineStopTestSystem>(); + std::string test_string; + auto callback1 = [&test_string]() { test_string.push_back('A'); }; + auto callback2 = [&test_string]() { test_string.push_back('B'); }; + auto callback3 = [&test_string]() { test_string.push_back('C'); }; + auto callback4 = [&test_string]() { test_string.push_back('D'); }; + system1->SetCallbackOnUpdate(callback1); + system2->SetCallbackOnUpdate(callback2); + system3->SetCallbackOnUpdate(callback3); + system4->SetCallbackOnUpdate(callback4); + + WHEN("We run the engine") { + engine->Run(); + THEN( + "The test systems have been updated in the same order in which they " + "were added") { + REQUIRE(test_string == "ABCD"); + } + } + + WHEN("We change the second system to update first") { + engine->GetUpdateOrder().MoveToFront(system2); + engine->Run(); + THEN("The systems are updated in the order 2-1-3-4") { + REQUIRE(test_string == "BACD"); + } + } + + WHEN("We change the second system to update last") { + engine->GetUpdateOrder().MoveToBack(system2); + engine->Run(); + THEN("The systems are updated in the order 1-3-4-2") { + REQUIRE(test_string == "ACDB"); + } + } + + WHEN("We change the update order to update system 2 after system 3") { + engine->GetUpdateOrder().MoveAfter(system2, system3); + engine->Run(); + THEN("The systems are updated in the order 1-3-2-4") { + REQUIRE(test_string == "ACBD"); + } + } + + WHEN("We change the update order to update system 4 before system 1") { + engine->GetUpdateOrder().MoveBefore(system4, system1); + engine->Run(); + THEN("The systems are updated in the order 4-1-2-3") { + REQUIRE(test_string == "DABC"); + } + } + } +} diff --git a/tests/src/test_frame_graph.cpp b/tests/src/test_frame_graph.cpp index cd98e045f714f0e7015a8ca5f574279275a40138..2008be84053496297c680dac6f2bc875e2dd2fec 100644 --- a/tests/src/test_frame_graph.cpp +++ b/tests/src/test_frame_graph.cpp @@ -48,25 +48,31 @@ SCENARIO("A frame graph manages render passes.", "[phx][phx::FrameGraph]") { WHEN("We add a mock render pass") { std::unique_ptr<MockRenderPass> pass = std::make_unique<MockRenderPass>(); - MockRenderPass* pass_ref = static_cast<MockRenderPass*>( + MockRenderPass* pass_ptr = static_cast<MockRenderPass*>( frame_graph.AddRenderPass(std::move(pass))); THEN("the frame graph has 1 render pass.") { REQUIRE(frame_graph.GetNumberOfPasses() == 1); } + THEN("we can get the mock render pass out again") { + auto mockrenderpasses = frame_graph.GetRenderPasses<MockRenderPass>(); + REQUIRE(mockrenderpasses.size() == 1); + REQUIRE(mockrenderpasses.front() == pass_ptr); + } + WHEN("we call create") { THEN("the mock render pass was initialized but not executed") { - REQUIRE_CALL(*pass_ref, Initialize()).TIMES(1); - FORBID_CALL(*pass_ref, Execute()); + REQUIRE_CALL(*pass_ptr, Initialize()).TIMES(1); + FORBID_CALL(*pass_ptr, Execute()); frame_graph.Initialize(); } } WHEN("we traverse the graph") { THEN("the mock render pass was executed") { - REQUIRE_CALL(*pass_ref, Execute()).TIMES(1); - FORBID_CALL(*pass_ref, Initialize()); + REQUIRE_CALL(*pass_ptr, Execute()).TIMES(1); + FORBID_CALL(*pass_ptr, Initialize()); frame_graph.Execute(); } } diff --git a/tests/src/test_geometry_pass.cpp b/tests/src/test_geometry_pass.cpp index 0111f5a71f2c0c2789d535a427d7857223ad4352..a111eeee252dcab4e8f899ebd40ea4f86b35d9bb 100644 --- a/tests/src/test_geometry_pass.cpp +++ b/tests/src/test_geometry_pass.cpp @@ -35,6 +35,7 @@ SUPPRESS_WARNINGS_BEGIN #include "trompeloeil.hpp" SUPPRESS_WARNINGS_END +#include "phx/entity.hpp" #include "phx/geometry_pass.hpp" #include "phx/render_target.hpp" @@ -87,13 +88,14 @@ SCENARIO( } WHEN("We add data for a mesh with 3 indices.") { + phx::Entity entity; + phx::Transform* transform = entity.AddComponent<phx::Transform>(); phx::Mesh mesh; - mesh.SetIndices({0u, 1u, 2u}); - phx::Transform transform; phx::Material material; + mesh.SetIndices({0u, 1u, 2u}); std::vector<phx::GeometryPass::RenderingInstance> geometry_pass_data; - geometry_pass_data.push_back({&mesh, &material, &transform}); + geometry_pass_data.push_back({&mesh, &material, transform}); geometry_pass.SetData(geometry_pass_data); THEN("Then one triangle is drawn") { @@ -105,18 +107,19 @@ SCENARIO( } WHEN("We add data for a mesh with 3 vertices, normals, indices.") { + phx::Entity entity; + phx::Transform* transform = entity.AddComponent<phx::Transform>(); phx::Mesh mesh; + phx::Material material; mesh.SetVertices( {{0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 0.0f}}); mesh.SetNormals( {{0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}); mesh.SetTextureCoords({{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}}); mesh.SetIndices({0u, 1u, 2u}); - phx::Transform transform; - phx::Material material; std::vector<phx::GeometryPass::RenderingInstance> geometry_pass_data; - geometry_pass_data.push_back({&mesh, &material, &transform}); + geometry_pass_data.push_back({&mesh, &material, transform}); THEN( "3 vertices, 3 normals, 3 texCoords, 3 indices are uploaded and " @@ -222,25 +225,31 @@ SCENARIO( phx::GeometryPass geometry_pass(&renderTarget); geometry_pass.Initialize(); - WHEN("We add two meshes with different transformations") { + WHEN("We add a meshes with two different transformations") { + phx::Entity entity1; + phx::Transform* transform1 = entity1.AddComponent<phx::Transform>(); + phx::Entity entity2; + phx::Transform* transform2 = entity2.AddComponent<phx::Transform>(); + phx::Mesh mesh; + phx::Material material; + mesh.SetVertices( {{0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 0.0f}}); mesh.SetNormals( {{0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}); mesh.SetTextureCoords({{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}}); mesh.SetIndices({0u, 1u, 2u}); - phx::Material material; - phx::Transform transform1(glm::vec3(-1, 0, 0)); - const glm::mat4 global_matrix1{transform1.GetGlobalMatrix()}; - phx::Transform transform2(glm::vec3(1, 0, 0)); - const glm::mat4 local_matrix2{transform2.GetLocalMatrix()}; + transform1->SetGlobalTranslation(glm::vec3(-1, 0, 0)); + const glm::mat4 global_matrix1{transform1->GetGlobalMatrix()}; + transform2->SetGlobalTranslation(glm::vec3(1, 0, 0)); + const glm::mat4 local_matrix2{transform2->GetLocalMatrix()}; phx::GeometryPass::RenderingInstance instance1{&mesh, &material, - &transform1}; + transform1}; phx::GeometryPass::RenderingInstance instance2{&mesh, &material, - &transform2}; + transform2}; geometry_pass.SetData({instance1, instance2}, {}); THEN( diff --git a/tests/src/test_light.cpp b/tests/src/test_light.cpp index 409151931f5c6cc0436438731486864007b8beff..c8a4fadf4720565da8848fc67326acb5c2f4ac43 100644 --- a/tests/src/test_light.cpp +++ b/tests/src/test_light.cpp @@ -20,8 +20,11 @@ // limitations under the License. //------------------------------------------------------------------------------ +#include <memory> + #include "catch/catch.hpp" +#include "phx/entity.hpp" #include "phx/light.hpp" #include "glm/glm.hpp" @@ -29,38 +32,39 @@ SCENARIO("The light component describes a light source in space.", "[phx][phx::Light]") { GIVEN("A new light component.") { - phx::Light light; + phx::Entity entity; + phx::Light* light = entity.AddComponent<phx::Light>(); WHEN("We set the type to spotlight.") { - light.SetType(phx::Light::Type::kSpot); + light->SetType(phx::Light::Type::kSpot); THEN("The type is set to spotlight.") { - REQUIRE(light.GetType() == phx::Light::Type::kSpot); + REQUIRE(light->GetType() == phx::Light::Type::kSpot); } } WHEN("We set the color to [1.0, 0.0, 0.0].") { - light.SetColor(glm::vec3(1.0, 0.0, 0.0)); + light->SetColor(glm::vec3(1.0, 0.0, 0.0)); THEN("The color is set to [1.0, 0.0, 0.0].") { - REQUIRE(light.GetColor() == glm::vec3(1.0, 0.0, 0.0)); + REQUIRE(light->GetColor() == glm::vec3(1.0, 0.0, 0.0)); } } WHEN("We set the intensity to 10.") { - light.SetIntensity(10.0f); + light->SetIntensity(10.0f); THEN("The intensity is set to 10.") { - REQUIRE(light.GetIntensity() == 10.0f); + REQUIRE(light->GetIntensity() == 10.0f); } } WHEN("We set the range to 100.") { - light.SetRange(100.0f); - THEN("The range is set to 100.") { REQUIRE(light.GetRange() == 100.0f); } + light->SetRange(100.0f); + THEN("The range is set to 100.") { REQUIRE(light->GetRange() == 100.0f); } } WHEN("We set the spot angle to 60.") { - light.SetSpotAngle(60.0f); + light->SetSpotAngle(60.0f); THEN("The spot angle is set to 60.") { - REQUIRE(light.GetSpotAngle() == 60.0f); + REQUIRE(light->GetSpotAngle() == 60.0f); } } } diff --git a/tests/src/test_model.cpp b/tests/src/test_model.cpp index e12c98e706b069845518997d1ce74d15cee0b88b..2d6b927ea4056039e4f4414a15b1c55671abfc9a 100644 --- a/tests/src/test_model.cpp +++ b/tests/src/test_model.cpp @@ -35,9 +35,8 @@ SCENARIO("A Model can be loaded that contains multiple meshes", GIVEN("A file name of an .obj file...") { std::string model_file_name{"models/2MeshTest/2meshTest.obj"}; WHEN("A model resource is declared and loaded") { - auto model_proxy = - phx::ResourceUtils::LoadResourceFromFile(model_file_name); - phx::Model* model = model_proxy->GetAs<phx::Model>(); + auto model = phx::ResourceUtils::LoadResourceFromFile<phx::Model>( + model_file_name); THEN("the returned model contains 2 meshes") { REQUIRE(model->GetMeshes().size() == 2u); } diff --git a/tests/src/test_openvr_controller_system.cpp b/tests/src/test_openvr_controller_system.cpp new file mode 100644 index 0000000000000000000000000000000000000000..225ea9b498109211629d506451deb94b6834fde4 --- /dev/null +++ b/tests/src/test_openvr_controller_system.cpp @@ -0,0 +1,118 @@ +//------------------------------------------------------------------------------ +// 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" + +SUPPRESS_WARNINGS_BEGIN +#include "GL/glew.h" +SUPPRESS_WARNINGS_END + +#include "trompeloeil.hpp" + +#include "phx/display_system_openvr.hpp" +#include "phx/entity.hpp" +#include "phx/material_handle.hpp" +#include "phx/mesh_handle.hpp" +#include "phx/openvr_controller_behavior.hpp" +#include "phx/openvr_controller_system.hpp" +#include "phx/rendering_system.hpp" +#include "phx/scene.hpp" + +#include "mocks/opengl_mock.hpp" +#include "mocks/openvr_mock.hpp" +#include "mocks/sdl_mock.hpp" + +using trompeloeil::_; +using trompeloeil::ne; + +extern template struct trompeloeil::reporter<trompeloeil::specialized>; + +class EngineStopTestSystem : public phx::System { + public: + explicit EngineStopTestSystem(phx::Engine* engine, int stop_in_frame = 1) + : System(engine), stop_on_update_(stop_in_frame) {} + + void Update(const phx::FrameTimer::TimeInfo& time_info) override { + update_counter_++; + if (update_counter_ >= stop_on_update_) { + GetEngine()->Stop(); + } + time_info_ = &time_info; + } + + const phx::FrameTimer::TimeInfo* time_info_ = nullptr; + + private: + int update_counter_ = 0; + int stop_on_update_ = 1; +}; + +SCENARIO( + "The OpenVRControllerSystem automatically inserts controller entities into " + "a scene if controllers are connected", + "[phx][phx::OpenVRControllerSystem]") { + 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>(); + + GIVEN( + "The display system has an HMD and the engine has a scene. Also, there's " + "an OpenVRControllerSystem.") { + display_system->CreateHMD(); + auto scene = std::make_shared<phx::Scene>(); + engine.SetScene(scene); + engine.CreateSystem<phx::OpenVRControllerSystem>(display_system); + engine.CreateSystem<EngineStopTestSystem>(); + engine.CreateSystem<phx::RenderingSystem>(display_system); + + WHEN("We run the engine for once frame (updating each system once)") { + engine.Run(); + + THEN( + "There are controller entities in the scene with controller " + "behaviors attached to them, as well as mesh and material handles, " + "and the controllers are left and right") { + auto controller_entities = + scene->GetEntitiesWithComponents<phx::OpenVRControllerBehavior>(); + REQUIRE(controller_entities.size() == 2); + + for (auto entity : controller_entities) { + REQUIRE(entity->GetFirstComponent<phx::MeshHandle>() != nullptr); + REQUIRE(entity->GetFirstComponent<phx::MaterialHandle>() != nullptr); + } + + auto side0 = controller_entities[0] + ->GetFirstComponent<phx::OpenVRControllerBehavior>() + ->GetSide(); + auto side1 = controller_entities[1] + ->GetFirstComponent<phx::OpenVRControllerBehavior>() + ->GetSide(); + REQUIRE(side0 != side1); + } + } + } +} diff --git a/tests/src/test_projection.cpp b/tests/src/test_projection.cpp index 896bdab742f7f778d9c2db50e8e70a93dc75ebdf..b1b4c963d59c733777bbe52adcb26cfcd378843b 100644 --- a/tests/src/test_projection.cpp +++ b/tests/src/test_projection.cpp @@ -26,6 +26,7 @@ SUPPRESS_WARNINGS_BEGIN #include "glm/glm.hpp" SUPPRESS_WARNINGS_END +#include "phx/entity.hpp" #include "phx/projection.hpp" #include "test_utilities/glm_mat4.hpp" @@ -33,9 +34,10 @@ SUPPRESS_WARNINGS_END SCENARIO("The projection component stores data for projections.", "[phx][phx::Projection]") { GIVEN("A new projection component.") { - phx::Projection projection; + phx::Entity entity; + phx::Projection* projection = entity.AddComponent<phx::Projection>(); WHEN("we query the matrix.") { - auto matrix = projection.GetMatrix(); + auto matrix = projection->GetMatrix(); THEN("It should yield an orthogonal projection to the xz-plane.") { const glm::mat4 ortho(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); @@ -46,9 +48,9 @@ SCENARIO("The projection component stores data for projections.", WHEN( "We set some projection parameters for a perspective projection and " "query the matrix.") { - projection.SetPerspective(glm::radians(68.0f), 4.0f / 3.0f, 0.01f, - 1000.0f); - auto matrix = projection.GetMatrix(); + projection->SetPerspective(glm::radians(68.0f), 4.0f / 3.0f, 0.01f, + 1000.0f); + auto matrix = projection->GetMatrix(); THEN("It should yield the appropriate projection matrix.") { const glm::mat4 ground_truth(1.11192f, 0.0f, 0.0f, 0.0f, 0.0f, 1.48256f, 0.0f, 0.0f, 0.0f, 0.0f, -1.00002f, -1.0f, @@ -61,9 +63,9 @@ SCENARIO("The projection component stores data for projections.", WHEN( "We set some projection parameters for an orthogonal projection and " "query the matrix.") { - projection.SetOrthogonal(-100.0f, 100.0f, -100.0f, 100.0f, 0.01f, - 1000.0f); - auto matrix = projection.GetMatrix(); + projection->SetOrthogonal(-100.0f, 100.0f, -100.0f, 100.0f, 0.01f, + 1000.0f); + auto matrix = projection->GetMatrix(); THEN("It should yield the appropriate projection matrix.") { const glm::mat4 ground_truth(0.01, 0.0f, 0.0f, 0.0f, 0.0f, 0.01f, 0.0f, 0.0f, 0.0f, 0.0f, -0.002f, 0.0f, 0.0f, diff --git a/tests/src/test_rendering_system.cpp b/tests/src/test_rendering_system.cpp index 4c83660c7f36a14b96d0264636bdf7294d8c2cbd..cd925e872804a65417c00adf684761e9f8741404 100644 --- a/tests/src/test_rendering_system.cpp +++ b/tests/src/test_rendering_system.cpp @@ -24,6 +24,7 @@ #include "catch/catch.hpp" +#include "phx/display_system_window.hpp" #include "phx/engine.hpp" #include "phx/entity.hpp" #include "phx/geometry_pass.hpp" @@ -48,7 +49,7 @@ SCENARIO( phx::Engine engine; GIVEN("A rendering system.") { - auto diplay_system = engine.CreateSystem<phx::DisplaySystem>(); + auto diplay_system = engine.CreateSystem<phx::DisplaySystemWindow>(); phx::RenderingSystem* rendering_system = engine.CreateSystem<phx::RenderingSystem>(diplay_system); diff --git a/tests/src/test_resource_pointer.cpp b/tests/src/test_resource_pointer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a95e89a53565bbedb982e9c6468f2d3a4d1e49bb --- /dev/null +++ b/tests/src/test_resource_pointer.cpp @@ -0,0 +1,96 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "catch/catch.hpp" + +#include "phx/image.hpp" +#include "phx/resource_manager.hpp" +#include "phx/resource_pointer.hpp" +#include "phx/resource_proxy.hpp" +#include "phx/resource_utils.hpp" + +SCENARIO( + "The resource pointer holds a raw pointer to a resource proxy and makes " + "the underlying resource available through the -> operator.", + "[phx][phx::ResourcePointer]") { + GIVEN("A proxy to an arbitrarily chosen image resource.") { + phx::ResourceProxy* proxy = + phx::ResourceManager::instance().DeclareResource( + phx::ResourceUtils::DeclarationFromFile( + "textures/splash_progress.png")); + WHEN("We initialize a resource pointer with that proxy.") { + phx::ResourcePointer<phx::Image> resource_pointer = + phx::ResourcePointer<phx::Image>(proxy); + THEN("We can access the image through the -> operator.") { + REQUIRE(resource_pointer.operator->() == proxy->GetAs<phx::Image>()); + } + THEN("We can access the proxy through the GetProxy() method.") { + REQUIRE(resource_pointer.GetProxy() == proxy); + } + THEN( + "We can can compare the pointer to nullptr using the != operator and " + "they are the same, because the resource is not loaded.") { + REQUIRE(resource_pointer == nullptr); + REQUIRE(!resource_pointer); + } + WHEN("We load the resource.") { + proxy->Load(); + THEN( + "We can compare the pointer to nullptr and they are not the " + "same.") { + REQUIRE(resource_pointer != nullptr); + REQUIRE(resource_pointer); + } + } + WHEN("We initialize a new pointer with the same proxy.") { + phx::ResourcePointer<phx::Image> resource_pointer2 = + phx::ResourcePointer<phx::Image>(proxy); + THEN( + "We can can compare the two pointers using the == operator and " + "they are the same") { + REQUIRE(resource_pointer == resource_pointer2); + } + } + WHEN("We initialize a new pointer with a different proxy.") { + phx::ResourceProxy* proxy2 = + phx::ResourceManager::instance().DeclareResource( + phx::ResourceUtils::DeclarationFromFile("textures/splash.png")); + phx::ResourcePointer<phx::Image> resource_pointer2 = + phx::ResourcePointer<phx::Image>(proxy2); + THEN( + "We can can compare the two pointers using the != operator and " + "they are not the same") { + REQUIRE(resource_pointer != resource_pointer2); + } + } + WHEN("We initialize a new pointer with nullptr.") { + phx::ResourcePointer<phx::Image> resource_pointer2 = + phx::ResourcePointer<phx::Image>(nullptr); + THEN( + "We can can compare the two pointers using the != operator and " + "they are not the same") { + REQUIRE(resource_pointer != resource_pointer2); + } + } + } + } +} diff --git a/tests/src/test_resource_utils.cpp b/tests/src/test_resource_utils.cpp index a59ff75f4ed472611e9d7906ceec1cd0cecb9870..45d092956465a30c8583ac65a7fae6a441df33aa 100644 --- a/tests/src/test_resource_utils.cpp +++ b/tests/src/test_resource_utils.cpp @@ -24,6 +24,7 @@ #include "catch/catch.hpp" +#include "phx/image.hpp" #include "phx/resource_declaration.hpp" #include "phx/resource_proxy.hpp" #include "phx/resource_utils.hpp" @@ -93,9 +94,9 @@ SCENARIO("Resource utils can load a file resource.", std::string file_name{"textures/splash_progress.png"}; THEN("It can load it") { - phx::ResourceProxy* proxy = - phx::ResourceUtils::LoadResourceFromFile(file_name); - REQUIRE(proxy->IsOnline()); + phx::ResourcePointer<phx::Image> image = + phx::ResourceUtils::LoadResourceFromFile<phx::Image>(file_name); + REQUIRE(image != nullptr); } } } diff --git a/tests/src/test_shader.cpp b/tests/src/test_shader.cpp index e7f87ff0af3852cefe14bb4baf9159f19767f295..b433156e3078559d9ae89dc3f3bc06059fa6f024 100644 --- a/tests/src/test_shader.cpp +++ b/tests/src/test_shader.cpp @@ -59,11 +59,12 @@ SCENARIO("We can load shaders.", "[phx][phx::Shader]") { REQUIRE_CALL(open_gl_mock, glCompileShader(1u)); REQUIRE_CALL(open_gl_mock, glAttachShader(1u, 1u)); - auto fragment_proxy = - phx::ResourceUtils::LoadResourceFromFile("shader/test.frag"); + auto fragment = + phx::ResourceUtils::LoadResourceFromFile<phx::ShaderSource>( + "shader/test.frag"); - bool success = shader_program.SetShaderProxy( - phx::ShaderProgram::FRAGMENT, fragment_proxy); + bool success = + shader_program.SetShader(phx::ShaderProgram::FRAGMENT, fragment); REQUIRE(success); REQUIRE(shader_program.is_valid()); @@ -83,15 +84,17 @@ SCENARIO("We can load shaders.", "[phx][phx::Shader]") { .SIDE_EFFECT(*_3 = 1); ALLOW_CALL(open_gl_mock, glCreateShader(_)).RETURN(1u); - auto vertex_proxy = - phx::ResourceUtils::LoadResourceFromFile("shader/phong.vert"); - auto fragment_proxy = - phx::ResourceUtils::LoadResourceFromFile("shader/phong.frag"); + auto vertex = + phx::ResourceUtils::LoadResourceFromFile<phx::ShaderSource>( + "shader/phong.vert"); + auto fragment = + phx::ResourceUtils::LoadResourceFromFile<phx::ShaderSource>( + "shader/phong.frag"); - bool success = shader_program.SetShaderProxy(phx::ShaderProgram::VERTEX, - vertex_proxy); - success &= shader_program.SetShaderProxy(phx::ShaderProgram::FRAGMENT, - fragment_proxy); + bool success = + shader_program.SetShader(phx::ShaderProgram::VERTEX, vertex); + success &= + shader_program.SetShader(phx::ShaderProgram::FRAGMENT, fragment); REQUIRE(success); REQUIRE(shader_program.is_valid()); @@ -107,15 +110,13 @@ SCENARIO("Shader Programs link themselves if they should be binded.", REQUIRE_CALL(open_gl_mock, glCreateProgram()).TIMES(1).RETURN(1u); phx::ShaderProgram shader_program; - auto vertex_proxy = - phx::ResourceUtils::LoadResourceFromFile("shader/phong.vert"); - auto fragment_proxy = - phx::ResourceUtils::LoadResourceFromFile("shader/phong.frag"); + auto vertex = phx::ResourceUtils::LoadResourceFromFile<phx::ShaderSource>( + "shader/phong.vert"); + auto fragment = phx::ResourceUtils::LoadResourceFromFile<phx::ShaderSource>( + "shader/phong.frag"); - bool success = - shader_program.SetShaderProxy(phx::ShaderProgram::VERTEX, vertex_proxy); - success &= shader_program.SetShaderProxy(phx::ShaderProgram::FRAGMENT, - fragment_proxy); + bool success = shader_program.SetShader(phx::ShaderProgram::VERTEX, vertex); + success &= shader_program.SetShader(phx::ShaderProgram::FRAGMENT, fragment); WHEN("we use this for the first time") { THEN(" it is also LinkShaderProgram links it") { diff --git a/tests/src/test_tracking_system.cpp b/tests/src/test_tracking_system.cpp index ea6f58a16e8e944ec443a99d475d82d88192aeff..467bf0b4ae7463e2e96ac722de572710e9f82466 100644 --- a/tests/src/test_tracking_system.cpp +++ b/tests/src/test_tracking_system.cpp @@ -31,12 +31,12 @@ SUPPRESS_WARNINGS_BEGIN SUPPRESS_WARNINGS_END #include "mocks/sdl_mock.hpp" -#include "phx/display_system.hpp" +#include "phx/display_system_openvr.hpp" #include "phx/entity.hpp" #include "phx/projection.hpp" #include "phx/runtime_component.hpp" #include "phx/scene.hpp" -#include "phx/tracking_system.hpp" +#include "phx/tracking_system_openvr.hpp" #include "phx/transform.hpp" #include "test_utilities/glm_mat4.hpp" @@ -46,8 +46,8 @@ using trompeloeil::ne; extern template struct trompeloeil::reporter<trompeloeil::specialized>; -void SetGlmToArrayMatrix_3_4(glm::mat4 m, float* array); -void SetGlmToArrayMatrix_3_4(glm::mat4 m, float* array) { +void SetGlmToArrayMatrix_3_4(glm::mat4 m, float *array); +void SetGlmToArrayMatrix_3_4(glm::mat4 m, float *array) { std::size_t index = 0; for (int x = 0; x < 3; x++) { for (int y = 0; y < 4; y++) { @@ -56,266 +56,412 @@ void SetGlmToArrayMatrix_3_4(glm::mat4 m, float* array) { } } +template <typename ComponentType> +bool HasComponent(phx::Entity *entity) { + return entity->GetFirstComponent<ComponentType>() != nullptr; +} + +bool HasUserPlatform(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>(entity); +} + +bool HasHMD(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::HEAD>>(entity); +} + +bool HasTransform(phx::Entity *entity) { + return HasComponent<phx::Transform>(entity); +} + +bool HasLeftEye(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::LEFT_EYE>>(entity); +} + +bool HasRightEye(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::RIGHT_EYE>>(entity); +} + +bool HasProjection(phx::Entity *entity) { + return HasComponent<phx::Projection>(entity); +} + +bool HasLeftController(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::LEFT_CONTROLLER>>(entity); +} + +bool HasRightController(phx::Entity *entity) { + return HasComponent<phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>(entity); +} + +bool ValidateUserPlatform(phx::Entity *entity, bool platform_present) { + if (HasUserPlatform(entity)) { + THEN("There is only one virtual platform.") { + REQUIRE(platform_present == false); + } + return true; + } + return false; +} + +bool ValidateHMD(phx::Entity *entity, bool hmd_present) { + if (HasHMD(entity)) { + THEN("There is only one HMD.") { REQUIRE(hmd_present == false); } + THEN("The HMD has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN( + "The parent of the HMD's transform is the platform's " + "transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE( + entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>()); + } + return true; + } + return false; +} + +bool ValidateLeftEye(phx::Entity *entity, bool left_eye_present) { + if (HasLeftEye(entity)) { + THEN("There is only one left eye.") { REQUIRE(left_eye_present == false); } + THEN("The left eye has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN("The left eye has a projection component.") { + REQUIRE(HasProjection(entity)); + } + THEN( + "The parent of the left eye's transform is the HMD's " + "transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE(entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()); + } + return true; + } + return false; +} + +bool ValidateRightEye(phx::Entity *entity, bool right_eye_present) { + if (HasRightEye(entity)) { + THEN("There is only one right eye.") { + REQUIRE(right_eye_present == false); + } + THEN("The right eye has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN("The right eye has a projection component.") { + REQUIRE(HasProjection(entity)); + } + THEN( + "The parent of the right eye's transform is the HMD's " + "transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE(entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()); + } + return true; + } + return false; +} + +bool ValidateLeftController(phx::Entity *entity, bool left_controller_present) { + if (HasLeftController(entity)) { + THEN("There is only one left controller.") { + REQUIRE(left_controller_present == false); + } + THEN("The left controller has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN( + "The parent of the left controller's transform is the " + "platform's transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE( + entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>()); + } + return true; + } + return false; +} + +bool ValidateRightController(phx::Entity *entity, + bool right_controller_present) { + if (HasRightController(entity)) { + THEN("There is only one right controller.") { + REQUIRE(right_controller_present == false); + } + THEN("The right controller has a transform component.") { + REQUIRE(HasTransform(entity)); + } + THEN( + "The parent of the right controller's transform is the " + "platform's transform.") { + REQUIRE(entity->GetFirstComponent<phx::Transform>()->GetParent() != + nullptr); + REQUIRE( + entity->GetFirstComponent<phx::Transform>() + ->GetParent() + ->GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::USER_PLATFORM>>()); + } + return true; + } + return false; +} + +bool ValidatePlatformTransform(phx::Entity *entity, + glm::mat4 platform_trans_mat) { + if (HasUserPlatform(entity)) { + THEN("The platform transformation did not change.") { + REQUIRE( + test_utilities::Approx<glm::mat4>( + entity->GetFirstComponent<phx::Transform>()->GetLocalMatrix()) == + platform_trans_mat); + } + return true; + } + return false; +} + +bool ValidateHMDTransform(phx::Entity *entity, glm::mat4 hmd_trans_mat) { + if (HasHMD(entity)) { + THEN( + "The hmd transformation changes to the one provided by " + "OpenVR.") { + REQUIRE( + test_utilities::Approx<glm::mat4>( + entity->GetFirstComponent<phx::Transform>()->GetLocalMatrix()) == + hmd_trans_mat); + } + return true; + } + return false; +} + +bool ValidateLeftEyeTransform(phx::Entity *entity, + glm::mat4 left_eye_trans_mat) { + if (HasLeftEye(entity)) { + THEN( + "The left eye transformation changes to the one provided " + "by OpenVR.") { + REQUIRE( + test_utilities::Approx<glm::mat4>( + entity->GetFirstComponent<phx::Transform>()->GetLocalMatrix()) == + left_eye_trans_mat); + } + return true; + } + return false; +} + +bool ValidateRightEyeTransform(phx::Entity *entity, + glm::mat4 right_eye_trans_mat) { + if (HasRightEye(entity)) { + THEN( + "The right eye transformation changes to the one provided " + "by OpenVR.") { + REQUIRE( + test_utilities::Approx<glm::mat4>( + entity->GetFirstComponent<phx::Transform>()->GetLocalMatrix()) == + right_eye_trans_mat); + } + return true; + } + return false; +} + +void TestRuntimeEntityStructure(std::shared_ptr<phx::Scene> scene) { + THEN("The HMD and the user's eyes are represented in the scene.") { + auto entities = scene->GetEntitiesWithComponents<phx::Transform>(); + bool platform_present = false; + bool hmd_present = false; + bool left_eye_present = false; + bool right_eye_present = false; + bool left_controller_present = false; + bool right_controller_present = false; + + for (auto entity : entities) { + platform_present |= ValidateUserPlatform(entity, platform_present); + + hmd_present |= ValidateHMD(entity, hmd_present); + + left_eye_present |= ValidateLeftEye(entity, left_eye_present); + right_eye_present |= ValidateRightEye(entity, right_eye_present); + + left_controller_present |= + ValidateLeftController(entity, left_controller_present); + right_controller_present |= + ValidateLeftController(entity, right_controller_present); + } + + THEN( + "There are six entities: The platform, the HMD, the left and " + "the right eyes, the left and right controllers.") { + REQUIRE(entities.size() == 6); + REQUIRE(platform_present); + REQUIRE(hmd_present); + REQUIRE(left_eye_present); + REQUIRE(right_eye_present); + REQUIRE(left_controller_present); + REQUIRE(right_controller_present); + } + } +} + +void InitMatrices(glm::mat4 *platform_trans_mat, glm::mat4 *hmd_trans_mat, + glm::mat4 *left_eye_trans_mat, + glm::mat4 *right_eye_trans_mat) { + *platform_trans_mat = + glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 1.0f, -1.0f, 0.5f, 1.0f); + *hmd_trans_mat = glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.8f, 0.2f, 1.0f); + *left_eye_trans_mat = + glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, -0.04f, 0.0f, 0.0f, 1.0f); + *right_eye_trans_mat = + glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.04f, 0.0f, 0.0f, 1.0f); + SetGlmToArrayMatrix_3_4(*hmd_trans_mat, openvr_mock.head_transformation_); + SetGlmToArrayMatrix_3_4(*left_eye_trans_mat, openvr_mock.eye_to_head_left_); + SetGlmToArrayMatrix_3_4(*right_eye_trans_mat, openvr_mock.eye_to_head_right_); +} + +void TestRuntimeEntityUpdate(std::shared_ptr<phx::Scene> scene, + phx::TrackingSystemOpenVR *tracking_system) { + glm::mat4 platform_trans_mat, hmd_trans_mat, left_eye_trans_mat, + right_eye_trans_mat; + InitMatrices(&platform_trans_mat, &hmd_trans_mat, &left_eye_trans_mat, + &right_eye_trans_mat); + + WHEN("The tracking system is updated.") { + tracking_system->Update(phx::FrameTimer::TimeInfo()); + THEN( + "It sets the transforms of all the runtime entities to the " + "correct values.") { + auto entities = scene->GetEntitiesWithComponents<phx::Transform>(); + bool platform_present = false; + bool hmd_present = false; + bool left_eye_present = false; + bool right_eye_present = false; + bool left_controller_present = false; + bool right_controller_present = false; + + for (auto entity : entities) { + platform_present |= + ValidatePlatformTransform(entity, platform_trans_mat); + + hmd_present |= ValidateHMDTransform(entity, hmd_trans_mat); + + left_eye_present |= + ValidateLeftEyeTransform(entity, left_eye_trans_mat); + right_eye_present |= + ValidateRightEyeTransform(entity, right_eye_trans_mat); + + left_controller_present |= HasLeftController(entity); + right_controller_present |= HasRightController(entity); + } + THEN( + "There are still six entities: The platform, the HMD, the " + "left and the right eyes, the left and the right controllers.") { + REQUIRE(entities.size() == 6); + REQUIRE(platform_present); + REQUIRE(hmd_present); + REQUIRE(left_eye_present); + REQUIRE(right_eye_present); + REQUIRE(left_controller_present); + REQUIRE(right_controller_present); + } + } + } +} + SCENARIO( "The tracking system tracks hardware and updates their software " "counterparts.", - "[phx][phx::TrackingSystem]") { + "[phx][phx::TrackingSystemOpenVR]") { OPENVR_MOCK_ALLOW_ANY_CALL; SDL_MOCK_ALLOW_ANY_CALL; phx::Engine engine; - auto display_system = engine.CreateSystem<phx::DisplaySystem>(); + auto display_system = engine.CreateSystem<phx::DisplaySystemOpenVR>(); GIVEN( "The display system has an HMD and the engine has a scene with a " "user platform.") { display_system->CreateHMD(); - auto scene = std::make_shared<phx::Scene>(); - engine.SetScene(scene); + auto first_scene = std::make_shared<phx::Scene>(); + engine.SetScene(first_scene); auto platform_trans_mat = glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.5f, 1.0f); - scene + first_scene ->GetEntitiesWithComponents< phx::RuntimeComponent<phx::USER_PLATFORM>>()[0] ->GetFirstComponent<phx::Transform>() ->SetLocalMatrix(platform_trans_mat); WHEN("A tracking system is created and initialized.") { auto tracking_system = - engine.CreateSystem<phx::TrackingSystem>(display_system); - THEN("The HMD and the user's eyes are represented in the scene.") { - auto entities = scene->GetEntitiesWithComponents<phx::Transform>(); - bool platform_present = false; - bool hmd_present = false; - bool left_eye_present = false; - bool right_eye_present = false; - bool left_controller_present = false; - bool right_controller_present = false; - for (auto entity : entities) { - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()) { - THEN("There is only one virtual platform.") { - REQUIRE(platform_present == false); - } - platform_present = true; - } - if (entity->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()) { - THEN("There is only one HMD.") { REQUIRE(hmd_present == false); } - THEN("The HMD has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN( - "The parent of the HMD's transform is the platform's " - "transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE(entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()); - } - hmd_present = true; - } - if (entity - ->GetFirstComponent<phx::RuntimeComponent<phx::LEFT_EYE>>()) { - THEN("There is only one left eye.") { - REQUIRE(left_eye_present == false); - } - THEN("The left eye has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN("The left eye has a projection component.") { - REQUIRE(entity->GetFirstComponent<phx::Projection>()); - } - THEN( - "The parent of the left eye's transform is the HMD's " - "transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE( - entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()); - } - left_eye_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::RIGHT_EYE>>()) { - THEN("There is only one right eye.") { - REQUIRE(right_eye_present == false); - } - THEN("The right eye has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN("The right eye has a projection component.") { - REQUIRE(entity->GetFirstComponent<phx::Projection>()); - } - THEN( - "The parent of the right eye's transform is the HMD's " - "transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE( - entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()); - } - right_eye_present = true; - } - - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::LEFT_CONTROLLER>>()) { - THEN("There is only one left controller.") { - REQUIRE(left_controller_present == false); - } - THEN("The left controller has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN( - "The parent of the left controller's transform is the " - "platform's transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE(entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()); - } - left_controller_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()) { - THEN("There is only one right controller.") { - REQUIRE(right_controller_present == false); - } - THEN("The right controller has a transform component.") { - REQUIRE(entity->GetFirstComponent<phx::Transform>()); - } - THEN( - "The parent of the right controller's transform is the " - "platform's transform.") { - REQUIRE( - entity->GetFirstComponent<phx::Transform>()->GetParent() != - nullptr); - REQUIRE(entity->GetFirstComponent<phx::Transform>() - ->GetParent() - ->GetEntity() - ->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()); - } - right_controller_present = true; - } - } - THEN( - "There are six entities: The platform, the HMD, the left and " - "the right eyes, the left and right controllers.") { - REQUIRE(entities.size() == 6); - REQUIRE(platform_present); - REQUIRE(hmd_present); - REQUIRE(left_eye_present); - REQUIRE(right_eye_present); - REQUIRE(left_controller_present); - REQUIRE(right_controller_present); - } - } - WHEN("The tracking system is then updated.") { - auto hmd_trans_mat = - glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.5f, 1.8f, 0.2f, 1.0f); - auto left_eye_trans_mat = - glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, -0.04f, 0.0f, 0.0f, 1.0f); - auto right_eye_trans_mat = - glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.04f, 0.0f, 0.0f, 1.0f); - SetGlmToArrayMatrix_3_4(hmd_trans_mat, - openvr_mock.head_transformation_); - SetGlmToArrayMatrix_3_4(left_eye_trans_mat, - openvr_mock.eye_to_head_left_); - SetGlmToArrayMatrix_3_4(right_eye_trans_mat, - openvr_mock.eye_to_head_right_); - tracking_system->Update(phx::FrameTimer::TimeInfo()); + engine.CreateSystem<phx::TrackingSystemOpenVR>(display_system); + + TestRuntimeEntityStructure(first_scene); + TestRuntimeEntityUpdate(first_scene, tracking_system); + + WHEN("The scene is then changed.") { + auto new_scene = std::make_shared<phx::Scene>(); + new_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::USER_PLATFORM>>()[0] + ->GetFirstComponent<phx::Transform>() + ->SetLocalMatrix(platform_trans_mat); + engine.SetScene(new_scene); + THEN( - "It sets the transforms of all the runtime entities to the " - "correct values.") { - auto entities = scene->GetEntitiesWithComponents<phx::Transform>(); - bool platform_present = false; - bool hmd_present = false; - bool left_eye_present = false; - bool right_eye_present = false; - bool left_controller_present = false; - bool right_controller_present = false; - for (auto entity : entities) { - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::USER_PLATFORM>>()) { - THEN("The platform transformation did not change.") { - REQUIRE(test_utilities::Approx<glm::mat4>( - entity->GetFirstComponent<phx::Transform>() - ->GetLocalMatrix()) == platform_trans_mat); - } - platform_present = true; - } - if (entity->GetFirstComponent<phx::RuntimeComponent<phx::HEAD>>()) { - THEN( - "The hmd transformation changes to the one provided by " - "OpenVR.") { - REQUIRE(test_utilities::Approx<glm::mat4>( - entity->GetFirstComponent<phx::Transform>() - ->GetLocalMatrix()) == hmd_trans_mat); - } - hmd_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::LEFT_EYE>>()) { - THEN( - "The left eye transformation changes to the one provided " - "by OpenVR.") { - REQUIRE(test_utilities::Approx<glm::mat4>( - entity->GetFirstComponent<phx::Transform>() - ->GetLocalMatrix()) == left_eye_trans_mat); - } - left_eye_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::RIGHT_EYE>>()) { - THEN( - "The right eye transformation changes to the one provided " - "by OpenVR.") { - REQUIRE(test_utilities::Approx<glm::mat4>( - entity->GetFirstComponent<phx::Transform>() - ->GetLocalMatrix()) == right_eye_trans_mat); - } - right_eye_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::LEFT_CONTROLLER>>()) { - left_controller_present = true; - } - if (entity->GetFirstComponent< - phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()) { - right_controller_present = true; - } - } - THEN( - "There are still six entities: The platform, the HMD, the " - "left and the right eyes, the left and the right controllers.") { - REQUIRE(entities.size() == 6); - REQUIRE(platform_present); - REQUIRE(hmd_present); - REQUIRE(left_eye_present); - REQUIRE(right_eye_present); - REQUIRE(left_controller_present); - REQUIRE(right_controller_present); - } + "The first scene does not contain runtime entities except for " + "exactly one user platform.") { + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::USER_PLATFORM>>() + .size() == 1); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::HEAD>>() + .size() == 0); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::LEFT_EYE>>() + .size() == 0); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::RIGHT_EYE>>() + .size() == 0); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::LEFT_CONTROLLER>>() + .size() == 0); + REQUIRE(first_scene + ->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>() + .size() == 0); } + + TestRuntimeEntityStructure(new_scene); + TestRuntimeEntityUpdate(new_scene, tracking_system); } } } diff --git a/tests/test_utilities/tests/src/test_reference_images.cpp b/tests/test_utilities/tests/src/test_reference_images.cpp index c6b3a2236bfb8e9db0127989c611fe13d981d975..f2005d2cc4c4a91fd7f15de4cdee2bd3cfd5510a 100644 --- a/tests/test_utilities/tests/src/test_reference_images.cpp +++ b/tests/test_utilities/tests/src/test_reference_images.cpp @@ -114,14 +114,12 @@ SCENARIO( THEN( "If they accept, the reference image now exists and is identical " "to the buffer") { - auto img_proxy = phx::ResourceUtils::LoadResourceFromFile( + auto img = phx::ResourceUtils::LoadResourceFromFile<phx::Image>( filename_with_path, {}, true); - auto test_ref_image = img_proxy->GetAs<phx::Image>(); - REQUIRE(test_ref_image != nullptr); - auto test_ref_image_buffer = - phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>:: - CreateFromImage(test_ref_image); + REQUIRE(img != nullptr); + auto test_ref_image_buffer = phx::OpenGLImageBufferData< + phx::OpenGLImageBufferDataType_RGB>::CreateFromImage(img.Get()); test_utilities::OpenGLBufferComparison::REQUIRE_SIMILARITY( buffer, *test_ref_image_buffer.get(), 1.0); @@ -156,13 +154,11 @@ SCENARIO( THEN( "If they accept, the reference image now exists and is identical " "to the buffer") { - auto img_proxy = phx::ResourceUtils::LoadResourceFromFile( + auto img = phx::ResourceUtils::LoadResourceFromFile<phx::Image>( filename_with_path, {}, true); - auto test_ref_image = img_proxy->GetAs<phx::Image>(); - REQUIRE(test_ref_image != nullptr); - auto test_ref_image_buffer = - phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB>:: - CreateFromImage(test_ref_image); + REQUIRE(img != nullptr); + auto test_ref_image_buffer = phx::OpenGLImageBufferData< + phx::OpenGLImageBufferDataType_RGB>::CreateFromImage(img.Get()); test_utilities::OpenGLBufferComparison::REQUIRE_SIMILARITY( buffer, *test_ref_image_buffer.get(), 1.0);