mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-24 19:32:55 +01:00
Render debug view UI with ImGui
This commit is contained in:
parent
cf38b449c5
commit
4202297e0e
@ -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);
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
glm::mat4 ViewFrustum::projection() {
|
||||
glm::mat4 ViewFrustum::projection() const {
|
||||
return glm::perspective(fov / aspectRatio, aspectRatio, near, far);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<VehicleObject*>(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<CharacterObject*>(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;
|
||||
|
@ -22,6 +22,16 @@
|
||||
#include <chrono>
|
||||
|
||||
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;
|
||||
};
|
||||
|
@ -1,5 +1,9 @@
|
||||
#include "RWImGui.hpp"
|
||||
|
||||
#include <ai/CharacterController.hpp>
|
||||
#include <objects/CharacterObject.hpp>
|
||||
#include <objects/VehicleObject.hpp>
|
||||
|
||||
#ifdef RW_IMGUI
|
||||
|
||||
#include "RWGame.hpp"
|
||||
@ -8,7 +12,125 @@
|
||||
#include <imgui_impl_sdl.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <gl/gl_core_3_3.h>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
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<double>::max();
|
||||
time_max = std::numeric_limits<double>::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<double>(1000.0f / io.Framerate),
|
||||
static_cast<double>(io.Framerate), time_average, time_min,
|
||||
time_max);
|
||||
ImGui::Text("Timescale %.2f",
|
||||
static_cast<double>(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<uintptr_t>(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<VehicleObject*>(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<CharacterObject*>(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<double>(1000.0f / io.Framerate), static_cast<double>(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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user