generated from AfonsoCMSousa/CPP-OpenGLTemplate
feat: 3D spaces and Camera
This commit is contained in:
parent
a31af8f9b8
commit
2980fda372
@ -89,20 +89,23 @@ target_include_directories(${EXECUTABLE_NAME} PRIVATE
|
|||||||
${CMAKE_SOURCE_DIR}/libraries
|
${CMAKE_SOURCE_DIR}/libraries
|
||||||
)
|
)
|
||||||
|
|
||||||
# 1. Find the GLFW library in your lib folder
|
# Link GLFW and the required Apple Frameworks
|
||||||
find_library(GLFW_LIB NAMES glfw glfw3 PATHS "${CMAKE_SOURCE_DIR}/libraries" NO_DEFAULT_PATH)
|
|
||||||
|
|
||||||
# 2. Link GLFW and the required Apple Frameworks
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
find_library(GLFW_LIB NAMES glfw3_macOS PATHS "${CMAKE_SOURCE_DIR}/libraries" NO_DEFAULT_PATH)
|
||||||
target_link_libraries(${EXECUTABLE_NAME} PRIVATE
|
target_link_libraries(${EXECUTABLE_NAME} PRIVATE
|
||||||
${GLFW_LIB}
|
${GLFW_LIB}
|
||||||
"-framework Cocoa"
|
"-framework Cocoa"
|
||||||
"-framework IOKit"
|
"-framework IOKit"
|
||||||
"-framework CoreVideo"
|
"-framework CoreVideo"
|
||||||
)
|
)
|
||||||
else()
|
elseif(UNIX)
|
||||||
# For Windows/Linux
|
find_library(GLFW_LIB NAMES glfw3_linux PATHS "${CMAKE_SOURCE_DIR}/libraries" NO_DEFAULT_PATH)
|
||||||
target_link_libraries(${EXECUTABLE_NAME} PRIVATE ${GLFW_LIB})
|
target_link_libraries(${EXECUTABLE_NAME} PRIVATE
|
||||||
|
${GLFW_LIB}
|
||||||
|
GL
|
||||||
|
dl
|
||||||
|
pthread
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
139
include/axis.h
Normal file
139
include/axis.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
116
include/camera.h
Normal file
116
include/camera.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
113
include/grid.h
Normal file
113
include/grid.h
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// 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<float> 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);
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define G_CONSTANT 9.807f
|
#define G_CONSTANT 0.207f
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
@ -49,14 +49,12 @@ int particle_update(Particle *particles, unsigned long long size, float time) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.velocity.y += (G_CONSTANT * time);
|
p.velocity.y -= (G_CONSTANT * time);
|
||||||
|
|
||||||
p.position.x += p.velocity.x * time;
|
p.position.x += p.velocity.x * time;
|
||||||
p.position.y += p.velocity.y * time;
|
p.position.y += p.velocity.y * time;
|
||||||
p.position.z += p.velocity.z * time;
|
p.position.z += p.velocity.z * time;
|
||||||
|
|
||||||
vec_add(p.position, (p.velocity));
|
|
||||||
|
|
||||||
if (p.life > 0) p.life -= time;
|
if (p.life > 0) p.life -= time;
|
||||||
|
|
||||||
particles[i] = p;
|
particles[i] = p;
|
||||||
|
|||||||
BIN
libraries/libglfw3_linux.a
Normal file
BIN
libraries/libglfw3_linux.a
Normal file
Binary file not shown.
152
source/main.cpp
152
source/main.cpp
@ -1,4 +1,7 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "axis.h"
|
||||||
|
#include "camera.h"
|
||||||
|
#include "grid.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "particle.h"
|
#include "particle.h"
|
||||||
|
|
||||||
@ -22,17 +25,45 @@ const unsigned int PARTICLE_COUNT = 1000;
|
|||||||
GLFWwindow *window;
|
GLFWwindow *window;
|
||||||
unsigned int VBO, VAO;
|
unsigned int VBO, VAO;
|
||||||
unsigned int shader;
|
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) {
|
__attribute__((constructor)) static void init_glfw(void) {
|
||||||
if (!glfwInit()) {
|
if (!glfwInit()) {
|
||||||
fprintf(stderr, "GLFW initialization failed\n");
|
fprintf(stderr, "GLFW initialization failed\n");
|
||||||
abort(); // program cannot continue
|
abort(); // program cannot continue
|
||||||
}
|
}
|
||||||
#ifdef __APPLE__
|
|
||||||
glsl_version = "#version 330";
|
glsl_version = "#version 330";
|
||||||
|
#ifdef __APPLE__
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||||
#else
|
|
||||||
glsl_version = "#version 130";
|
|
||||||
#endif
|
#endif
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
@ -42,7 +73,7 @@ __attribute__((constructor)) static void init_glfw(void) {
|
|||||||
if (window == NULL) {
|
if (window == NULL) {
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
fprintf(stderr, "Failed to create GLFW window\n");
|
fprintf(stderr, "Failed to create GLFW window\n");
|
||||||
abort(); // program cannot continue
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwMakeContextCurrent(window);
|
glfwMakeContextCurrent(window);
|
||||||
@ -51,9 +82,13 @@ __attribute__((constructor)) static void init_glfw(void) {
|
|||||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
||||||
std::cout << "Failed to initialize GLAD" << std::endl;
|
std::cout << "Failed to initialize GLAD" << std::endl;
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
glGenBuffers(1, &VBO);
|
glGenBuffers(1, &VBO);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 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);
|
glGenVertexArrays(1, &VAO);
|
||||||
glBindVertexArray(VAO);
|
glBindVertexArray(VAO);
|
||||||
@ -78,18 +113,42 @@ __attribute__((constructor)) static void init_glfw(void) {
|
|||||||
char *vertex_shader_source = read_file(VERTEX_SHADER_SOURCE);
|
char *vertex_shader_source = read_file(VERTEX_SHADER_SOURCE);
|
||||||
char *fragment_shader_source = read_file(FRAGMENT_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);
|
unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
|
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
|
||||||
glCompileShader(vertex_shader);
|
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);
|
unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
|
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
|
||||||
glCompileShader(fragment_shader);
|
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();
|
shader = glCreateProgram();
|
||||||
glAttachShader(shader, vertex_shader);
|
glAttachShader(shader, vertex_shader);
|
||||||
glAttachShader(shader, fragment_shader);
|
glAttachShader(shader, fragment_shader);
|
||||||
glLinkProgram(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(vertex_shader);
|
||||||
glDeleteShader(fragment_shader);
|
glDeleteShader(fragment_shader);
|
||||||
@ -99,8 +158,10 @@ __attribute__((constructor)) static void init_glfw(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((destructor)) static void shutdown_glfw(void) {
|
__attribute__((destructor)) static void shutdown_glfw(void) {
|
||||||
|
axis_destroy(g_axis);
|
||||||
glDeleteVertexArrays(1, &VAO);
|
glDeleteVertexArrays(1, &VAO);
|
||||||
glDeleteBuffers(1, &VBO);
|
glDeleteBuffers(1, &VBO);
|
||||||
|
glDeleteProgram(shader);
|
||||||
glfwDestroyWindow(window);
|
glfwDestroyWindow(window);
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
}
|
}
|
||||||
@ -117,21 +178,33 @@ int main() {
|
|||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
init_imgui();
|
init_imgui();
|
||||||
#endif
|
#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);
|
Particle *particles = (Particle *)malloc(sizeof(Particle) * PARTICLE_COUNT);
|
||||||
|
|
||||||
char errLog[128];
|
|
||||||
if (particles == NULL) {
|
if (particles == NULL) {
|
||||||
snprintf(errLog, 128, "There was a issue allocating memory for the particle list\n");
|
snprintf(errLog, 128, "There was a issue allocating memory for the particle list\n");
|
||||||
} else {
|
} else {
|
||||||
snprintf(errLog, 128, "Successfully allocated memory for the particle list\n");
|
|
||||||
|
|
||||||
for (unsigned long long i = 0; i < PARTICLE_COUNT; i++) {
|
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] = 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].life = -1.0f; // Start with imortal particles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float current_time, last_time, delta_time;
|
float current_time;
|
||||||
|
float last_time = static_cast<float>(glfwGetTime());
|
||||||
|
float delta_time;
|
||||||
int alive_count = 0;
|
int alive_count = 0;
|
||||||
|
|
||||||
while (!glfwWindowShouldClose(window)) {
|
while (!glfwWindowShouldClose(window)) {
|
||||||
@ -143,38 +216,61 @@ int main() {
|
|||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
// ESC releases cursor; if cursor already free, close window
|
||||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
|
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<double>(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
|
#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);
|
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);
|
glUseProgram(shader);
|
||||||
|
glUniformMatrix4fv(glGetUniformLocation(shader, "uMVP"), 1, GL_FALSE, glm::value_ptr(mvp));
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Particle) * PARTICLE_COUNT, particles);
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Particle) * PARTICLE_COUNT, particles);
|
||||||
|
|
||||||
glBindVertexArray(VAO);
|
glBindVertexArray(VAO);
|
||||||
glDrawArrays(GL_POINTS, 0, alive_count);
|
glDrawArrays(GL_POINTS, 0, alive_count);
|
||||||
|
glBindVertexArray(0);
|
||||||
// Render
|
|
||||||
glClearColor(0.03f, 0.03f, 0.03f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#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
|
// Render ImGui
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
|
in vec4 vColor;
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
FragColor = vColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user