generated from AfonsoCMSousa/CPP-OpenGLTemplate
feat: 3D spaces and Camera
This commit is contained in:
+124
-28
@@ -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<float>(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<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
|
||||
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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user