-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParticleSimulator.cpp
More file actions
341 lines (288 loc) · 15.9 KB
/
ParticleSimulator.cpp
File metadata and controls
341 lines (288 loc) · 15.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/******************************************************************************
* File Name: ParticleSimulator.java
* Author: Raashtra KC
* Date: 2025-11-06
* Language: C++
* Notes: Make sure the required libraries are installed
******************************************************************************/
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
#ifdef _WIN32
#include <windows.h>
#define M_PI 3.14159265358979323846 // Window doesn't seem to define this
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#endif
#include <cmath>
#include <iostream>
//defining array for storing indexes of x,y and z coordinates of the mouse
float mouseX[500]; //x coordinate of particles
float mouseY[500]; //y coordinate of particles
float mouseZ[500]; //z coordinate of particles
float lifespan[500]; //storing lifespan for each particle as we draw
const float FIX_LIFE = 3.0f; //setting the constant starting life for each new particles
float alpha[500]; //alpha value setting for fading effect of particles
float velocityX[500]; //velocity of particles in x direction
float velocityY[500]; //velocity of particles in y direction
float velocityZ[500]; //velocity of particles in z direction
int numDraws = 0; //storing the number of elements stored in array
int Cindex = 0; //index to put the current coordinate into the mouse
//mouse tracking variables
bool hold = false; //stores the condition of mouse click hold or released
int currentX, currentY; //storing the current posiiton/x and y of the mouse at the screen
int previousX = 0, previousY = 0; //previous mouse positions holder variables
//variables for gravity simulations
const float Gravity = -0.002f; //downward pulling force/speed
float dt; //declaring a timestep which will be set accordingly for key presses g or G
const float smallTime_gravity = 0.06f; //small timestep for gravity
const float largeTime_gravity = 0.5f; //large timestep for gravity
//variables for blackhole simulations
const float BlackHole = 0.01f; //strength of a black hole// similar to pulling force
float DT; //declaring a timestep which will be set accordingly for key presses b or B
const float smallTime_BH = 0.006f; //small timestep for blackhole
const float largeTime_BH = 0.1f; //large timestep for blackhole
bool gravityON = false; //storing the condition/status of gravity on or off
bool blackholeON = false; //storing the condition/status of blackhole on or off
float mouseSpeed=0.0f; //setting the initial mousespeed, which will be changed accordingly per frame of its motion
//This method draws all the points whenever called
//It uses a for loop to loop through all the index of the array
//also uses alpha for coloring to create a fading look when its life reduces
void draw() {
//drawing all the existing points we have in the array
for (int i = 0; i < numDraws; i++) {
//only draw points that are not dead/ >0.0f lifeline
if (lifespan[i] > 0.0f) {
//we will declare a alpha variable to make a fading away effect in some time
glColor4f(1.0f, 1.0f, 0.0f, alpha[i]);
glVertex3f(mouseX[i], mouseY[i], mouseZ[i]); //drawing the specific point in the space using glvector3f
}
}
}
//Timer function to update the particle position and different states for respective simulations
//this function is called repeatedly for each simulations, it updates the next available index with current mouse position and
//assigns velocity for x,y and z directions as well.
//Also handles the lifeline for each particles and includes seperate conditions for gravity and black hole modes
//Keeps on updating the next index of the array by tracking a new mouse position in the screen
void timerF(int val) {
//hold refers to the mouse click holded
//this if condiiton mainly acts as our stop simulation where particles are just drawn but are not moved in any directions
//even if it's not moved, the velocity of respective direction continues to be stored, so that when any simulation is enabled it starts with it's initial velocity
if (hold) {
//normalizing the screen points to coordinates in the range -1 to 1.
mouseX[Cindex] = (float)currentX / 300.0f - 1.0f; //converting the x point clicked by mouse into out game size of -1, 1 for x direction
mouseY[Cindex] = 1.0f - (float)currentY / 300.0f; //when y =0, opengl it is y=+1, when y=600, in opengl it is y=-1 : so flipping
mouseZ[Cindex] = 0.0f;
lifespan[Cindex] = FIX_LIFE; //starting lifeline for the particle
alpha[Cindex] = 1.0f; //setting the alpha to 1, which is maximum transparency at the begigning after a particle is created
//generating random angles for giving a random direction to particle at the start
float randAngle1 = ((rand() % 100) / 100.0f) * 2 * M_PI; //0 to 360 degrees
float randAngle2 = ((rand() % 50) / 100.0f) * (M_PI / 2); //0 to 90 degrees
float speedConst = 0.015f; //scaling factor for velocity(acts as how far the particles goes when we start to draw them, lower the better as it can jump off the screen viewarea)
//setting the initial velocity based on the random direction and the speed of mouse(mousespeed is constantly changed in the mousemotion function, for accurate tracking of speed of each particles throughout the dragging of mouse)
velocityX[Cindex] = cos(randAngle2) * cos(randAngle1) * mouseSpeed * speedConst;
velocityY[Cindex] = sin(randAngle2) * mouseSpeed * speedConst;
velocityZ[Cindex] = cos(randAngle2) * sin(randAngle1) * mouseSpeed * speedConst;
//wraping around 500
Cindex = (Cindex + 1) % 500; //updating the index by making it loop back to 0 when reach 500
if (numDraws < 500) { //updating the number of items in the array until it reaches 500
numDraws++;
} //we have the numdraws in the cases when the drawing is less than 500, so we dont have to loop through empty space in the array
}
//for loop for changing the states/position of the particles which are drawn/already exists on the screen
for (int i = 0; i < numDraws; i++) {
if (lifespan[i] > 0.0f) {
lifespan[i] = lifespan[i] - 0.0035f; //decreasing the life span
//decreasing the alpha/opacity to make fade out effect
alpha[i] = lifespan[i] / FIX_LIFE; //makes a linear slower fade from 1 towards 0
//main logic for gravity simulation
if (gravityON) {
//using forward euler formula i.e. v_2 = v1 + a*dt (a is acceleration and dt is the time difference between two frames)
velocityY[i] = velocityY[i] + (Gravity * dt);
//with velocity on Y direction, we update the position for each particle in respective coordinates
mouseX[i] = mouseX[i] + velocityX[i] * dt;
mouseY[i] = mouseY[i] + velocityY[i] * dt;
mouseZ[i] = mouseZ[i] + velocityZ[i] * dt;
//we use forward euler only on y axis as gravity acts in y direction, other velocities we have is randomly initialized when particles are created
}
//main starting point for Black hole simulation
if (blackholeON) {
//direction towards origin from current coordinates(we just negate every coordinate)
//this also is the difference between point position and center towards the origin
float dirX = -mouseX[i];
float dirY = -mouseY[i];
float dirZ = -mouseZ[i];
//using ditance formula to find the distance between origin 0,0,0 and the point x,y,z
float distance = sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
if (distance == 0.00f) {
distance = 0.0001f; //avoiding divide by zero by initializing small distance in case when we have distance equal to 0
}
//finding unit vector pointing in the direction towards origin
float X = (dirX / distance);
float Y = (dirY / distance);
float Z = (dirZ / distance);
//we know force is inversly proportional to the distance square and from F=ma=1/d^2 we can say a=1/d^2 for constant mass
float acceleration = BlackHole / (distance * distance);
//now using forward euler to find next posiiton of the point towards the origin
// firstly finding next velocity with v2 = v2 + speed*dt
velocityX[i] = velocityX[i] + X * acceleration* DT;
velocityY[i] = velocityY[i] + Y * acceleration * DT;
velocityZ[i] = velocityZ[i] + Z * acceleration * DT;
//using position euler: p_2 = p_1 + velocity_1*dt to change the position based on changed velocity
mouseX[i] = mouseX[i] + velocityX[i] * DT;
mouseY[i] = mouseY[i] + velocityY[i] * DT;
mouseZ[i] = mouseZ[i] + velocityZ[i] * DT;
}
}
}
glutPostRedisplay(); //everyrun of timer, says to redraw window now
//calling the timer function again after 10miliseconds
//this helps create a loop that updates the scene around 10-14 frames per second making smooth appearance in screen
glutTimerFunc(10, timerF, 0); //calling the refresh of timerfunction every 10 ms
}
//Mouse function to detect mouse down and mouse up
//this function controls whether particles should be generated on mouse click and stores the current position of mouse click
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON) {
if (state == GLUT_DOWN) { //when we have the left mouse clicked and is currently in the state down, than we initialize our mouse posiiton and enable the drawing from timer function
//enablinb our particle drawing when hold is true
hold = true;
//storing the current mouse coordinates
currentX = x;
currentY = y;
}
else if (state == GLUT_UP) {
//left mouse button released condition
hold = false; //disabling the draw when mouse is released
}
//calling the screen to redraw to update particle accordinagly
glutPostRedisplay();
}
}
//display function sets the color and size of the point to be drawn in the screen. It also sets the type of drawing
//it clears the screen, can calls the draw function to render particles on the screen
void display(void) {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // setting the background color to black
//clearing the color bugger and applying the background color we just set using glclearcolor
glClear(GL_COLOR_BUFFER_BIT); //clearing the color buffer
//reseting the old changes to the matrix and setting it as identity(makes in default position and orientation)
glLoadIdentity(); //reset modelview
glPointSize(5.0f); //declaring size of point to be drawn in screen
//beggining the points draw and calling the draw function accordingly
glBegin(GL_POINTS);
//calling the draw function to render all active particles for respective mouse clicks and drags
draw();
glEnd();
glutSwapBuffers(); //swap front and back buffers to display the rendered frame
}
//reshape function for making the environment 3D whenever the window is resized
//we used it mainly to define the viewport and projection for the scene
void reshape(int w, int h) {
//sets the viewport to cover the whole window
glViewport(0, 0, w, h);
//switch to projection matrix to set up the camera/projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); //resetting teh projection matrix before applying fresh one
glOrtho(-1, 1, -1, 1, -1, 1); //defining the orthographic projection world with coordinates -1 to 1 in all directions
//we have a 3D space
//switching to modelview for all the transformations/movements that we are doing with the drawn particles
glMatrixMode(GL_MODELVIEW);
}
//function to continously update the new mouse posiiton so we properly draw the particles
//this also continously calculates the speed of mouse movement and updates the current and previous position of the mouse as well
void motionMouse(int x, int y) {
//calculating speed for each particle using the formula sqrtt(dx^2 + dy^2), considering time as a constant factor
mouseSpeed = sqrt((currentX - previousX) * (currentX - previousX) + (currentY - previousY) * (currentY - previousY)); //this speed is per frame/dt when mouse is moved. so the formula becomes speed = distance/(dt=1.constatn)
//updating the previous mouse coordinates to the current one
previousX = currentX;
previousY = currentY;
//setting the new mouse coordinates
currentX = x;
currentY = y;
}
//keybord function to handle different key presses/toggle features in our simulation
//allows to clear particle, enable gravity or black hole simulations and stop the simulations on their place
void keyboard(unsigned char key, int x, int y) {
//clearing all the particles on screen when o is pressed
if (key == 'o' || key == 'O') {
std::cout << "Particles Cleared!" << std::endl;
//reseting the numDraws to 0 for fresh drawing
numDraws = 0;
Cindex = 0;
//resetting the lifespan of all particles to 0 using a loop
for (int i = 0; i < numDraws; i++) {
lifespan[i] = 0.0f;
}
} else if (key == 'g') { //enabling the gravity simulation with small timestep when g is pressed
dt = smallTime_gravity; //setting a small dt/timestep
std::cout << "Gravity Simulation Enabled! - Status: Small_TimeStep" << std::endl;
//disabling blackhole(ifon) and enabling the gravity for respective methods to run in timer function
blackholeON = false;
gravityON = true;
} else if (key == 'G') { //enabling the gravity simulation with large timestep when G is pressed
dt = largeTime_gravity;
std::cout << "Gravity Simulation Enabled! - Status: Large_TimeStep" << std::endl;
//disabling blackhole(ifon) and enabling the gravity for respective methods to run in timer function
blackholeON = false;
gravityON = true;
}
else if (key == 'b') { //enabling the BlackHole simulation with small timestep when b is pressed
DT = smallTime_BH;
std::cout << "BlackHole Simulation Enabled! - Status: Small_TimeStep" << std::endl;
//disabling gravity(ifon) and enabling the blackhole for respective methods to run in timer function
gravityON = false;
blackholeON = true;
} else if (key == 'B') { //enabling the BlackHole simulation with large timestep when B is pressed
DT = largeTime_BH;
std::cout << "BlackHole Simulation Enabled! - Status: Large_TimeStep" << std::endl;
//disabling gravity(ifon) and enabling the blackhole for respective methods to run in timer function
gravityON = false;
blackholeON = true;
}
else if (key == 's' || key == 'S') { //stoping the simulation/and particles to their current position by disabling all of their velocity
std::cout << "Simulations Stopped!" << std::endl;
//disabling both the simulations and setting them to false
gravityON = false;
blackholeON = false;
for (int i = 0; i < 500; i++) { //setting velocities in all direction to zero
velocityX[i] = 0.0f;
velocityY[i] = 0.0f;
velocityZ[i] = 0.0f;
}
}
}
//This method is the main entry point for our particle simulations,
// It sets the window sizes
// Sets the window title
// and registers callback functions for input and rendering
int main(int argc, char** argv) {
// First set up GLUT
glutInit(&argc, argv);
//setting up the display mode as GLUT_DOUBLE for double buffering, GLUT_RGB for color mode
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
//setting the initial window size
glutInitWindowSize(600, 600);
//setting the initial window position on the screen
glutInitWindowPosition(100, 100);
// Make the window with a title
int windowHandle = glutCreateWindow("Particle Simulations");
glutSetWindow(windowHandle);
glEnable(GL_BLEND); //enablind blend for making a fading look to our points after certain time
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glutDisplayFunc(display); //calling the callback for display, mouse, reshape, motion and tiemr whenever window needs to be redisplayed
glutMouseFunc(mouse);
//handling window resize for any change in window dimensio, and mainly handles setting the projection and viewport
glutReshapeFunc(reshape);
//motion function for live tracking of our mouse position throughout it's click and drag motion
glutMotionFunc(motionMouse);
//handling standard keyboard keys like g,G,o,b,s,S,B presses
glutKeyboardFunc(keyboard);
//seting up the timer function inside the main so that it is called repeatedly for smooth drawing and updating of our particles
glutTimerFunc(0, timerF, 0);
glutMainLoop();
return 0; // required since main returns an int, but this line will never be reached
}