From 51abf1be2c6cae9b8857d1739f7e76f2ae5d3e82 Mon Sep 17 00:00:00 2001 From: USS Constellation <98734231+CptMacTavish404@users.noreply.github.com> Date: Wed, 13 Aug 2025 22:14:11 +0545 Subject: [PATCH] Update black_hole.cpp Improved code readability --- black_hole.cpp | 1419 +++++++++++++++++++++++++----------------------- 1 file changed, 754 insertions(+), 665 deletions(-) diff --git a/black_hole.cpp b/black_hole.cpp index 926ac7c..a49e8ad 100644 --- a/black_hole.cpp +++ b/black_hole.cpp @@ -22,689 +22,778 @@ using Clock = std::chrono::high_resolution_clock; // VARS double lastPrintTime = 0.0; -int framesCount = 0; +int framesCount = 0; double c = 299792458.0; double G = 6.67430e-11; struct Ray; bool Gravity = false; -struct Camera { - // Center the camera orbit on the black hole at (0, 0, 0) - vec3 target = vec3(0.0f, 0.0f, 0.0f); // Always look at the black hole center - float radius = 6.34194e10f; - float minRadius = 1e10f, maxRadius = 1e12f; - - float azimuth = 0.0f; - float elevation = M_PI / 2.0f; - - float orbitSpeed = 0.01f; - float panSpeed = 0.01f; - double zoomSpeed = 25e9f; - - bool dragging = false; - bool panning = false; - bool moving = false; // For compute shader optimization - double lastX = 0.0, lastY = 0.0; - - // Calculate camera position in world space - vec3 position() const { - float clampedElevation = glm::clamp(elevation, 0.01f, float(M_PI) - 0.01f); - // Orbit around (0,0,0) always - return vec3( - radius * sin(clampedElevation) * cos(azimuth), - radius * cos(clampedElevation), - radius * sin(clampedElevation) * sin(azimuth) - ); - } - void update() { - // Always keep target at black hole center - target = vec3(0.0f, 0.0f, 0.0f); - if(dragging | panning) { - moving = true; - } else { - moving = false; - } - } - - void processMouseMove(double x, double y) { - float dx = float(x - lastX); - float dy = float(y - lastY); - - if (dragging && panning) { - // Pan: Shift + Left or Middle Mouse - // Disable panning to keep camera centered on black hole - } - else if (dragging && !panning) { - // Orbit: Left mouse only - azimuth += dx * orbitSpeed; - elevation -= dy * orbitSpeed; - elevation = glm::clamp(elevation, 0.01f, float(M_PI) - 0.01f); - } - - lastX = x; - lastY = y; - update(); - } - void processMouseButton(int button, int action, int mods, GLFWwindow* win) { - if (button == GLFW_MOUSE_BUTTON_LEFT || button == GLFW_MOUSE_BUTTON_MIDDLE) { - if (action == GLFW_PRESS) { - dragging = true; - // Disable panning so camera always orbits center - panning = false; - glfwGetCursorPos(win, &lastX, &lastY); - } else if (action == GLFW_RELEASE) { - dragging = false; - panning = false; - } - } - if (button == GLFW_MOUSE_BUTTON_RIGHT) { - if (action == GLFW_PRESS) { - Gravity = true; - } else if (action == GLFW_RELEASE) { - Gravity = false; - } - } - } - void processScroll(double xoffset, double yoffset) { - radius -= yoffset * zoomSpeed; - radius = glm::clamp(radius, minRadius, maxRadius); - update(); - } - void processKey(int key, int scancode, int action, int mods) { - if (action == GLFW_PRESS && key == GLFW_KEY_G) { - Gravity = !Gravity; - cout << "[INFO] Gravity turned " << (Gravity ? "ON" : "OFF") << endl; - } - } +struct Camera +{ + // Center the camera orbit on the black hole at (0, 0, 0) + vec3 target = vec3(0.0f, 0.0f, 0.0f); // Always look at the black hole center + float radius = 6.34194e10f; + float minRadius = 1e10f, maxRadius = 1e12f; + + float azimuth = 0.0f; + float elevation = M_PI / 2.0f; + + float orbitSpeed = 0.01f; + float panSpeed = 0.01f; + double zoomSpeed = 25e9f; + + bool dragging = false; + bool panning = false; + bool moving = false; // For compute shader optimization + double lastX = 0.0, lastY = 0.0; + + // Calculate camera position in world space + vec3 position() const + { + float clampedElevation = glm::clamp(elevation, 0.01f, float(M_PI) - 0.01f); + // Orbit around (0,0,0) always + return vec3 + ( + radius * sin(clampedElevation) * cos(azimuth), + radius * cos(clampedElevation), + radius * sin(clampedElevation) * sin(azimuth) + ); + } + void update() + { + // Always keep target at black hole center + target = vec3(0.0f, 0.0f, 0.0f); + if (dragging | panning) + { + moving = true; + } + else + { + moving = false; + } + } + + void processMouseMove(double x, double y) + { + float dx = float(x - lastX); + float dy = float(y - lastY); + + if (dragging && panning) + { + // Pan: Shift + Left or Middle Mouse + // Disable panning to keep camera centered on black hole + } + else if (dragging && !panning) + { + // Orbit: Left mouse only + azimuth += dx * orbitSpeed; + elevation -= dy * orbitSpeed; + elevation = glm::clamp(elevation, 0.01f, float(M_PI) - 0.01f); + } + + lastX = x; + lastY = y; + update(); + } + void processMouseButton(int button, int action, int mods, GLFWwindow* win) + { + if (button == GLFW_MOUSE_BUTTON_LEFT || button == GLFW_MOUSE_BUTTON_MIDDLE) + { + if (action == GLFW_PRESS) + { + dragging = true; + // Disable panning so camera always orbits center + panning = false; + glfwGetCursorPos(win, &lastX, &lastY); + } + else if (action == GLFW_RELEASE) + { + dragging = false; + panning = false; + } + } + if (button == GLFW_MOUSE_BUTTON_RIGHT) + { + if (action == GLFW_PRESS) + { + Gravity = true; + } + else if (action == GLFW_RELEASE) + { + Gravity = false; + } + } + } + void processScroll(double xoffset, double yoffset) + { + radius -= yoffset * zoomSpeed; + radius = glm::clamp(radius, minRadius, maxRadius); + update(); + } + void processKey(int key, int scancode, int action, int mods) + { + if (action == GLFW_PRESS && key == GLFW_KEY_G) + { + Gravity = !Gravity; + cout << "[INFO] Gravity turned " << (Gravity ? "ON" : "OFF") << endl; + } + } }; Camera camera; -struct BlackHole { - vec3 position; - double mass; - double radius; - double r_s; - - BlackHole(vec3 pos, float m) : position(pos), mass(m) {r_s = 2.0 * G * mass / (c*c);} - bool Intercept(float px, float py, float pz) const { - double dx = double(px) - double(position.x); - double dy = double(py) - double(position.y); - double dz = double(pz) - double(position.z); - double dist2 = dx * dx + dy * dy + dz * dz; - return dist2 < r_s * r_s; - } +struct BlackHole +{ + vec3 position; + double mass; + double radius; + double r_s; + + BlackHole(vec3 pos, float m) : position(pos), mass(m) { r_s = 2.0 * G * mass / (c * c); } + bool Intercept(float px, float py, float pz) const + { + double dx = double(px) - double(position.x); + double dy = double(py) - double(position.y); + double dz = double(pz) - double(position.z); + double dist2 = dx * dx + dy * dy + dz * dz; + return dist2 < r_s * r_s; + } }; BlackHole SagA(vec3(0.0f, 0.0f, 0.0f), 8.54e36); // Sagittarius A black hole -struct ObjectData { - vec4 posRadius; // xyz = position, w = radius - vec4 color; // rgb = color, a = unused - float mass; - vec3 velocity = vec3(0.0f, 0.0f, 0.0f); // Initial velocity +struct ObjectData +{ + vec4 posRadius; // xyz = position, w = radius + vec4 color; // rgb = color, a = unused + float mass; + vec3 velocity = vec3(0.0f, 0.0f, 0.0f); // Initial velocity }; vector objects = { - { vec4(4e11f, 0.0f, 0.0f, 4e10f) , vec4(1,1,0,1), 1.98892e30 }, - { vec4(0.0f, 0.0f, 4e11f, 4e10f) , vec4(1,0,0,1), 1.98892e30 }, - { vec4(0.0f, 0.0f, 0.0f, SagA.r_s) , vec4(0,0,0,1), static_cast(SagA.mass) }, - //{ vec4(6e10f, 0.0f, 0.0f, 5e10f), vec4(0,1,0,1) } + { vec4(4e11f, 0.0f, 0.0f, 4e10f) , vec4(1,1,0,1), 1.98892e30 }, + { vec4(0.0f, 0.0f, 4e11f, 4e10f) , vec4(1,0,0,1), 1.98892e30 }, + { vec4(0.0f, 0.0f, 0.0f, SagA.r_s) , vec4(0,0,0,1), static_cast(SagA.mass) }, + //{ vec4(6e10f, 0.0f, 0.0f, 5e10f), vec4(0,1,0,1) } }; -struct Engine { - GLuint gridShaderProgram; - // -- Quad & Texture render -- // - GLFWwindow* window; - GLuint quadVAO; - GLuint texture; - GLuint shaderProgram; - GLuint computeProgram = 0; - // -- UBOs -- // - GLuint cameraUBO = 0; - GLuint diskUBO = 0; - GLuint objectsUBO = 0; - // -- grid mess vars -- // - GLuint gridVAO = 0; - GLuint gridVBO = 0; - GLuint gridEBO = 0; - int gridIndexCount = 0; - - int WIDTH = 800; // Window width - int HEIGHT = 600; // Window height - int COMPUTE_WIDTH = 200; // Compute resolution width - int COMPUTE_HEIGHT = 150; // Compute resolution height - float width = 100000000000.0f; // Width of the viewport in meters - float height = 75000000000.0f; // Height of the viewport in meters - - Engine() { - if (!glfwInit()) { - cerr << "GLFW init failed\n"; - exit(EXIT_FAILURE); - } - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - window = glfwCreateWindow(WIDTH, HEIGHT, "Black Hole", nullptr, nullptr); - if (!window) { - cerr << "Failed to create GLFW window\n"; - glfwTerminate(); - exit(EXIT_FAILURE); - } - glfwMakeContextCurrent(window); - glewExperimental = GL_TRUE; - GLenum glewErr = glewInit(); - if (glewErr != GLEW_OK) { - cerr << "Failed to initialize GLEW: " - << (const char*)glewGetErrorString(glewErr) - << "\n"; - glfwTerminate(); - exit(EXIT_FAILURE); - } - cout << "OpenGL " << glGetString(GL_VERSION) << "\n"; - this->shaderProgram = CreateShaderProgram(); - gridShaderProgram = CreateShaderProgram("grid.vert", "grid.frag"); - - computeProgram = CreateComputeProgram("geodesic.comp"); - glGenBuffers(1, &cameraUBO); - glBindBuffer(GL_UNIFORM_BUFFER, cameraUBO); - glBufferData(GL_UNIFORM_BUFFER, 128, nullptr, GL_DYNAMIC_DRAW); // alloc ~128 bytes - glBindBufferBase(GL_UNIFORM_BUFFER, 1, cameraUBO); // binding = 1 matches shader - - glGenBuffers(1, &diskUBO); - glBindBuffer(GL_UNIFORM_BUFFER, diskUBO); - glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 4, nullptr, GL_DYNAMIC_DRAW); // 3 values + 1 padding - glBindBufferBase(GL_UNIFORM_BUFFER, 2, diskUBO); // binding = 2 matches compute shader - - glGenBuffers(1, &objectsUBO); - glBindBuffer(GL_UNIFORM_BUFFER, objectsUBO); - // allocate space for 16 objects: - // sizeof(int) + padding + 16×(vec4 posRadius + vec4 color) - GLsizeiptr objUBOSize = sizeof(int) + 3 * sizeof(float) - + 16 * (sizeof(vec4) + sizeof(vec4)) - + 16 * sizeof(float); // 16 floats for mass - glBufferData(GL_UNIFORM_BUFFER, objUBOSize, nullptr, GL_DYNAMIC_DRAW); - glBindBufferBase(GL_UNIFORM_BUFFER, 3, objectsUBO); // binding = 3 matches shader - - auto result = QuadVAO(); - this->quadVAO = result[0]; - this->texture = result[1]; - } - void generateGrid(const vector& objects) { - const int gridSize = 25; - const float spacing = 1e10f; // tweak this - - vector vertices; - vector indices; - - for (int z = 0; z <= gridSize; ++z) { - for (int x = 0; x <= gridSize; ++x) { - float worldX = (x - gridSize / 2) * spacing; - float worldZ = (z - gridSize / 2) * spacing; - - float y = 0.0f; - - // ✅ Warp grid using Schwarzschild geometry - for (const auto& obj : objects) { - vec3 objPos = vec3(obj.posRadius); - double mass = obj.mass; - double radius = obj.posRadius.w; - - double r_s = 2.0 * G * mass / (c * c); - double dx = worldX - objPos.x; - double dz = worldZ - objPos.z; - double dist = sqrt(dx * dx + dz * dz); - - // prevent sqrt of negative or divide-by-zero (inside or at the black hole center) - if (dist > r_s) { - double deltaY = 2.0 * sqrt(r_s * (dist - r_s)); - y += static_cast(deltaY) - 3e10f; - } else { - // 🔴 For points inside or at r_s: make it dip down sharply - y += 2.0f * static_cast(sqrt(r_s * r_s)) - 3e10f; // or add a deep pit - } - } - - vertices.emplace_back(worldX, y, worldZ); - } - } - - // 🧩 Add indices for GL_LINE rendering - for (int z = 0; z < gridSize; ++z) { - for (int x = 0; x < gridSize; ++x) { - int i = z * (gridSize + 1) + x; - indices.push_back(i); - indices.push_back(i + 1); - - indices.push_back(i); - indices.push_back(i + gridSize + 1); - } - } - - // 🔌 Upload to GPU - if (gridVAO == 0) glGenVertexArrays(1, &gridVAO); - if (gridVBO == 0) glGenBuffers(1, &gridVBO); - if (gridEBO == 0) glGenBuffers(1, &gridEBO); - - glBindVertexArray(gridVAO); - - glBindBuffer(GL_ARRAY_BUFFER, gridVBO); - glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vec3), vertices.data(), GL_DYNAMIC_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gridEBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW); - - glEnableVertexAttribArray(0); // location = 0 - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), (void*)0); - - gridIndexCount = indices.size(); - - glBindVertexArray(0); - } - void drawGrid(const mat4& viewProj) { - glUseProgram(gridShaderProgram); - glUniformMatrix4fv(glGetUniformLocation(gridShaderProgram, "viewProj"), - 1, GL_FALSE, glm::value_ptr(viewProj)); - glBindVertexArray(gridVAO); - - glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glDrawElements(GL_LINES, gridIndexCount, GL_UNSIGNED_INT, 0); - - glBindVertexArray(0); - glEnable(GL_DEPTH_TEST); - } - void drawFullScreenQuad() { - glUseProgram(shaderProgram); // fragment + vertex shader - glBindVertexArray(quadVAO); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture); - glUniform1i(glGetUniformLocation(shaderProgram, "screenTexture"), 0); - - glDisable(GL_DEPTH_TEST); // draw as background - glDrawArrays(GL_TRIANGLE_STRIP, 0, 6); // 2 triangles - glEnable(GL_DEPTH_TEST); - } - GLuint CreateShaderProgram(){ - const char* vertexShaderSource = R"( - #version 330 core - layout (location = 0) in vec2 aPos; // Changed to vec2 - layout (location = 1) in vec2 aTexCoord; - out vec2 TexCoord; - void main() { - gl_Position = vec4(aPos, 0.0, 1.0); // Explicit z=0 - TexCoord = aTexCoord; - })"; - - const char* fragmentShaderSource = R"( - #version 330 core - in vec2 TexCoord; - out vec4 FragColor; - uniform sampler2D screenTexture; - void main() { - FragColor = texture(screenTexture, TexCoord); - })"; - - // vertex shader - GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); - glCompileShader(vertexShader); - - // fragment shader - GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); - glCompileShader(fragmentShader); - - GLuint shaderProgram = glCreateProgram(); - glAttachShader(shaderProgram, vertexShader); - glAttachShader(shaderProgram, fragmentShader); - glLinkProgram(shaderProgram); - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - - return shaderProgram; - }; - GLuint CreateShaderProgram(const char* vertPath, const char* fragPath) { - auto loadShader = [](const char* path, GLenum type) -> GLuint { - std::ifstream in(path); - if (!in.is_open()) { - std::cerr << "Failed to open shader: " << path << "\n"; - exit(EXIT_FAILURE); - } - std::stringstream ss; - ss << in.rdbuf(); - std::string srcStr = ss.str(); - const char* src = srcStr.c_str(); - - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &src, nullptr); - glCompileShader(shader); - - GLint success; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLint logLen; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen); - std::vector log(logLen); - glGetShaderInfoLog(shader, logLen, nullptr, log.data()); - std::cerr << "Shader compile error (" << path << "):\n" << log.data() << "\n"; - exit(EXIT_FAILURE); - } - return shader; - }; - - GLuint vertShader = loadShader(vertPath, GL_VERTEX_SHADER); - GLuint fragShader = loadShader(fragPath, GL_FRAGMENT_SHADER); - - GLuint program = glCreateProgram(); - glAttachShader(program, vertShader); - glAttachShader(program, fragShader); - glLinkProgram(program); - - GLint linkSuccess; - glGetProgramiv(program, GL_LINK_STATUS, &linkSuccess); - if (!linkSuccess) { - GLint logLen; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLen); - std::vector log(logLen); - glGetProgramInfoLog(program, logLen, nullptr, log.data()); - std::cerr << "Shader link error:\n" << log.data() << "\n"; - exit(EXIT_FAILURE); - } - - glDeleteShader(vertShader); - glDeleteShader(fragShader); - - return program; - } - GLuint CreateComputeProgram(const char* path) { - // 1) read GLSL source - std::ifstream in(path); - if(!in.is_open()) { - std::cerr << "Failed to open compute shader: " << path << "\n"; - exit(EXIT_FAILURE); - } - std::stringstream ss; - ss << in.rdbuf(); - std::string srcStr = ss.str(); - const char* src = srcStr.c_str(); - - // 2) compile - GLuint cs = glCreateShader(GL_COMPUTE_SHADER); - glShaderSource(cs, 1, &src, nullptr); - glCompileShader(cs); - GLint ok; - glGetShaderiv(cs, GL_COMPILE_STATUS, &ok); - if(!ok) { - GLint logLen; - glGetShaderiv(cs, GL_INFO_LOG_LENGTH, &logLen); - std::vector log(logLen); - glGetShaderInfoLog(cs, logLen, nullptr, log.data()); - std::cerr << "Compute shader compile error:\n" << log.data() << "\n"; - exit(EXIT_FAILURE); - } - - // 3) link - GLuint prog = glCreateProgram(); - glAttachShader(prog, cs); - glLinkProgram(prog); - glGetProgramiv(prog, GL_LINK_STATUS, &ok); - if(!ok) { - GLint logLen; - glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLen); - std::vector log(logLen); - glGetProgramInfoLog(prog, logLen, nullptr, log.data()); - std::cerr << "Compute shader link error:\n" << log.data() << "\n"; - exit(EXIT_FAILURE); - } - - glDeleteShader(cs); - return prog; - } - void dispatchCompute(const Camera& cam) { - // determine target compute‐res - int cw = cam.moving ? COMPUTE_WIDTH : 200; - int ch = cam.moving ? COMPUTE_HEIGHT : 150; - - // 1) reallocate the texture if needed - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, - 0, // mip - GL_RGBA8, // internal format - cw, // width - ch, // height - 0, GL_RGBA, - GL_UNSIGNED_BYTE, - nullptr); - - // 2) bind compute program & UBOs - glUseProgram(computeProgram); - uploadCameraUBO(cam); - uploadDiskUBO(); - uploadObjectsUBO(objects); - - // 3) bind it as image unit 0 - glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); - - // 4) dispatch grid - GLuint groupsX = (GLuint)std::ceil(cw / 16.0f); - GLuint groupsY = (GLuint)std::ceil(ch / 16.0f); - glDispatchCompute(groupsX, groupsY, 1); - - // 5) sync - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); - } - void uploadCameraUBO(const Camera& cam) { - struct UBOData { - vec3 pos; float _pad0; - vec3 right; float _pad1; - vec3 up; float _pad2; - vec3 forward; float _pad3; - float tanHalfFov; - float aspect; - bool moving; - int _pad4; - } data; - vec3 fwd = normalize(cam.target - cam.position()); - vec3 up = vec3(0, 1, 0); // y axis is up, so disk is in x-z plane - vec3 right = normalize(cross(fwd, up)); - up = cross(right, fwd); - - data.pos = cam.position(); - data.right = right; - data.up = up; - data.forward = fwd; - data.tanHalfFov = tan(radians(60.0f * 0.5f)); - data.aspect = float(WIDTH) / float(HEIGHT); - data.moving = cam.dragging || cam.panning; - - glBindBuffer(GL_UNIFORM_BUFFER, cameraUBO); - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UBOData), &data); - } - void uploadObjectsUBO(const vector& objs) { - struct UBOData { - int numObjects; - float _pad0, _pad1, _pad2; // <-- pad out to 16 bytes - vec4 posRadius[16]; - vec4 color[16]; - float mass[16]; - } data; - - size_t count = std::min(objs.size(), size_t(16)); - data.numObjects = static_cast(count); - - for (size_t i = 0; i < count; ++i) { - data.posRadius[i] = objs[i].posRadius; - data.color[i] = objs[i].color; - data.mass[i] = objs[i].mass; - } - - // Upload - glBindBuffer(GL_UNIFORM_BUFFER, objectsUBO); - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(data), &data); - } - void uploadDiskUBO() { - // disk - float r1 = SagA.r_s * 2.2f; // inner radius just outside the event horizon - float r2 = SagA.r_s * 5.2f; // outer radius of the disk - float num = 2.0; // number of rays - float thickness = 1e9f; // padding for std140 alignment - float diskData[4] = { r1, r2, num, thickness }; - - glBindBuffer(GL_UNIFORM_BUFFER, diskUBO); - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(diskData), diskData); - } - - vector QuadVAO(){ - float quadVertices[] = { - // positions // texCoords - -1.0f, 1.0f, 0.0f, 1.0f, // top left - -1.0f, -1.0f, 0.0f, 0.0f, // bottom left - 1.0f, -1.0f, 1.0f, 0.0f, // bottom right - - -1.0f, 1.0f, 0.0f, 1.0f, // top left - 1.0f, -1.0f, 1.0f, 0.0f, // bottom right - 1.0f, 1.0f, 1.0f, 1.0f // top right - }; - - GLuint VAO, VBO; - glGenVertexArrays(1, &VAO); - glGenBuffers(1, &VBO); - - glBindVertexArray(VAO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); - - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); - glEnableVertexAttribArray(1); - - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, - 0, // mip - GL_RGBA8, // internal format - COMPUTE_WIDTH, - COMPUTE_HEIGHT, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - nullptr); - vector VAOtexture = {VAO, texture}; - return VAOtexture; - } - void renderScene() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glUseProgram(shaderProgram); - glBindVertexArray(quadVAO); - // make sure your fragment shader samples from texture unit 0: - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture); - glDrawArrays(GL_TRIANGLES, 0, 6); - glfwSwapBuffers(window); - glfwPollEvents(); - }; +struct Engine +{ + GLuint gridShaderProgram; + // -- Quad & Texture render -- // + GLFWwindow* window; + GLuint quadVAO; + GLuint texture; + GLuint shaderProgram; + GLuint computeProgram = 0; + // -- UBOs -- // + GLuint cameraUBO = 0; + GLuint diskUBO = 0; + GLuint objectsUBO = 0; + // -- grid mess vars -- // + GLuint gridVAO = 0; + GLuint gridVBO = 0; + GLuint gridEBO = 0; + int gridIndexCount = 0; + + int WIDTH = 800; // Window width + int HEIGHT = 600; // Window height + int COMPUTE_WIDTH = 200; // Compute resolution width + int COMPUTE_HEIGHT = 150; // Compute resolution height + float width = 100000000000.0f; // Width of the viewport in meters + float height = 75000000000.0f; // Height of the viewport in meters + + Engine() + { + if (!glfwInit()) + { + cerr << "GLFW init failed\n"; + exit(EXIT_FAILURE); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + window = glfwCreateWindow(WIDTH, HEIGHT, "Black Hole", nullptr, nullptr); + if (!window) + { + cerr << "Failed to create GLFW window\n"; + glfwTerminate(); + exit(EXIT_FAILURE); + } + glfwMakeContextCurrent(window); + glewExperimental = GL_TRUE; + GLenum glewErr = glewInit(); + if (glewErr != GLEW_OK) + { + cerr << "Failed to initialize GLEW: " + << (const char*)glewGetErrorString(glewErr) + << "\n"; + glfwTerminate(); + exit(EXIT_FAILURE); + } + cout << "OpenGL " << glGetString(GL_VERSION) << "\n"; + this->shaderProgram = CreateShaderProgram(); + gridShaderProgram = CreateShaderProgram("grid.vert", "grid.frag"); + + computeProgram = CreateComputeProgram("geodesic.comp"); + glGenBuffers(1, &cameraUBO); + glBindBuffer(GL_UNIFORM_BUFFER, cameraUBO); + glBufferData(GL_UNIFORM_BUFFER, 128, nullptr, GL_DYNAMIC_DRAW); // alloc ~128 bytes + glBindBufferBase(GL_UNIFORM_BUFFER, 1, cameraUBO); // binding = 1 matches shader + + glGenBuffers(1, &diskUBO); + glBindBuffer(GL_UNIFORM_BUFFER, diskUBO); + glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 4, nullptr, GL_DYNAMIC_DRAW); // 3 values + 1 padding + glBindBufferBase(GL_UNIFORM_BUFFER, 2, diskUBO); // binding = 2 matches compute shader + + glGenBuffers(1, &objectsUBO); + glBindBuffer(GL_UNIFORM_BUFFER, objectsUBO); + // allocate space for 16 objects: + // sizeof(int) + padding + 16×(vec4 posRadius + vec4 color) + GLsizeiptr objUBOSize = sizeof(int) + 3 * sizeof(float) + + 16 * (sizeof(vec4) + sizeof(vec4)) + + 16 * sizeof(float); // 16 floats for mass + glBufferData(GL_UNIFORM_BUFFER, objUBOSize, nullptr, GL_DYNAMIC_DRAW); + glBindBufferBase(GL_UNIFORM_BUFFER, 3, objectsUBO); // binding = 3 matches shader + + auto result = QuadVAO(); + this->quadVAO = result[0]; + this->texture = result[1]; + } + void generateGrid(const vector& objects) + { + const int gridSize = 25; + const float spacing = 1e10f; // tweak this + + vector vertices; + vector indices; + + for (int z = 0; z <= gridSize; ++z) + { + for (int x = 0; x <= gridSize; ++x) + { + float worldX = (x - gridSize / 2) * spacing; + float worldZ = (z - gridSize / 2) * spacing; + + float y = 0.0f; + + // ✅ Warp grid using Schwarzschild geometry + for (const auto& obj : objects) + { + vec3 objPos = vec3(obj.posRadius); + double mass = obj.mass; + double radius = obj.posRadius.w; + + double r_s = 2.0 * G * mass / (c * c); + double dx = worldX - objPos.x; + double dz = worldZ - objPos.z; + double dist = sqrt(dx * dx + dz * dz); + + // prevent sqrt of negative or divide-by-zero (inside or at the black hole center) + if (dist > r_s) + { + double deltaY = 2.0 * sqrt(r_s * (dist - r_s)); + y += static_cast(deltaY) - 3e10f; + } + else + { + // 🔴 For points inside or at r_s: make it dip down sharply + y += 2.0f * static_cast(sqrt(r_s * r_s)) - 3e10f; // or add a deep pit + } + } + + vertices.emplace_back(worldX, y, worldZ); + } + } + + // 🧩 Add indices for GL_LINE rendering + for (int z = 0; z < gridSize; ++z) + { + for (int x = 0; x < gridSize; ++x) + { + int i = z * (gridSize + 1) + x; + indices.push_back(i); + indices.push_back(i + 1); + + indices.push_back(i); + indices.push_back(i + gridSize + 1); + } + } + + // 🔌 Upload to GPU + if (gridVAO == 0) glGenVertexArrays(1, &gridVAO); + if (gridVBO == 0) glGenBuffers(1, &gridVBO); + if (gridEBO == 0) glGenBuffers(1, &gridEBO); + + glBindVertexArray(gridVAO); + + glBindBuffer(GL_ARRAY_BUFFER, gridVBO); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vec3), vertices.data(), GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gridEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); // location = 0 + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), (void*)0); + + gridIndexCount = indices.size(); + + glBindVertexArray(0); + } + void drawGrid(const mat4& viewProj) + { + glUseProgram(gridShaderProgram); + glUniformMatrix4fv(glGetUniformLocation(gridShaderProgram, "viewProj"), + 1, GL_FALSE, glm::value_ptr(viewProj)); + glBindVertexArray(gridVAO); + + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDrawElements(GL_LINES, gridIndexCount, GL_UNSIGNED_INT, 0); + + glBindVertexArray(0); + glEnable(GL_DEPTH_TEST); + } + void drawFullScreenQuad() + { + glUseProgram(shaderProgram); // fragment + vertex shader + glBindVertexArray(quadVAO); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glUniform1i(glGetUniformLocation(shaderProgram, "screenTexture"), 0); + + glDisable(GL_DEPTH_TEST); // draw as background + glDrawArrays(GL_TRIANGLE_STRIP, 0, 6); // 2 triangles + glEnable(GL_DEPTH_TEST); + } + GLuint CreateShaderProgram() + { + const char* vertexShaderSource = + R"( + #version 330 core + layout (location = 0) in vec2 aPos; // Changed to vec2 + layout (location = 1) in vec2 aTexCoord; + out vec2 TexCoord; + void main() + { + gl_Position = vec4(aPos, 0.0, 1.0); // Explicit z=0 + TexCoord = aTexCoord; + } + )"; + + const char* fragmentShaderSource = + R"( + #version 330 core + in vec2 TexCoord; + out vec4 FragColor; + uniform sampler2D screenTexture; + void main() + { + FragColor = texture(screenTexture, TexCoord); + } + )"; + + // vertex shader + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); + glCompileShader(vertexShader); + + // fragment shader + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); + glCompileShader(fragmentShader); + + GLuint shaderProgram = glCreateProgram(); + glAttachShader(shaderProgram, vertexShader); + glAttachShader(shaderProgram, fragmentShader); + glLinkProgram(shaderProgram); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + return shaderProgram; + }; + GLuint CreateShaderProgram(const char* vertPath, const char* fragPath) + { + auto loadShader = [](const char* path, GLenum type) -> GLuint + { + std::ifstream in(path); + if (!in.is_open()) + { + std::cerr << "Failed to open shader: " << path << "\n"; + exit(EXIT_FAILURE); + } + std::stringstream ss; + ss << in.rdbuf(); + std::string srcStr = ss.str(); + const char* src = srcStr.c_str(); + + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &src, nullptr); + glCompileShader(shader); + + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) + { + GLint logLen; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen); + std::vector log(logLen); + glGetShaderInfoLog(shader, logLen, nullptr, log.data()); + std::cerr << "Shader compile error (" << path << "):\n" << log.data() << "\n"; + exit(EXIT_FAILURE); + } + return shader; + }; + + GLuint vertShader = loadShader(vertPath, GL_VERTEX_SHADER); + GLuint fragShader = loadShader(fragPath, GL_FRAGMENT_SHADER); + + GLuint program = glCreateProgram(); + glAttachShader(program, vertShader); + glAttachShader(program, fragShader); + glLinkProgram(program); + + GLint linkSuccess; + glGetProgramiv(program, GL_LINK_STATUS, &linkSuccess); + if (!linkSuccess) + { + GLint logLen; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLen); + std::vector log(logLen); + glGetProgramInfoLog(program, logLen, nullptr, log.data()); + std::cerr << "Shader link error:\n" << log.data() << "\n"; + exit(EXIT_FAILURE); + } + + glDeleteShader(vertShader); + glDeleteShader(fragShader); + + return program; + } + GLuint CreateComputeProgram(const char* path) + { + // 1) read GLSL source + std::ifstream in(path); + if (!in.is_open()) + { + std::cerr << "Failed to open compute shader: " << path << "\n"; + exit(EXIT_FAILURE); + } + std::stringstream ss; + ss << in.rdbuf(); + std::string srcStr = ss.str(); + const char* src = srcStr.c_str(); + + // 2) compile + GLuint cs = glCreateShader(GL_COMPUTE_SHADER); + glShaderSource(cs, 1, &src, nullptr); + glCompileShader(cs); + GLint ok; + glGetShaderiv(cs, GL_COMPILE_STATUS, &ok); + if (!ok) + { + GLint logLen; + glGetShaderiv(cs, GL_INFO_LOG_LENGTH, &logLen); + std::vector log(logLen); + glGetShaderInfoLog(cs, logLen, nullptr, log.data()); + std::cerr << "Compute shader compile error:\n" << log.data() << "\n"; + exit(EXIT_FAILURE); + } + + // 3) link + GLuint prog = glCreateProgram(); + glAttachShader(prog, cs); + glLinkProgram(prog); + glGetProgramiv(prog, GL_LINK_STATUS, &ok); + if (!ok) + { + GLint logLen; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLen); + std::vector log(logLen); + glGetProgramInfoLog(prog, logLen, nullptr, log.data()); + std::cerr << "Compute shader link error:\n" << log.data() << "\n"; + exit(EXIT_FAILURE); + } + + glDeleteShader(cs); + return prog; + } + void dispatchCompute(const Camera& cam) + { + // determine target compute‐res + int cw = cam.moving ? COMPUTE_WIDTH : 200; + int ch = cam.moving ? COMPUTE_HEIGHT : 150; + + // 1) reallocate the texture if needed + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, + 0, // mip + GL_RGBA8, // internal format + cw, // width + ch, // height + 0, GL_RGBA, + GL_UNSIGNED_BYTE, + nullptr); + + // 2) bind compute program & UBOs + glUseProgram(computeProgram); + uploadCameraUBO(cam); + uploadDiskUBO(); + uploadObjectsUBO(objects); + + // 3) bind it as image unit 0 + glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); + + // 4) dispatch grid + GLuint groupsX = (GLuint)std::ceil(cw / 16.0f); + GLuint groupsY = (GLuint)std::ceil(ch / 16.0f); + glDispatchCompute(groupsX, groupsY, 1); + + // 5) sync + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + } + void uploadCameraUBO(const Camera& cam) + { + struct UBOData + { + vec3 pos; float _pad0; + vec3 right; float _pad1; + vec3 up; float _pad2; + vec3 forward; float _pad3; + float tanHalfFov; + float aspect; + bool moving; + int _pad4; + } data; + vec3 fwd = normalize(cam.target - cam.position()); + vec3 up = vec3(0, 1, 0); // y axis is up, so disk is in x-z plane + vec3 right = normalize(cross(fwd, up)); + up = cross(right, fwd); + + data.pos = cam.position(); + data.right = right; + data.up = up; + data.forward = fwd; + data.tanHalfFov = tan(radians(60.0f * 0.5f)); + data.aspect = float(WIDTH) / float(HEIGHT); + data.moving = cam.dragging || cam.panning; + + glBindBuffer(GL_UNIFORM_BUFFER, cameraUBO); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UBOData), &data); + } + void uploadObjectsUBO(const vector& objs) + { + struct UBOData + { + int numObjects; + float _pad0, _pad1, _pad2; // <-- pad out to 16 bytes + vec4 posRadius[16]; + vec4 color[16]; + float mass[16]; + } data; + + size_t count = std::min(objs.size(), size_t(16)); + data.numObjects = static_cast(count); + + for (size_t i = 0; i < count; ++i) + { + data.posRadius[i] = objs[i].posRadius; + data.color[i] = objs[i].color; + data.mass[i] = objs[i].mass; + } + + // Upload + glBindBuffer(GL_UNIFORM_BUFFER, objectsUBO); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(data), &data); + } + void uploadDiskUBO() + { + // disk + float r1 = SagA.r_s * 2.2f; // inner radius just outside the event horizon + float r2 = SagA.r_s * 5.2f; // outer radius of the disk + float num = 2.0; // number of rays + float thickness = 1e9f; // padding for std140 alignment + float diskData[4] = { r1, r2, num, thickness }; + + glBindBuffer(GL_UNIFORM_BUFFER, diskUBO); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(diskData), diskData); + } + + vector QuadVAO() + { + float quadVertices[] = { + // positions // texCoords + -1.0f, 1.0f, 0.0f, 1.0f, // top left + -1.0f, -1.0f, 0.0f, 0.0f, // bottom left + 1.0f, -1.0f, 1.0f, 0.0f, // bottom right + + -1.0f, 1.0f, 0.0f, 1.0f, // top left + 1.0f, -1.0f, 1.0f, 0.0f, // bottom right + 1.0f, 1.0f, 1.0f, 1.0f // top right + }; + + GLuint VAO, VBO; + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + glEnableVertexAttribArray(1); + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, + 0, // mip + GL_RGBA8, // internal format + COMPUTE_WIDTH, + COMPUTE_HEIGHT, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + nullptr); + vector VAOtexture = { VAO, texture }; + return VAOtexture; + } + void renderScene() + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glUseProgram(shaderProgram); + glBindVertexArray(quadVAO); + // make sure your fragment shader samples from texture unit 0: + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glDrawArrays(GL_TRIANGLES, 0, 6); + glfwSwapBuffers(window); + glfwPollEvents(); + }; }; Engine engine; -void setupCameraCallbacks(GLFWwindow* window) { - glfwSetWindowUserPointer(window, &camera); - - glfwSetMouseButtonCallback(window, [](GLFWwindow* win, int button, int action, int mods) { - Camera* cam = (Camera*)glfwGetWindowUserPointer(win); - cam->processMouseButton(button, action, mods, win); - }); - - glfwSetCursorPosCallback(window, [](GLFWwindow* win, double x, double y) { - Camera* cam = (Camera*)glfwGetWindowUserPointer(win); - cam->processMouseMove(x, y); - }); - - glfwSetScrollCallback(window, [](GLFWwindow* win, double xoffset, double yoffset) { - Camera* cam = (Camera*)glfwGetWindowUserPointer(win); - cam->processScroll(xoffset, yoffset); - }); - - glfwSetKeyCallback(window, [](GLFWwindow* win, int key, int scancode, int action, int mods) { - Camera* cam = (Camera*)glfwGetWindowUserPointer(win); - cam->processKey(key, scancode, action, mods); - }); +void setupCameraCallbacks(GLFWwindow* window) +{ + glfwSetWindowUserPointer(window, &camera); + + glfwSetMouseButtonCallback(window, [] + ( + GLFWwindow* win, int button, int action, int mods) + { + Camera* cam = (Camera*)glfwGetWindowUserPointer(win); + cam->processMouseButton(button, action, mods, win); + } + ); + + glfwSetCursorPosCallback(window, [] + ( + GLFWwindow* win, double x, double y) + { + Camera* cam = (Camera*)glfwGetWindowUserPointer(win); + cam->processMouseMove(x, y); + } + ); + + glfwSetScrollCallback(window, [] + ( + GLFWwindow* win, double xoffset, double yoffset) + { + Camera* cam = (Camera*)glfwGetWindowUserPointer(win); + cam->processScroll(xoffset, yoffset); + } + ); + + glfwSetKeyCallback(window, [] + ( + GLFWwindow* win, int key, int scancode, int action, int mods) + { + Camera* cam = (Camera*)glfwGetWindowUserPointer(win); + cam->processKey(key, scancode, action, mods); + } + ); } // -- MAIN -- // -int main() { - setupCameraCallbacks(engine.window); - vector pixels(engine.WIDTH * engine.HEIGHT * 3); - - auto t0 = Clock::now(); - lastPrintTime = chrono::duration(t0.time_since_epoch()).count(); - - double lastTime = glfwGetTime(); - int renderW = 800, renderH = 600, numSteps = 80000; - while (!glfwWindowShouldClose(engine.window)) { - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // optional, but good practice - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - double now = glfwGetTime(); - double dt = now - lastTime; // seconds since last frame - lastTime = now; - - // Gravity - for (auto& obj : objects) { - for (auto& obj2 : objects) { - if (&obj == &obj2) continue; // skip self-interaction - float dx = obj2.posRadius.x - obj.posRadius.x; - float dy = obj2.posRadius.y - obj.posRadius.y; - float dz = obj2.posRadius.z - obj.posRadius.z; - float distance = sqrt(dx * dx + dy * dy + dz * dz); - if (distance > 0) { - vector direction = {dx / distance, dy / distance, dz / distance}; - //distance *= 1000; - double Gforce = (G * obj.mass * obj2.mass) / (distance * distance); - - double acc1 = Gforce / obj.mass; - std::vector acc = {direction[0] * acc1, direction[1] * acc1, direction[2] * acc1}; - if (Gravity) { - obj.velocity.x += acc[0]; - obj.velocity.y += acc[1]; - obj.velocity.z += acc[2]; - - obj.posRadius.x += obj.velocity.x; - obj.posRadius.y += obj.velocity.y; - obj.posRadius.z += obj.velocity.z; - cout << "velocity: " < pixels(engine.WIDTH * engine.HEIGHT * 3); + + auto t0 = Clock::now(); + lastPrintTime = chrono::duration(t0.time_since_epoch()).count(); + + double lastTime = glfwGetTime(); + int renderW = 800, renderH = 600, numSteps = 80000; + while (!glfwWindowShouldClose(engine.window)) + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // optional, but good practice + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + double now = glfwGetTime(); + double dt = now - lastTime; // seconds since last frame + lastTime = now; + + // Gravity + for (auto& obj : objects) + { + for (auto& obj2 : objects) + { + if (&obj == &obj2) continue; // skip self-interaction + float dx = obj2.posRadius.x - obj.posRadius.x; + float dy = obj2.posRadius.y - obj.posRadius.y; + float dz = obj2.posRadius.z - obj.posRadius.z; + float distance = sqrt(dx * dx + dy * dy + dz * dz); + if (distance > 0) + { + vector direction = { dx / distance, dy / distance, dz / distance }; + //distance *= 1000; + double Gforce = (G * obj.mass * obj2.mass) / (distance * distance); + + double acc1 = Gforce / obj.mass; + std::vector acc = { direction[0] * acc1, direction[1] * acc1, direction[2] * acc1 }; + if (Gravity) + { + obj.velocity.x += acc[0]; + obj.velocity.y += acc[1]; + obj.velocity.z += acc[2]; + + obj.posRadius.x += obj.velocity.x; + obj.posRadius.y += obj.velocity.y; + obj.posRadius.z += obj.velocity.z; + cout << "velocity: " << obj.velocity.x << ", " << obj.velocity.y << ", " << obj.velocity.z << endl; + } + } + } + } + + + + // ---------- GRID ------------- // + // 2) rebuild grid mesh on CPU + engine.generateGrid(objects); + // 5) overlay the bent grid + mat4 view = lookAt(camera.position(), camera.target, vec3(0, 1, 0)); + mat4 proj = perspective(radians(60.0f), float(engine.COMPUTE_WIDTH) / engine.COMPUTE_HEIGHT, 1e9f, 1e14f); + mat4 viewProj = proj * view; + engine.drawGrid(viewProj); + + // ---------- RUN RAYTRACER ------------- // + glViewport(0, 0, engine.WIDTH, engine.HEIGHT); + engine.dispatchCompute(camera); + engine.drawFullScreenQuad(); + + // 6) present to screen + glfwSwapBuffers(engine.window); + glfwPollEvents(); + } + + glfwDestroyWindow(engine.window); + glfwTerminate(); + return 0; }