#include "stdafx.h" #include "uilayer.h" #include "uitranscripts.h" #include "Globals.h" #include "translation.h" #include "simulation.h" #include "mtable.h" #include "Train.h" #include "DynObj.h" #include "Model3d.h" #include "renderer.h" #include "Timer.h" #include "utilities.h" #include "Logs.h" #include "simulationtime.h" #include "sceneeditor.h" #include "application.h" #include "imgui/imgui_impl_glfw.h" #include "imgui/imgui_impl_opengl3.h" ui_layer UILayer; ui_layer::~ui_layer() {} bool ui_layer::init(GLFWwindow *Window) { m_window = Window; log_active = Global.loading_log; IMGUI_CHECKVERSION(); ImGui::CreateContext(); imgui_io = &ImGui::GetIO(); // imgui_io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // imgui_io->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; imgui_io->Fonts->AddFontFromFileTTF("DejaVuSansMono.ttf", 13.0f); if (Global.map_enabled) m_map = std::make_unique(); ImGui_ImplGlfw_InitForOpenGL(m_window); ImGui_ImplOpenGL3_Init(); ImGui::StyleColorsClassic(); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); return true; } void ui_layer::cleanup() { ImGui::EndFrame(); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } bool ui_layer::key_callback(int key, int scancode, int action, int mods) { ImGui_ImplGlfw_KeyCallback(m_window, key, scancode, action, mods); return imgui_io->WantCaptureKeyboard; } bool ui_layer::char_callback(unsigned int c) { ImGui_ImplGlfw_CharCallback(m_window, c); return imgui_io->WantCaptureKeyboard; } bool ui_layer::scroll_callback(double xoffset, double yoffset) { ImGui_ImplGlfw_ScrollCallback(m_window, xoffset, yoffset); return imgui_io->WantCaptureMouse; } bool ui_layer::mouse_button_callback(int button, int action, int mods) { ImGui_ImplGlfw_MouseButtonCallback(m_window, button, action, mods); return imgui_io->WantCaptureMouse; } void ui_layer::set_cursor(const int Mode) { glfwSetInputMode(m_window, GLFW_CURSOR, Mode); m_cursorvisible = (Mode != GLFW_CURSOR_DISABLED); } // potentially processes provided input key. returns: true if key was processed, false otherwise bool ui_layer::on_key(int const Key, int const Action) { // TODO: pass the input first through an active ui element if there's any // if the ui element shows no interest or we don't have one, try to interpret the input yourself: // shared conditions switch (Key) { case GLFW_KEY_F1: case GLFW_KEY_F2: case GLFW_KEY_F3: case GLFW_KEY_F8: case GLFW_KEY_F9: case GLFW_KEY_F10: case GLFW_KEY_F11: case GLFW_KEY_F12: case GLFW_KEY_Y: case GLFW_KEY_N: case GLFW_KEY_TAB: { // ui mode selectors if ((true == Global.ctrlState) || (true == Global.shiftState)) { // only react to keys without modifiers return false; } if (Action == GLFW_RELEASE) { return true; } // recognized, but ignored break; } default: { // everything else return false; } } switch (Key) { case GLFW_KEY_F1: { // basic consist info basic_info_active = !basic_info_active; return true; } case GLFW_KEY_F2: { // parametry pojazdu timetable_active = !timetable_active; return true; } case GLFW_KEY_F3: { // timetable vehicle_info_active = !vehicle_info_active; return true; } case GLFW_KEY_F8: { // renderer debug data renderer_debug_active = !renderer_debug_active; return true; } case GLFW_KEY_F9: { // wersja about_active = !about_active; return true; } case GLFW_KEY_F10: { // quit quit_active = !quit_active; return true; } case GLFW_KEY_F11: { // scenario inspector EditorModeFlag = !EditorModeFlag; if ((true == EditorModeFlag) && (false == Global.ControlPicking)) { set_cursor(GLFW_CURSOR_NORMAL); Global.ControlPicking = true; } return true; } case GLFW_KEY_F12: { log_active = !log_active; return true; } case GLFW_KEY_Y: { if (quit_active) glfwSetWindowShouldClose(m_window, GLFW_TRUE); return true; } case GLFW_KEY_N: { if (quit_active) quit_active = false; return true; } case GLFW_KEY_TAB: { if (m_map) m_map->toggle_window(); return true; } default: { break; } } return false; } void ui_layer::render() { // render code here Timer::subsystem.gfx_gui.start(); render_background(); render_progress(); // ImGui::ShowDemoWindow(); std::string uitextline1, uitextline2, uitextline3, uitextline4; UILayer.set_tooltip(""); auto const *train{(Global.pWorld ? Global.pWorld->train() : nullptr)}; auto const *controlled{(Global.pWorld ? Global.pWorld->controlled() : nullptr)}; auto const *camera{Global.pCamera}; if ((train != nullptr) && (false == FreeFlyModeFlag)) { if (false == DebugModeFlag) { // in regular mode show control functions, for defined controls UILayer.set_tooltip(locale::label_cab_control(train->GetLabel(GfxRenderer.Pick_Control()))); } else { // in debug mode show names of submodels, to help with cab setup and/or debugging auto const cabcontrol = GfxRenderer.Pick_Control(); UILayer.set_tooltip((cabcontrol ? cabcontrol->pName : "")); } } if ((true == Global.ControlPicking) && (true == FreeFlyModeFlag) && (true == DebugModeFlag)) { auto const scenerynode = GfxRenderer.Pick_Node(); UILayer.set_tooltip((scenerynode ? scenerynode->name() : "")); } render_tooltip(); if (basic_info_active) { ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Vehicle info", &basic_info_active, ImGuiWindowFlags_NoResize); uitextline1 = "Time: " + std::string( simulation::Time ); if( Global.iPause ) { uitextline1 += " (paused)"; } ImGui::TextUnformatted(uitextline1.c_str()); if ((controlled != nullptr) && (controlled->Mechanik != nullptr)) { auto const &mover = controlled->MoverParameters; auto const &driver = controlled->Mechanik; uitextline2 = "Throttle: " + to_string(driver->Controlling()->MainCtrlPos, 0, 2) + "+" + std::to_string(driver->Controlling()->ScndCtrlPos); if (mover->ActiveDir > 0) { uitextline2 += " D"; } else if (mover->ActiveDir < 0) { uitextline2 += " R"; } else { uitextline2 += " N"; } uitextline3 = "Brakes:" + to_string(mover->fBrakeCtrlPos, 1, 5) + "+" + std::to_string(mover->LocalBrakePosA) + (mover->SlippingWheels ? " !" : " "); uitextline4 = (true == TestFlag(mover->SecuritySystem.Status, s_aware) ? "!ALERTER! " : " "); uitextline4 += (true == TestFlag(mover->SecuritySystem.Status, s_active) ? "!SHP! " : " "); static bool detail = false; ImGui::Checkbox("More", &detail); if (detail) { auto const speedlimit{static_cast(std::floor(driver->VelDesired))}; uitextline2 += " Speed: " + std::to_string(static_cast(std::floor(mover->Vel))) + " km/h" + " (limit: " + std::to_string(speedlimit) + " km/h"; auto const nextspeedlimit{static_cast(std::floor(controlled->Mechanik->VelNext))}; if (nextspeedlimit != speedlimit) { uitextline2 += ", new limit: " + std::to_string(nextspeedlimit) + " km/h" + " in " + to_string(controlled->Mechanik->ActualProximityDist * 0.001, 1) + " km"; } uitextline2 += ")"; uitextline3 += " Pressure: " + to_string(mover->BrakePress * 100.0, 2) + " kPa" + " (train pipe: " + to_string(mover->PipePress * 100.0, 2) + " kPa)"; auto const stoptime{static_cast(-1.0 * controlled->Mechanik->fStopTime)}; if (stoptime > 0) { uitextline4 += " Loading/unloading in progress (" + to_string(stoptime) + (stoptime > 1 ? " seconds" : " second") + " left)"; } else { auto const trackblockdistance{std::abs(controlled->Mechanik->TrackBlock())}; if (trackblockdistance <= 75.0) { uitextline4 += " Another vehicle ahead (distance: " + to_string(trackblockdistance, 1) + " m)"; } } } ImGui::TextUnformatted(uitextline2.c_str()); ImGui::TextUnformatted(uitextline3.c_str()); ImGui::TextUnformatted(uitextline4.c_str()); } ImGui::End(); } if (timetable_active) { // timetable auto *vehicle{(FreeFlyModeFlag ? std::get(simulation::Region->find_vehicle(camera->Pos, 20, false, false)) : controlled)}; // w trybie latania lokalizujemy wg mapy if (vehicle == nullptr) goto f2_cancel; // if the nearest located vehicle doesn't have a direct driver, try to query its owner auto const owner = (((vehicle->Mechanik != nullptr) && (vehicle->Mechanik->Primary())) ? vehicle->Mechanik : vehicle->ctOwner); if (owner == nullptr) goto f2_cancel; auto const *table = owner->TrainTimetable(); if (table == nullptr) goto f2_cancel; auto const &time = simulation::Time.data(); uitextline1 = "Time: " + to_string(time.wHour) + ":" + (time.wMinute < 10 ? "0" : "") + to_string(time.wMinute) + ":" + (time.wSecond < 10 ? "0" : "") + to_string(time.wSecond); if (Global.iPause) { uitextline1 += " (paused)"; } uitextline2 = std::string(owner->Relation()) + " (" + std::string(owner->TrainName()) + ")"; auto const nextstation = std::string(owner->NextStop()); uitextline3 = ""; if (!nextstation.empty()) { // jeśli jest podana relacja, to dodajemy punkt następnego zatrzymania uitextline3 = " -> " + nextstation; } ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Timetable", &timetable_active, ImGuiWindowFlags_NoResize); ImGui::TextUnformatted(uitextline1.c_str()); ImGui::TextUnformatted(uitextline2.c_str()); ImGui::TextUnformatted(uitextline3.c_str()); if (table->StationCount > 0) { static bool show = false; ImGui::Checkbox("show", &show); if (show) { // header ImGui::TextUnformatted("+-----+------------------------------------+-------+-----+"); TMTableLine const *tableline; for (int i = owner->iStationStart; i <= table->StationCount; ++i) { // wyświetlenie pozycji z rozkładu tableline = table->TimeTable + i; // linijka rozkładu std::string vmax = " " + to_string(tableline->vmax, 0); vmax = vmax.substr(vmax.size() - 3, 3); // z wyrównaniem do prawej std::string const station = (std::string(tableline->StationName) + " ").substr(0, 34); std::string const location = ((tableline->km > 0.0 ? to_string(tableline->km, 2) : "") + " ").substr(0, 34 - tableline->StationWare.size()); std::string const arrival = (tableline->Ah >= 0 ? to_string(int(100 + tableline->Ah)).substr(1, 2) + ":" + to_string(int(100 + tableline->Am)).substr(1, 2) : " | "); std::string const departure = (tableline->Dh >= 0 ? to_string(int(100 + tableline->Dh)).substr(1, 2) + ":" + to_string(int(100 + tableline->Dm)).substr(1, 2) : " | "); auto const candeparture = ((owner->iStationStart < table->StationIndex) && (i < table->StationIndex) && ((time.wHour * 60 + time.wMinute) >= (tableline->Dh * 60 + tableline->Dm))); auto traveltime = " " + (i < 2 ? "" : tableline->Ah >= 0 ? to_string(CompareTime(table->TimeTable[i - 1].Dh, table->TimeTable[i - 1].Dm, tableline->Ah, tableline->Am), 0) : to_string(std::max(0.0, CompareTime(table->TimeTable[i - 1].Dh, table->TimeTable[i - 1].Dm, tableline->Dh, tableline->Dm) - 0.5), 0)); traveltime = traveltime.substr(traveltime.size() - 3, 3); // z wyrównaniem do prawej uitextline1 = "| " + vmax + " | " + station + " | " + arrival + " | " + traveltime + " |"; uitextline2 = "| | " + location + tableline->StationWare + " | " + departure + " | |"; if (candeparture) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 1.0f, 0.0f, 1.0f)); ImGui::TextUnformatted(uitextline1.c_str()); ImGui::TextUnformatted(uitextline2.c_str()); if (candeparture) ImGui::PopStyleColor(); // divider/footer ImGui::TextUnformatted("+-----+------------------------------------+-------+-----+"); } } } ImGui::End(); } f2_cancel:; if (vehicle_info_active) { ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Vehicle status", &vehicle_info_active, ImGuiWindowFlags_NoResize); uitextline1 = ""; uitextline2 = ""; uitextline3 = ""; uitextline4 = ""; auto *vehicle{(FreeFlyModeFlag ? std::get(simulation::Region->find_vehicle(camera->Pos, 20, false, false)) : controlled)}; // w trybie latania lokalizujemy wg mapy if (vehicle != nullptr) { // jeśli domyślny ekran po pierwszym naciśnięciu uitextline1 = "Vehicle name: " + vehicle->MoverParameters->Name; if ((vehicle->Mechanik == nullptr) && (vehicle->ctOwner)) { // for cars other than leading unit indicate the leader uitextline1 += ", owned by " + vehicle->ctOwner->OwnerName(); } uitextline1 += "; Status: " + vehicle->MoverParameters->EngineDescription(0); // informacja o sprzęgach uitextline1 += "; C0:" + (vehicle->PrevConnected ? vehicle->PrevConnected->name() + ":" + to_string(vehicle->MoverParameters->Couplers[0].CouplingFlag) + (vehicle->MoverParameters->Couplers[0].CouplingFlag == 0 ? " (" + to_string(vehicle->MoverParameters->Couplers[0].CoupleDist, 1) + " m)" : "") : "none"); uitextline1 += " C1:" + (vehicle->NextConnected ? vehicle->NextConnected->name() + ":" + to_string(vehicle->MoverParameters->Couplers[1].CouplingFlag) + (vehicle->MoverParameters->Couplers[1].CouplingFlag == 0 ? " (" + to_string(vehicle->MoverParameters->Couplers[1].CoupleDist, 1) + " m)" : "") : "none"); // equipment flags uitextline2 = (vehicle->MoverParameters->Battery ? "B" : "."); uitextline2 += (vehicle->MoverParameters->Mains ? "M" : "."); uitextline2 += (vehicle->MoverParameters->PantRearUp ? (vehicle->MoverParameters->PantRearVolt > 0.0 ? "O" : "o") : "."); uitextline2 += (vehicle->MoverParameters->PantFrontUp ? (vehicle->MoverParameters->PantFrontVolt > 0.0 ? "P" : "p") : "."); uitextline2 += (vehicle->MoverParameters->PantPressLockActive ? "!" : (vehicle->MoverParameters->PantPressSwitchActive ? "*" : ".")); uitextline2 += (vehicle->MoverParameters->WaterPump.is_active ? "W" : (false == vehicle->MoverParameters->WaterPump.breaker ? "-" : (vehicle->MoverParameters->WaterPump.is_enabled ? "w" : "."))); uitextline2 += (true == vehicle->MoverParameters->WaterHeater.is_damaged ? "!" : (vehicle->MoverParameters->WaterHeater.is_active ? "H" : (false == vehicle->MoverParameters->WaterHeater.breaker ? "-" : (vehicle->MoverParameters->WaterHeater.is_enabled ? "h" : ".")))); uitextline2 += (vehicle->MoverParameters->FuelPump.is_active ? "F" : (vehicle->MoverParameters->FuelPump.is_enabled ? "f" : ".")); uitextline2 += (vehicle->MoverParameters->OilPump.is_active ? "O" : (vehicle->MoverParameters->OilPump.is_enabled ? "o" : ".")); uitextline2 += (false == vehicle->MoverParameters->ConverterAllowLocal ? "-" : (vehicle->MoverParameters->ConverterAllow ? (vehicle->MoverParameters->ConverterFlag ? "X" : "x") : ".")); uitextline2 += (vehicle->MoverParameters->ConvOvldFlag ? "!" : "."); uitextline2 += (vehicle->MoverParameters->CompressorFlag ? "C" : (false == vehicle->MoverParameters->CompressorAllowLocal ? "-" : ((vehicle->MoverParameters->CompressorAllow || vehicle->MoverParameters->CompressorStart == start_t::automatic) ? "c" : "."))); uitextline2 += (vehicle->MoverParameters->CompressorGovernorLock ? "!" : "."); auto const train{Global.pWorld->train()}; if ((train != nullptr) && (train->Dynamic() == vehicle)) { uitextline2 += (vehicle->MoverParameters->Radio ? " R: " : " r: ") + std::to_string(train->RadioChannel()); } /* uitextline2 += " AnlgB: " + to_string( tmp->MoverParameters->AnPos, 1 ) + "+" + to_string( tmp->MoverParameters->LocalBrakePosA, 1 ) */ uitextline2 += " Bdelay: "; if ((vehicle->MoverParameters->BrakeDelayFlag & bdelay_G) == bdelay_G) uitextline2 += "G"; if ((vehicle->MoverParameters->BrakeDelayFlag & bdelay_P) == bdelay_P) uitextline2 += "P"; if ((vehicle->MoverParameters->BrakeDelayFlag & bdelay_R) == bdelay_R) uitextline2 += "R"; if ((vehicle->MoverParameters->BrakeDelayFlag & bdelay_M) == bdelay_M) uitextline2 += "+Mg"; uitextline2 += ", Load: " + to_string(vehicle->MoverParameters->Load, 0) + " (" + to_string(vehicle->MoverParameters->LoadFlag, 0) + ")"; uitextline2 += "; Pant: " + to_string(vehicle->MoverParameters->PantPress, 2) + (vehicle->MoverParameters->bPantKurek3 ? "-ZG" : "|ZG"); uitextline2 += "; Ft: " + to_string(vehicle->MoverParameters->Ft * 0.001f * (vehicle->MoverParameters->ActiveCab ? vehicle->MoverParameters->ActiveCab : vehicle->ctOwner ? vehicle->ctOwner->Controlling()->ActiveCab : 1), 1) + ", Fb: " + to_string(vehicle->MoverParameters->Fb * 0.001f, 1) + ", Fr: " + to_string(vehicle->MoverParameters->Adhesive(vehicle->MoverParameters->RunningTrack.friction), 2) + (vehicle->MoverParameters->SlippingWheels ? " (!)" : ""); if (vehicle->Mechanik) { uitextline2 += "; Ag: " + to_string(vehicle->Mechanik->fAccGravity, 2); } uitextline2 += "; TC:" + to_string(vehicle->MoverParameters->TotalCurrent, 0); auto const frontcouplerhighvoltage = to_string(vehicle->MoverParameters->Couplers[side::front].power_high.voltage, 0) + "@" + to_string(vehicle->MoverParameters->Couplers[side::front].power_high.current, 0); auto const rearcouplerhighvoltage = to_string(vehicle->MoverParameters->Couplers[side::rear].power_high.voltage, 0) + "@" + to_string(vehicle->MoverParameters->Couplers[side::rear].power_high.current, 0); uitextline2 += ", HV: "; if (vehicle->MoverParameters->Couplers[side::front].power_high.local == false) { uitextline2 += "(" + frontcouplerhighvoltage + ")-" + ":F" + (vehicle->DirectionGet() ? "<<" : ">>") + "R:" + "-(" + rearcouplerhighvoltage + ")"; } else { uitextline2 += frontcouplerhighvoltage + ":F" + (vehicle->DirectionGet() ? "<<" : ">>") + "R:" + rearcouplerhighvoltage; } uitextline3 += "TrB: " + to_string(vehicle->MoverParameters->BrakePress, 2) + ", " + to_hex_str(vehicle->MoverParameters->Hamulec->GetBrakeStatus(), 2); uitextline3 += "; LcB: " + to_string(vehicle->MoverParameters->LocBrakePress, 2) + "; hat: " + to_string(vehicle->MoverParameters->BrakeCtrlPos2, 2) + "; pipes: " + to_string(vehicle->MoverParameters->PipePress, 2) + "/" + to_string(vehicle->MoverParameters->ScndPipePress, 2) + "/" + to_string(vehicle->MoverParameters->EqvtPipePress, 2) + ", MT: " + to_string(vehicle->MoverParameters->CompressedVolume, 3) + ", BT: " + to_string(vehicle->MoverParameters->Volume, 3) + ", CtlP: " + to_string(vehicle->MoverParameters->CntrlPipePress, 3) + ", CtlT: " + to_string(vehicle->MoverParameters->Hamulec->GetCRP(), 3); if (vehicle->MoverParameters->ManualBrakePos > 0) { uitextline3 += "; manual brake on"; } /* if( tmp->MoverParameters->LocalBrakePos > 0 ) { uitextline3 += ", local brake on"; } else { uitextline3 += ", local brake off"; } */ if (vehicle->Mechanik) { // o ile jest ktoś w środku std::string flags = "cpapcplhhndoiefgvdpseil "; // flagi AI (definicja w Driver.h) for (int i = 0, j = 1; i < 23; ++i, j <<= 1) if (false == (vehicle->Mechanik->DrivigFlags() & j)) // jak bit ustawiony flags[i] = '.'; // std::toupper( flags[ i ] ); // ^= 0x20; // to zmiana na wielką literę uitextline4 = flags; uitextline4 += "Driver: Vd=" + to_string(vehicle->Mechanik->VelDesired, 0) + " Ad=" + to_string(vehicle->Mechanik->AccDesired, 2) + " Ah=" + to_string(vehicle->Mechanik->fAccThreshold, 2) + "@" + to_string(vehicle->Mechanik->fBrake_a0[0], 2) + "+" + to_string(vehicle->Mechanik->fBrake_a1[0], 2) + " Bd=" + to_string(vehicle->Mechanik->fBrakeDist, 0) + " Pd=" + to_string(vehicle->Mechanik->ActualProximityDist, 0) + " Vn=" + to_string(vehicle->Mechanik->VelNext, 0) + " VSl=" + to_string(vehicle->Mechanik->VelSignalLast, 0) + " VLl=" + to_string(vehicle->Mechanik->VelLimitLast, 0) + " VRd=" + to_string(vehicle->Mechanik->VelRoad, 0) + " VRst=" + to_string(vehicle->Mechanik->VelRestricted, 0); if ((vehicle->Mechanik->VelNext == 0.0) && (vehicle->Mechanik->eSignNext)) { // jeśli ma zapamiętany event semafora, nazwa eventu semafora uitextline4 += " (" + (vehicle->Mechanik->eSignNext->asName) + ")"; } // biezaca komenda dla AI uitextline4 += ", command: " + vehicle->Mechanik->OrderCurrent(); } static bool scantable = false; if (scantable) { // f2 screen, track scan mode if (vehicle->Mechanik) { ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Scan table", &scantable, ImGuiWindowFlags_NoResize); std::size_t i = 0; std::size_t const speedtablesize = clamp(static_cast(vehicle->Mechanik->TableSize()) - 1, 0, 30); do { std::string scanline = vehicle->Mechanik->TableText(i); if (scanline.empty()) { break; } ImGui::TextUnformatted(scanline.c_str()); ++i; } while (i < speedtablesize); // TController:iSpeedTableSize TODO: change when the table gets recoded ImGui::End(); } } ImGui::TextUnformatted(uitextline1.c_str()); ImGui::TextUnformatted(uitextline2.c_str()); ImGui::TextUnformatted(uitextline3.c_str()); ImGui::TextUnformatted(uitextline4.c_str()); ImGui::Checkbox("Show scan table", &scantable); } else { // wyświetlenie współrzędnych w scenerii oraz kąta kamery, gdy nie mamy wskaźnika uitextline1 = "Camera position: " + to_string(camera->Pos.x, 2) + " " + to_string(camera->Pos.y, 2) + " " + to_string(camera->Pos.z, 2) + ", azimuth: " + to_string(180.0 - glm::degrees(camera->Yaw), 0) // ma być azymut, czyli 0 na północy i rośnie na wschód + " " + std::string("S SEE NEN NWW SW").substr(0 + 2 * floor(fmod(8 + (camera->Yaw + 0.5 * M_PI_4) / M_PI_4, 8)), 2); // current luminance level uitextline2 = "Light level: " + to_string(Global.fLuminance, 3); if (Global.FakeLight) { uitextline2 += "(*)"; } ImGui::TextUnformatted(uitextline1.c_str()); ImGui::TextUnformatted(uitextline2.c_str()); } ImGui::End(); } if (renderer_debug_active) { ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Renderer stats", &renderer_debug_active, ImGuiWindowFlags_NoResize); // gfx renderer data uitextline1 = "FoV: " + to_string(Global.FieldOfView / Global.ZoomFactor, 1) + ", Draw range x " + to_string(Global.fDistanceFactor, 1) // + "; sectors: " + std::to_string( GfxRenderer.m_drawcount ) // + ", FPS: " + to_string( Timer::GetFPS(), 2 ); + ", FPS: " + std::to_string(static_cast(std::round(GfxRenderer.Framerate()))); if (Global.iSlowMotion) { uitextline1 += " (slowmotion " + to_string(Global.iSlowMotion) + ")"; } uitextline2 = std::string("Rendering mode: VBO"); if (false == Global.LastGLError.empty()) { uitextline2 += "Last openGL error: " + Global.LastGLError; } // renderer stats uitextline3 = GfxRenderer.info_times(); uitextline4 = GfxRenderer.info_stats(); ImGui::TextUnformatted(uitextline1.c_str()); ImGui::TextUnformatted(uitextline2.c_str()); ImGui::TextUnformatted(uitextline3.c_str()); ImGui::TextUnformatted(uitextline4.c_str()); ImGui::End(); } if (about_active) { ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("About", &about_active, ImGuiWindowFlags_NoResize); // informacja o wersji uitextline1 = "MaSzyna " + Global.asVersion; // informacja o wersji if (Global.iMultiplayer) { uitextline1 += " (multiplayer mode is active)"; } uitextline3 = "vehicles: " + to_string(Timer::subsystem.sim_dynamics.average(), 2) + " msec" + " update total: " + to_string(Timer::subsystem.sim_total.average(), 2) + " msec"; ImGui::TextUnformatted(uitextline1.c_str()); ImGui::TextUnformatted(uitextline3.c_str()); ImGui::End(); } if (quit_active) { ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Quit", &quit_active, ImGuiWindowFlags_NoResize); ImGui::TextUnformatted("Quit simulation?"); if (ImGui::Button("Yes")) glfwSetWindowShouldClose(m_window, GLFW_TRUE); ImGui::SameLine(); if (ImGui::Button("No")) quit_active = false; ImGui::End(); } if (EditorModeFlag) { uitextline1 = ""; uitextline2 = ""; uitextline3 = ""; uitextline4 = ""; auto const *node{scene::Editor.node()}; if (node == nullptr) { auto const mouseposition{Global.pCamera->Pos + GfxRenderer.Mouse_Position()}; uitextline1 = "mouse location: [" + to_string(mouseposition.x, 2) + ", " + to_string(mouseposition.y, 2) + ", " + to_string(mouseposition.z, 2) + "]"; } else { uitextline1 = "node name: " + node->name() + "; location: [" + to_string(node->location().x, 2) + ", " + to_string(node->location().y, 2) + ", " + to_string(node->location().z, 2) + "]" + " (distance: " + to_string(glm::length(glm::dvec3{node->location().x, 0.0, node->location().z} - glm::dvec3{Global.pCameraPosition.x, 0.0, Global.pCameraPosition.z}), 1) + " m)"; // subclass-specific data // TBD, TODO: specialized data dump method in each node subclass, or data imports in the panel for provided subclass pointer? if (typeid(*node) == typeid(TAnimModel)) { auto const *subnode = static_cast(node); uitextline2 = "angle: " + to_string(clamp_circular(subnode->vAngle.y, 360.f), 2) + " deg"; uitextline2 += "; lights: "; if (subnode->iNumLights > 0) { uitextline2 += '['; for (int lightidx = 0; lightidx < subnode->iNumLights; ++lightidx) { uitextline2 += to_string(subnode->lsLights[lightidx]); if (lightidx < subnode->iNumLights - 1) { uitextline2 += ", "; } } uitextline2 += ']'; } else { uitextline2 += "none"; } // 3d shape auto modelfile{(subnode->pModel ? subnode->pModel->NameGet() : "none")}; if (modelfile.find(szModelPath) == 0) { // don't include 'models/' in the path modelfile.erase(0, std::string{szModelPath}.size()); } // texture auto texturefile{(subnode->Material()->replacable_skins[1] != null_handle ? GfxRenderer.Material(subnode->Material()->replacable_skins[1]).name : "none")}; if (texturefile.find(szTexturePath) == 0) { // don't include 'textures/' in the path texturefile.erase(0, std::string{szTexturePath}.size()); } uitextline3 = "mesh: " + modelfile; uitextline4 = "skin: " + texturefile; } else if (typeid(*node) == typeid(TTrack)) { auto const *subnode = static_cast(node); // basic attributes uitextline2 = "isolated: " + (subnode->pIsolated ? subnode->pIsolated->asName : "none") + "; velocity: " + to_string(subnode->SwitchExtension ? subnode->SwitchExtension->fVelocity : subnode->fVelocity) + "; width: " + to_string(subnode->fTrackWidth) + " m" + "; friction: " + to_string(subnode->fFriction, 2) + "; quality: " + to_string(subnode->iQualityFlag); // textures auto texturefile{(subnode->m_material1 != null_handle ? GfxRenderer.Material(subnode->m_material1).name : "none")}; if (texturefile.find(szTexturePath) == 0) { texturefile.erase(0, std::string{szTexturePath}.size()); } auto texturefile2{(subnode->m_material2 != null_handle ? GfxRenderer.Material(subnode->m_material2).name : "none")}; if (texturefile2.find(szTexturePath) == 0) { texturefile2.erase(0, std::string{szTexturePath}.size()); } uitextline2 += "; skins: [" + texturefile + ", " + texturefile2 + "]"; // paths uitextline3 = "paths: "; for (auto const &path : subnode->m_paths) { uitextline3 += "[" + to_string(path.points[segment_data::point::start].x, 3) + ", " + to_string(path.points[segment_data::point::start].y, 3) + ", " + to_string(path.points[segment_data::point::start].z, 3) + "]->" + "[" + to_string(path.points[segment_data::point::end].x, 3) + ", " + to_string(path.points[segment_data::point::end].y, 3) + ", " + to_string(path.points[segment_data::point::end].z, 3) + "] "; } // events std::vector> const eventsequences{{"ev0", &subnode->m_events0}, {"ev0all", &subnode->m_events0all}, {"ev1", &subnode->m_events1}, {"ev1all", &subnode->m_events1all}, {"ev2", &subnode->m_events2}, {"ev2all", &subnode->m_events2all}}; for (auto const &eventsequence : eventsequences) { if (eventsequence.second->empty()) { continue; } uitextline4 += eventsequence.first + ": ["; for (auto const &event : *(eventsequence.second)) { if (uitextline4.back() != '[') { uitextline4 += ", "; } if (event.second) { uitextline4 += event.second->asName; } } uitextline4 += "] "; } } else if (typeid(*node) == typeid(TMemCell)) { auto const *subnode = static_cast(node); uitextline2 = "data: [" + subnode->Text() + "]" + " [" + to_string(subnode->Value1(), 2) + "]" + " [" + to_string(subnode->Value2(), 2) + "]"; uitextline3 = "track: " + (subnode->asTrackName.empty() ? "none" : subnode->asTrackName); } } ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Inspector", &EditorModeFlag, ImGuiWindowFlags_NoResize); ImGui::TextUnformatted(uitextline1.c_str()); ImGui::TextUnformatted(uitextline2.c_str()); ImGui::TextUnformatted(uitextline3.c_str()); ImGui::TextUnformatted(uitextline4.c_str()); ImGui::End(); } if (events_active) { ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Events", &events_active, ImGuiWindowFlags_NoResize); auto const time{Timer::GetTime()}; auto const *event{simulation::Events.begin()}; auto eventtableindex{0}; while ((event != nullptr)) { if ((false == event->m_ignored) && (true == event->bEnabled)) { auto const delay{" " + to_string(std::max(0.0, event->fStartTime - time), 1)}; auto const eventline = "Delay: " + delay.substr(delay.length() - 6) + ", Event: " + event->asName + (event->Activator ? " (by: " + event->Activator->asName + ")" : "") + (event->evJoined ? " (joint event)" : ""); ImGui::TextUnformatted(eventline.c_str()); ++eventtableindex; } event = event->evNext; } ImGui::End(); } auto *vehicle{(FreeFlyModeFlag ? std::get(simulation::Region->find_vehicle(camera->Pos, 20, false, false)) : controlled)}; // w trybie latania lokalizujemy wg mapy if (DebugModeFlag && vehicle) { uitextline1 = "vel: " + to_string(vehicle->GetVelocity(), 2) + "/" + to_string(vehicle->MoverParameters->nrot * M_PI * vehicle->MoverParameters->WheelDiameter * 3.6, 2) + " km/h;" + (vehicle->MoverParameters->SlippingWheels ? " (!)" : " ") + " dist: " + to_string(vehicle->MoverParameters->DistCounter, 2) + " km" + "; pos: [" + to_string(vehicle->GetPosition().x, 2) + ", " + to_string(vehicle->GetPosition().y, 2) + ", " + to_string(vehicle->GetPosition().z, 2) + "]" + ", PM=" + to_string(vehicle->MoverParameters->WheelFlat, 1) + " mm; enpwr=" + to_string(vehicle->MoverParameters->EnginePower, 1) + "; enrot=" + to_string(vehicle->MoverParameters->enrot * 60, 0) + " tmrot=" + to_string(std::abs(vehicle->MoverParameters->nrot) * vehicle->MoverParameters->Transmision.Ratio * 60, 0) + "; ventrot=" + to_string(vehicle->MoverParameters->RventRot * 60, 1) + "; fanrot=" + to_string(vehicle->MoverParameters->dizel_heat.rpmw, 1) + ", " + to_string(vehicle->MoverParameters->dizel_heat.rpmw2, 1); uitextline2 = "HamZ=" + to_string(vehicle->MoverParameters->fBrakeCtrlPos, 2) + "; HamP=" + std::to_string(vehicle->MoverParameters->LocalBrakePosA) + "; NasJ=" + std::to_string(vehicle->MoverParameters->MainCtrlPos) + "(" + std::to_string(vehicle->MoverParameters->MainCtrlActualPos) + ")" + ((vehicle->MoverParameters->ShuntMode && vehicle->MoverParameters->EngineType == TEngineType::DieselElectric) ? "; NasB=" + to_string(vehicle->MoverParameters->AnPos, 2) : "; NasB=" + std::to_string(vehicle->MoverParameters->ScndCtrlPos) + "(" + std::to_string(vehicle->MoverParameters->ScndCtrlActualPos) + ")") + "; I=" + (vehicle->MoverParameters->TrainType == dt_EZT ? std::to_string(int(vehicle->MoverParameters->ShowCurrent(0))) : std::to_string(int(vehicle->MoverParameters->Im))) + "; U=" + to_string(int(vehicle->MoverParameters->RunningTraction.TractionVoltage + 0.5)) + "; R=" + (std::abs(vehicle->MoverParameters->RunningShape.R) > 10000.0 ? "~0.0" : to_string(vehicle->MoverParameters->RunningShape.R, 1)) + " An=" + to_string(vehicle->MoverParameters->AccN, 2); // przyspieszenie poprzeczne if (tprev != simulation::Time.data().wSecond) { tprev = simulation::Time.data().wSecond; Acc = (vehicle->MoverParameters->Vel - VelPrev) / 3.6; VelPrev = vehicle->MoverParameters->Vel; } uitextline2 += "; As=" + to_string(Acc, 2); // przyspieszenie wzdłużne /* uitextline2 += " eAngle=" + to_string( std::cos( vehicle->MoverParameters->eAngle ), 2 ); */ uitextline2 += "; oilP=" + to_string(vehicle->MoverParameters->OilPump.pressure_present, 3); uitextline2 += " oilT=" + to_string(vehicle->MoverParameters->dizel_heat.To, 2); uitextline2 += "; waterT=" + to_string(vehicle->MoverParameters->dizel_heat.temperatura1, 2); uitextline2 += (vehicle->MoverParameters->WaterCircuitsLink ? "-" : "|"); uitextline2 += to_string(vehicle->MoverParameters->dizel_heat.temperatura2, 2); uitextline2 += "; engineT=" + to_string(vehicle->MoverParameters->dizel_heat.Ts, 2); uitextline3 = "cyl.ham. " + to_string(vehicle->MoverParameters->BrakePress, 2) + "; prz.gl. " + to_string(vehicle->MoverParameters->PipePress, 2) + "; zb.gl. " + to_string(vehicle->MoverParameters->CompressedVolume, 2) // youBy - drugi wezyk + "; p.zas. " + to_string(vehicle->MoverParameters->ScndPipePress, 2); // McZapkie: warto wiedziec w jakim stanie sa przelaczniki if (vehicle->MoverParameters->ConvOvldFlag) uitextline3 += " C! "; else if (vehicle->MoverParameters->FuseFlag) uitextline3 += " F! "; else if (!vehicle->MoverParameters->Mains) uitextline3 += " () "; else { switch (vehicle->MoverParameters->ActiveDir * (vehicle->MoverParameters->Imin == vehicle->MoverParameters->IminLo ? 1 : 2)) { case 2: { uitextline3 += " >> "; break; } case 1: { uitextline3 += " -> "; break; } case 0: { uitextline3 += " -- "; break; } case -1: { uitextline3 += " <- "; break; } case -2: { uitextline3 += " << "; break; } } } // McZapkie: predkosc szlakowa if (vehicle->MoverParameters->RunningTrack.Velmax == -1) { uitextline3 += " Vtrack=Vmax"; } else { uitextline3 += " Vtrack " + to_string(vehicle->MoverParameters->RunningTrack.Velmax, 2); } if ((vehicle->MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) || (vehicle->MoverParameters->TrainType == dt_EZT)) { uitextline3 += "; pant. " + to_string(vehicle->MoverParameters->PantPress, 2) + (vehicle->MoverParameters->bPantKurek3 ? "=" : "^") + "ZG"; } uitextline4 = ""; // McZapkie: komenda i jej parametry if (vehicle->MoverParameters->CommandIn.Command != ("")) { uitextline4 = "C:" + vehicle->MoverParameters->CommandIn.Command + " V1=" + to_string(vehicle->MoverParameters->CommandIn.Value1, 0) + " V2=" + to_string(vehicle->MoverParameters->CommandIn.Value2, 0); } if ((vehicle->Mechanik) && (vehicle->Mechanik->AIControllFlag == AIdriver)) { uitextline4 += "AI: Vd=" + to_string(vehicle->Mechanik->VelDesired, 0) + " ad=" + to_string(vehicle->Mechanik->AccDesired, 2) + "/" + to_string(vehicle->Mechanik->AccDesired * vehicle->Mechanik->BrakeAccFactor(), 2) + " atrain=" + to_string(vehicle->Mechanik->fBrake_a0[0], 2) + "+" + to_string(vehicle->Mechanik->fBrake_a1[0], 2) + " aS=" + to_string(vehicle->Mechanik->AbsAccS_pub, 2) + " Pd=" + to_string(vehicle->Mechanik->ActualProximityDist, 0) + " Vn=" + to_string(vehicle->Mechanik->VelNext, 0); } std::list table; // induction motor data if (vehicle->MoverParameters->EngineType == TEngineType::ElectricInductionMotor) { table.emplace_back(" eimc: eimv: press:"); for (int i = 0; i <= 20; ++i) { std::string parameters = vehicle->MoverParameters->eimc_labels[i] + to_string(vehicle->MoverParameters->eimc[i], 2, 9) + " | " + vehicle->MoverParameters->eimv_labels[i] + to_string(vehicle->MoverParameters->eimv[i], 2, 9); if (i < 10) { parameters += " | " + train->fPress_labels[i] + to_string(train->fPress[i][0], 2, 9); } else if (i == 12) { parameters += " med:"; } else if (i >= 13) { parameters += " | " + vehicle->MED_labels[i - 13] + to_string(vehicle->MED[0][i - 13], 2, 9); } table.emplace_back(parameters); } } if (vehicle->MoverParameters->EngineType == TEngineType::DieselEngine) { std::string parameters = "param value"; table.emplace_back(parameters); parameters = "efill: " + to_string(vehicle->MoverParameters->dizel_fill, 2, 9); table.emplace_back(parameters); parameters = "etorq: " + to_string(vehicle->MoverParameters->dizel_Torque, 2, 9); table.emplace_back(parameters); parameters = "creal: " + to_string(vehicle->MoverParameters->dizel_engage, 2, 9); table.emplace_back(parameters); parameters = "cdesi: " + to_string(vehicle->MoverParameters->dizel_engagestate, 2, 9); table.emplace_back(parameters); parameters = "cdelt: " + to_string(vehicle->MoverParameters->dizel_engagedeltaomega, 2, 9); table.emplace_back(parameters); parameters = "gears: " + to_string(vehicle->MoverParameters->dizel_automaticgearstatus, 2, 9); table.emplace_back(parameters); parameters = "hydro value"; table.emplace_back(parameters); parameters = "hTCnI: " + to_string(vehicle->MoverParameters->hydro_TC_nIn, 2, 9); table.emplace_back(parameters); parameters = "hTCnO: " + to_string(vehicle->MoverParameters->hydro_TC_nOut, 2, 9); table.emplace_back(parameters); parameters = "hTCTM: " + to_string(vehicle->MoverParameters->hydro_TC_TMRatio, 2, 9); table.emplace_back(parameters); parameters = "hTCTI: " + to_string(vehicle->MoverParameters->hydro_TC_TorqueIn, 2, 9); table.emplace_back(parameters); parameters = "hTCTO: " + to_string(vehicle->MoverParameters->hydro_TC_TorqueOut, 2, 9); table.emplace_back(parameters); parameters = "hTCfl: " + to_string(vehicle->MoverParameters->hydro_TC_Fill, 2, 9); table.emplace_back(parameters); parameters = "hTCLR: " + to_string(vehicle->MoverParameters->hydro_TC_LockupRate, 2, 9); table.emplace_back(parameters); // parameters = "hTCXX: " + to_string(vehicle->MoverParameters->hydro_TC_nIn, 2, 9); // table.emplace_back(parameters); } ImGui::Begin("Debug", &DebugModeFlag, ImGuiWindowFlags_NoResize); ImGui::TextUnformatted(uitextline1.c_str()); ImGui::TextUnformatted(uitextline2.c_str()); ImGui::TextUnformatted(uitextline3.c_str()); ImGui::TextUnformatted(uitextline4.c_str()); for (const std::string &str : table) ImGui::TextUnformatted(str.c_str()); ImGui::End(); } #ifdef EU07_USE_OLD_UI_CODE if (Controlled && DebugModeFlag && !Global.iTextMode) { uitextline1 += ("; d_omega ") + to_string(Controlled->MoverParameters->dizel_engagedeltaomega, 3); if (Controlled->MoverParameters->EngineType == ElectricInductionMotor) { for (int i = 0; i <= 8; i++) { for (int j = 0; j <= 9; j++) { glRasterPos2f(0.05f + 0.03f * i, 0.16f - 0.01f * j); uitextline4 = to_string(Train->fEIMParams[i][j], 2); } } } } #endif // stenogramy dźwięków (ukryć, gdy tabelka skanowania lub rozkład?) std::vector transcripts; for (auto const &transcript : ui::Transcripts.aLines) { if (Global.fTimeAngleDeg >= transcript.fShow) { cParser parser(transcript.asText); while (true == parser.getTokens(1, false, "|")) { std::string transcriptline; parser >> transcriptline; transcripts.emplace_back(transcriptline); } } } if (!transcripts.empty()) { ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Transcript", nullptr, ImGuiWindowFlags_NoResize); for (auto const &s : transcripts) ImGui::TextUnformatted(s.c_str()); ImGui::End(); } if (log_active) { ImGui::SetNextWindowSize(ImVec2(700, 400)); if (m_progress != 0.0f) ImGui::SetNextWindowPos(ImVec2(50, 150)); ImGui::Begin("Log", &log_active, ImGuiWindowFlags_NoResize); for (const std::string &s : log) ImGui::TextUnformatted(s.c_str()); if (ImGui::GetScrollY() == ImGui::GetScrollMaxY()) ImGui::SetScrollHere(1.0f); ImGui::End(); } glm::dvec2 mousepos = Application.get_cursor_pos(); if (((Global.ControlPicking && mousepos.y < 50.0f) || imgui_io->WantCaptureMouse) && m_progress == 0.0f) { if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("Scenery")) { ImGui::MenuItem("Debug mode", nullptr, &DebugModeFlag); ImGui::MenuItem("Quit", "F10", &quit_active); ImGui::EndMenu(); } if (ImGui::BeginMenu("Window")) { ImGui::MenuItem("Basic info", "F1", &basic_info_active); ImGui::MenuItem("Timetable", "F2", &timetable_active); ImGui::MenuItem("Vehicle info", "F3", &vehicle_info_active); ImGui::MenuItem("Renderer stats", "F8", &renderer_debug_active); ImGui::MenuItem("Scenery inspector", "F11", &EditorModeFlag); ImGui::MenuItem("Events", nullptr, &events_active); ImGui::MenuItem("Log", "F12", &log_active); if (Global.map_enabled && m_map) ImGui::MenuItem("Map", "Tab", &m_map->map_opened); ImGui::EndMenu(); } if (ImGui::BeginMenu("Tools")) { static bool log = Global.iWriteLogEnabled & 1; ImGui::MenuItem("Logging to log.txt", nullptr, &log); if (log) Global.iWriteLogEnabled |= 1; else Global.iWriteLogEnabled &= ~1; if (ImGui::MenuItem("Screenshot", "PrtScr")) Application.queue_screenshot(); ImGui::EndMenu(); } if (ImGui::BeginMenu("Help")) { ImGui::MenuItem("About", "F9", &about_active); ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } } if (m_map && World.InitPerformed()) m_map->render(simulation::Region); glDebug("uilayer render"); ImGui::Render(); Timer::subsystem.gfx_gui.stop(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); glDebug("uilayer render done"); } void ui_layer::set_progress(float const Progress, float const Subtaskprogress) { m_progress = Progress * 0.01f; m_subtaskprogress = Subtaskprogress * 0.01f; } void ui_layer::set_background(std::string const &Filename) { if (false == Filename.empty()) { m_background = GfxRenderer.Fetch_Texture(Filename); } else { m_background = null_handle; } if (m_background != null_handle) { auto const &texture = GfxRenderer.Texture(m_background); } } void ui_layer::render_progress() { if ((m_progress == 0.0f) && (m_subtaskprogress == 0.0f)) return; ImGui::SetNextWindowPos(ImVec2(50, 50)); ImGui::SetNextWindowSize(ImVec2(0, 0)); ImGui::Begin("Progress", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); if (!m_progresstext.empty()) ImGui::ProgressBar(m_progress, ImVec2(300, 0), m_progresstext.c_str()); else ImGui::ProgressBar(m_progress, ImVec2(300, 0)); ImGui::ProgressBar(m_subtaskprogress, ImVec2(300, 0)); ImGui::End(); } void ui_layer::render_tooltip() { if (!m_cursorvisible || m_tooltip.empty()) { return; } ImGui::BeginTooltip(); ImGui::TextUnformatted(m_tooltip.c_str()); ImGui::EndTooltip(); } void ui_layer::render_background() { if (m_background == 0) return; ImVec2 size = ImGui::GetIO().DisplaySize; opengl_texture &tex = GfxRenderer.Texture(m_background); tex.create(); ImGui::SetNextWindowPos(ImVec2(0, 0)); ImGui::SetNextWindowSize(size); ImGui::Begin("Logo", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoBringToFrontOnFocus); ImGui::Image(reinterpret_cast(tex.id), size, ImVec2(0, 1), ImVec2(1, 0)); ImGui::End(); }