generated from AfonsoCMSousa/CPP-OpenGLTemplate
feat: added multithreading
This commit is contained in:
parent
2980fda372
commit
2b9aa3922e
@ -1,7 +1,10 @@
|
|||||||
#ifndef PARTICLE_H
|
#ifndef PARTICLE_H
|
||||||
#define PARTICLE_H
|
#define PARTICLE_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
#define G_CONSTANT 0.207f
|
#define G_CONSTANT 0.207f
|
||||||
|
|
||||||
@ -18,9 +21,25 @@ typedef struct {
|
|||||||
Vec3 velocity;
|
Vec3 velocity;
|
||||||
Color color;
|
Color color;
|
||||||
|
|
||||||
|
float dampening;
|
||||||
float life;
|
float life;
|
||||||
|
float size;
|
||||||
} Particle;
|
} Particle;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Particle *particle;
|
||||||
|
unsigned long long thread_id;
|
||||||
|
unsigned long long interval_start;
|
||||||
|
unsigned long long interval_end;
|
||||||
|
int alive_count;
|
||||||
|
float time;
|
||||||
|
|
||||||
|
pthread_mutex_t *mutex;
|
||||||
|
pthread_cond_t *cond;
|
||||||
|
int *barrier;
|
||||||
|
atomic_int exit;
|
||||||
|
} Physics_params;
|
||||||
|
|
||||||
// INFO: This adds adder to out, basicaly out + adder
|
// INFO: This adds adder to out, basicaly out + adder
|
||||||
void vec_add(Vec3 &out, Vec3 adder) {
|
void vec_add(Vec3 &out, Vec3 adder) {
|
||||||
out.x += adder.x;
|
out.x += adder.x;
|
||||||
@ -35,6 +54,7 @@ Particle particle_init(Vec3 position, Color color) {
|
|||||||
p.velocity = {0.0f, 0.0f, 0.0f};
|
p.velocity = {0.0f, 0.0f, 0.0f};
|
||||||
|
|
||||||
p.life = 10;
|
p.life = 10;
|
||||||
|
p.dampening = 0.97f;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,19 +63,24 @@ int particle_update(Particle *particles, unsigned long long size, float time) {
|
|||||||
for (unsigned long long i = 0; i < size; i++) {
|
for (unsigned long long i = 0; i < size; i++) {
|
||||||
Particle p = particles[i];
|
Particle p = particles[i];
|
||||||
if (p.life == 0) {
|
if (p.life == 0) {
|
||||||
particles[i].position = {0.0f, 0.0f, 0.0f}; // Start at center
|
|
||||||
particles[i].velocity = {(rand() % 100 - 50) / 50.0f, 5.0f, 0.0f}; // Random spray up
|
|
||||||
particles[i].life = 2.0f; // Give it 2 seconds of life
|
|
||||||
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;
|
||||||
|
|
||||||
if (p.life > 0) p.life -= time;
|
if (p.position.y <= 0) {
|
||||||
|
p.position.y = 0;
|
||||||
|
p.velocity.y *= (-1 * p.dampening);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.life > 0)
|
||||||
|
p.life -= time;
|
||||||
|
if (p.life < 0 && p.life >= -1.0f)
|
||||||
|
p.life = 0;
|
||||||
|
|
||||||
particles[i] = p;
|
particles[i] = p;
|
||||||
alive_count++;
|
alive_count++;
|
||||||
@ -64,4 +89,59 @@ int particle_update(Particle *particles, unsigned long long size, float time) {
|
|||||||
return alive_count;
|
return alive_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *t_update(void *args) {
|
||||||
|
Physics_params *params = (Physics_params *)args;
|
||||||
|
while (params->exit != 1) {
|
||||||
|
pthread_mutex_lock(params->mutex);
|
||||||
|
if (params->(missing) == 0) {
|
||||||
|
sleep(100);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(params->mutex);
|
||||||
|
|
||||||
|
params->alive_count = 0;
|
||||||
|
for (unsigned long long i = params->interval_start; i < params->interval_end; i++) {
|
||||||
|
Particle p = params->particle[i];
|
||||||
|
|
||||||
|
if (p.life == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.velocity.y -= (G_CONSTANT * params->time);
|
||||||
|
|
||||||
|
p.position.x += p.velocity.x * params->time;
|
||||||
|
p.position.y += p.velocity.y * params->time;
|
||||||
|
p.position.z += p.velocity.z * params->time;
|
||||||
|
|
||||||
|
if (p.position.y <= 0) {
|
||||||
|
p.position.y = 0;
|
||||||
|
p.velocity.y *= (-1 * p.dampening);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.life > 0)
|
||||||
|
p.life -= params->time;
|
||||||
|
if (p.life < 0 && p.life >= -1.0f)
|
||||||
|
p.life = 0;
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
if (params->thread_id == 0) {
|
||||||
|
p.color = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
} else if (params->thread_id == 1) {
|
||||||
|
p.color = {1.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
} else if (params->thread_id == 2) {
|
||||||
|
p.color = {0.0f, 1.0f, 0.0f, 1.0f};
|
||||||
|
} else if (params->thread_id == 3) {
|
||||||
|
p.color = {0.0f, 0.0f, 1.0f, 1.0f};
|
||||||
|
} else {
|
||||||
|
p.color = {0.7f, 0.7f, 0.7f, 1.0f};
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
params->particle[i] = p;
|
||||||
|
params->alive_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // PARTICLE_H
|
#endif // PARTICLE_H
|
||||||
|
|||||||
@ -9,18 +9,24 @@
|
|||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
#include "imgui/imgui_impl_glfw.h"
|
#include "imgui/imgui_impl_glfw.h"
|
||||||
#include "imgui/imgui_impl_opengl3.h"
|
#include "imgui/imgui_impl_opengl3.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
const char FRAGMENT_SHADER_SOURCE[] = "./source/shader/fragment.glsl";
|
const char FRAGMENT_SHADER_SOURCE[] = "./source/shader/fragment.glsl";
|
||||||
const char VERTEX_SHADER_SOURCE[] = "./source/shader/vertex.glsl";
|
const char VERTEX_SHADER_SOURCE[] = "./source/shader/vertex.glsl";
|
||||||
|
|
||||||
const char PROGRAM_NAME[] = "Particle Sim";
|
const char PROGRAM_NAME[] = "Particle Sim";
|
||||||
|
const unsigned int THREAD_COUNT = 4;
|
||||||
|
|
||||||
const unsigned int SCR_WIDTH = 1920;
|
const unsigned int SCR_WIDTH = 1920;
|
||||||
const unsigned int SCR_HEIGHT = 1080;
|
const unsigned int SCR_HEIGHT = 1080;
|
||||||
const char *glsl_version;
|
const char *glsl_version;
|
||||||
|
|
||||||
const unsigned int PARTICLE_COUNT = 1000;
|
const unsigned int PARTICLE_COUNT = 100000;
|
||||||
|
|
||||||
GLFWwindow *window;
|
GLFWwindow *window;
|
||||||
unsigned int VBO, VAO;
|
unsigned int VBO, VAO;
|
||||||
@ -31,6 +37,10 @@ Camera *g_camera = nullptr;
|
|||||||
AxisRenderer g_axis;
|
AxisRenderer g_axis;
|
||||||
GridRenderer g_grid;
|
GridRenderer g_grid;
|
||||||
|
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
pthread_cond_t cond;
|
||||||
|
pthread_barrier_t barrier;
|
||||||
|
|
||||||
static void cursor_callback(GLFWwindow *w, double xpos, double ypos) {
|
static void cursor_callback(GLFWwindow *w, double xpos, double ypos) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// Let ImGui eat mouse when cursor is free
|
// Let ImGui eat mouse when cursor is free
|
||||||
@ -57,6 +67,8 @@ static void mouse_button_callback(GLFWwindow *w, int button, int action, int mod
|
|||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((constructor)) static void init_glfw(void) {
|
__attribute__((constructor)) static void init_glfw(void) {
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
if (!glfwInit()) {
|
if (!glfwInit()) {
|
||||||
fprintf(stderr, "GLFW initialization failed\n");
|
fprintf(stderr, "GLFW initialization failed\n");
|
||||||
abort(); // program cannot continue
|
abort(); // program cannot continue
|
||||||
@ -97,17 +109,25 @@ __attribute__((constructor)) static void init_glfw(void) {
|
|||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
// Position: 3 floats, starts at 0
|
// Position: 3 floats, starts at 0
|
||||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void *)0);
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 13 * sizeof(float), (void *)0);
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
// Color: 4 floats, starts after 6 floats (Position + Velocity)
|
// Color: 4 floats, starts after 6 floats (Position + Velocity)
|
||||||
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void *)(6 * sizeof(float)));
|
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 13 * sizeof(float), (void *)(6 * sizeof(float)));
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
// Life: 1 float, starts after 10 floats (Position + Velocity + Color)
|
// Life: 1 float, starts after 10 floats (Position + Velocity + Color)
|
||||||
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void *)(10 * sizeof(float)));
|
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 13 * sizeof(float), (void *)(10 * sizeof(float)));
|
||||||
glEnableVertexAttribArray(2);
|
glEnableVertexAttribArray(2);
|
||||||
|
|
||||||
|
// Dampening: 1 float, starts after 11 floars (Positon + Velocity + Color + Life)
|
||||||
|
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 13 * sizeof(float), (void *)(11 * sizeof(float)));
|
||||||
|
glEnableVertexAttribArray(3);
|
||||||
|
|
||||||
|
// Size: 1 float, starts after 11 floars (Positon + Velocity + Color + Life + Dampening)
|
||||||
|
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, 13 * sizeof(float), (void *)(12 * sizeof(float)));
|
||||||
|
glEnableVertexAttribArray(4);
|
||||||
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(Particle) * PARTICLE_COUNT, NULL, GL_DYNAMIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, sizeof(Particle) * PARTICLE_COUNT, NULL, GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
char *vertex_shader_source = read_file(VERTEX_SHADER_SOURCE);
|
char *vertex_shader_source = read_file(VERTEX_SHADER_SOURCE);
|
||||||
@ -179,6 +199,9 @@ int main() {
|
|||||||
init_imgui();
|
init_imgui();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
pthread_t *threads = (pthread_t *)malloc(sizeof(pthread_t) * THREAD_COUNT);
|
||||||
|
Physics_params *thread_params = (Physics_params *)malloc(sizeof(Physics_params) * THREAD_COUNT);
|
||||||
|
|
||||||
Camera camera = camera_init(glm::vec3(2.0f, 2.0f, 5.0f), SCR_WIDTH, SCR_HEIGHT);
|
Camera camera = camera_init(glm::vec3(2.0f, 2.0f, 5.0f), SCR_WIDTH, SCR_HEIGHT);
|
||||||
g_camera = &camera;
|
g_camera = &camera;
|
||||||
|
|
||||||
@ -197,8 +220,16 @@ int main() {
|
|||||||
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 {
|
||||||
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].life = -1.0f; // Start with imortal particles
|
float x, y, z;
|
||||||
|
|
||||||
|
x = (float)(rand() % 200) / 100; // [0.0 , 2.0]
|
||||||
|
y = (float)(rand() % 200) / 100; // [0.0 , 2.0]
|
||||||
|
z = (float)(rand() % 200) / 100; // [0.0 , 2.0]
|
||||||
|
|
||||||
|
particles[i] = particle_init({x, y, z}, {x, y, z, 1.0});
|
||||||
|
particles[i].life = -2.0f; // Start with imortal particles
|
||||||
|
particles[i].size = 20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,6 +238,24 @@ int main() {
|
|||||||
float delta_time;
|
float delta_time;
|
||||||
int alive_count = 0;
|
int alive_count = 0;
|
||||||
|
|
||||||
|
pthread_mutex_init(&mutex, NULL);
|
||||||
|
pthread_cond_init(&cond, NULL);
|
||||||
|
pthread_barrier_init(&barrier, NULL, THREAD_COUNT + 1);
|
||||||
|
|
||||||
|
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||||
|
thread_params[i].particle = particles;
|
||||||
|
thread_params[i].interval_start = (PARTICLE_COUNT / THREAD_COUNT) * i;
|
||||||
|
thread_params[i].interval_end = (PARTICLE_COUNT / THREAD_COUNT) * (i + 1);
|
||||||
|
|
||||||
|
thread_params[i].time = 0;
|
||||||
|
thread_params[i].exit = 0;
|
||||||
|
thread_params[i].alive_count = 0;
|
||||||
|
|
||||||
|
thread_params[i].thread_id = i;
|
||||||
|
thread_params[i].mutex = global_mutex;
|
||||||
|
pthread_create(&threads[i], NULL, t_update, &thread_params[i]);
|
||||||
|
}
|
||||||
|
|
||||||
while (!glfwWindowShouldClose(window)) {
|
while (!glfwWindowShouldClose(window)) {
|
||||||
current_time = static_cast<float>(glfwGetTime());
|
current_time = static_cast<float>(glfwGetTime());
|
||||||
delta_time = current_time - last_time;
|
delta_time = current_time - last_time;
|
||||||
@ -235,7 +284,11 @@ int main() {
|
|||||||
glm::mat4 projection = camera_projection(camera, aspect);
|
glm::mat4 projection = camera_projection(camera, aspect);
|
||||||
glm::mat4 mvp = projection * view; // model is identity
|
glm::mat4 mvp = projection * view; // model is identity
|
||||||
|
|
||||||
alive_count = particle_update(particles, PARTICLE_COUNT, delta_time);
|
alive_count = 0;
|
||||||
|
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||||
|
thread_params->time = delta_time;
|
||||||
|
thread_params->run = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
glClearColor(0.03f, 0.03f, 0.03f, 1.0f);
|
glClearColor(0.03f, 0.03f, 0.03f, 1.0f);
|
||||||
@ -264,7 +317,8 @@ int main() {
|
|||||||
ImGui::Text("Camera: %.2f %.2f %.2f", (double)camera.position.x, (double)camera.position.y, (double)camera.position.z);
|
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);
|
ImGui::Text("Alive particles: %d", alive_count);
|
||||||
if (particles)
|
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::Text("P[0]: %3.2f %3.2f %3.2f, Life: %0.2f", (double)particles[0].position.x, (double)particles[0].position.y, (double)particles[0].position.z,
|
||||||
|
(double)particles[0].life);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Left-click to capture mouse");
|
ImGui::Text("Left-click to capture mouse");
|
||||||
ImGui::Text("ESC = release mouse / quit");
|
ImGui::Text("ESC = release mouse / quit");
|
||||||
|
|||||||
@ -4,7 +4,10 @@ in vec4 vColor;
|
|||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
// Soft circular point — discard corners to make round dots
|
||||||
|
vec2 coord = gl_PointCoord - vec2(0.5);
|
||||||
|
if (dot(coord, coord) > 0.25)
|
||||||
|
discard;
|
||||||
|
|
||||||
FragColor = vColor;
|
FragColor = vColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,26 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
layout(location = 0) in vec3 aPos; // Position
|
layout(location = 0) in vec3 aPos;
|
||||||
layout(location = 1) in vec4 aColor; // Color (RGBA)
|
layout(location = 1) in vec4 aColor;
|
||||||
layout(location = 2) in float aLife; // Life
|
layout(location = 2) in float aLife;
|
||||||
|
layout(location = 4) in float size;
|
||||||
|
|
||||||
out vec4 vColor;
|
out vec4 vColor;
|
||||||
|
|
||||||
|
uniform mat4 uMVP;
|
||||||
|
uniform float uScreenHeight;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float alpha = aLife;
|
float alpha;
|
||||||
if (aLife > 0.0) {
|
if (aLife < 0.0) {
|
||||||
alpha = aLife / 10.0; // Fade mortal particles
|
alpha = 1.0; // immortal — fully opaque
|
||||||
} else if (aLife < 0.0) {
|
} else {
|
||||||
alpha = 1.0; // Eternal particles are fully opaque
|
alpha = clamp(aLife / 2.0, 0.0, 1.0); // fade over last 2 seconds of life
|
||||||
}
|
}
|
||||||
|
|
||||||
vColor = vec4(aColor.rgb, aColor.a * alpha);
|
vColor = vec4(aColor.rgb, aColor.a * alpha);
|
||||||
gl_Position = vec4(aPos, 1.0);
|
vec4 clip_pos = uMVP * vec4(aPos, 1.0);
|
||||||
gl_PointSize = 10.0; // Make them big enough to see!
|
gl_Position = clip_pos;
|
||||||
|
|
||||||
|
float base_size = 0.2 + size;
|
||||||
|
gl_PointSize = max(1.0, base_size / clip_pos.w);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user