diff --git a/CMakeLists.txt b/CMakeLists.txt index 69bd994..9c98fba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,20 +89,23 @@ target_include_directories(${EXECUTABLE_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/libraries ) -# 1. Find the GLFW library in your lib folder -find_library(GLFW_LIB NAMES glfw glfw3 PATHS "${CMAKE_SOURCE_DIR}/libraries" NO_DEFAULT_PATH) - -# 2. Link GLFW and the required Apple Frameworks +# Link GLFW and the required Apple Frameworks if(APPLE) + find_library(GLFW_LIB NAMES glfw3_macOS PATHS "${CMAKE_SOURCE_DIR}/libraries" NO_DEFAULT_PATH) target_link_libraries(${EXECUTABLE_NAME} PRIVATE ${GLFW_LIB} "-framework Cocoa" "-framework IOKit" "-framework CoreVideo" ) -else() - # For Windows/Linux - target_link_libraries(${EXECUTABLE_NAME} PRIVATE ${GLFW_LIB}) +elseif(UNIX) + find_library(GLFW_LIB NAMES glfw3_linux PATHS "${CMAKE_SOURCE_DIR}/libraries" NO_DEFAULT_PATH) + target_link_libraries(${EXECUTABLE_NAME} PRIVATE + ${GLFW_LIB} + GL + dl + pthread + ) endif() diff --git a/include/axis.h b/include/axis.h new file mode 100644 index 0000000..3d3eb29 --- /dev/null +++ b/include/axis.h @@ -0,0 +1,139 @@ +#pragma once +#include +#include +#include + +// 3 axis lines: X=red, Y=green, Z=blue +// Each line: 2 vertices, each vertex has pos(3) + color(3) +static const float AXIS_VERTICES[] = { + // X axis - red + 0.0f, + 0.0f, + 0.0f, + 1.0f, + 0.2f, + 0.2f, + 1.0f, + 0.0f, + 0.0f, + 1.0f, + 0.2f, + 0.2f, + // Y axis - green + 0.0f, + 0.0f, + 0.0f, + 0.2f, + 1.0f, + 0.2f, + 0.0f, + 1.0f, + 0.0f, + 0.2f, + 1.0f, + 0.2f, + // Z axis - blue + 0.0f, + 0.0f, + 0.0f, + 0.2f, + 0.4f, + 1.0f, + 0.0f, + 0.0f, + 1.0f, + 0.2f, + 0.4f, + 1.0f, +}; + +struct AxisRenderer { + unsigned int VAO, VBO; + unsigned int shader; +}; + +static const char *AXIS_VERT_SRC = R"( +#version 330 core +layout(location = 0) in vec3 aPos; +layout(location = 1) in vec3 aColor; +out vec3 vColor; +uniform mat4 uMVP; +void main() { + vColor = aColor; + gl_Position = uMVP * vec4(aPos, 1.0); +} +)"; + +static const char *AXIS_FRAG_SRC = R"( +#version 330 core +in vec3 vColor; +out vec4 FragColor; +void main() { + FragColor = vec4(vColor, 1.0); +} +)"; + +inline AxisRenderer axis_init() { + AxisRenderer r; + + // Compile inline shaders + int success; + char log[512]; + + unsigned int vert = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vert, 1, &AXIS_VERT_SRC, NULL); + glCompileShader(vert); + glGetShaderiv(vert, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(vert, 512, NULL, log); + fprintf(stderr, "Axis vert: %s\n", log); + } + + unsigned int frag = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(frag, 1, &AXIS_FRAG_SRC, NULL); + glCompileShader(frag); + glGetShaderiv(frag, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(frag, 512, NULL, log); + fprintf(stderr, "Axis frag: %s\n", log); + } + + r.shader = glCreateProgram(); + glAttachShader(r.shader, vert); + glAttachShader(r.shader, frag); + glLinkProgram(r.shader); + glDeleteShader(vert); + glDeleteShader(frag); + + glGenVertexArrays(1, &r.VAO); + glGenBuffers(1, &r.VBO); + + glBindVertexArray(r.VAO); + glBindBuffer(GL_ARRAY_BUFFER, r.VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(AXIS_VERTICES), AXIS_VERTICES, GL_STATIC_DRAW); + + // pos + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0); + glEnableVertexAttribArray(0); + // color + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + + glBindVertexArray(0); + return r; +} + +inline void axis_draw(const AxisRenderer &r, const glm::mat4 &mvp) { + glUseProgram(r.shader); + glUniformMatrix4fv(glGetUniformLocation(r.shader, "uMVP"), 1, GL_FALSE, glm::value_ptr(mvp)); + glBindVertexArray(r.VAO); + glLineWidth(2.0f); + glDrawArrays(GL_LINES, 0, 6); + glBindVertexArray(0); +} + +inline void axis_destroy(AxisRenderer &r) { + glDeleteVertexArrays(1, &r.VAO); + glDeleteBuffers(1, &r.VBO); + glDeleteProgram(r.shader); +} diff --git a/include/camera.h b/include/camera.h new file mode 100644 index 0000000..3a1528b --- /dev/null +++ b/include/camera.h @@ -0,0 +1,116 @@ +#pragma once +#include +#include +#include + +struct Camera { + glm::vec3 position; + glm::vec3 front; + glm::vec3 up; + glm::vec3 right; + glm::vec3 world_up; + + float yaw; // horizontal angle, degrees + float pitch; // vertical angle, degrees + float fov; + float speed; + float sensitivity; + + // Mouse state + float last_x, last_y; + bool first_mouse; + bool cursor_captured; +}; + +inline Camera camera_init(glm::vec3 position, float scr_width, float scr_height) { + Camera c; + c.position = position; + c.world_up = glm::vec3(0.0f, 1.0f, 0.0f); + c.yaw = -90.0f; // face -Z by default + c.pitch = -20.0f; + c.fov = 60.0f; + c.speed = 5.0f; + c.sensitivity = 0.1f; + c.last_x = scr_width * 0.5f; + c.last_y = scr_height * 0.5f; + c.first_mouse = true; + c.cursor_captured = false; + + // Compute initial vectors from yaw/pitch + glm::vec3 front; + front.x = cos(glm::radians(c.yaw)) * cos(glm::radians(c.pitch)); + front.y = sin(glm::radians(c.pitch)); + front.z = sin(glm::radians(c.yaw)) * cos(glm::radians(c.pitch)); + c.front = glm::normalize(front); + c.right = glm::normalize(glm::cross(c.front, c.world_up)); + c.up = glm::normalize(glm::cross(c.right, c.front)); + return c; +} + +inline glm::mat4 camera_view(const Camera &c) { + return glm::lookAt(c.position, c.position + c.front, c.up); +} + +inline glm::mat4 camera_projection(const Camera &c, float aspect) { + return glm::perspective(glm::radians(c.fov), aspect, 0.1f, 1000.0f); +} + +// Call every frame with GLFW window +inline void camera_keyboard(Camera &c, GLFWwindow *window, float dt) { + if (!c.cursor_captured) + return; + float vel = c.speed * dt; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + c.position += c.front * vel; + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + c.position -= c.front * vel; + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + c.position -= c.right * vel; + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + c.position += c.right * vel; + if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) + c.position += c.world_up * vel; + if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) + c.position -= c.world_up * vel; +} + +// Call from your GLFW cursor position callback +inline void camera_mouse(Camera &c, double xpos, double ypos) { + if (!c.cursor_captured) + return; + + if (c.first_mouse) { + c.last_x = (float)xpos; + c.last_y = (float)ypos; + c.first_mouse = false; + } + + float dx = ((float)xpos - c.last_x) * c.sensitivity; + float dy = (c.last_y - (float)ypos) * c.sensitivity; // inverted Y + c.last_x = (float)xpos; + c.last_y = (float)ypos; + + c.yaw += dx; + c.pitch += dy; + if (c.pitch > 89.0f) + c.pitch = 89.0f; + if (c.pitch < -89.0f) + c.pitch = -89.0f; + + glm::vec3 front; + front.x = cos(glm::radians(c.yaw)) * cos(glm::radians(c.pitch)); + front.y = sin(glm::radians(c.pitch)); + front.z = sin(glm::radians(c.yaw)) * cos(glm::radians(c.pitch)); + c.front = glm::normalize(front); + c.right = glm::normalize(glm::cross(c.front, c.world_up)); + c.up = glm::normalize(glm::cross(c.right, c.front)); +} + +// Call from scroll callback +inline void camera_scroll(Camera &c, double yoffset) { + c.fov -= (float)yoffset; + if (c.fov < 10.0f) + c.fov = 10.0f; + if (c.fov > 90.0f) + c.fov = 90.0f; +} diff --git a/include/grid.h b/include/grid.h new file mode 100644 index 0000000..1f60d50 --- /dev/null +++ b/include/grid.h @@ -0,0 +1,113 @@ +#pragma once +#include +#include +#include +#include + +// XZ floor grid, centered at origin +// SIZE = half-extent (25 means grid goes -25 to +25) +// STEP = spacing between lines +static constexpr int GRID_SIZE = 25; +static constexpr float GRID_STEP = 1.0f; + +struct GridRenderer { + unsigned int VAO, VBO; + unsigned int shader; + int vertex_count; +}; + +static const char *GRID_VERT_SRC = R"( +#version 330 core +layout(location = 0) in vec3 aPos; +uniform mat4 uMVP; +void main() { + gl_Position = uMVP * vec4(aPos, 1.0); +} +)"; + +static const char *GRID_FRAG_SRC = R"( +#version 330 core +out vec4 FragColor; +void main() { + FragColor = vec4(0.35, 0.35, 0.35, 1.0); +} +)"; + +inline GridRenderer grid_init() { + GridRenderer r; + + // Build vertices on the CPU — two endpoints per line, X-parallel and Z-parallel + std::vector verts; + verts.reserve(4 * 3 * (GRID_SIZE * 2 + 1) * 2); // rough upper bound + + float lo = -GRID_SIZE * GRID_STEP; + float hi = GRID_SIZE * GRID_STEP; + + for (int i = -GRID_SIZE; i <= GRID_SIZE; i++) { + float t = i * GRID_STEP; + + // Line parallel to X axis (varies X, fixed Z=t) + verts.insert(verts.end(), { lo, 0.0f, t, + hi, 0.0f, t }); + + // Line parallel to Z axis (fixed X=t, varies Z) + verts.insert(verts.end(), { t, 0.0f, lo, + t, 0.0f, hi }); + } + + r.vertex_count = (int)(verts.size() / 3); + + // Compile shaders + int success; + char log[512]; + + unsigned int vert = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vert, 1, &GRID_VERT_SRC, NULL); + glCompileShader(vert); + glGetShaderiv(vert, GL_COMPILE_STATUS, &success); + if (!success) { glGetShaderInfoLog(vert, 512, NULL, log); fprintf(stderr, "Grid vert: %s\n", log); } + + unsigned int frag = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(frag, 1, &GRID_FRAG_SRC, NULL); + glCompileShader(frag); + glGetShaderiv(frag, GL_COMPILE_STATUS, &success); + if (!success) { glGetShaderInfoLog(frag, 512, NULL, log); fprintf(stderr, "Grid frag: %s\n", log); } + + r.shader = glCreateProgram(); + glAttachShader(r.shader, vert); + glAttachShader(r.shader, frag); + glLinkProgram(r.shader); + glGetProgramiv(r.shader, GL_LINK_STATUS, &success); + if (!success) { glGetProgramInfoLog(r.shader, 512, NULL, log); fprintf(stderr, "Grid link: %s\n", log); } + glDeleteShader(vert); + glDeleteShader(frag); + + // Upload geometry + glGenVertexArrays(1, &r.VAO); + glGenBuffers(1, &r.VBO); + + glBindVertexArray(r.VAO); + glBindBuffer(GL_ARRAY_BUFFER, r.VBO); + glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(float), verts.data(), GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glBindVertexArray(0); + return r; +} + +inline void grid_draw(const GridRenderer &r, const glm::mat4 &mvp) { + glUseProgram(r.shader); + glUniformMatrix4fv(glGetUniformLocation(r.shader, "uMVP"), 1, GL_FALSE, glm::value_ptr(mvp)); + glBindVertexArray(r.VAO); + glLineWidth(1.0f); + glDrawArrays(GL_LINES, 0, r.vertex_count); + glBindVertexArray(0); +} + +inline void grid_destroy(GridRenderer &r) { + glDeleteVertexArrays(1, &r.VAO); + glDeleteBuffers(1, &r.VBO); + glDeleteProgram(r.shader); +} diff --git a/include/particle.h b/include/particle.h index 634cfab..3114940 100644 --- a/include/particle.h +++ b/include/particle.h @@ -3,7 +3,7 @@ #include -#define G_CONSTANT 9.807f +#define G_CONSTANT 0.207f typedef struct { float x, y, z; @@ -49,14 +49,12 @@ int particle_update(Particle *particles, unsigned long long size, float time) { continue; } - p.velocity.y += (G_CONSTANT * time); + p.velocity.y -= (G_CONSTANT * time); p.position.x += p.velocity.x * time; p.position.y += p.velocity.y * time; p.position.z += p.velocity.z * time; - vec_add(p.position, (p.velocity)); - if (p.life > 0) p.life -= time; particles[i] = p; diff --git a/libraries/libglfw3_linux.a b/libraries/libglfw3_linux.a new file mode 100644 index 0000000..7f279cc Binary files /dev/null and b/libraries/libglfw3_linux.a differ diff --git a/libraries/libglfw3.a b/libraries/libglfw3_macOS.a similarity index 100% rename from libraries/libglfw3.a rename to libraries/libglfw3_macOS.a diff --git a/source/main.cpp b/source/main.cpp index 8e9281a..a7cfc78 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,4 +1,7 @@ #include "main.h" +#include "axis.h" +#include "camera.h" +#include "grid.h" #include "io.h" #include "particle.h" @@ -22,17 +25,45 @@ const unsigned int PARTICLE_COUNT = 1000; GLFWwindow *window; unsigned int VBO, VAO; unsigned int shader; +char errLog[128]; + +Camera *g_camera = nullptr; +AxisRenderer g_axis; +GridRenderer g_grid; + +static void cursor_callback(GLFWwindow *w, double xpos, double ypos) { +#ifndef NDEBUG + // Let ImGui eat mouse when cursor is free + if (!g_camera->cursor_captured) + return; +#endif + if (g_camera) + camera_mouse(*g_camera, xpos, ypos); +} + +static void scroll_callback(GLFWwindow *w, double xoffset, double yoffset) { + if (g_camera && g_camera->cursor_captured) + camera_scroll(*g_camera, yoffset); +} + +static void mouse_button_callback(GLFWwindow *w, int button, int action, int mods) { + if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) { + if (g_camera && !g_camera->cursor_captured) { + g_camera->cursor_captured = true; + g_camera->first_mouse = true; + glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } + } +} __attribute__((constructor)) static void init_glfw(void) { if (!glfwInit()) { fprintf(stderr, "GLFW initialization failed\n"); abort(); // program cannot continue } -#ifdef __APPLE__ glsl_version = "#version 330"; +#ifdef __APPLE__ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac -#else - glsl_version = "#version 130"; #endif glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); @@ -42,7 +73,7 @@ __attribute__((constructor)) static void init_glfw(void) { if (window == NULL) { glfwTerminate(); fprintf(stderr, "Failed to create GLFW window\n"); - abort(); // program cannot continue + abort(); } glfwMakeContextCurrent(window); @@ -51,9 +82,13 @@ __attribute__((constructor)) static void init_glfw(void) { if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; glfwTerminate(); + abort(); } glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); + glEnable(GL_PROGRAM_POINT_SIZE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); @@ -78,18 +113,42 @@ __attribute__((constructor)) static void init_glfw(void) { char *vertex_shader_source = read_file(VERTEX_SHADER_SOURCE); char *fragment_shader_source = read_file(FRAGMENT_SHADER_SOURCE); + if (!vertex_shader_source || !fragment_shader_source) { + fprintf(stderr, "Failed to read shader files. CWD is probably wrong.\n"); + abort(); + } + + int success; + unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); glCompileShader(vertex_shader); + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(vertex_shader, 512, NULL, errLog); + fprintf(stderr, "VERTEX ERROR: %s\n", errLog); + } + unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); glCompileShader(fragment_shader); + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(fragment_shader, 512, NULL, errLog); + fprintf(stderr, "FRAGMENT ERROR: %s\n", errLog); + } + shader = glCreateProgram(); glAttachShader(shader, vertex_shader); glAttachShader(shader, fragment_shader); glLinkProgram(shader); + glGetProgramiv(shader, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(shader, 512, NULL, errLog); + fprintf(stderr, "LINK: %s\n", errLog); + } glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); @@ -99,8 +158,10 @@ __attribute__((constructor)) static void init_glfw(void) { } __attribute__((destructor)) static void shutdown_glfw(void) { + axis_destroy(g_axis); glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); + glDeleteProgram(shader); glfwDestroyWindow(window); glfwTerminate(); } @@ -117,21 +178,33 @@ int main() { #ifndef NDEBUG init_imgui(); #endif + + Camera camera = camera_init(glm::vec3(2.0f, 2.0f, 5.0f), SCR_WIDTH, SCR_HEIGHT); + g_camera = &camera; + + // Axis renderer + g_axis = axis_init(); + g_grid = grid_init(); + + // Register callbacks AFTER ImGui init so ImGui gets them first via chain + glfwSetCursorPosCallback(window, cursor_callback); + glfwSetScrollCallback(window, scroll_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); + Particle *particles = (Particle *)malloc(sizeof(Particle) * PARTICLE_COUNT); - char errLog[128]; if (particles == NULL) { snprintf(errLog, 128, "There was a issue allocating memory for the particle list\n"); } else { - snprintf(errLog, 128, "Successfully allocated memory for the particle list\n"); - for (unsigned long long i = 0; i < PARTICLE_COUNT; i++) { - particles[i] = particle_init({0.0,0.0,0.0}, {1.0,1.0,1.0,1.0}); - particles[i].life = -1.0f; // Start with imortal particles + particles[i] = particle_init({0.0, 0.0, 0.0}, {1.0, 1.0, 1.0, 1.0}); + particles[i].life = -1.0f; // Start with imortal particles } } - float current_time, last_time, delta_time; + float current_time; + float last_time = static_cast(glfwGetTime()); + float delta_time; int alive_count = 0; while (!glfwWindowShouldClose(window)) { @@ -143,38 +216,61 @@ int main() { glfwPollEvents(); #ifndef NDEBUG + // ESC releases cursor; if cursor already free, close window if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { - glfwSetWindowShouldClose(window, true); + if (camera.cursor_captured) { + camera.cursor_captured = false; + camera.first_mouse = true; + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } else { + glfwSetWindowShouldClose(window, true); + } } - // Start ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - // Create ImGui window - ImGui::Begin("Debug"); - ImGui::Text("FPS: %.1f", static_cast(ImGui::GetIO().Framerate)); - ImGui::Text("Particle %d: X=%3.2f Y=%3.2f Z=%3.2f",0, particles[0].position.x, particles[0].position.y, particles[0].position.z); - ImGui::Text("Thread Status: "); - ImGui::TextColored({0.0, 0.7f ,0.0, 1.0}, "READY\n"); - ImGui::Text("%s", errLog); - ImGui::End(); #endif + camera_keyboard(camera, window, delta_time); + + // Build MVP + float aspect = (float)SCR_WIDTH / (float)SCR_HEIGHT; + glm::mat4 view = camera_view(camera); + glm::mat4 projection = camera_projection(camera, aspect); + glm::mat4 mvp = projection * view; // model is identity + alive_count = particle_update(particles, PARTICLE_COUNT, delta_time); + // Render + glClearColor(0.03f, 0.03f, 0.03f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + grid_draw(g_grid, mvp); + axis_draw(g_axis, mvp); + glUseProgram(shader); + glUniformMatrix4fv(glGetUniformLocation(shader, "uMVP"), 1, GL_FALSE, glm::value_ptr(mvp)); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Particle) * PARTICLE_COUNT, particles); glBindVertexArray(VAO); glDrawArrays(GL_POINTS, 0, alive_count); - - // Render - glClearColor(0.03f, 0.03f, 0.03f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + glBindVertexArray(0); #ifndef NDEBUG + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + ImGui::Begin("Debug"); + ImGui::Text("FPS: %.1f", (double)ImGui::GetIO().Framerate); + ImGui::Text("Camera: %.2f %.2f %.2f", (double)camera.position.x, (double)camera.position.y, (double)camera.position.z); + ImGui::Text("Alive particles: %d", alive_count); + if (particles) + ImGui::Text("P[0]: %.2f %.2f %.2f", (double)particles[0].position.x, (double)particles[0].position.y, (double)particles[0].position.z); + ImGui::Separator(); + ImGui::Text("Left-click to capture mouse"); + ImGui::Text("ESC = release mouse / quit"); + ImGui::Text("%s", errLog); + ImGui::End(); + // Render ImGui ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); diff --git a/source/shader/fragment.glsl b/source/shader/fragment.glsl index b446312..2387eed 100644 --- a/source/shader/fragment.glsl +++ b/source/shader/fragment.glsl @@ -1,8 +1,10 @@ #version 330 core + +in vec4 vColor; out vec4 FragColor; void main() { - FragColor = vec4(1.0, 1.0, 1.0, 1.0); + FragColor = vColor; }