Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/cmake-multi-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
59 changes: 35 additions & 24 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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)
Expand All @@ -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()

Expand All @@ -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)
8 changes: 4 additions & 4 deletions src/conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
}
169 changes: 152 additions & 17 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <vector>
#include <string>
#include "rlImGui.h"
#include "imgui.h"

int main() {
SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_ALWAYS_RUN);
InitWindow(800, 600, "CxxGRAV");
rlImGuiSetup(true);
std::vector<Particle> 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;
Expand All @@ -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;
}

Expand All @@ -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);
Expand All @@ -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();
}
}

rlImGuiShutdown();
CloseWindow();
}