2018-12-20 20:31:14 +01:00
|
|
|
#include "RWImGui.hpp"
|
|
|
|
|
2019-05-21 21:04:55 +02:00
|
|
|
#include <ai/CharacterController.hpp>
|
|
|
|
#include <objects/CharacterObject.hpp>
|
|
|
|
#include <objects/VehicleObject.hpp>
|
|
|
|
|
2018-12-20 20:31:14 +01:00
|
|
|
#include "RWGame.hpp"
|
|
|
|
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <imgui_impl_sdl.h>
|
|
|
|
#include <imgui_impl_opengl3.h>
|
|
|
|
|
2019-05-21 21:04:55 +02:00
|
|
|
#include <glm/glm.hpp>
|
|
|
|
|
2018-12-20 20:31:14 +01:00
|
|
|
#include <gl/gl_core_3_3.h>
|
2019-05-21 21:04:55 +02:00
|
|
|
#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
|
2018-12-20 20:31:14 +01:00
|
|
|
|
|
|
|
RWImGui::RWImGui(RWGame &game)
|
|
|
|
: _game(game) {
|
|
|
|
}
|
|
|
|
|
|
|
|
RWImGui::~RWImGui() {
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RWImGui::init() {
|
|
|
|
IMGUI_CHECKVERSION();
|
|
|
|
_context = ImGui::CreateContext();
|
|
|
|
|
|
|
|
auto [window, context] = _game.getWindow().getSDLContext();
|
|
|
|
|
|
|
|
ImGui_ImplSDL2_InitForOpenGL(window, context);
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 150");
|
|
|
|
}
|
|
|
|
|
|
|
|
void RWImGui::destroy() {
|
|
|
|
if (!_context) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ImGui::SetCurrentContext(_context);
|
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
|
|
ImGui_ImplSDL2_Shutdown();
|
|
|
|
ImGui::DestroyContext();
|
|
|
|
_context = nullptr;
|
|
|
|
}
|
|
|
|
|
2019-05-21 21:04:55 +02:00
|
|
|
bool RWImGui::processEvent(SDL_Event& event) {
|
2018-12-20 20:31:14 +01:00
|
|
|
if (!_context) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ImGui::SetCurrentContext(_context);
|
|
|
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
|
|
|
auto& io = ImGui::GetIO();
|
|
|
|
return io.WantCaptureMouse || io.WantCaptureKeyboard;
|
|
|
|
}
|
|
|
|
|
2019-05-21 21:04:55 +02:00
|
|
|
void RWImGui::startFrame() {
|
2018-12-20 20:31:14 +01:00
|
|
|
if (!_context) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ImGui::SetCurrentContext(_context);
|
|
|
|
|
2018-12-28 00:53:05 +01:00
|
|
|
auto [window, sdl_glcontext] = _game.getWindow().getSDLContext();
|
2018-12-20 20:31:14 +01:00
|
|
|
|
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
|
|
ImGui_ImplSDL2_NewFrame(window);
|
|
|
|
ImGui::NewFrame();
|
2019-05-21 21:04:55 +02:00
|
|
|
}
|
2018-12-20 20:31:14 +01:00
|
|
|
|
2019-05-21 21:04:55 +02:00
|
|
|
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;
|
|
|
|
}
|
2018-12-20 20:31:14 +01:00
|
|
|
|
2019-05-21 21:04:55 +02:00
|
|
|
static bool show_demo_window = false;
|
2018-12-20 20:31:14 +01:00
|
|
|
if (show_demo_window) {
|
|
|
|
ImGui::ShowDemoWindow(&show_demo_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Render();
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
}
|