renderer.cpp
1 // 作者: feng_xingzhe_cn_.86 2 3 #include <onyx/render/renderer.hpp> 4 #include <imgui.h> 5 #include <imgui_impl_opengl3.h> 6 #include <ctime> 7 #include <GLES3/gl3.h> 8 #include <algorithm> 9 #include <sstream> 10 #include <iomanip> 11 #include "../../Deps/Roboto-Regular.h" 12 13 namespace onyx::render { 14 15 void Renderer::render_frame(const core::EspManager& esp, sdk::game::CameraSystem& camera, uintptr_t base_address) noexcept { 16 17 ImGuiIO& io = ImGui::GetIO(); 18 19 struct timespec ts{}; 20 clock_gettime(CLOCK_MONOTONIC, &ts); 21 double now = static_cast<double>(ts.tv_sec) + ts.tv_nsec / 1e9; 22 23 io.DeltaTime = m_last_time > 0.0 ? static_cast<float>(now - m_last_time) : 1.0f / 60.0f; 24 m_last_time = now; 25 26 draw_esp(esp, camera); 27 draw_offscreen_indicator(esp, camera); 28 draw_launcher_bar(); 29 draw_menu(esp, camera); 30 31 } 32 33 void Renderer::initialize() noexcept { 34 35 if (m_is_initialized) return; 36 37 ImGuiIO& io = ImGui::GetIO(); 38 39 m_font_roboto = io.Fonts->AddFontFromMemoryTTF(Roboto_Regular, sizeof(Roboto_Regular), 20.0f); 40 m_font_roboto_small = io.Fonts->AddFontFromMemoryTTF(Roboto_Regular, sizeof(Roboto_Regular), 17.0f); 41 42 apply_styling(); 43 44 m_is_initialized = true; 45 46 } 47 48 void Renderer::draw_esp(const core::EspManager& esp, const sdk::game::CameraSystem& camera) noexcept { 49 50 auto& settings = core::Settings::get(); 51 52 if (!settings.esp_enabled) return; 53 54 ImGuiIO& io = ImGui::GetIO(); 55 56 auto players = esp.players(); 57 58 for (const auto& player : players) { 59 60 if (!player.is_visible) continue; 61 62 ImVec2 tl, br; 63 64 bool has_2d = false; 65 66 if (settings.esp_box_type > 0) { 67 68 has_2d = get_2d_bounds(player, camera, tl, br); 69 70 } 71 72 if (settings.esp_box_type == 1 && has_2d) { 73 74 draw_2d_box(player, camera); 75 76 } else if (settings.esp_box_type == 2) { 77 78 draw_3d_box(player, camera); 79 80 } 81 82 if (settings.esp_health_enabled && player.health > 0) { 83 84 if (settings.esp_box_type == 1 && has_2d) { 85 86 draw_health_bar_2d(tl, br, player.health); 87 88 } else { 89 90 ImDrawList* dl = ImGui::GetBackgroundDrawList(); 91 92 std::string hp_text = "HP: " + std::to_string(player.health); 93 94 if (m_font_roboto) ImGui::PushFont(m_font_roboto); 95 96 ImVec2 text_size = ImGui::CalcTextSize(hp_text.c_str()); 97 ImVec2 text_pos = { player.screen_pos.x - text_size.x * 0.5f, player.screen_pos.y + 10.0f }; 98 99 dl->AddRectFilled({text_pos.x - 4.0f, text_pos.y}, {text_pos.x + text_size.x + 4.0f, text_pos.y + text_size.y},IM_COL32(0, 0, 0, 160), 4.0f); 100 dl->AddText(text_pos, IM_COL32(0, 255, 100, 255), hp_text.c_str()); 101 102 if (m_font_roboto) ImGui::PopFont(); 103 104 } 105 106 } 107 108 if (settings.esp_distance_enabled) { 109 110 ImDrawList* dl = ImGui::GetBackgroundDrawList(); 111 112 std::string dist_text = std::to_string(static_cast<int>(player.distance)) + "m"; 113 114 if (m_font_roboto) ImGui::PushFont(m_font_roboto); 115 116 ImVec2 text_size = ImGui::CalcTextSize(dist_text.c_str()); 117 118 if (settings.esp_box_type == 2) { 119 120 ImVec2 text_pos = { player.screen_pos.x - text_size.x * 0.5f, player.screen_pos.y + 30.0f }; 121 122 dl->AddRectFilled({text_pos.x - 4.0f, text_pos.y}, {text_pos.x + text_size.x + 4.0f, text_pos.y + text_size.y},IM_COL32(0, 0, 0, 160), 4.0f); 123 dl->AddText(text_pos, IM_COL32(0, 255, 100, 255), dist_text.c_str()); 124 125 } else if (settings.esp_box_type == 1 && has_2d) { 126 127 ImVec2 text_pos = { (tl.x + br.x) * 0.5f - text_size.x * 0.5f, br.y + 5.0f }; 128 129 dl->AddText(text_pos, IM_COL32(255, 255, 255, 255), dist_text.c_str()); 130 131 } 132 133 if (m_font_roboto) ImGui::PopFont(); 134 135 } 136 137 if (settings.esp_lines_enabled) { 138 139 ImDrawList* dl = ImGui::GetBackgroundDrawList(); 140 141 ImVec2 screen_top = { io.DisplaySize.x * 0.5f, 0.0f }; 142 ImVec2 target_top; 143 144 if (settings.esp_box_type == 1 && has_2d) { 145 146 target_top = { (tl.x + br.x) * 0.5f, tl.y }; 147 148 } else { 149 150 target_top = player.screen_pos; // Default to feet if no box 151 152 } 153 154 dl->AddLine(screen_top, target_top, IM_COL32(200, 200, 200, 120), 1.0f); 155 156 } 157 158 if (settings.esp_skeleton && player.bone_count > 0) { 159 160 draw_skeleton(player); 161 162 } 163 164 } 165 166 } 167 168 void Renderer::draw_2d_box(const core::PlayerData& player, const sdk::game::CameraSystem& camera) noexcept { 169 170 ImVec2 tl, br; 171 172 if (!get_2d_bounds(player, camera, tl, br)) return; 173 174 auto& settings = core::Settings::get(); 175 176 ImDrawList* dl = ImGui::GetBackgroundDrawList(); 177 178 const ImU32 color = ImGui::ColorConvertFloat4ToU32(settings.esp_box_color); 179 180 if (settings.esp_box_2d_style == 0) { 181 182 dl->AddRect(tl, br, IM_COL32(0, 0, 0, 255), 0.0f, ImDrawFlags_None, 3.5f); 183 dl->AddRect(tl, br, color, 0.0f, ImDrawFlags_None, 1.5f); 184 185 } else { 186 187 float w = br.x - tl.x; 188 float h = br.y - tl.y; 189 float line_w = w * 0.25f; 190 float line_h = h * 0.25f; 191 192 auto draw_corner = [&](ImVec2 p, float dx, float dy) { 193 194 dl->AddLine({p.x, p.y}, {p.x + dx, p.y}, IM_COL32(0, 0, 0, 255), 3.5f); 195 dl->AddLine({p.x, p.y}, {p.x, p.y + dy}, IM_COL32(0, 0, 0, 255), 3.5f); 196 dl->AddLine({p.x, p.y}, {p.x + dx, p.y}, color, 1.5f); 197 dl->AddLine({p.x, p.y}, {p.x, p.y + dy}, color, 1.5f); 198 199 }; 200 201 draw_corner(tl, line_w, line_h); 202 draw_corner({br.x, tl.y}, -line_w, line_h); 203 draw_corner({tl.x, br.y}, line_w, -line_h); 204 draw_corner(br, -line_w, -line_h); 205 206 } 207 208 } 209 210 void Renderer::draw_3d_box(const core::PlayerData& player, const sdk::game::CameraSystem& camera) noexcept { 211 212 auto& settings = core::Settings::get(); 213 214 sdk::Vector3 bmin, bmax; 215 216 if (player.has_bounds) { 217 bmin = player.aabb_min; 218 bmax = player.aabb_max; 219 } 220 221 sdk::Vector3 corners[8] = { 222 223 {bmin.x, bmin.y, bmin.z}, // 0: 下-左-后 224 {bmax.x, bmin.y, bmin.z}, // 1: 下-右-后 225 {bmax.x, bmin.y, bmax.z}, // 2: 下-右-前 226 {bmin.x, bmin.y, bmax.z}, // 3: 下-左-前 227 {bmin.x, bmax.y, bmin.z}, // 4: 上-左-后 228 {bmax.x, bmax.y, bmin.z}, // 5: 上-右-后 229 {bmax.x, bmax.y, bmax.z}, // 6: 上-右-前 230 {bmin.x, bmax.y, bmax.z} // 7: 上-左-前 231 232 }; 233 234 ImVec2 screen_points[8]; 235 236 const float screen_w = ImGui::GetIO().DisplaySize.x; 237 const float screen_h = ImGui::GetIO().DisplaySize.y; 238 239 for (int i = 0; i < 8; ++i) { 240 241 if (!camera.world_to_screen(corners[i], screen_points[i], screen_w, screen_h)) { 242 243 return; 244 245 } 246 247 } 248 249 ImDrawList* draw_list = ImGui::GetBackgroundDrawList(); 250 251 const ImU32 color = ImGui::ColorConvertFloat4ToU32(settings.esp_box_color); 252 253 draw_list->AddLine(screen_points[0], screen_points[1], color); 254 draw_list->AddLine(screen_points[1], screen_points[2], color); 255 draw_list->AddLine(screen_points[2], screen_points[3], color); 256 draw_list->AddLine(screen_points[3], screen_points[0], color); 257 258 draw_list->AddLine(screen_points[4], screen_points[5], color); 259 draw_list->AddLine(screen_points[5], screen_points[6], color); 260 draw_list->AddLine(screen_points[6], screen_points[7], color); 261 draw_list->AddLine(screen_points[7], screen_points[4], color); 262 263 draw_list->AddLine(screen_points[0], screen_points[4], color); 264 draw_list->AddLine(screen_points[1], screen_points[5], color); 265 draw_list->AddLine(screen_points[2], screen_points[6], color); 266 draw_list->AddLine(screen_points[3], screen_points[7], color); 267 268 } 269 270 void Renderer::draw_health_bar_2d(ImVec2 tl, ImVec2 br, int health) noexcept { 271 272 ImDrawList* dl = ImGui::GetBackgroundDrawList(); 273 274 float h = br.y - tl.y; 275 float w = 4.0f; 276 float x = tl.x - 6.0f; 277 278 dl->AddRectFilled({x - 1.0f, tl.y - 1.0f}, {x + w + 1.0f, br.y + 1.0f}, IM_COL32(0, 0, 0, 180)); 279 280 float health_height = (h * (health / 100.0f)); 281 ImU32 health_color = IM_COL32(0, 255, 100, 255); 282 if (health < 30) health_color = IM_COL32(255, 0, 0, 255); 283 else if (health < 60) health_color = IM_COL32(255, 255, 0, 255); 284 285 dl->AddRectFilled({x, br.y - health_height}, {x + w, br.y}, health_color); 286 287 } 288 289 bool Renderer::get_2d_bounds(const core::PlayerData& player, const sdk::game::CameraSystem& camera, ImVec2& tl, ImVec2& br) noexcept { 290 291 sdk::Vector3 bmin, bmax; 292 293 if (player.has_bounds) { 294 295 bmin = player.aabb_min; 296 bmax = player.aabb_max; 297 298 } 299 300 sdk::Vector3 corners[8] = { 301 302 {bmin.x, bmin.y, bmin.z}, {bmax.x, bmin.y, bmin.z}, 303 {bmax.x, bmin.y, bmax.z}, {bmin.x, bmin.y, bmax.z}, 304 {bmin.x, bmax.y, bmin.z}, {bmax.x, bmax.y, bmin.z}, 305 {bmax.x, bmax.y, bmax.z}, {bmin.x, bmax.y, bmax.z} 306 307 }; 308 309 float min_x = 10000.0f, min_y = 10000.0f, max_x = -10000.0f, max_y = -10000.0f; 310 311 const float sw = ImGui::GetIO().DisplaySize.x; 312 const float sh = ImGui::GetIO().DisplaySize.y; 313 314 bool any_visible = false; 315 316 for (int i = 0; i < 8; ++i) { 317 318 ImVec2 sp; 319 320 if (camera.world_to_screen(corners[i], sp, sw, sh)) { 321 322 min_x = std::min(min_x, sp.x); 323 min_y = std::min(min_y, sp.y); 324 max_x = std::max(max_x, sp.x); 325 max_y = std::max(max_y, sp.y); 326 327 any_visible = true; 328 329 } 330 331 } 332 333 if (!any_visible) return false; 334 335 tl = {min_x, min_y}; 336 br = {max_x, max_y}; 337 338 return true; 339 } 340 341 void Renderer::draw_skeleton(const core::PlayerData& player) noexcept { 342 343 auto& settings = core::Settings::get(); 344 345 ImDrawList* draw_list = ImGui::GetBackgroundDrawList(); 346 347 const ImU32 color = ImGui::ColorConvertFloat4ToU32(settings.esp_skeleton_color); 348 const float thickness = settings.esp_skeleton_thickness; 349 350 for (int32_t i = 0; i < player.bone_count; ++i) { 351 352 const auto& bone = player.bones[i]; 353 if (!bone.is_valid) continue; 354 355 int32_t pi = bone.parent_index; 356 357 if (pi < 0 || pi >= player.bone_count) continue; 358 if (!player.bones[pi].is_valid) continue; 359 360 draw_list->AddLine(player.bones[pi].screen_pos, bone.screen_pos, color, thickness); 361 362 } 363 } 364 365 void Renderer::draw_launcher_bar() noexcept { 366 367 ImGuiIO& io = ImGui::GetIO(); 368 369 float screen_w = io.DisplaySize.x; 370 371 std::time_t t = std::time(nullptr); 372 std::tm tm = *std::gmtime(&t); 373 374 std::stringstream time_ss; 375 376 time_ss << std::put_time(&tm, "%H:%M:%S"); 377 378 std::string text = "Onyx 1.60.0.f3153 | " + time_ss.str() + " | " + std::to_string(static_cast<int>(io.Framerate)) + " FPS"; 379 380 if (m_font_roboto) ImGui::PushFont(m_font_roboto); 381 382 ImVec2 text_size = ImGui::CalcTextSize(text.c_str()); 383 384 float padding = 8.0f; 385 float button_w = 90.0f; 386 float button_h = 30.0f; 387 float window_h = 46.0f; 388 float window_w = text_size.x + button_w + (padding * 4.0f); 389 390 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); 391 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); 392 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); 393 ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.5f, 0.515f)); 394 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); 395 396 ImVec2 pos = { (screen_w - window_w) * 0.5f, io.DisplaySize.y - window_h - 20.0f }; 397 398 ImGui::SetNextWindowPos(pos); 399 ImGui::SetNextWindowSize({ window_w, window_h }); 400 401 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoBackground; 402 403 if (ImGui::Begin("##LauncherBar", nullptr, flags)) { 404 405 ImDrawList* dl = ImGui::GetWindowDrawList(); 406 407 dl->AddRectFilled(pos, { pos.x + window_w, pos.y + window_h }, IM_COL32(15, 15, 15, 245)); 408 409 dl->AddRectFilled(pos, { pos.x + window_w, pos.y + 2.0f }, IM_COL32(0, 255, 230, 255)); 410 411 float available_h = window_h - 2.0f; 412 float content_start_y = pos.y + 2.0f; 413 414 float text_y = content_start_y + (available_h - text_size.y) * 0.5f; 415 float button_y = content_start_y + (available_h - button_h) * 0.5f; 416 417 ImGui::SetCursorScreenPos({ pos.x + padding * 1.5f, text_y }); 418 ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "%s", text.c_str()); 419 420 ImGui::SetCursorScreenPos({ pos.x + window_w - button_w - padding, button_y }); 421 422 if (m_font_roboto_small) { 423 424 ImGui::PopFont(); 425 ImGui::PushFont(m_font_roboto_small); 426 427 } 428 429 const char* btn_label = m_is_menu_open ? "Close" : "Open"; 430 431 if (ImGui::Button(btn_label, { button_w, button_h })) { 432 433 m_is_menu_open = !m_is_menu_open; 434 } 435 436 437 if (m_font_roboto_small) { 438 439 ImGui::PopFont(); 440 441 if (m_font_roboto) ImGui::PushFont(m_font_roboto); 442 443 } 444 445 } 446 447 ImGui::End(); 448 ImGui::PopStyleVar(5); 449 450 if (m_font_roboto) ImGui::PopFont(); 451 452 } 453 454 void Renderer::draw_menu(const core::EspManager& esp, const sdk::game::CameraSystem& camera) noexcept { 455 456 if (!m_is_menu_open) return; 457 458 auto& settings = core::Settings::get(); 459 460 ImGuiIO& io = ImGui::GetIO(); 461 462 if (m_font_roboto) ImGui::PushFont(m_font_roboto); 463 464 ImGui::SetNextWindowPos({io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f}, ImGuiCond_FirstUseEver, {0.5f, 0.5f}); 465 ImGui::SetNextWindowSize({io.DisplaySize.x * 0.4f, io.DisplaySize.y * 0.6f}, ImGuiCond_FirstUseEver); 466 467 if (ImGui::Begin("Onyx - External ESP Framework", nullptr, ImGuiWindowFlags_NoCollapse)) { 468 469 if (ImGui::BeginTabBar("MenuTabs")) { 470 471 if (ImGui::BeginTabItem("Visuals")) { 472 473 ImGui::Spacing(); 474 475 ImGui::Text("Box Style:"); 476 ImGui::RadioButton("None", &settings.esp_box_type, 0); ImGui::SameLine(); 477 ImGui::RadioButton("2D", &settings.esp_box_type, 1); ImGui::SameLine(); 478 ImGui::RadioButton("3D", &settings.esp_box_type, 2); 479 480 if (settings.esp_box_type == 1) { // 2D specific options 481 482 ImGui::Text("2D Style:"); 483 ImGui::RadioButton("Full##2D", &settings.esp_box_2d_style, 0); ImGui::SameLine(); 484 ImGui::RadioButton("Corners##2D", &settings.esp_box_2d_style, 1); 485 486 } 487 488 if (settings.esp_box_type > 0) { 489 490 ImGui::ColorEdit4("Box Color", (float*)&settings.esp_box_color, ImGuiColorEditFlags_NoInputs); 491 492 } 493 494 ImGui::Spacing(); 495 ImGui::Checkbox("Skeleton ESP", &settings.esp_skeleton); 496 497 if (settings.esp_skeleton) { 498 499 ImGui::ColorEdit4("Skeleton Color", (float*)&settings.esp_skeleton_color, ImGuiColorEditFlags_NoInputs); 500 ImGui::SliderFloat("Thickness", &settings.esp_skeleton_thickness, 0.5f, 5.0f); 501 ImGui::SliderFloat("Y Offset", &settings.esp_skeleton_y_offset, -0.5f, 0.5f); 502 503 } 504 505 ImGui::Separator(); 506 507 ImGui::Checkbox("Snap-Lines", &settings.esp_lines_enabled); 508 ImGui::Checkbox("Display Health", &settings.esp_health_enabled); 509 ImGui::Checkbox("Display Distance", &settings.esp_distance_enabled); 510 ImGui::Checkbox("Offscreen Indicator", &settings.esp_offscreen_indicator); 511 512 ImGui::EndTabItem(); 513 514 } 515 516 if (ImGui::BeginTabItem("Misc")) { 517 518 ImGui::Spacing(); 519 520 sdk::Vector3 cam_pos = camera.main_camera.position; 521 522 ImGui::Text("Camera World Position:"); 523 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.9f, 1.0f), "X: %.2f Y: %.2f Z: %.2f", cam_pos.x, cam_pos.y, cam_pos.z); 524 525 ImGui::Spacing(); 526 527 const auto& lp = esp.local_player(); 528 529 if (lp.is_valid()) { 530 531 ImGui::Text("Local Player Found:"); 532 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.5f, 1.0f), "Address: 0x%lX", (unsigned long)lp.address); 533 sdk::Vector3 lp_pos = lp.transform.position(); 534 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.5f, 1.0f), "Pos: X: %.2f Y: %.2f Z: %.2f", lp_pos.x, lp_pos.y, lp_pos.z); 535 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.5f, 1.0f), "Health: %d", lp.health); 536 537 } else { 538 539 ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Local Player: Not Identified"); 540 541 } 542 543 ImGui::Separator(); 544 ImGui::TextDisabled("Build: " __DATE__ " " __TIME__); 545 ImGui::TextDisabled("Platform: Android x86_64"); 546 ImGui::Separator(); 547 ImGui::TextWrapped("Onyx External ESP for Critical Ops. Use responsibly."); 548 ImGui::EndTabItem(); 549 550 } 551 552 ImGui::EndTabBar(); 553 554 } 555 556 } 557 558 ImGui::End(); 559 560 if (m_font_roboto) ImGui::PopFont(); 561 562 } 563 564 void Renderer::apply_styling() noexcept { 565 566 auto& style = ImGui::GetStyle(); 567 568 style.WindowRounding = 12.0f; 569 style.ChildRounding = 8.0f; 570 style.FrameRounding = 8.0f; 571 style.PopupRounding = 8.0f; 572 style.ScrollbarRounding = 12.0f; 573 style.GrabRounding = 8.0f; 574 style.TabRounding = 8.0f; 575 style.WindowBorderSize = 1.0f; 576 style.FrameBorderSize = 0.0f; 577 578 style.AntiAliasedLines = true; 579 style.AntiAliasedFill = true; 580 style.WindowPadding = { 12, 12 }; 581 style.ItemSpacing = { 10, 8 }; 582 583 ImVec4* colors = style.Colors; 584 colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f); 585 colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); 586 colors[ImGuiCol_WindowBg] = ImVec4(0.08f, 0.08f, 0.09f, 1.00f); 587 colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); 588 colors[ImGuiCol_PopupBg] = ImVec4(0.10f, 0.10f, 0.11f, 1.00f); 589 colors[ImGuiCol_Border] = ImVec4(0.20f, 0.20f, 0.22f, 1.00f); 590 colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); 591 colors[ImGuiCol_FrameBg] = ImVec4(0.14f, 0.14f, 0.16f, 1.00f); 592 colors[ImGuiCol_FrameBgHovered] = ImVec4(0.18f, 0.18f, 0.20f, 1.00f); 593 colors[ImGuiCol_FrameBgActive] = ImVec4(0.22f, 0.22f, 0.24f, 1.00f); 594 colors[ImGuiCol_TitleBg] = ImVec4(0.05f, 0.05f, 0.06f, 1.00f); 595 colors[ImGuiCol_TitleBgActive] = ImVec4(0.10f, 0.10f, 0.11f, 1.00f); 596 colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); 597 colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); 598 colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f); 599 colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); 600 colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); 601 colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); 602 colors[ImGuiCol_CheckMark] = ImVec4(0.00f, 1.00f, 0.90f, 1.00f); 603 colors[ImGuiCol_SliderGrab] = ImVec4(0.00f, 0.80f, 0.70f, 1.00f); 604 colors[ImGuiCol_SliderGrabActive] = ImVec4(0.00f, 1.00f, 0.90f, 1.00f); 605 colors[ImGuiCol_Button] = ImVec4(0.14f, 0.14f, 0.16f, 1.00f); 606 colors[ImGuiCol_ButtonHovered] = ImVec4(0.18f, 0.18f, 0.20f, 1.00f); 607 colors[ImGuiCol_ButtonActive] = ImVec4(0.22f, 0.22f, 0.24f, 1.00f); 608 colors[ImGuiCol_Header] = ImVec4(0.12f, 0.11f, 0.14f, 1.00f); 609 colors[ImGuiCol_HeaderHovered] = ImVec4(0.19f, 0.18f, 0.21f, 1.00f); 610 colors[ImGuiCol_HeaderActive] = ImVec4(0.25f, 0.24f, 0.27f, 1.00f); 611 colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.20f, 0.22f, 1.00f); 612 colors[ImGuiCol_SeparatorHovered] = ImVec4(0.30f, 0.30f, 0.32f, 1.00f); 613 colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.40f, 0.42f, 1.00f); 614 colors[ImGuiCol_ResizeGrip] = ImVec4(0.00f, 1.00f, 0.90f, 0.20f); 615 colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.00f, 1.00f, 0.90f, 0.67f); 616 colors[ImGuiCol_ResizeGripActive] = ImVec4(0.00f, 1.00f, 0.90f, 0.95f); 617 colors[ImGuiCol_Tab] = ImVec4(0.11f, 0.11f, 0.13f, 1.00f); 618 colors[ImGuiCol_TabHovered] = ImVec4(0.00f, 1.00f, 0.90f, 0.80f); 619 colors[ImGuiCol_TabActive] = ImVec4(0.00f, 0.80f, 0.70f, 1.00f); 620 colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.11f, 0.13f, 1.00f); 621 colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.14f, 0.14f, 0.16f, 1.00f); 622 623 } 624 625 void Renderer::draw_offscreen_indicator(const core::EspManager& esp, const sdk::game::CameraSystem& camera) noexcept { 626 627 auto& settings = core::Settings::get(); 628 629 if (!settings.esp_offscreen_indicator) return; 630 631 632 ImDrawList* dl = ImGui::GetBackgroundDrawList(); 633 634 const ImVec2 display_size = ImGui::GetIO().DisplaySize; 635 const ImVec2 screen_center = { display_size.x * 0.5f, display_size.y * 0.5f }; 636 637 constexpr float k_radius = 180.0f; 638 constexpr float k_arrow_size = 12.0f; 639 constexpr float k_half_width = 6.0f; 640 641 const ImU32 color = IM_COL32(0, 255, 230, 255); // Onyx Cyan 642 643 const sdk::Matrix4x4 view = camera.view_matrix(); 644 const auto players = esp.players(); 645 646 for (const auto& player : players) { 647 648 if (player.is_visible) continue; 649 650 const sdk::Vector3 local = view.MultiplyPoint3x4(player.world_pos); 651 652 const float angle = std::atan2(local.x, local.z); 653 654 const float s = std::sin(angle); 655 const float c = std::cos(angle); 656 657 const ImVec2 pos = {screen_center.x + s * k_radius,screen_center.y - c * k_radius }; 658 659 const ImVec2 p1 = pos; 660 const ImVec2 p2 = { pos.x - s * k_arrow_size - c * k_half_width, pos.y + c * k_arrow_size - s * k_half_width }; 661 const ImVec2 p3 = { pos.x - s * k_arrow_size + c * k_half_width, pos.y + c * k_arrow_size + s * k_half_width }; 662 663 dl->AddTriangleFilled(p1, p2, p3, color); 664 dl->AddTriangle(p1, p2, p3, IM_COL32(0, 0, 0, 180), 1.0f); 665 666 } 667 668 } 669 670 } // namespace onyx::render