diff --git a/rwengine/src/render/ViewCamera.hpp b/rwengine/src/render/ViewCamera.hpp index d2cd5927..b112f4ee 100644 --- a/rwengine/src/render/ViewCamera.hpp +++ b/rwengine/src/render/ViewCamera.hpp @@ -19,7 +19,7 @@ public: , rotation(rot) { } - glm::mat4 getView() { + glm::mat4 getView() const { auto up = rotation * glm::vec3(0.f, 0.f, 1.f); return glm::lookAt(position, position + rotation * glm::vec3(1.f, 0.f, 0.f), up); diff --git a/rwengine/src/render/ViewFrustum.cpp b/rwengine/src/render/ViewFrustum.cpp index 135fd2a1..ab6366b5 100644 --- a/rwengine/src/render/ViewFrustum.cpp +++ b/rwengine/src/render/ViewFrustum.cpp @@ -3,7 +3,7 @@ #include #include -glm::mat4 ViewFrustum::projection() { +glm::mat4 ViewFrustum::projection() const { return glm::perspective(fov / aspectRatio, aspectRatio, near, far); } diff --git a/rwengine/src/render/ViewFrustum.hpp b/rwengine/src/render/ViewFrustum.hpp index 927a0133..f00cd184 100644 --- a/rwengine/src/render/ViewFrustum.hpp +++ b/rwengine/src/render/ViewFrustum.hpp @@ -27,7 +27,7 @@ public: : near(near), far(far), fov(fov), aspectRatio(aspect) { } - glm::mat4 projection(); + glm::mat4 projection() const; void update(const glm::mat4& proj); diff --git a/rwgame/RWGame.cpp b/rwgame/RWGame.cpp index 7af0232a..e2e539d7 100644 --- a/rwgame/RWGame.cpp +++ b/rwgame/RWGame.cpp @@ -483,7 +483,7 @@ bool RWGame::updateInput() { RW_PROFILE_SCOPE(__func__); SDL_Event event; while (SDL_PollEvent(&event)) { - if (imgui.process_event(event)) { + if (imgui.processEvent(event)) { continue; } switch (event.type) { @@ -634,10 +634,12 @@ void RWGame::tickObjects(float dt) const { void RWGame::render(float alpha, float time) { RW_PROFILE_SCOPEC(__func__, MP_CORNFLOWERBLUE); + RW_UNUSED(time); lastDraws = getRenderer().getRenderer().getDrawCount(); getRenderer().getRenderer().swap(); + imgui.startFrame(); // Update the camera if (!stateManager.states.empty()) { @@ -667,7 +669,7 @@ void RWGame::render(float alpha, float time) { renderer.getRenderer().popDebugGroup(); - renderDebugView(time, viewCam); + renderDebugView(); if (!world->isPaused()) hudDrawer.drawOnScreenText(world.get(), renderer); @@ -676,99 +678,25 @@ void RWGame::render(float alpha, float time) { stateManager.draw(renderer); } - imgui.tick(); + imgui.endFrame(viewCam); } -void RWGame::renderDebugView(float time, ViewCamera &viewCam) { +void RWGame::renderDebugView() { RW_PROFILE_SCOPE(__func__); switch (debugview_) { - case DebugViewMode::General: - renderDebugStats(time); - break; case DebugViewMode::Physics: world->dynamicsWorld->debugDrawWorld(); debug.flush(renderer); break; case DebugViewMode::Navigation: - renderDebugPaths(time); - break; - case DebugViewMode::Objects: - renderDebugObjects(time, viewCam); + renderDebugPaths(); break; default: break; } } -void RWGame::renderDebugStats(float time) { - // Turn time into milliseconds - float time_ms = time * 1000.f; - constexpr size_t average_every_frame = 15; - static float times[average_every_frame]; - static size_t times_index = 0; - static float time_average = 0; - times[times_index++] = time_ms; - if (times_index >= average_every_frame) { - times_index = 0; - time_average = 0; - - for (size_t i = 0; i < average_every_frame; ++i) { - time_average += times[i]; - } - time_average /= average_every_frame; - } - - std::stringstream ss; - ss << "FPS: " << (1000.f / time_average) << " (" << time_average << "ms)\n" - << "Frame: " << time_ms << "ms\n" - << "Draws/Culls/Textures/Buffers: " << lastDraws << "/" - << renderer.getCulledCount() << "/" - << renderer.getRenderer().getTextureCount() << "/" - << renderer.getRenderer().getBufferCount() << "\n" - << "Timescale: " << world->state->basic.timeScale; - - TextRenderer::TextInfo ti; - ti.font = FONT_ARIAL; - ti.text = GameStringUtil::fromString(ss.str(), FONT_ARIAL); - ti.screenPosition = glm::vec2(10.f, 10.f); - ti.size = 15.f; - ti.baseColour = glm::u8vec3(255); - renderer.text.renderText(ti); - - /*while( engine->log.size() > 0 && engine->log.front().time + 10.f < - engine->gameTime ) { - engine->log.pop_front(); - } - - ti.screenPosition = glm::vec2( 10.f, 500.f ); - ti.size = 15.f; - for(auto it = engine->log.begin(); it != engine->log.end(); ++it) { - ti.text = it->message; - switch(it->type) { - case GameWorld::LogEntry::Error: - ti.baseColour = glm::vec3(1.f, 0.f, 0.f); - break; - case GameWorld::LogEntry::Warning: - ti.baseColour = glm::vec3(1.f, 1.f, 0.f); - break; - default: - ti.baseColour = glm::vec3(1.f, 1.f, 1.f); - break; - } - - // Interpolate the color - // c.a = (engine->gameTime - it->time > 5.f) ? 255 - (((engine->gameTime - - it->time) - 5.f)/5.f) * 255 : 255; - // text.setColor(c); - - engine->renderer.text.renderText(ti); - ti.screenPosition.y -= ti.size; - }*/ -} - -void RWGame::renderDebugPaths(float time) { - RW_UNUSED(time); - +void RWGame::renderDebugPaths() { btVector3 roadColour(1.f, 0.f, 0.f); btVector3 pedColour(0.f, 0.f, 1.f); @@ -872,74 +800,6 @@ void RWGame::renderDebugPaths(float time) { debug.flush(renderer); } -void RWGame::renderDebugObjects(float time, ViewCamera& camera) { - RW_UNUSED(time); - - std::stringstream ss; - - ss << "Models: " << data.modelinfo.size() << "\n" - << "Dynamic Objects:\n" - << " Vehicles: " << world->vehiclePool.objects.size() << "\n" - << " Peds: " << world->pedestrianPool.objects.size() << "\n"; - - TextRenderer::TextInfo ti; - ti.font = FONT_ARIAL; - ti.text = GameStringUtil::fromString(ss.str(), FONT_ARIAL); - ti.screenPosition = glm::vec2(10.f, 10.f); - ti.size = 15.f; - ti.baseColour = glm::u8vec3(255); - renderer.text.renderText(ti); - - // Render worldspace overlay for nearby objects - constexpr float kNearbyDistance = 25.f; - const auto& view = camera.position; - const auto& model = camera.getView(); - const auto& proj = camera.frustum.projection(); - const auto& size = getWindow().getSize(); - glm::vec4 viewport(0.f, 0.f, size.x, size.y); - auto isnearby = [&](GameObject* o) { - return glm::distance2(o->getPosition(), view) < - kNearbyDistance * kNearbyDistance; - }; - auto showdata = [&](GameObject* o, std::stringstream& ss) { - auto screen = glm::project(o->getPosition(), model, proj, viewport); - if (screen.z >= 1.f) { - return; - } - ti.text = GameStringUtil::fromString(ss.str(), FONT_ARIAL); - screen.y = viewport.w - screen.y; - ti.screenPosition = glm::vec2(screen); - ti.size = 10.f; - renderer.text.renderText(ti); - }; - - for (auto& p : world->vehiclePool.objects) { - if (!isnearby(p.second.get())) continue; - auto v = static_cast(p.second.get()); - - std::stringstream ss; - ss << v->getVehicle()->vehiclename_ << "\n" - << (v->isFlipped() ? "Flipped" : "Upright") << "\n" - << (v->isStopped() ? "Stopped" : "Moving") << "\n" - << v->getVelocity() << "m/s\n"; - - showdata(v, ss); - } - for (auto& p : world->pedestrianPool.objects) { - if (!isnearby(p.second.get())) continue; - auto c = static_cast(p.second.get()); - const auto& state = c->getCurrentState(); - auto act = c->controller->getCurrentActivity(); - - std::stringstream ss; - ss << "Health: " << state.health << " (" << state.armour << ")\n" - << (c->isAlive() ? "Alive" : "Dead") << "\n" - << "Activity: " << (act ? act->name() : "Idle") << "\n"; - - showdata(c, ss); - } -} - void RWGame::globalKeyEvent(const SDL_Event& event) { const auto toggle_debug = [&](DebugViewMode m) { debugview_ = debugview_ == m ? DebugViewMode::Disabled : m; diff --git a/rwgame/RWGame.hpp b/rwgame/RWGame.hpp index d8910f50..ffd11aba 100644 --- a/rwgame/RWGame.hpp +++ b/rwgame/RWGame.hpp @@ -22,6 +22,16 @@ #include class RWGame final : public GameBase { +public: + enum class DebugViewMode { + Disabled, + General, + Physics, + Navigation, + Objects + }; + +private: GameData data; GameRenderer renderer; RWImGui imgui; @@ -40,14 +50,6 @@ class RWGame final : public GameBase { bool inFocus = true; ViewCamera currentCam; - enum class DebugViewMode { - Disabled, - General, - Physics, - Navigation, - Objects - }; - DebugViewMode debugview_ = DebugViewMode::Disabled; int lastDraws{0}; /// Number of draws issued for the last frame. @@ -92,6 +94,10 @@ public: return hudDrawer; } + DebugViewMode getDebugViewMode() const { + return debugview_; + } + bool hitWorldRay(glm::vec3& hit, glm::vec3& normal, GameObject** object = nullptr); @@ -112,9 +118,7 @@ private: void tick(float dt); void render(float alpha, float dt); - void renderDebugStats(float time); - void renderDebugPaths(float time); - void renderDebugObjects(float time, ViewCamera& camera); + void renderDebugPaths(); void handleCheatInput(char symbol); @@ -124,7 +128,7 @@ private: float tickWorld(const float deltaTime, float accumulatedTime); - void renderDebugView(float time, ViewCamera &viewCam); + void renderDebugView(); void tickObjects(float dt) const; }; diff --git a/rwgame/RWImGui.cpp b/rwgame/RWImGui.cpp index d3affa05..d58ab49a 100644 --- a/rwgame/RWImGui.cpp +++ b/rwgame/RWImGui.cpp @@ -1,5 +1,9 @@ #include "RWImGui.hpp" +#include +#include +#include + #ifdef RW_IMGUI #include "RWGame.hpp" @@ -8,7 +12,125 @@ #include #include +#include + #include +#include + +namespace { +void WindowDebugStats(RWGame& game) { + auto& io = ImGui::GetIO(); + + auto time_ms = 1000.0f / io.Framerate; + constexpr size_t average_every_frame = 240; + static float times[average_every_frame]; + static size_t times_index = 0; + static double time_average = 0, time_min = 0, time_max = 0; + times[times_index++] = time_ms; + if (times_index >= average_every_frame) { + times_index = 0; + time_average = 0; + time_min = std::numeric_limits::max(); + time_max = std::numeric_limits::lowest(); + + for (double time : times) { + time_average += time; + time_min = std::min(time, time_min); + time_max = std::max(time, time_max); + } + time_average /= average_every_frame; + } + + const auto& world = game.getWorld(); + auto& renderer = game.getRenderer(); + + ImGui::SetNextWindowPos({20.f, 20.f}); + ImGui::Begin("Engine Information", nullptr, + ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoInputs); + ImGui::Text("%.3f ms/frame (%.1f FPS)\n%.3f / %.3f / %.3f ms", + static_cast(1000.0f / io.Framerate), + static_cast(io.Framerate), time_average, time_min, + time_max); + ImGui::Text("Timescale %.2f", + static_cast(world->state->basic.timeScale)); + ImGui::Text("%i Drawn %lu Culled", renderer.getRenderer().getDrawCount(), + renderer.getCulledCount()); + ImGui::Text("%i Textures %i Buffers", + renderer.getRenderer().getTextureCount(), + renderer.getRenderer().getBufferCount()); + ImGui::End(); +} + +void WindowDebugObjects(RWGame& game, const ViewCamera& camera) { + auto& data = game.getGameData(); + auto world = game.getWorld(); + + ImGui::SetNextWindowPos({20.f, 20.f}); + ImGui::Begin("Object Information", nullptr, + ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoInputs); + ImGui::Text("%lu Models", data.modelinfo.size()); + ImGui::Text("Dynamic Objects\n %lu Vehicles\n %lu Peds", + world->vehiclePool.objects.size(), + world->pedestrianPool.objects.size()); + ImGui::End(); + + // Render worldspace overlay for nearby objects + constexpr float kNearbyDistance = 25.f; + const auto& view = camera.position; + const auto& model = camera.getView(); + const auto& proj = camera.frustum.projection(); + const auto& size = game.getWindow().getSize(); + glm::vec4 viewport(0.f, 0.f, size.x, size.y); + auto isnearby = [&](GameObject* o) { + return glm::distance2(o->getPosition(), view) < + kNearbyDistance * kNearbyDistance; + }; + auto showdata = [&](GameObject* o, std::stringstream& ss) { + auto screen = glm::project(o->getPosition(), model, proj, viewport); + if (screen.z >= 1.f) { + return; + } + ImGui::SetNextWindowPos({screen.x, viewport.w - screen.y}, 0, + {0.5f, 0.5f}); + ImGui::Begin( + std::to_string(reinterpret_cast(o)).c_str(), nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoInputs); + ImGui::Text("%s", ss.str().c_str()); + ImGui::End(); + }; + + for (auto& p : world->vehiclePool.objects) { + if (!isnearby(p.second.get())) continue; + auto v = static_cast(p.second.get()); + + std::stringstream ss; + ss << v->getVehicle()->vehiclename_ << "\n" + << (v->isFlipped() ? "Flipped" : "Upright") << "\n" + << (v->isStopped() ? "Stopped" : "Moving") << "\n" + << v->getVelocity() << "m/s\n"; + + showdata(v, ss); + } + for (auto& p : world->pedestrianPool.objects) { + if (!isnearby(p.second.get())) continue; + auto c = static_cast(p.second.get()); + const auto& state = c->getCurrentState(); + auto act = c->controller->getCurrentActivity(); + + std::stringstream ss; + ss << "Health: " << state.health << " (" << state.armour << ")\n" + << (c->isAlive() ? "Alive" : "Dead") << "\n" + << "Activity: " << (act ? act->name() : "Idle") << "\n"; + + showdata(c, ss); + } +} +} // namespace RWImGui::RWImGui(RWGame &game) : _game(game) { @@ -39,7 +161,7 @@ void RWImGui::destroy() { _context = nullptr; } -bool RWImGui::process_event(SDL_Event &event) { +bool RWImGui::processEvent(SDL_Event& event) { if (!_context) { return false; } @@ -49,27 +171,32 @@ bool RWImGui::process_event(SDL_Event &event) { return io.WantCaptureMouse || io.WantCaptureKeyboard; } -void RWImGui::tick() { +void RWImGui::startFrame() { if (!_context) { return; } ImGui::SetCurrentContext(_context); - auto& io = ImGui::GetIO(); auto [window, sdl_glcontext] = _game.getWindow().getSDLContext(); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(window); ImGui::NewFrame(); +} - static float f = 0.0f; +void RWImGui::endFrame(const ViewCamera& camera) { + switch (_game.getDebugViewMode()) { + case RWGame::DebugViewMode::General: + WindowDebugStats(_game); + break; + case RWGame::DebugViewMode::Objects: + WindowDebugObjects(_game, camera); + break; + default: + break; + } - ImGui::Begin("Hello, world!"); - ImGui::Text("Hello, world!"); - ImGui::SliderFloat("float", &f, 0.0f, 1.0f); - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", static_cast(1000.0f / io.Framerate), static_cast(io.Framerate)); ImGui::End(); - - static bool show_demo_window = true; + static bool show_demo_window = false; if (show_demo_window) { ImGui::ShowDemoWindow(&show_demo_window); } diff --git a/rwgame/RWImGui.hpp b/rwgame/RWImGui.hpp index cb87d08b..765d0a3a 100644 --- a/rwgame/RWImGui.hpp +++ b/rwgame/RWImGui.hpp @@ -5,6 +5,7 @@ class RWGame; struct ImGuiContext; +class ViewCamera; class RWImGui { RWGame &_game; @@ -14,8 +15,9 @@ public: ~RWImGui(); void init(); void destroy(); - bool process_event(SDL_Event &event); - void tick(); + bool processEvent(SDL_Event &event); + void startFrame(); + void endFrame(const ViewCamera &); }; #endif // RWGAME_RWIMGUI_HPP