diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 16480ea..e79f8a1 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -7,10 +7,12 @@ on: branches: - main - glsl-experiments + - v1.0.0-beta pull_request: branches: - main - glsl-experiments + - v1.0.0-beta jobs: build: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5de0036..c6cd95b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ - cmake_minimum_required(VERSION 3.15) -project(CxxGRAV VERSION 0.9.3 LANGUAGES CXX) +project(CxxGRAV VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -15,15 +14,12 @@ set_target_properties(CxxGRAV PROPERTIES target_compile_options(CxxGRAV PRIVATE -O2) -# --- Auto-download Raylib per platform --- if (WIN32) if (MSVC) - # MSVC-compatible Raylib binary set(RAYLIB_URL https://github.com/raysan5/raylib/releases/download/5.5/raylib-5.5_win64_msvc16.zip) set(RAYLIB_ARCHIVE ${CMAKE_BINARY_DIR}/raylib_msvc.zip) set(RAYLIB_ROOT ${CMAKE_SOURCE_DIR}/libmsvc) else() - # MinGW-compatible Raylib binary set(RAYLIB_URL https://github.com/raysan5/raylib/releases/download/5.5/raylib-5.5_win64_mingw-w64.zip) set(RAYLIB_ARCHIVE ${CMAKE_BINARY_DIR}/raylib.zip) set(RAYLIB_ROOT ${CMAKE_SOURCE_DIR}/lib) @@ -39,57 +35,41 @@ endif() file(MAKE_DIRECTORY ${RAYLIB_ROOT}) if (NOT EXISTS ${RAYLIB_ARCHIVE}) - message(STATUS "Downloading Raylib from ${RAYLIB_URL}") file(DOWNLOAD ${RAYLIB_URL} ${RAYLIB_ARCHIVE} SHOW_PROGRESS) endif() -# Unpack archive -message(STATUS "Extracting Raylib to ${RAYLIB_ROOT}") file(ARCHIVE_EXTRACT INPUT ${RAYLIB_ARCHIVE} DESTINATION ${RAYLIB_ROOT}) -# Detect the extracted subdirectory file(GLOB RAYLIB_SUBDIRS LIST_DIRECTORIES true "${RAYLIB_ROOT}/raylib-*") -list(LENGTH RAYLIB_SUBDIRS _raylib_count) -if (_raylib_count EQUAL 0) - message(FATAL_ERROR "Raylib extraction failed—no raylib-* directory found in ${RAYLIB_ROOT}") -elseif(_raylib_count GREATER 1) - list(SORT RAYLIB_SUBDIRS) - list(GET RAYLIB_SUBDIRS 0 RAYLIB_DIR) -else() - list(GET RAYLIB_SUBDIRS 0 RAYLIB_DIR) -endif() +list(SORT RAYLIB_SUBDIRS) +list(GET RAYLIB_SUBDIRS 0 RAYLIB_DIR) set(RAYLIB_INCLUDE_DIR "${RAYLIB_DIR}/include") set(RAYLIB_LIB_DIR "${RAYLIB_DIR}/lib") -# --- Platform-specific includes and libs --- if (WIN32) target_include_directories(CxxGRAV PRIVATE ${RAYLIB_INCLUDE_DIR}) target_link_directories(CxxGRAV PRIVATE ${RAYLIB_LIB_DIR}) target_link_libraries(CxxGRAV PRIVATE raylib opengl32 gdi32 winmm ws2_32) if (MSVC) - # On MSVC, system libs are provided by the Windows SDK — no setup.sh needed target_compile_options(CxxGRAV PRIVATE /O2) target_link_options(CxxGRAV PRIVATE /INCREMENTAL:NO) else() - # MinGW target_link_options(CxxGRAV PRIVATE -s) endif() elseif(UNIX) - # Check for system dependencies find_package(OpenGL) find_package(X11) if (NOT OpenGL_FOUND OR NOT X11_FOUND) - message(WARNING "Missing system libraries. Running setup.sh to install dependencies...") execute_process( COMMAND bash ${CMAKE_SOURCE_DIR}/setup.sh RESULT_VARIABLE setup_result ) if (NOT setup_result EQUAL 0) - message(FATAL_ERROR "setup.sh failed. Please run it manually with sudo.") + message(FATAL_ERROR "setup.sh failed.") endif() endif() @@ -99,4 +79,35 @@ elseif(UNIX) target_compile_options(CxxGRAV PRIVATE -Wall -Wextra -Wpedantic) endif() +include(FetchContent) + +FetchContent_Declare( + imgui + URL https://github.com/ocornut/imgui/archive/refs/heads/master.zip +) + +FetchContent_Declare( + rlimgui + URL https://github.com/raylib-extras/rlImGui/archive/refs/heads/main.zip +) + +FetchContent_MakeAvailable(imgui rlimgui) + +add_library(imgui_bundle STATIC + ${imgui_SOURCE_DIR}/imgui.cpp + ${imgui_SOURCE_DIR}/imgui_draw.cpp + ${imgui_SOURCE_DIR}/imgui_tables.cpp + ${imgui_SOURCE_DIR}/imgui_widgets.cpp + ${rlimgui_SOURCE_DIR}/rlImGui.cpp +) + +target_include_directories(imgui_bundle PUBLIC + ${imgui_SOURCE_DIR} + ${imgui_SOURCE_DIR}/backends + ${rlimgui_SOURCE_DIR} + ${RAYLIB_INCLUDE_DIR} +) + +target_link_libraries(CxxGRAV PRIVATE imgui_bundle) + install(TARGETS CxxGRAV DESTINATION bin) diff --git a/src/conf.h b/src/conf.h index ec2d501..7da2dd9 100644 --- a/src/conf.h +++ b/src/conf.h @@ -19,7 +19,7 @@ struct Particle inline void Gravity(Particle& a, Particle& b, double dt) { - const double G (6.67430e-11); + constexpr double G (6.67430e-11); double dx = b.x - a.x; double dy = b.y - a.y; double distSq = dx*dx + dy*dy; @@ -30,12 +30,12 @@ inline void Gravity(Particle& a, Particle& b, double dt) double F = G * a.m * b.m / distSq; double ax = F * dx / dist / a.m; double ay = F * dy / dist / a.m; - double bx = -F * dx / dist / b.m; - double by = -F * dy / dist / b.m; + double bx = -ax * a.m / b.m; + double by = -ay * a.m / b.m; a.vx += ax * dt; a.vy += ay * dt; b.vx += bx * dt; b.vy += by * dt; } -} \ No newline at end of file +} diff --git a/src/main.cpp b/src/main.cpp index a4520c6..1da5912 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,21 @@ -// Copyright 2026 Timofey Zakharchuk (GitHub: TimGoTheCreator) -// Licensed under the Apache License, Version 2.0 +// Copyright 2026 Timofey Zakharchuk (GitHub: TimGoTheCreator) +// Licensed under the Apache License, Version 2.0 #include "conf.h" #include "raylib.h" #include #include +#include "rlImGui.h" +#include "imgui.h" int main() { + SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_ALWAYS_RUN); InitWindow(800, 600, "CxxGRAV"); + rlImGuiSetup(true); std::vector particles; particles.emplace_back(0, 0, 0, 0, 1.99e30, 6.96e8, "Sun"); particles.emplace_back(150000000000, 0, 0, 29780, 5.97e24, 6.37e6, "Earth"); particles.emplace_back(150384400000, 0, 0, 30802, 7.35e22, 1.74e6, "Moon"); - SetTargetFPS(0); double dt = 0.4; @@ -21,18 +24,73 @@ int main() { double simTime = 0.0; double scale = 1.5e6; int steps = 10000; - double camSpeed = 5000000; - bool paused = false; + static bool paused = false; + Vector2 dragStart = {0, 0}; + bool dragging = false; + static bool left_locked = true; + static bool right_locked = true; + double userSpeed = 1.0; + int trackedIndex = -1; + static double shotMass = 5.97e24; + static double shotRadius = 6.37e6; + static char shotName[64] = "Shot"; while (!WindowShouldClose()) { - if (IsKeyDown(KEY_W)) camY -= camSpeed; - if (IsKeyDown(KEY_S)) camY += camSpeed; - if (IsKeyDown(KEY_A)) camX -= camSpeed; - if (IsKeyDown(KEY_D)) camX += camSpeed; + double effectiveSpeed = scale * userSpeed * (IsKeyDown(KEY_LEFT_SHIFT) ? 5.0 : 1.0); + static int lastW = 0; + static int lastH = 0; + + int w = GetScreenWidth(); + int h = GetScreenHeight(); + + if (w != lastW || h != lastH) + { + SetWindowSize(w, h); + lastW = w; + lastH = h; + } + + int cx = w / 2; + int cy = h / 2; + + BeginDrawing(); + ClearBackground(BLACK); + + if (IsKeyPressed(KEY_F11)) ToggleBorderlessWindowed(); + + if (!ImGui::GetIO().WantTextInput) + { + if (IsKeyDown(KEY_W)) camY -= effectiveSpeed; + if (IsKeyDown(KEY_S)) camY += effectiveSpeed; + if (IsKeyDown(KEY_A)) camX -= effectiveSpeed; + if (IsKeyDown(KEY_D)) camX += effectiveSpeed; + } + if (IsKeyDown(KEY_UP)) scale *= 1.005; + if (!ImGui::GetIO().WantCaptureMouse) { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + dragStart = GetMousePosition(); + dragging = true; + } + } + + if (dragging && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { + dragging = false; + + Vector2 dragEnd = GetMousePosition(); + double worldX = (dragStart.x - cx) * scale + camX; + double worldY = (dragStart.y - cy) * scale + camY; + + double dvx = (dragEnd.x - dragStart.x) * 1000; + double dvy = (dragEnd.y - dragStart.y) * 1000; + + particles.emplace_back(worldX, worldY, dvx, dvy, shotMass, shotRadius, shotName); + } + if (IsKeyDown(KEY_DOWN)) scale /= 1.005; - if (IsKeyPressed(KEY_SPACE)) { + + if (IsKeyPressed(KEY_SPACE)) { paused = !paused; } @@ -53,8 +111,13 @@ int main() { } } - BeginDrawing(); - ClearBackground(BLACK); + if (trackedIndex >= 0 && trackedIndex < particles.size()) + { + camX = particles[trackedIndex].x; + camY = particles[trackedIndex].y; + } + + for (auto& p : particles) { int radius = (int)(p.r / scale); @@ -66,17 +129,89 @@ int main() { else if (p.name == "Moon") color = GRAY; DrawCircle( - (int)((p.x - camX) / scale) + 400, - (int)((p.y - camY) / scale) + 300, + (int)((p.x - camX) / scale) + cx, + (int)((p.y - camY) / scale) + cy, radius, color ); } - if (paused) { - DrawText("PAUSED", 10, 10, 20, WHITE); + if (dragging) { + DrawLineV(dragStart, GetMousePosition(), RED); } + rlImGuiBegin(); + + { + ImGuiWindowFlags flags = ImGuiWindowFlags_NoCollapse; + if (left_locked) + { + ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(250, ImGui::GetMainViewport()->Size.y), ImGuiCond_Always); + + flags |= ImGuiWindowFlags_NoMove; + flags |= ImGuiWindowFlags_NoResize; + } + + ImGui::Begin("CXXGrav Panel", nullptr, flags); + + if (ImGui::Button(left_locked ? "Unlock" : "Lock")) + left_locked = !left_locked; + + if (ImGui::Button(paused ? "Resume" : "Pause")) { + paused = !paused; + } + + ImGui::DragScalar("Camera Speed", ImGuiDataType_Double, &userSpeed, 0.1, nullptr, nullptr, "%.6f"); + + ImGui::Separator(); + + ImGui::DragScalar("Mass (KG)", ImGuiDataType_Double, &shotMass, 1e20, nullptr, nullptr, "%.6e"); + ImGui::DragScalar("Radius (M)", ImGuiDataType_Double, &shotRadius, 1e5, nullptr, nullptr, "%.6e"); + ImGui::InputText("Name", shotName, IM_ARRAYSIZE(shotName)); + ImGui::End(); + + { + ImGuiWindowFlags flags = ImGuiWindowFlags_NoCollapse; + if (right_locked) + { + ImGui::SetNextWindowPos(ImVec2(GetScreenWidth() - 250, 0), ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(250, ImGui::GetMainViewport()->Size.y), ImGuiCond_Always); + + flags |= ImGuiWindowFlags_NoMove; + flags |= ImGuiWindowFlags_NoResize; + } + + ImGui::Begin("Bodies", nullptr, flags); + if (ImGui::Button(right_locked ? "Unlock" : "Lock")) + right_locked = !right_locked; + + ImGui::Separator(); + ImGui::Text("Bodies in simulation:"); + + for (int i = 0; i < particles.size(); i++) { + std::string label = particles[i].name + "##" + std::to_string(i); + if (ImGui::Selectable(label.c_str(), trackedIndex == i)) { + trackedIndex = i;} + } + + if (trackedIndex != -1) + { + if (ImGui::Button("Stop Tracking")) { + trackedIndex = -1; + } + } + + + ImGui::End(); + + } + } + + rlImGuiEnd(); EndDrawing(); } -} \ No newline at end of file + + rlImGuiShutdown(); + CloseWindow(); +}