1b877906bSopenharmony_ci//======================================================================== 2b877906bSopenharmony_ci// A simple particle engine with threaded physics 3b877906bSopenharmony_ci// Copyright (c) Marcus Geelnard 4b877906bSopenharmony_ci// Copyright (c) Camilla Löwy <elmindreda@glfw.org> 5b877906bSopenharmony_ci// 6b877906bSopenharmony_ci// This software is provided 'as-is', without any express or implied 7b877906bSopenharmony_ci// warranty. In no event will the authors be held liable for any damages 8b877906bSopenharmony_ci// arising from the use of this software. 9b877906bSopenharmony_ci// 10b877906bSopenharmony_ci// Permission is granted to anyone to use this software for any purpose, 11b877906bSopenharmony_ci// including commercial applications, and to alter it and redistribute it 12b877906bSopenharmony_ci// freely, subject to the following restrictions: 13b877906bSopenharmony_ci// 14b877906bSopenharmony_ci// 1. The origin of this software must not be misrepresented; you must not 15b877906bSopenharmony_ci// claim that you wrote the original software. If you use this software 16b877906bSopenharmony_ci// in a product, an acknowledgment in the product documentation would 17b877906bSopenharmony_ci// be appreciated but is not required. 18b877906bSopenharmony_ci// 19b877906bSopenharmony_ci// 2. Altered source versions must be plainly marked as such, and must not 20b877906bSopenharmony_ci// be misrepresented as being the original software. 21b877906bSopenharmony_ci// 22b877906bSopenharmony_ci// 3. This notice may not be removed or altered from any source 23b877906bSopenharmony_ci// distribution. 24b877906bSopenharmony_ci// 25b877906bSopenharmony_ci//======================================================================== 26b877906bSopenharmony_ci 27b877906bSopenharmony_ci#if defined(_MSC_VER) 28b877906bSopenharmony_ci // Make MS math.h define M_PI 29b877906bSopenharmony_ci #define _USE_MATH_DEFINES 30b877906bSopenharmony_ci#endif 31b877906bSopenharmony_ci 32b877906bSopenharmony_ci#include <stdlib.h> 33b877906bSopenharmony_ci#include <stdio.h> 34b877906bSopenharmony_ci#include <string.h> 35b877906bSopenharmony_ci#include <math.h> 36b877906bSopenharmony_ci#include <time.h> 37b877906bSopenharmony_ci 38b877906bSopenharmony_ci#include <tinycthread.h> 39b877906bSopenharmony_ci#include <getopt.h> 40b877906bSopenharmony_ci#include <linmath.h> 41b877906bSopenharmony_ci 42b877906bSopenharmony_ci#define GLAD_GL_IMPLEMENTATION 43b877906bSopenharmony_ci#include <glad/gl.h> 44b877906bSopenharmony_ci#define GLFW_INCLUDE_NONE 45b877906bSopenharmony_ci#include <GLFW/glfw3.h> 46b877906bSopenharmony_ci 47b877906bSopenharmony_ci// Define tokens for GL_EXT_separate_specular_color if not already defined 48b877906bSopenharmony_ci#ifndef GL_EXT_separate_specular_color 49b877906bSopenharmony_ci#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 50b877906bSopenharmony_ci#define GL_SINGLE_COLOR_EXT 0x81F9 51b877906bSopenharmony_ci#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA 52b877906bSopenharmony_ci#endif // GL_EXT_separate_specular_color 53b877906bSopenharmony_ci 54b877906bSopenharmony_ci 55b877906bSopenharmony_ci//======================================================================== 56b877906bSopenharmony_ci// Type definitions 57b877906bSopenharmony_ci//======================================================================== 58b877906bSopenharmony_ci 59b877906bSopenharmony_citypedef struct 60b877906bSopenharmony_ci{ 61b877906bSopenharmony_ci float x, y, z; 62b877906bSopenharmony_ci} Vec3; 63b877906bSopenharmony_ci 64b877906bSopenharmony_ci// This structure is used for interleaved vertex arrays (see the 65b877906bSopenharmony_ci// draw_particles function) 66b877906bSopenharmony_ci// 67b877906bSopenharmony_ci// NOTE: This structure SHOULD be packed on most systems. It uses 32-bit fields 68b877906bSopenharmony_ci// on 32-bit boundaries, and is a multiple of 64 bits in total (6x32=3x64). If 69b877906bSopenharmony_ci// it does not work, try using pragmas or whatever to force the structure to be 70b877906bSopenharmony_ci// packed. 71b877906bSopenharmony_citypedef struct 72b877906bSopenharmony_ci{ 73b877906bSopenharmony_ci GLfloat s, t; // Texture coordinates 74b877906bSopenharmony_ci GLuint rgba; // Color (four ubytes packed into an uint) 75b877906bSopenharmony_ci GLfloat x, y, z; // Vertex coordinates 76b877906bSopenharmony_ci} Vertex; 77b877906bSopenharmony_ci 78b877906bSopenharmony_ci 79b877906bSopenharmony_ci//======================================================================== 80b877906bSopenharmony_ci// Program control global variables 81b877906bSopenharmony_ci//======================================================================== 82b877906bSopenharmony_ci 83b877906bSopenharmony_ci// Window dimensions 84b877906bSopenharmony_cifloat aspect_ratio; 85b877906bSopenharmony_ci 86b877906bSopenharmony_ci// "wireframe" flag (true if we use wireframe view) 87b877906bSopenharmony_ciint wireframe; 88b877906bSopenharmony_ci 89b877906bSopenharmony_ci// Thread synchronization 90b877906bSopenharmony_cistruct { 91b877906bSopenharmony_ci double t; // Time (s) 92b877906bSopenharmony_ci float dt; // Time since last frame (s) 93b877906bSopenharmony_ci int p_frame; // Particle physics frame number 94b877906bSopenharmony_ci int d_frame; // Particle draw frame number 95b877906bSopenharmony_ci cnd_t p_done; // Condition: particle physics done 96b877906bSopenharmony_ci cnd_t d_done; // Condition: particle draw done 97b877906bSopenharmony_ci mtx_t particles_lock; // Particles data sharing mutex 98b877906bSopenharmony_ci} thread_sync; 99b877906bSopenharmony_ci 100b877906bSopenharmony_ci 101b877906bSopenharmony_ci//======================================================================== 102b877906bSopenharmony_ci// Texture declarations (we hard-code them into the source code, since 103b877906bSopenharmony_ci// they are so simple) 104b877906bSopenharmony_ci//======================================================================== 105b877906bSopenharmony_ci 106b877906bSopenharmony_ci#define P_TEX_WIDTH 8 // Particle texture dimensions 107b877906bSopenharmony_ci#define P_TEX_HEIGHT 8 108b877906bSopenharmony_ci#define F_TEX_WIDTH 16 // Floor texture dimensions 109b877906bSopenharmony_ci#define F_TEX_HEIGHT 16 110b877906bSopenharmony_ci 111b877906bSopenharmony_ci// Texture object IDs 112b877906bSopenharmony_ciGLuint particle_tex_id, floor_tex_id; 113b877906bSopenharmony_ci 114b877906bSopenharmony_ci// Particle texture (a simple spot) 115b877906bSopenharmony_ciconst unsigned char particle_texture[ P_TEX_WIDTH * P_TEX_HEIGHT ] = { 116b877906bSopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 117b877906bSopenharmony_ci 0x00, 0x00, 0x11, 0x22, 0x22, 0x11, 0x00, 0x00, 118b877906bSopenharmony_ci 0x00, 0x11, 0x33, 0x88, 0x77, 0x33, 0x11, 0x00, 119b877906bSopenharmony_ci 0x00, 0x22, 0x88, 0xff, 0xee, 0x77, 0x22, 0x00, 120b877906bSopenharmony_ci 0x00, 0x22, 0x77, 0xee, 0xff, 0x88, 0x22, 0x00, 121b877906bSopenharmony_ci 0x00, 0x11, 0x33, 0x77, 0x88, 0x33, 0x11, 0x00, 122b877906bSopenharmony_ci 0x00, 0x00, 0x11, 0x33, 0x22, 0x11, 0x00, 0x00, 123b877906bSopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 124b877906bSopenharmony_ci}; 125b877906bSopenharmony_ci 126b877906bSopenharmony_ci// Floor texture (your basic checkered floor) 127b877906bSopenharmony_ciconst unsigned char floor_texture[ F_TEX_WIDTH * F_TEX_HEIGHT ] = { 128b877906bSopenharmony_ci 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 129b877906bSopenharmony_ci 0xff, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 130b877906bSopenharmony_ci 0xf0, 0xcc, 0xee, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x66, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 131b877906bSopenharmony_ci 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xee, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 132b877906bSopenharmony_ci 0xf0, 0xf0, 0xf0, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x55, 0x30, 0x30, 0x44, 0x30, 0x30, 133b877906bSopenharmony_ci 0xf0, 0xdd, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 134b877906bSopenharmony_ci 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x30, 135b877906bSopenharmony_ci 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 136b877906bSopenharmony_ci 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 137b877906bSopenharmony_ci 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0xf0, 0xff, 0xf0, 0xf0, 0xdd, 0xf0, 0xf0, 0xff, 138b877906bSopenharmony_ci 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x55, 0x33, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, 139b877906bSopenharmony_ci 0x30, 0x44, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 140b877906bSopenharmony_ci 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xaa, 0xf0, 0xf0, 0xcc, 0xf0, 141b877906bSopenharmony_ci 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xff, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xdd, 0xf0, 142b877906bSopenharmony_ci 0x30, 0x30, 0x30, 0x77, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 143b877906bSopenharmony_ci 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 144b877906bSopenharmony_ci}; 145b877906bSopenharmony_ci 146b877906bSopenharmony_ci 147b877906bSopenharmony_ci//======================================================================== 148b877906bSopenharmony_ci// These are fixed constants that control the particle engine. In a 149b877906bSopenharmony_ci// modular world, these values should be variables... 150b877906bSopenharmony_ci//======================================================================== 151b877906bSopenharmony_ci 152b877906bSopenharmony_ci// Maximum number of particles 153b877906bSopenharmony_ci#define MAX_PARTICLES 3000 154b877906bSopenharmony_ci 155b877906bSopenharmony_ci// Life span of a particle (in seconds) 156b877906bSopenharmony_ci#define LIFE_SPAN 8.f 157b877906bSopenharmony_ci 158b877906bSopenharmony_ci// A new particle is born every [BIRTH_INTERVAL] second 159b877906bSopenharmony_ci#define BIRTH_INTERVAL (LIFE_SPAN/(float)MAX_PARTICLES) 160b877906bSopenharmony_ci 161b877906bSopenharmony_ci// Particle size (meters) 162b877906bSopenharmony_ci#define PARTICLE_SIZE 0.7f 163b877906bSopenharmony_ci 164b877906bSopenharmony_ci// Gravitational constant (m/s^2) 165b877906bSopenharmony_ci#define GRAVITY 9.8f 166b877906bSopenharmony_ci 167b877906bSopenharmony_ci// Base initial velocity (m/s) 168b877906bSopenharmony_ci#define VELOCITY 8.f 169b877906bSopenharmony_ci 170b877906bSopenharmony_ci// Bounce friction (1.0 = no friction, 0.0 = maximum friction) 171b877906bSopenharmony_ci#define FRICTION 0.75f 172b877906bSopenharmony_ci 173b877906bSopenharmony_ci// "Fountain" height (m) 174b877906bSopenharmony_ci#define FOUNTAIN_HEIGHT 3.f 175b877906bSopenharmony_ci 176b877906bSopenharmony_ci// Fountain radius (m) 177b877906bSopenharmony_ci#define FOUNTAIN_RADIUS 1.6f 178b877906bSopenharmony_ci 179b877906bSopenharmony_ci// Minimum delta-time for particle phisics (s) 180b877906bSopenharmony_ci#define MIN_DELTA_T (BIRTH_INTERVAL * 0.5f) 181b877906bSopenharmony_ci 182b877906bSopenharmony_ci 183b877906bSopenharmony_ci//======================================================================== 184b877906bSopenharmony_ci// Particle system global variables 185b877906bSopenharmony_ci//======================================================================== 186b877906bSopenharmony_ci 187b877906bSopenharmony_ci// This structure holds all state for a single particle 188b877906bSopenharmony_citypedef struct { 189b877906bSopenharmony_ci float x,y,z; // Position in space 190b877906bSopenharmony_ci float vx,vy,vz; // Velocity vector 191b877906bSopenharmony_ci float r,g,b; // Color of particle 192b877906bSopenharmony_ci float life; // Life of particle (1.0 = newborn, < 0.0 = dead) 193b877906bSopenharmony_ci int active; // Tells if this particle is active 194b877906bSopenharmony_ci} PARTICLE; 195b877906bSopenharmony_ci 196b877906bSopenharmony_ci// Global vectors holding all particles. We use two vectors for double 197b877906bSopenharmony_ci// buffering. 198b877906bSopenharmony_cistatic PARTICLE particles[MAX_PARTICLES]; 199b877906bSopenharmony_ci 200b877906bSopenharmony_ci// Global variable holding the age of the youngest particle 201b877906bSopenharmony_cistatic float min_age; 202b877906bSopenharmony_ci 203b877906bSopenharmony_ci// Color of latest born particle (used for fountain lighting) 204b877906bSopenharmony_cistatic float glow_color[4]; 205b877906bSopenharmony_ci 206b877906bSopenharmony_ci// Position of latest born particle (used for fountain lighting) 207b877906bSopenharmony_cistatic float glow_pos[4]; 208b877906bSopenharmony_ci 209b877906bSopenharmony_ci 210b877906bSopenharmony_ci//======================================================================== 211b877906bSopenharmony_ci// Object material and fog configuration constants 212b877906bSopenharmony_ci//======================================================================== 213b877906bSopenharmony_ci 214b877906bSopenharmony_ciconst GLfloat fountain_diffuse[4] = { 0.7f, 1.f, 1.f, 1.f }; 215b877906bSopenharmony_ciconst GLfloat fountain_specular[4] = { 1.f, 1.f, 1.f, 1.f }; 216b877906bSopenharmony_ciconst GLfloat fountain_shininess = 12.f; 217b877906bSopenharmony_ciconst GLfloat floor_diffuse[4] = { 1.f, 0.6f, 0.6f, 1.f }; 218b877906bSopenharmony_ciconst GLfloat floor_specular[4] = { 0.6f, 0.6f, 0.6f, 1.f }; 219b877906bSopenharmony_ciconst GLfloat floor_shininess = 18.f; 220b877906bSopenharmony_ciconst GLfloat fog_color[4] = { 0.1f, 0.1f, 0.1f, 1.f }; 221b877906bSopenharmony_ci 222b877906bSopenharmony_ci 223b877906bSopenharmony_ci//======================================================================== 224b877906bSopenharmony_ci// Print usage information 225b877906bSopenharmony_ci//======================================================================== 226b877906bSopenharmony_ci 227b877906bSopenharmony_cistatic void usage(void) 228b877906bSopenharmony_ci{ 229b877906bSopenharmony_ci printf("Usage: particles [-bfhs]\n"); 230b877906bSopenharmony_ci printf("Options:\n"); 231b877906bSopenharmony_ci printf(" -f Run in full screen\n"); 232b877906bSopenharmony_ci printf(" -h Display this help\n"); 233b877906bSopenharmony_ci printf(" -s Run program as single thread (default is to use two threads)\n"); 234b877906bSopenharmony_ci printf("\n"); 235b877906bSopenharmony_ci printf("Program runtime controls:\n"); 236b877906bSopenharmony_ci printf(" W Toggle wireframe mode\n"); 237b877906bSopenharmony_ci printf(" Esc Exit program\n"); 238b877906bSopenharmony_ci} 239b877906bSopenharmony_ci 240b877906bSopenharmony_ci 241b877906bSopenharmony_ci//======================================================================== 242b877906bSopenharmony_ci// Initialize a new particle 243b877906bSopenharmony_ci//======================================================================== 244b877906bSopenharmony_ci 245b877906bSopenharmony_cistatic void init_particle(PARTICLE *p, double t) 246b877906bSopenharmony_ci{ 247b877906bSopenharmony_ci float xy_angle, velocity; 248b877906bSopenharmony_ci 249b877906bSopenharmony_ci // Start position of particle is at the fountain blow-out 250b877906bSopenharmony_ci p->x = 0.f; 251b877906bSopenharmony_ci p->y = 0.f; 252b877906bSopenharmony_ci p->z = FOUNTAIN_HEIGHT; 253b877906bSopenharmony_ci 254b877906bSopenharmony_ci // Start velocity is up (Z)... 255b877906bSopenharmony_ci p->vz = 0.7f + (0.3f / 4096.f) * (float) (rand() & 4095); 256b877906bSopenharmony_ci 257b877906bSopenharmony_ci // ...and a randomly chosen X/Y direction 258b877906bSopenharmony_ci xy_angle = (2.f * (float) M_PI / 4096.f) * (float) (rand() & 4095); 259b877906bSopenharmony_ci p->vx = 0.4f * (float) cos(xy_angle); 260b877906bSopenharmony_ci p->vy = 0.4f * (float) sin(xy_angle); 261b877906bSopenharmony_ci 262b877906bSopenharmony_ci // Scale velocity vector according to a time-varying velocity 263b877906bSopenharmony_ci velocity = VELOCITY * (0.8f + 0.1f * (float) (sin(0.5 * t) + sin(1.31 * t))); 264b877906bSopenharmony_ci p->vx *= velocity; 265b877906bSopenharmony_ci p->vy *= velocity; 266b877906bSopenharmony_ci p->vz *= velocity; 267b877906bSopenharmony_ci 268b877906bSopenharmony_ci // Color is time-varying 269b877906bSopenharmony_ci p->r = 0.7f + 0.3f * (float) sin(0.34 * t + 0.1); 270b877906bSopenharmony_ci p->g = 0.6f + 0.4f * (float) sin(0.63 * t + 1.1); 271b877906bSopenharmony_ci p->b = 0.6f + 0.4f * (float) sin(0.91 * t + 2.1); 272b877906bSopenharmony_ci 273b877906bSopenharmony_ci // Store settings for fountain glow lighting 274b877906bSopenharmony_ci glow_pos[0] = 0.4f * (float) sin(1.34 * t); 275b877906bSopenharmony_ci glow_pos[1] = 0.4f * (float) sin(3.11 * t); 276b877906bSopenharmony_ci glow_pos[2] = FOUNTAIN_HEIGHT + 1.f; 277b877906bSopenharmony_ci glow_pos[3] = 1.f; 278b877906bSopenharmony_ci glow_color[0] = p->r; 279b877906bSopenharmony_ci glow_color[1] = p->g; 280b877906bSopenharmony_ci glow_color[2] = p->b; 281b877906bSopenharmony_ci glow_color[3] = 1.f; 282b877906bSopenharmony_ci 283b877906bSopenharmony_ci // The particle is new-born and active 284b877906bSopenharmony_ci p->life = 1.f; 285b877906bSopenharmony_ci p->active = 1; 286b877906bSopenharmony_ci} 287b877906bSopenharmony_ci 288b877906bSopenharmony_ci 289b877906bSopenharmony_ci//======================================================================== 290b877906bSopenharmony_ci// Update a particle 291b877906bSopenharmony_ci//======================================================================== 292b877906bSopenharmony_ci 293b877906bSopenharmony_ci#define FOUNTAIN_R2 (FOUNTAIN_RADIUS+PARTICLE_SIZE/2)*(FOUNTAIN_RADIUS+PARTICLE_SIZE/2) 294b877906bSopenharmony_ci 295b877906bSopenharmony_cistatic void update_particle(PARTICLE *p, float dt) 296b877906bSopenharmony_ci{ 297b877906bSopenharmony_ci // If the particle is not active, we need not do anything 298b877906bSopenharmony_ci if (!p->active) 299b877906bSopenharmony_ci return; 300b877906bSopenharmony_ci 301b877906bSopenharmony_ci // The particle is getting older... 302b877906bSopenharmony_ci p->life -= dt * (1.f / LIFE_SPAN); 303b877906bSopenharmony_ci 304b877906bSopenharmony_ci // Did the particle die? 305b877906bSopenharmony_ci if (p->life <= 0.f) 306b877906bSopenharmony_ci { 307b877906bSopenharmony_ci p->active = 0; 308b877906bSopenharmony_ci return; 309b877906bSopenharmony_ci } 310b877906bSopenharmony_ci 311b877906bSopenharmony_ci // Apply gravity 312b877906bSopenharmony_ci p->vz = p->vz - GRAVITY * dt; 313b877906bSopenharmony_ci 314b877906bSopenharmony_ci // Update particle position 315b877906bSopenharmony_ci p->x = p->x + p->vx * dt; 316b877906bSopenharmony_ci p->y = p->y + p->vy * dt; 317b877906bSopenharmony_ci p->z = p->z + p->vz * dt; 318b877906bSopenharmony_ci 319b877906bSopenharmony_ci // Simple collision detection + response 320b877906bSopenharmony_ci if (p->vz < 0.f) 321b877906bSopenharmony_ci { 322b877906bSopenharmony_ci // Particles should bounce on the fountain (with friction) 323b877906bSopenharmony_ci if ((p->x * p->x + p->y * p->y) < FOUNTAIN_R2 && 324b877906bSopenharmony_ci p->z < (FOUNTAIN_HEIGHT + PARTICLE_SIZE / 2)) 325b877906bSopenharmony_ci { 326b877906bSopenharmony_ci p->vz = -FRICTION * p->vz; 327b877906bSopenharmony_ci p->z = FOUNTAIN_HEIGHT + PARTICLE_SIZE / 2 + 328b877906bSopenharmony_ci FRICTION * (FOUNTAIN_HEIGHT + 329b877906bSopenharmony_ci PARTICLE_SIZE / 2 - p->z); 330b877906bSopenharmony_ci } 331b877906bSopenharmony_ci 332b877906bSopenharmony_ci // Particles should bounce on the floor (with friction) 333b877906bSopenharmony_ci else if (p->z < PARTICLE_SIZE / 2) 334b877906bSopenharmony_ci { 335b877906bSopenharmony_ci p->vz = -FRICTION * p->vz; 336b877906bSopenharmony_ci p->z = PARTICLE_SIZE / 2 + 337b877906bSopenharmony_ci FRICTION * (PARTICLE_SIZE / 2 - p->z); 338b877906bSopenharmony_ci } 339b877906bSopenharmony_ci } 340b877906bSopenharmony_ci} 341b877906bSopenharmony_ci 342b877906bSopenharmony_ci 343b877906bSopenharmony_ci//======================================================================== 344b877906bSopenharmony_ci// The main frame for the particle engine. Called once per frame. 345b877906bSopenharmony_ci//======================================================================== 346b877906bSopenharmony_ci 347b877906bSopenharmony_cistatic void particle_engine(double t, float dt) 348b877906bSopenharmony_ci{ 349b877906bSopenharmony_ci int i; 350b877906bSopenharmony_ci float dt2; 351b877906bSopenharmony_ci 352b877906bSopenharmony_ci // Update particles (iterated several times per frame if dt is too large) 353b877906bSopenharmony_ci while (dt > 0.f) 354b877906bSopenharmony_ci { 355b877906bSopenharmony_ci // Calculate delta time for this iteration 356b877906bSopenharmony_ci dt2 = dt < MIN_DELTA_T ? dt : MIN_DELTA_T; 357b877906bSopenharmony_ci 358b877906bSopenharmony_ci for (i = 0; i < MAX_PARTICLES; i++) 359b877906bSopenharmony_ci update_particle(&particles[i], dt2); 360b877906bSopenharmony_ci 361b877906bSopenharmony_ci min_age += dt2; 362b877906bSopenharmony_ci 363b877906bSopenharmony_ci // Should we create any new particle(s)? 364b877906bSopenharmony_ci while (min_age >= BIRTH_INTERVAL) 365b877906bSopenharmony_ci { 366b877906bSopenharmony_ci min_age -= BIRTH_INTERVAL; 367b877906bSopenharmony_ci 368b877906bSopenharmony_ci // Find a dead particle to replace with a new one 369b877906bSopenharmony_ci for (i = 0; i < MAX_PARTICLES; i++) 370b877906bSopenharmony_ci { 371b877906bSopenharmony_ci if (!particles[i].active) 372b877906bSopenharmony_ci { 373b877906bSopenharmony_ci init_particle(&particles[i], t + min_age); 374b877906bSopenharmony_ci update_particle(&particles[i], min_age); 375b877906bSopenharmony_ci break; 376b877906bSopenharmony_ci } 377b877906bSopenharmony_ci } 378b877906bSopenharmony_ci } 379b877906bSopenharmony_ci 380b877906bSopenharmony_ci dt -= dt2; 381b877906bSopenharmony_ci } 382b877906bSopenharmony_ci} 383b877906bSopenharmony_ci 384b877906bSopenharmony_ci 385b877906bSopenharmony_ci//======================================================================== 386b877906bSopenharmony_ci// Draw all active particles. We use OpenGL 1.1 vertex 387b877906bSopenharmony_ci// arrays for this in order to accelerate the drawing. 388b877906bSopenharmony_ci//======================================================================== 389b877906bSopenharmony_ci 390b877906bSopenharmony_ci#define BATCH_PARTICLES 70 // Number of particles to draw in each batch 391b877906bSopenharmony_ci // (70 corresponds to 7.5 KB = will not blow 392b877906bSopenharmony_ci // the L1 data cache on most CPUs) 393b877906bSopenharmony_ci#define PARTICLE_VERTS 4 // Number of vertices per particle 394b877906bSopenharmony_ci 395b877906bSopenharmony_cistatic void draw_particles(GLFWwindow* window, double t, float dt) 396b877906bSopenharmony_ci{ 397b877906bSopenharmony_ci int i, particle_count; 398b877906bSopenharmony_ci Vertex vertex_array[BATCH_PARTICLES * PARTICLE_VERTS]; 399b877906bSopenharmony_ci Vertex* vptr; 400b877906bSopenharmony_ci float alpha; 401b877906bSopenharmony_ci GLuint rgba; 402b877906bSopenharmony_ci Vec3 quad_lower_left, quad_lower_right; 403b877906bSopenharmony_ci GLfloat mat[16]; 404b877906bSopenharmony_ci PARTICLE* pptr; 405b877906bSopenharmony_ci 406b877906bSopenharmony_ci // Here comes the real trick with flat single primitive objects (s.c. 407b877906bSopenharmony_ci // "billboards"): We must rotate the textured primitive so that it 408b877906bSopenharmony_ci // always faces the viewer (is coplanar with the view-plane). 409b877906bSopenharmony_ci // We: 410b877906bSopenharmony_ci // 1) Create the primitive around origo (0,0,0) 411b877906bSopenharmony_ci // 2) Rotate it so that it is coplanar with the view plane 412b877906bSopenharmony_ci // 3) Translate it according to the particle position 413b877906bSopenharmony_ci // Note that 1) and 2) is the same for all particles (done only once). 414b877906bSopenharmony_ci 415b877906bSopenharmony_ci // Get modelview matrix. We will only use the upper left 3x3 part of 416b877906bSopenharmony_ci // the matrix, which represents the rotation. 417b877906bSopenharmony_ci glGetFloatv(GL_MODELVIEW_MATRIX, mat); 418b877906bSopenharmony_ci 419b877906bSopenharmony_ci // 1) & 2) We do it in one swift step: 420b877906bSopenharmony_ci // Although not obvious, the following six lines represent two matrix/ 421b877906bSopenharmony_ci // vector multiplications. The matrix is the inverse 3x3 rotation 422b877906bSopenharmony_ci // matrix (i.e. the transpose of the same matrix), and the two vectors 423b877906bSopenharmony_ci // represent the lower left corner of the quad, PARTICLE_SIZE/2 * 424b877906bSopenharmony_ci // (-1,-1,0), and the lower right corner, PARTICLE_SIZE/2 * (1,-1,0). 425b877906bSopenharmony_ci // The upper left/right corners of the quad is always the negative of 426b877906bSopenharmony_ci // the opposite corners (regardless of rotation). 427b877906bSopenharmony_ci quad_lower_left.x = (-PARTICLE_SIZE / 2) * (mat[0] + mat[1]); 428b877906bSopenharmony_ci quad_lower_left.y = (-PARTICLE_SIZE / 2) * (mat[4] + mat[5]); 429b877906bSopenharmony_ci quad_lower_left.z = (-PARTICLE_SIZE / 2) * (mat[8] + mat[9]); 430b877906bSopenharmony_ci quad_lower_right.x = (PARTICLE_SIZE / 2) * (mat[0] - mat[1]); 431b877906bSopenharmony_ci quad_lower_right.y = (PARTICLE_SIZE / 2) * (mat[4] - mat[5]); 432b877906bSopenharmony_ci quad_lower_right.z = (PARTICLE_SIZE / 2) * (mat[8] - mat[9]); 433b877906bSopenharmony_ci 434b877906bSopenharmony_ci // Don't update z-buffer, since all particles are transparent! 435b877906bSopenharmony_ci glDepthMask(GL_FALSE); 436b877906bSopenharmony_ci 437b877906bSopenharmony_ci glEnable(GL_BLEND); 438b877906bSopenharmony_ci glBlendFunc(GL_SRC_ALPHA, GL_ONE); 439b877906bSopenharmony_ci 440b877906bSopenharmony_ci // Select particle texture 441b877906bSopenharmony_ci if (!wireframe) 442b877906bSopenharmony_ci { 443b877906bSopenharmony_ci glEnable(GL_TEXTURE_2D); 444b877906bSopenharmony_ci glBindTexture(GL_TEXTURE_2D, particle_tex_id); 445b877906bSopenharmony_ci } 446b877906bSopenharmony_ci 447b877906bSopenharmony_ci // Set up vertex arrays. We use interleaved arrays, which is easier to 448b877906bSopenharmony_ci // handle (in most situations) and it gives a linear memory access 449b877906bSopenharmony_ci // access pattern (which may give better performance in some 450b877906bSopenharmony_ci // situations). GL_T2F_C4UB_V3F means: 2 floats for texture coords, 451b877906bSopenharmony_ci // 4 ubytes for color and 3 floats for vertex coord (in that order). 452b877906bSopenharmony_ci // Most OpenGL cards / drivers are optimized for this format. 453b877906bSopenharmony_ci glInterleavedArrays(GL_T2F_C4UB_V3F, 0, vertex_array); 454b877906bSopenharmony_ci 455b877906bSopenharmony_ci // Wait for particle physics thread to be done 456b877906bSopenharmony_ci mtx_lock(&thread_sync.particles_lock); 457b877906bSopenharmony_ci while (!glfwWindowShouldClose(window) && 458b877906bSopenharmony_ci thread_sync.p_frame <= thread_sync.d_frame) 459b877906bSopenharmony_ci { 460b877906bSopenharmony_ci struct timespec ts; 461b877906bSopenharmony_ci clock_gettime(CLOCK_REALTIME, &ts); 462b877906bSopenharmony_ci ts.tv_nsec += 100 * 1000 * 1000; 463b877906bSopenharmony_ci ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); 464b877906bSopenharmony_ci ts.tv_nsec %= 1000 * 1000 * 1000; 465b877906bSopenharmony_ci cnd_timedwait(&thread_sync.p_done, &thread_sync.particles_lock, &ts); 466b877906bSopenharmony_ci } 467b877906bSopenharmony_ci 468b877906bSopenharmony_ci // Store the frame time and delta time for the physics thread 469b877906bSopenharmony_ci thread_sync.t = t; 470b877906bSopenharmony_ci thread_sync.dt = dt; 471b877906bSopenharmony_ci 472b877906bSopenharmony_ci // Update frame counter 473b877906bSopenharmony_ci thread_sync.d_frame++; 474b877906bSopenharmony_ci 475b877906bSopenharmony_ci // Loop through all particles and build vertex arrays. 476b877906bSopenharmony_ci particle_count = 0; 477b877906bSopenharmony_ci vptr = vertex_array; 478b877906bSopenharmony_ci pptr = particles; 479b877906bSopenharmony_ci 480b877906bSopenharmony_ci for (i = 0; i < MAX_PARTICLES; i++) 481b877906bSopenharmony_ci { 482b877906bSopenharmony_ci if (pptr->active) 483b877906bSopenharmony_ci { 484b877906bSopenharmony_ci // Calculate particle intensity (we set it to max during 75% 485b877906bSopenharmony_ci // of its life, then it fades out) 486b877906bSopenharmony_ci alpha = 4.f * pptr->life; 487b877906bSopenharmony_ci if (alpha > 1.f) 488b877906bSopenharmony_ci alpha = 1.f; 489b877906bSopenharmony_ci 490b877906bSopenharmony_ci // Convert color from float to 8-bit (store it in a 32-bit 491b877906bSopenharmony_ci // integer using endian independent type casting) 492b877906bSopenharmony_ci ((GLubyte*) &rgba)[0] = (GLubyte)(pptr->r * 255.f); 493b877906bSopenharmony_ci ((GLubyte*) &rgba)[1] = (GLubyte)(pptr->g * 255.f); 494b877906bSopenharmony_ci ((GLubyte*) &rgba)[2] = (GLubyte)(pptr->b * 255.f); 495b877906bSopenharmony_ci ((GLubyte*) &rgba)[3] = (GLubyte)(alpha * 255.f); 496b877906bSopenharmony_ci 497b877906bSopenharmony_ci // 3) Translate the quad to the correct position in modelview 498b877906bSopenharmony_ci // space and store its parameters in vertex arrays (we also 499b877906bSopenharmony_ci // store texture coord and color information for each vertex). 500b877906bSopenharmony_ci 501b877906bSopenharmony_ci // Lower left corner 502b877906bSopenharmony_ci vptr->s = 0.f; 503b877906bSopenharmony_ci vptr->t = 0.f; 504b877906bSopenharmony_ci vptr->rgba = rgba; 505b877906bSopenharmony_ci vptr->x = pptr->x + quad_lower_left.x; 506b877906bSopenharmony_ci vptr->y = pptr->y + quad_lower_left.y; 507b877906bSopenharmony_ci vptr->z = pptr->z + quad_lower_left.z; 508b877906bSopenharmony_ci vptr ++; 509b877906bSopenharmony_ci 510b877906bSopenharmony_ci // Lower right corner 511b877906bSopenharmony_ci vptr->s = 1.f; 512b877906bSopenharmony_ci vptr->t = 0.f; 513b877906bSopenharmony_ci vptr->rgba = rgba; 514b877906bSopenharmony_ci vptr->x = pptr->x + quad_lower_right.x; 515b877906bSopenharmony_ci vptr->y = pptr->y + quad_lower_right.y; 516b877906bSopenharmony_ci vptr->z = pptr->z + quad_lower_right.z; 517b877906bSopenharmony_ci vptr ++; 518b877906bSopenharmony_ci 519b877906bSopenharmony_ci // Upper right corner 520b877906bSopenharmony_ci vptr->s = 1.f; 521b877906bSopenharmony_ci vptr->t = 1.f; 522b877906bSopenharmony_ci vptr->rgba = rgba; 523b877906bSopenharmony_ci vptr->x = pptr->x - quad_lower_left.x; 524b877906bSopenharmony_ci vptr->y = pptr->y - quad_lower_left.y; 525b877906bSopenharmony_ci vptr->z = pptr->z - quad_lower_left.z; 526b877906bSopenharmony_ci vptr ++; 527b877906bSopenharmony_ci 528b877906bSopenharmony_ci // Upper left corner 529b877906bSopenharmony_ci vptr->s = 0.f; 530b877906bSopenharmony_ci vptr->t = 1.f; 531b877906bSopenharmony_ci vptr->rgba = rgba; 532b877906bSopenharmony_ci vptr->x = pptr->x - quad_lower_right.x; 533b877906bSopenharmony_ci vptr->y = pptr->y - quad_lower_right.y; 534b877906bSopenharmony_ci vptr->z = pptr->z - quad_lower_right.z; 535b877906bSopenharmony_ci vptr ++; 536b877906bSopenharmony_ci 537b877906bSopenharmony_ci // Increase count of drawable particles 538b877906bSopenharmony_ci particle_count ++; 539b877906bSopenharmony_ci } 540b877906bSopenharmony_ci 541b877906bSopenharmony_ci // If we have filled up one batch of particles, draw it as a set 542b877906bSopenharmony_ci // of quads using glDrawArrays. 543b877906bSopenharmony_ci if (particle_count >= BATCH_PARTICLES) 544b877906bSopenharmony_ci { 545b877906bSopenharmony_ci // The first argument tells which primitive type we use (QUAD) 546b877906bSopenharmony_ci // The second argument tells the index of the first vertex (0) 547b877906bSopenharmony_ci // The last argument is the vertex count 548b877906bSopenharmony_ci glDrawArrays(GL_QUADS, 0, PARTICLE_VERTS * particle_count); 549b877906bSopenharmony_ci particle_count = 0; 550b877906bSopenharmony_ci vptr = vertex_array; 551b877906bSopenharmony_ci } 552b877906bSopenharmony_ci 553b877906bSopenharmony_ci // Next particle 554b877906bSopenharmony_ci pptr++; 555b877906bSopenharmony_ci } 556b877906bSopenharmony_ci 557b877906bSopenharmony_ci // We are done with the particle data 558b877906bSopenharmony_ci mtx_unlock(&thread_sync.particles_lock); 559b877906bSopenharmony_ci cnd_signal(&thread_sync.d_done); 560b877906bSopenharmony_ci 561b877906bSopenharmony_ci // Draw final batch of particles (if any) 562b877906bSopenharmony_ci glDrawArrays(GL_QUADS, 0, PARTICLE_VERTS * particle_count); 563b877906bSopenharmony_ci 564b877906bSopenharmony_ci // Disable vertex arrays (Note: glInterleavedArrays implicitly called 565b877906bSopenharmony_ci // glEnableClientState for vertex, texture coord and color arrays) 566b877906bSopenharmony_ci glDisableClientState(GL_VERTEX_ARRAY); 567b877906bSopenharmony_ci glDisableClientState(GL_TEXTURE_COORD_ARRAY); 568b877906bSopenharmony_ci glDisableClientState(GL_COLOR_ARRAY); 569b877906bSopenharmony_ci 570b877906bSopenharmony_ci glDisable(GL_TEXTURE_2D); 571b877906bSopenharmony_ci glDisable(GL_BLEND); 572b877906bSopenharmony_ci 573b877906bSopenharmony_ci glDepthMask(GL_TRUE); 574b877906bSopenharmony_ci} 575b877906bSopenharmony_ci 576b877906bSopenharmony_ci 577b877906bSopenharmony_ci//======================================================================== 578b877906bSopenharmony_ci// Fountain geometry specification 579b877906bSopenharmony_ci//======================================================================== 580b877906bSopenharmony_ci 581b877906bSopenharmony_ci#define FOUNTAIN_SIDE_POINTS 14 582b877906bSopenharmony_ci#define FOUNTAIN_SWEEP_STEPS 32 583b877906bSopenharmony_ci 584b877906bSopenharmony_cistatic const float fountain_side[FOUNTAIN_SIDE_POINTS * 2] = 585b877906bSopenharmony_ci{ 586b877906bSopenharmony_ci 1.2f, 0.f, 1.f, 0.2f, 0.41f, 0.3f, 0.4f, 0.35f, 587b877906bSopenharmony_ci 0.4f, 1.95f, 0.41f, 2.f, 0.8f, 2.2f, 1.2f, 2.4f, 588b877906bSopenharmony_ci 1.5f, 2.7f, 1.55f,2.95f, 1.6f, 3.f, 1.f, 3.f, 589b877906bSopenharmony_ci 0.5f, 3.f, 0.f, 3.f 590b877906bSopenharmony_ci}; 591b877906bSopenharmony_ci 592b877906bSopenharmony_cistatic const float fountain_normal[FOUNTAIN_SIDE_POINTS * 2] = 593b877906bSopenharmony_ci{ 594b877906bSopenharmony_ci 1.0000f, 0.0000f, 0.6428f, 0.7660f, 0.3420f, 0.9397f, 1.0000f, 0.0000f, 595b877906bSopenharmony_ci 1.0000f, 0.0000f, 0.3420f,-0.9397f, 0.4226f,-0.9063f, 0.5000f,-0.8660f, 596b877906bSopenharmony_ci 0.7660f,-0.6428f, 0.9063f,-0.4226f, 0.0000f,1.00000f, 0.0000f,1.00000f, 597b877906bSopenharmony_ci 0.0000f,1.00000f, 0.0000f,1.00000f 598b877906bSopenharmony_ci}; 599b877906bSopenharmony_ci 600b877906bSopenharmony_ci 601b877906bSopenharmony_ci//======================================================================== 602b877906bSopenharmony_ci// Draw a fountain 603b877906bSopenharmony_ci//======================================================================== 604b877906bSopenharmony_ci 605b877906bSopenharmony_cistatic void draw_fountain(void) 606b877906bSopenharmony_ci{ 607b877906bSopenharmony_ci static GLuint fountain_list = 0; 608b877906bSopenharmony_ci double angle; 609b877906bSopenharmony_ci float x, y; 610b877906bSopenharmony_ci int m, n; 611b877906bSopenharmony_ci 612b877906bSopenharmony_ci // The first time, we build the fountain display list 613b877906bSopenharmony_ci if (!fountain_list) 614b877906bSopenharmony_ci { 615b877906bSopenharmony_ci fountain_list = glGenLists(1); 616b877906bSopenharmony_ci glNewList(fountain_list, GL_COMPILE_AND_EXECUTE); 617b877906bSopenharmony_ci 618b877906bSopenharmony_ci glMaterialfv(GL_FRONT, GL_DIFFUSE, fountain_diffuse); 619b877906bSopenharmony_ci glMaterialfv(GL_FRONT, GL_SPECULAR, fountain_specular); 620b877906bSopenharmony_ci glMaterialf(GL_FRONT, GL_SHININESS, fountain_shininess); 621b877906bSopenharmony_ci 622b877906bSopenharmony_ci // Build fountain using triangle strips 623b877906bSopenharmony_ci for (n = 0; n < FOUNTAIN_SIDE_POINTS - 1; n++) 624b877906bSopenharmony_ci { 625b877906bSopenharmony_ci glBegin(GL_TRIANGLE_STRIP); 626b877906bSopenharmony_ci for (m = 0; m <= FOUNTAIN_SWEEP_STEPS; m++) 627b877906bSopenharmony_ci { 628b877906bSopenharmony_ci angle = (double) m * (2.0 * M_PI / (double) FOUNTAIN_SWEEP_STEPS); 629b877906bSopenharmony_ci x = (float) cos(angle); 630b877906bSopenharmony_ci y = (float) sin(angle); 631b877906bSopenharmony_ci 632b877906bSopenharmony_ci // Draw triangle strip 633b877906bSopenharmony_ci glNormal3f(x * fountain_normal[n * 2 + 2], 634b877906bSopenharmony_ci y * fountain_normal[n * 2 + 2], 635b877906bSopenharmony_ci fountain_normal[n * 2 + 3]); 636b877906bSopenharmony_ci glVertex3f(x * fountain_side[n * 2 + 2], 637b877906bSopenharmony_ci y * fountain_side[n * 2 + 2], 638b877906bSopenharmony_ci fountain_side[n * 2 +3 ]); 639b877906bSopenharmony_ci glNormal3f(x * fountain_normal[n * 2], 640b877906bSopenharmony_ci y * fountain_normal[n * 2], 641b877906bSopenharmony_ci fountain_normal[n * 2 + 1]); 642b877906bSopenharmony_ci glVertex3f(x * fountain_side[n * 2], 643b877906bSopenharmony_ci y * fountain_side[n * 2], 644b877906bSopenharmony_ci fountain_side[n * 2 + 1]); 645b877906bSopenharmony_ci } 646b877906bSopenharmony_ci 647b877906bSopenharmony_ci glEnd(); 648b877906bSopenharmony_ci } 649b877906bSopenharmony_ci 650b877906bSopenharmony_ci glEndList(); 651b877906bSopenharmony_ci } 652b877906bSopenharmony_ci else 653b877906bSopenharmony_ci glCallList(fountain_list); 654b877906bSopenharmony_ci} 655b877906bSopenharmony_ci 656b877906bSopenharmony_ci 657b877906bSopenharmony_ci//======================================================================== 658b877906bSopenharmony_ci// Recursive function for building variable tessellated floor 659b877906bSopenharmony_ci//======================================================================== 660b877906bSopenharmony_ci 661b877906bSopenharmony_cistatic void tessellate_floor(float x1, float y1, float x2, float y2, int depth) 662b877906bSopenharmony_ci{ 663b877906bSopenharmony_ci float delta, x, y; 664b877906bSopenharmony_ci 665b877906bSopenharmony_ci // Last recursion? 666b877906bSopenharmony_ci if (depth >= 5) 667b877906bSopenharmony_ci delta = 999999.f; 668b877906bSopenharmony_ci else 669b877906bSopenharmony_ci { 670b877906bSopenharmony_ci x = (float) (fabs(x1) < fabs(x2) ? fabs(x1) : fabs(x2)); 671b877906bSopenharmony_ci y = (float) (fabs(y1) < fabs(y2) ? fabs(y1) : fabs(y2)); 672b877906bSopenharmony_ci delta = x*x + y*y; 673b877906bSopenharmony_ci } 674b877906bSopenharmony_ci 675b877906bSopenharmony_ci // Recurse further? 676b877906bSopenharmony_ci if (delta < 0.1f) 677b877906bSopenharmony_ci { 678b877906bSopenharmony_ci x = (x1 + x2) * 0.5f; 679b877906bSopenharmony_ci y = (y1 + y2) * 0.5f; 680b877906bSopenharmony_ci tessellate_floor(x1, y1, x, y, depth + 1); 681b877906bSopenharmony_ci tessellate_floor(x, y1, x2, y, depth + 1); 682b877906bSopenharmony_ci tessellate_floor(x1, y, x, y2, depth + 1); 683b877906bSopenharmony_ci tessellate_floor(x, y, x2, y2, depth + 1); 684b877906bSopenharmony_ci } 685b877906bSopenharmony_ci else 686b877906bSopenharmony_ci { 687b877906bSopenharmony_ci glTexCoord2f(x1 * 30.f, y1 * 30.f); 688b877906bSopenharmony_ci glVertex3f( x1 * 80.f, y1 * 80.f, 0.f); 689b877906bSopenharmony_ci glTexCoord2f(x2 * 30.f, y1 * 30.f); 690b877906bSopenharmony_ci glVertex3f( x2 * 80.f, y1 * 80.f, 0.f); 691b877906bSopenharmony_ci glTexCoord2f(x2 * 30.f, y2 * 30.f); 692b877906bSopenharmony_ci glVertex3f( x2 * 80.f, y2 * 80.f, 0.f); 693b877906bSopenharmony_ci glTexCoord2f(x1 * 30.f, y2 * 30.f); 694b877906bSopenharmony_ci glVertex3f( x1 * 80.f, y2 * 80.f, 0.f); 695b877906bSopenharmony_ci } 696b877906bSopenharmony_ci} 697b877906bSopenharmony_ci 698b877906bSopenharmony_ci 699b877906bSopenharmony_ci//======================================================================== 700b877906bSopenharmony_ci// Draw floor. We build the floor recursively and let the tessellation in the 701b877906bSopenharmony_ci// center (near x,y=0,0) be high, while the tessellation around the edges be 702b877906bSopenharmony_ci// low. 703b877906bSopenharmony_ci//======================================================================== 704b877906bSopenharmony_ci 705b877906bSopenharmony_cistatic void draw_floor(void) 706b877906bSopenharmony_ci{ 707b877906bSopenharmony_ci static GLuint floor_list = 0; 708b877906bSopenharmony_ci 709b877906bSopenharmony_ci if (!wireframe) 710b877906bSopenharmony_ci { 711b877906bSopenharmony_ci glEnable(GL_TEXTURE_2D); 712b877906bSopenharmony_ci glBindTexture(GL_TEXTURE_2D, floor_tex_id); 713b877906bSopenharmony_ci } 714b877906bSopenharmony_ci 715b877906bSopenharmony_ci // The first time, we build the floor display list 716b877906bSopenharmony_ci if (!floor_list) 717b877906bSopenharmony_ci { 718b877906bSopenharmony_ci floor_list = glGenLists(1); 719b877906bSopenharmony_ci glNewList(floor_list, GL_COMPILE_AND_EXECUTE); 720b877906bSopenharmony_ci 721b877906bSopenharmony_ci glMaterialfv(GL_FRONT, GL_DIFFUSE, floor_diffuse); 722b877906bSopenharmony_ci glMaterialfv(GL_FRONT, GL_SPECULAR, floor_specular); 723b877906bSopenharmony_ci glMaterialf(GL_FRONT, GL_SHININESS, floor_shininess); 724b877906bSopenharmony_ci 725b877906bSopenharmony_ci // Draw floor as a bunch of triangle strips (high tessellation 726b877906bSopenharmony_ci // improves lighting) 727b877906bSopenharmony_ci glNormal3f(0.f, 0.f, 1.f); 728b877906bSopenharmony_ci glBegin(GL_QUADS); 729b877906bSopenharmony_ci tessellate_floor(-1.f, -1.f, 0.f, 0.f, 0); 730b877906bSopenharmony_ci tessellate_floor( 0.f, -1.f, 1.f, 0.f, 0); 731b877906bSopenharmony_ci tessellate_floor( 0.f, 0.f, 1.f, 1.f, 0); 732b877906bSopenharmony_ci tessellate_floor(-1.f, 0.f, 0.f, 1.f, 0); 733b877906bSopenharmony_ci glEnd(); 734b877906bSopenharmony_ci 735b877906bSopenharmony_ci glEndList(); 736b877906bSopenharmony_ci } 737b877906bSopenharmony_ci else 738b877906bSopenharmony_ci glCallList(floor_list); 739b877906bSopenharmony_ci 740b877906bSopenharmony_ci glDisable(GL_TEXTURE_2D); 741b877906bSopenharmony_ci 742b877906bSopenharmony_ci} 743b877906bSopenharmony_ci 744b877906bSopenharmony_ci 745b877906bSopenharmony_ci//======================================================================== 746b877906bSopenharmony_ci// Position and configure light sources 747b877906bSopenharmony_ci//======================================================================== 748b877906bSopenharmony_ci 749b877906bSopenharmony_cistatic void setup_lights(void) 750b877906bSopenharmony_ci{ 751b877906bSopenharmony_ci float l1pos[4], l1amb[4], l1dif[4], l1spec[4]; 752b877906bSopenharmony_ci float l2pos[4], l2amb[4], l2dif[4], l2spec[4]; 753b877906bSopenharmony_ci 754b877906bSopenharmony_ci // Set light source 1 parameters 755b877906bSopenharmony_ci l1pos[0] = 0.f; l1pos[1] = -9.f; l1pos[2] = 8.f; l1pos[3] = 1.f; 756b877906bSopenharmony_ci l1amb[0] = 0.2f; l1amb[1] = 0.2f; l1amb[2] = 0.2f; l1amb[3] = 1.f; 757b877906bSopenharmony_ci l1dif[0] = 0.8f; l1dif[1] = 0.4f; l1dif[2] = 0.2f; l1dif[3] = 1.f; 758b877906bSopenharmony_ci l1spec[0] = 1.f; l1spec[1] = 0.6f; l1spec[2] = 0.2f; l1spec[3] = 0.f; 759b877906bSopenharmony_ci 760b877906bSopenharmony_ci // Set light source 2 parameters 761b877906bSopenharmony_ci l2pos[0] = -15.f; l2pos[1] = 12.f; l2pos[2] = 1.5f; l2pos[3] = 1.f; 762b877906bSopenharmony_ci l2amb[0] = 0.f; l2amb[1] = 0.f; l2amb[2] = 0.f; l2amb[3] = 1.f; 763b877906bSopenharmony_ci l2dif[0] = 0.2f; l2dif[1] = 0.4f; l2dif[2] = 0.8f; l2dif[3] = 1.f; 764b877906bSopenharmony_ci l2spec[0] = 0.2f; l2spec[1] = 0.6f; l2spec[2] = 1.f; l2spec[3] = 0.f; 765b877906bSopenharmony_ci 766b877906bSopenharmony_ci glLightfv(GL_LIGHT1, GL_POSITION, l1pos); 767b877906bSopenharmony_ci glLightfv(GL_LIGHT1, GL_AMBIENT, l1amb); 768b877906bSopenharmony_ci glLightfv(GL_LIGHT1, GL_DIFFUSE, l1dif); 769b877906bSopenharmony_ci glLightfv(GL_LIGHT1, GL_SPECULAR, l1spec); 770b877906bSopenharmony_ci glLightfv(GL_LIGHT2, GL_POSITION, l2pos); 771b877906bSopenharmony_ci glLightfv(GL_LIGHT2, GL_AMBIENT, l2amb); 772b877906bSopenharmony_ci glLightfv(GL_LIGHT2, GL_DIFFUSE, l2dif); 773b877906bSopenharmony_ci glLightfv(GL_LIGHT2, GL_SPECULAR, l2spec); 774b877906bSopenharmony_ci glLightfv(GL_LIGHT3, GL_POSITION, glow_pos); 775b877906bSopenharmony_ci glLightfv(GL_LIGHT3, GL_DIFFUSE, glow_color); 776b877906bSopenharmony_ci glLightfv(GL_LIGHT3, GL_SPECULAR, glow_color); 777b877906bSopenharmony_ci 778b877906bSopenharmony_ci glEnable(GL_LIGHT1); 779b877906bSopenharmony_ci glEnable(GL_LIGHT2); 780b877906bSopenharmony_ci glEnable(GL_LIGHT3); 781b877906bSopenharmony_ci} 782b877906bSopenharmony_ci 783b877906bSopenharmony_ci 784b877906bSopenharmony_ci//======================================================================== 785b877906bSopenharmony_ci// Main rendering function 786b877906bSopenharmony_ci//======================================================================== 787b877906bSopenharmony_ci 788b877906bSopenharmony_cistatic void draw_scene(GLFWwindow* window, double t) 789b877906bSopenharmony_ci{ 790b877906bSopenharmony_ci double xpos, ypos, zpos, angle_x, angle_y, angle_z; 791b877906bSopenharmony_ci static double t_old = 0.0; 792b877906bSopenharmony_ci float dt; 793b877906bSopenharmony_ci mat4x4 projection; 794b877906bSopenharmony_ci 795b877906bSopenharmony_ci // Calculate frame-to-frame delta time 796b877906bSopenharmony_ci dt = (float) (t - t_old); 797b877906bSopenharmony_ci t_old = t; 798b877906bSopenharmony_ci 799b877906bSopenharmony_ci mat4x4_perspective(projection, 800b877906bSopenharmony_ci 65.f * (float) M_PI / 180.f, 801b877906bSopenharmony_ci aspect_ratio, 802b877906bSopenharmony_ci 1.0, 60.0); 803b877906bSopenharmony_ci 804b877906bSopenharmony_ci glClearColor(0.1f, 0.1f, 0.1f, 1.f); 805b877906bSopenharmony_ci glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 806b877906bSopenharmony_ci 807b877906bSopenharmony_ci glMatrixMode(GL_PROJECTION); 808b877906bSopenharmony_ci glLoadMatrixf((const GLfloat*) projection); 809b877906bSopenharmony_ci 810b877906bSopenharmony_ci // Setup camera 811b877906bSopenharmony_ci glMatrixMode(GL_MODELVIEW); 812b877906bSopenharmony_ci glLoadIdentity(); 813b877906bSopenharmony_ci 814b877906bSopenharmony_ci // Rotate camera 815b877906bSopenharmony_ci angle_x = 90.0 - 10.0; 816b877906bSopenharmony_ci angle_y = 10.0 * sin(0.3 * t); 817b877906bSopenharmony_ci angle_z = 10.0 * t; 818b877906bSopenharmony_ci glRotated(-angle_x, 1.0, 0.0, 0.0); 819b877906bSopenharmony_ci glRotated(-angle_y, 0.0, 1.0, 0.0); 820b877906bSopenharmony_ci glRotated(-angle_z, 0.0, 0.0, 1.0); 821b877906bSopenharmony_ci 822b877906bSopenharmony_ci // Translate camera 823b877906bSopenharmony_ci xpos = 15.0 * sin((M_PI / 180.0) * angle_z) + 824b877906bSopenharmony_ci 2.0 * sin((M_PI / 180.0) * 3.1 * t); 825b877906bSopenharmony_ci ypos = -15.0 * cos((M_PI / 180.0) * angle_z) + 826b877906bSopenharmony_ci 2.0 * cos((M_PI / 180.0) * 2.9 * t); 827b877906bSopenharmony_ci zpos = 4.0 + 2.0 * cos((M_PI / 180.0) * 4.9 * t); 828b877906bSopenharmony_ci glTranslated(-xpos, -ypos, -zpos); 829b877906bSopenharmony_ci 830b877906bSopenharmony_ci glFrontFace(GL_CCW); 831b877906bSopenharmony_ci glCullFace(GL_BACK); 832b877906bSopenharmony_ci glEnable(GL_CULL_FACE); 833b877906bSopenharmony_ci 834b877906bSopenharmony_ci setup_lights(); 835b877906bSopenharmony_ci glEnable(GL_LIGHTING); 836b877906bSopenharmony_ci 837b877906bSopenharmony_ci glEnable(GL_FOG); 838b877906bSopenharmony_ci glFogi(GL_FOG_MODE, GL_EXP); 839b877906bSopenharmony_ci glFogf(GL_FOG_DENSITY, 0.05f); 840b877906bSopenharmony_ci glFogfv(GL_FOG_COLOR, fog_color); 841b877906bSopenharmony_ci 842b877906bSopenharmony_ci draw_floor(); 843b877906bSopenharmony_ci 844b877906bSopenharmony_ci glEnable(GL_DEPTH_TEST); 845b877906bSopenharmony_ci glDepthFunc(GL_LEQUAL); 846b877906bSopenharmony_ci glDepthMask(GL_TRUE); 847b877906bSopenharmony_ci 848b877906bSopenharmony_ci draw_fountain(); 849b877906bSopenharmony_ci 850b877906bSopenharmony_ci glDisable(GL_LIGHTING); 851b877906bSopenharmony_ci glDisable(GL_FOG); 852b877906bSopenharmony_ci 853b877906bSopenharmony_ci // Particles must be drawn after all solid objects have been drawn 854b877906bSopenharmony_ci draw_particles(window, t, dt); 855b877906bSopenharmony_ci 856b877906bSopenharmony_ci // Z-buffer not needed anymore 857b877906bSopenharmony_ci glDisable(GL_DEPTH_TEST); 858b877906bSopenharmony_ci} 859b877906bSopenharmony_ci 860b877906bSopenharmony_ci 861b877906bSopenharmony_ci//======================================================================== 862b877906bSopenharmony_ci// Window resize callback function 863b877906bSopenharmony_ci//======================================================================== 864b877906bSopenharmony_ci 865b877906bSopenharmony_cistatic void resize_callback(GLFWwindow* window, int width, int height) 866b877906bSopenharmony_ci{ 867b877906bSopenharmony_ci glViewport(0, 0, width, height); 868b877906bSopenharmony_ci aspect_ratio = height ? width / (float) height : 1.f; 869b877906bSopenharmony_ci} 870b877906bSopenharmony_ci 871b877906bSopenharmony_ci 872b877906bSopenharmony_ci//======================================================================== 873b877906bSopenharmony_ci// Key callback functions 874b877906bSopenharmony_ci//======================================================================== 875b877906bSopenharmony_ci 876b877906bSopenharmony_cistatic void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) 877b877906bSopenharmony_ci{ 878b877906bSopenharmony_ci if (action == GLFW_PRESS) 879b877906bSopenharmony_ci { 880b877906bSopenharmony_ci switch (key) 881b877906bSopenharmony_ci { 882b877906bSopenharmony_ci case GLFW_KEY_ESCAPE: 883b877906bSopenharmony_ci glfwSetWindowShouldClose(window, GLFW_TRUE); 884b877906bSopenharmony_ci break; 885b877906bSopenharmony_ci case GLFW_KEY_W: 886b877906bSopenharmony_ci wireframe = !wireframe; 887b877906bSopenharmony_ci glPolygonMode(GL_FRONT_AND_BACK, 888b877906bSopenharmony_ci wireframe ? GL_LINE : GL_FILL); 889b877906bSopenharmony_ci break; 890b877906bSopenharmony_ci default: 891b877906bSopenharmony_ci break; 892b877906bSopenharmony_ci } 893b877906bSopenharmony_ci } 894b877906bSopenharmony_ci} 895b877906bSopenharmony_ci 896b877906bSopenharmony_ci 897b877906bSopenharmony_ci//======================================================================== 898b877906bSopenharmony_ci// Thread for updating particle physics 899b877906bSopenharmony_ci//======================================================================== 900b877906bSopenharmony_ci 901b877906bSopenharmony_cistatic int physics_thread_main(void* arg) 902b877906bSopenharmony_ci{ 903b877906bSopenharmony_ci GLFWwindow* window = arg; 904b877906bSopenharmony_ci 905b877906bSopenharmony_ci for (;;) 906b877906bSopenharmony_ci { 907b877906bSopenharmony_ci mtx_lock(&thread_sync.particles_lock); 908b877906bSopenharmony_ci 909b877906bSopenharmony_ci // Wait for particle drawing to be done 910b877906bSopenharmony_ci while (!glfwWindowShouldClose(window) && 911b877906bSopenharmony_ci thread_sync.p_frame > thread_sync.d_frame) 912b877906bSopenharmony_ci { 913b877906bSopenharmony_ci struct timespec ts; 914b877906bSopenharmony_ci clock_gettime(CLOCK_REALTIME, &ts); 915b877906bSopenharmony_ci ts.tv_nsec += 100 * 1000 * 1000; 916b877906bSopenharmony_ci ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); 917b877906bSopenharmony_ci ts.tv_nsec %= 1000 * 1000 * 1000; 918b877906bSopenharmony_ci cnd_timedwait(&thread_sync.d_done, &thread_sync.particles_lock, &ts); 919b877906bSopenharmony_ci } 920b877906bSopenharmony_ci 921b877906bSopenharmony_ci if (glfwWindowShouldClose(window)) 922b877906bSopenharmony_ci break; 923b877906bSopenharmony_ci 924b877906bSopenharmony_ci // Update particles 925b877906bSopenharmony_ci particle_engine(thread_sync.t, thread_sync.dt); 926b877906bSopenharmony_ci 927b877906bSopenharmony_ci // Update frame counter 928b877906bSopenharmony_ci thread_sync.p_frame++; 929b877906bSopenharmony_ci 930b877906bSopenharmony_ci // Unlock mutex and signal drawing thread 931b877906bSopenharmony_ci mtx_unlock(&thread_sync.particles_lock); 932b877906bSopenharmony_ci cnd_signal(&thread_sync.p_done); 933b877906bSopenharmony_ci } 934b877906bSopenharmony_ci 935b877906bSopenharmony_ci return 0; 936b877906bSopenharmony_ci} 937b877906bSopenharmony_ci 938b877906bSopenharmony_ci 939b877906bSopenharmony_ci//======================================================================== 940b877906bSopenharmony_ci// main 941b877906bSopenharmony_ci//======================================================================== 942b877906bSopenharmony_ci 943b877906bSopenharmony_ciint main(int argc, char** argv) 944b877906bSopenharmony_ci{ 945b877906bSopenharmony_ci int ch, width, height; 946b877906bSopenharmony_ci thrd_t physics_thread = 0; 947b877906bSopenharmony_ci GLFWwindow* window; 948b877906bSopenharmony_ci GLFWmonitor* monitor = NULL; 949b877906bSopenharmony_ci 950b877906bSopenharmony_ci if (!glfwInit()) 951b877906bSopenharmony_ci { 952b877906bSopenharmony_ci fprintf(stderr, "Failed to initialize GLFW\n"); 953b877906bSopenharmony_ci exit(EXIT_FAILURE); 954b877906bSopenharmony_ci } 955b877906bSopenharmony_ci 956b877906bSopenharmony_ci while ((ch = getopt(argc, argv, "fh")) != -1) 957b877906bSopenharmony_ci { 958b877906bSopenharmony_ci switch (ch) 959b877906bSopenharmony_ci { 960b877906bSopenharmony_ci case 'f': 961b877906bSopenharmony_ci monitor = glfwGetPrimaryMonitor(); 962b877906bSopenharmony_ci break; 963b877906bSopenharmony_ci case 'h': 964b877906bSopenharmony_ci usage(); 965b877906bSopenharmony_ci exit(EXIT_SUCCESS); 966b877906bSopenharmony_ci } 967b877906bSopenharmony_ci } 968b877906bSopenharmony_ci 969b877906bSopenharmony_ci if (monitor) 970b877906bSopenharmony_ci { 971b877906bSopenharmony_ci const GLFWvidmode* mode = glfwGetVideoMode(monitor); 972b877906bSopenharmony_ci 973b877906bSopenharmony_ci glfwWindowHint(GLFW_RED_BITS, mode->redBits); 974b877906bSopenharmony_ci glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); 975b877906bSopenharmony_ci glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); 976b877906bSopenharmony_ci glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); 977b877906bSopenharmony_ci 978b877906bSopenharmony_ci width = mode->width; 979b877906bSopenharmony_ci height = mode->height; 980b877906bSopenharmony_ci } 981b877906bSopenharmony_ci else 982b877906bSopenharmony_ci { 983b877906bSopenharmony_ci width = 640; 984b877906bSopenharmony_ci height = 480; 985b877906bSopenharmony_ci } 986b877906bSopenharmony_ci 987b877906bSopenharmony_ci window = glfwCreateWindow(width, height, "Particle Engine", monitor, NULL); 988b877906bSopenharmony_ci if (!window) 989b877906bSopenharmony_ci { 990b877906bSopenharmony_ci fprintf(stderr, "Failed to create GLFW window\n"); 991b877906bSopenharmony_ci glfwTerminate(); 992b877906bSopenharmony_ci exit(EXIT_FAILURE); 993b877906bSopenharmony_ci } 994b877906bSopenharmony_ci 995b877906bSopenharmony_ci if (monitor) 996b877906bSopenharmony_ci glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 997b877906bSopenharmony_ci 998b877906bSopenharmony_ci glfwMakeContextCurrent(window); 999b877906bSopenharmony_ci gladLoadGL(glfwGetProcAddress); 1000b877906bSopenharmony_ci glfwSwapInterval(1); 1001b877906bSopenharmony_ci 1002b877906bSopenharmony_ci glfwSetFramebufferSizeCallback(window, resize_callback); 1003b877906bSopenharmony_ci glfwSetKeyCallback(window, key_callback); 1004b877906bSopenharmony_ci 1005b877906bSopenharmony_ci // Set initial aspect ratio 1006b877906bSopenharmony_ci glfwGetFramebufferSize(window, &width, &height); 1007b877906bSopenharmony_ci resize_callback(window, width, height); 1008b877906bSopenharmony_ci 1009b877906bSopenharmony_ci // Upload particle texture 1010b877906bSopenharmony_ci glGenTextures(1, &particle_tex_id); 1011b877906bSopenharmony_ci glBindTexture(GL_TEXTURE_2D, particle_tex_id); 1012b877906bSopenharmony_ci glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 1013b877906bSopenharmony_ci glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 1014b877906bSopenharmony_ci glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 1015b877906bSopenharmony_ci glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1016b877906bSopenharmony_ci glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 1017b877906bSopenharmony_ci glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, P_TEX_WIDTH, P_TEX_HEIGHT, 1018b877906bSopenharmony_ci 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, particle_texture); 1019b877906bSopenharmony_ci 1020b877906bSopenharmony_ci // Upload floor texture 1021b877906bSopenharmony_ci glGenTextures(1, &floor_tex_id); 1022b877906bSopenharmony_ci glBindTexture(GL_TEXTURE_2D, floor_tex_id); 1023b877906bSopenharmony_ci glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 1024b877906bSopenharmony_ci glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 1025b877906bSopenharmony_ci glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 1026b877906bSopenharmony_ci glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1027b877906bSopenharmony_ci glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 1028b877906bSopenharmony_ci glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, F_TEX_WIDTH, F_TEX_HEIGHT, 1029b877906bSopenharmony_ci 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, floor_texture); 1030b877906bSopenharmony_ci 1031b877906bSopenharmony_ci if (glfwExtensionSupported("GL_EXT_separate_specular_color")) 1032b877906bSopenharmony_ci { 1033b877906bSopenharmony_ci glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, 1034b877906bSopenharmony_ci GL_SEPARATE_SPECULAR_COLOR_EXT); 1035b877906bSopenharmony_ci } 1036b877906bSopenharmony_ci 1037b877906bSopenharmony_ci // Set filled polygon mode as default (not wireframe) 1038b877906bSopenharmony_ci glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 1039b877906bSopenharmony_ci wireframe = 0; 1040b877906bSopenharmony_ci 1041b877906bSopenharmony_ci // Set initial times 1042b877906bSopenharmony_ci thread_sync.t = 0.0; 1043b877906bSopenharmony_ci thread_sync.dt = 0.001f; 1044b877906bSopenharmony_ci thread_sync.p_frame = 0; 1045b877906bSopenharmony_ci thread_sync.d_frame = 0; 1046b877906bSopenharmony_ci 1047b877906bSopenharmony_ci mtx_init(&thread_sync.particles_lock, mtx_timed); 1048b877906bSopenharmony_ci cnd_init(&thread_sync.p_done); 1049b877906bSopenharmony_ci cnd_init(&thread_sync.d_done); 1050b877906bSopenharmony_ci 1051b877906bSopenharmony_ci if (thrd_create(&physics_thread, physics_thread_main, window) != thrd_success) 1052b877906bSopenharmony_ci { 1053b877906bSopenharmony_ci glfwTerminate(); 1054b877906bSopenharmony_ci exit(EXIT_FAILURE); 1055b877906bSopenharmony_ci } 1056b877906bSopenharmony_ci 1057b877906bSopenharmony_ci glfwSetTime(0.0); 1058b877906bSopenharmony_ci 1059b877906bSopenharmony_ci while (!glfwWindowShouldClose(window)) 1060b877906bSopenharmony_ci { 1061b877906bSopenharmony_ci draw_scene(window, glfwGetTime()); 1062b877906bSopenharmony_ci 1063b877906bSopenharmony_ci glfwSwapBuffers(window); 1064b877906bSopenharmony_ci glfwPollEvents(); 1065b877906bSopenharmony_ci } 1066b877906bSopenharmony_ci 1067b877906bSopenharmony_ci thrd_join(physics_thread, NULL); 1068b877906bSopenharmony_ci 1069b877906bSopenharmony_ci glfwDestroyWindow(window); 1070b877906bSopenharmony_ci glfwTerminate(); 1071b877906bSopenharmony_ci 1072b877906bSopenharmony_ci exit(EXIT_SUCCESS); 1073b877906bSopenharmony_ci} 1074b877906bSopenharmony_ci 1075