-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.js
262 lines (179 loc) · 7.95 KB
/
main.js
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
import * as BasicShaders from './shaders/basic_shaders.js'
import * as BuildTools from './utils/buildProgram.js'
import {Circle} from './shapes/circle.js'
import * as MatrixOps from './utils/2dmatrixops.js'
import {getCanvasMousePos} from './utils/clickutils.js'
const MAX_PARTICLES = 1000
const SIDES_PER_CIRCLE = 10;
function init() {
const canvas = document.getElementById("mainCanvas");
const gl = canvas.getContext("webgl");
if (gl === null) {
alert('Failed to get WebGL Context. Your browser/machine may block or not support WebGL.');
return;
}
main(canvas, gl);
}
function main(canvas, gl) {
// Builds a program using some basic shaders
var program = BuildTools.buildProgram(gl, BasicShaders.vertex_shader, BasicShaders.fragment_shader);
if (program === null) {
console.log('Execution Halted due to failed program build');
return;
}
// Create initial snow particles
var particles = []
for (var i=0;i<MAX_PARTICLES;i++) {
var circle = addSnow(gl);
particles.push(circle);
}
// Get Locations of Variables in the linked program.
var locOfposAttrb = gl.getAttribLocation(program, "pos");
var matrixloc = gl.getUniformLocation(program, "transformation_matrix");
var colourloc = gl.getUniformLocation(program, "colour");
tick();
function tick() {
updateParticles();
renderFrame();
function updateParticles() {
var newParticles = []
for (var particle of particles) {
// Checks whether the particle should be culled.
if (particle.y + particle.radius >= -1) {
newParticles.push(particle);
// Does vertical movement and adds downward acceleration.
particle.y += particle.v;
particle.v -= 0.00001;
// Calculations to stop too much movement in any direction.
// Adds some random deviations to simulate eddy currents.
if (particle.v > 0) {
particle.v -= 0.01;
particle.vh += (Math.random()-0.5)*0.0001;
}
if (particle.v < -0.01) {
particle.v += 0.001;
particle.vh += (Math.random()-0.5)*0.0001;
}
if (particle.vh > 0.01) {
particle.vh -= 0.01;
}
if (particle.vh < 0.01) {
particle.vh += 0.01;
}
// Handles click mechanics. Any particle within a circle of radius 0.0625 will be repelled.
if (mouseDown && (Math.pow(particle.x-mouseX,2) + Math.pow(particle.y-mouseY,2) < 0.0625)) {
// Calculates a displacement vector towards the edge of the circle.
var diffVec = [particle.x-mouseX,particle.y-mouseY];
var mag = Math.sqrt(Math.pow(diffVec[0],2)+Math.pow(diffVec[1],2));
diffVec = [diffVec[0]*0.25/mag,diffVec[1]*0.25/mag];
// Partially displaces the particles and gives them a 'scattering velocity'.
particle.x += diffVec[0]*0.1;
particle.hv += (Math.random()+0.5)*0.01*diffVec[0];
particle.y += diffVec[1]*0.1;
particle.v += (Math.random()+0.5)*0.01*diffVec[1];
}
// Adds horizontal velocity to particle
particle.hv += Math.random()*0.00001*(particle.direction-0.5)*2;
particle.x += particle.hv
} else {
// If a particle is culled, a new one is added in its place
var circle = addSnow(gl);
circle.y = 1.001 + circle.radius;
newParticles.push(circle)
}
// 1/10 chance of switching the drift direction.
if (Math.random()<0.1) {
particle.direction ^= 1;
}
}
// Only retains particles which are not being culled. This is the culling step.
particles = newParticles
}
function renderFrame() {
// Readjusts canvas size using a utils library - https://webglfundamentals.org
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas to black
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
for (var particle of particles) {
gl.enableVertexAttribArray(locOfposAttrb);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, particle.posBuffer);
// Indicate how to read data from the position buffer - 2 floats, unnormalized
// And with no offset or stride.
var size = 2;
var type = gl.FLOAT;
var normalize = false;
gl.vertexAttribPointer(locOfposAttrb, size, type, normalize, 0,0 )
// Transforms particle location based on its coordinates.
var matrix = MatrixOps.translation(particle.x,particle.y);
gl.uniformMatrix3fv(matrixloc, false, matrix);
// Colour snow particle with its unique colour.
gl.uniform4fv(colourloc, particle.colour);
var primitiveType = gl.TRIANGLES;
var count = SIDES_PER_CIRCLE*3;
gl.drawArrays(primitiveType, 0, count);
}
window.requestAnimationFrame(tick);
}
}
}
function addSnow(gl) {
// Sets a random position for a new snow particle
var randx = (Math.random()-0.5)*2;
var randy = (Math.random()-0.5)*2;
// Creates a particle object
var circle = new Circle(Math.random()*0.005,randx, randy);
// Chooses a random direction for snow to move initially
if (Math.random()<0.5) {
circle.direction = 0;
} else {
circle.direction = 1;
}
// Vary colour for snow particles
var r = Math.random()*0.2;
circle.colour = [1-r,1-r,1-r,1];
// Creates vertices for the circle and buffers them
var vertices = circle.getVertices(SIDES_PER_CIRCLE);
var posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
// Stores the buffer with the particle
circle.posBuffer = posBuffer;
return circle;
}
window.onload = init;
/********************** Event Listeners **********************/
var mouseX;
var mouseY;
// Mouse Click Detection. Allows both left and right clicking simultaneously.
var mouseDown = 0;
document.body.onmousedown = function() {
mouseDown++;
document.getElementById('overlay').style.display = 'none';
}
document.body.ontouchstart = function(e) {
e.preventDefault();
mouseDown++;
moveMouse(); // Updates touch coords on initial touch
document.getElementById('overlay').style.display = 'none';
}
document.body.ontouchend = function() {
mouseDown--;
console.log(mouseDown);
}
document.body.onmouseup = function() {
mouseDown--;
}
function moveMouse(e) {
e.preventDefault();
var gl = document.getElementById("mainCanvas").getContext("webgl");
var relPos = getCanvasMousePos(e, gl.canvas);
mouseX = relPos.x / gl.canvas.width * 2 - 1;
mouseY = relPos.y / gl.canvas.height * -2 + 1;
return false;
}
window.addEventListener('mousemove', moveMouse);
window.addEventListener('touchmove', moveMouse);