Skip to content

Commit a00b90c

Browse files
committed
wip
1 parent 07224d4 commit a00b90c

File tree

5 files changed

+246
-173
lines changed

5 files changed

+246
-173
lines changed

src/App.js

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { ResizeHandler } from "./Helpers/ResizeHandler.js";
2+
import { GlManager } from "./Helpers/GlManager.js";
3+
import { Program } from "./Entities/Program.js";
4+
import { ParticleSystem } from "./Entities/ParticleSystem.js";
5+
import { ShaderSource } from "./ShaderSource/ShaderSource.js";
6+
import { ShaderManager } from "./Helpers/ShaderManager.js";
7+
import { Utils } from "./Helpers/Utils.js";
8+
import { FrameLoop } from "./Helpers/FrameLoop.js";
9+
10+
const frameLoop = new FrameLoop(60);
11+
12+
/**
13+
* @param config {Object}
14+
* @param config.fpsCountViewElement {Element}
15+
* @param config.settings.flameCount {number}
16+
* @param config.settings.flameParticleCount {number}
17+
* @param config.settings.widthRange {[number, number]}
18+
* @param config.settings.heightRange {[number, number]}
19+
* @param config.settings.noiseRange {[number, number]}
20+
* @param config.settings.speedRange {[number, number]}
21+
* @param config.settings.particleTopColor {[number, number, number]}
22+
* @param config.settings.particleBottomColor {[number, number, number]}
23+
*
24+
* @returns {{destroy:Function}}
25+
*/
26+
const renderApp = (config) => {
27+
const { fpsCountViewElement, settings } = config;
28+
29+
let unsubscriber = null;
30+
31+
const MAX_PARTICLE_COUNT = settings.flameCount * settings.flameParticleCount;
32+
33+
const { canvas, ctx } = GlManager.getRenderer("canvas");
34+
35+
ResizeHandler.handle(canvas, ctx);
36+
37+
ctx.enable(ctx.BLEND);
38+
ctx.blendFunc(ctx.SRC_ALPHA, ctx.ONE);
39+
40+
const vertexShader = ShaderManager.compileVertex(ctx, ShaderSource.VERTEX_SHADER_SOURCE);
41+
const fragmentShader = ShaderManager.compileFragment(ctx, ShaderSource.FRAGMENT_SHADER_SOURCE);
42+
43+
const program = new Program(ctx, vertexShader, fragmentShader);
44+
45+
program.use();
46+
47+
program.setUniform3fv(ShaderSource.TOP_COLOR_UNIFORM, settings.particleTopColor);
48+
program.setUniform3fv(ShaderSource.BOTTOM_COLOR_UNIFORM, settings.particleBottomColor);
49+
50+
const positionAttrLocation = program.getAttribLocation(ShaderSource.POSITION_ATTRIBUTE);
51+
const sizeAttrLocation = program.getAttribLocation(ShaderSource.SIZE_ATTRIBUTE);
52+
const timeAttrLocation = program.getAttribLocation(ShaderSource.TIME_ATTRIBUTE);
53+
54+
ctx.enableVertexAttribArray(positionAttrLocation);
55+
ctx.enableVertexAttribArray(sizeAttrLocation);
56+
ctx.enableVertexAttribArray(timeAttrLocation);
57+
58+
const positionArray = new Float32Array(MAX_PARTICLE_COUNT * 2);
59+
const positionBuffer = ctx.createBuffer();
60+
61+
const timeArray = new Float32Array(MAX_PARTICLE_COUNT);
62+
const timeBuffer = ctx.createBuffer();
63+
64+
const sizeArray = new Float32Array(MAX_PARTICLE_COUNT);
65+
const sizeBuffer = ctx.createBuffer();
66+
67+
const particleSystem = new ParticleSystem({
68+
canvas,
69+
flameCount: settings.flameCount,
70+
flameParticlesCount: settings.flameParticleCount,
71+
widthRange: settings.widthRange,
72+
heightRange: settings.heightRange,
73+
noiseRange: settings.noiseRange,
74+
speedRange: settings.speedRange
75+
});
76+
77+
let frames = 0;
78+
let fps = 0;
79+
let lastFpsUpdate = 0;
80+
81+
let previousFrameTimestamp = performance.now();
82+
83+
unsubscriber = frameLoop.subscribe(
84+
() => runRenderLoop(previousFrameTimestamp),
85+
)
86+
87+
const runRenderLoop = (timestamp) => {
88+
const now = performance.now();
89+
const deltaTime = Utils.msToSeconds(now - timestamp);
90+
91+
previousFrameTimestamp = now;
92+
93+
frames++;
94+
95+
if (now - lastFpsUpdate >= 1000) {
96+
fps = frames;
97+
frames = 0;
98+
lastFpsUpdate = now;
99+
100+
fpsCountViewElement.innerText = `${fps} FPS`;
101+
}
102+
103+
particleSystem.update(deltaTime);
104+
particleSystem.fillBuffers(positionArray, timeArray, sizeArray);
105+
106+
ctx.bindBuffer(ctx.ARRAY_BUFFER, positionBuffer);
107+
ctx.bufferData(ctx.ARRAY_BUFFER, positionArray, ctx.DYNAMIC_DRAW);
108+
ctx.vertexAttribPointer(positionAttrLocation, 2, ctx.FLOAT, false, 0, 0);
109+
110+
ctx.bindBuffer(ctx.ARRAY_BUFFER, sizeBuffer);
111+
ctx.bufferData(ctx.ARRAY_BUFFER, sizeArray, ctx.DYNAMIC_DRAW);
112+
ctx.vertexAttribPointer(sizeAttrLocation, 1, ctx.FLOAT, false, 0, 0);
113+
114+
ctx.bindBuffer(ctx.ARRAY_BUFFER, timeBuffer);
115+
ctx.bufferData(ctx.ARRAY_BUFFER, timeArray, ctx.DYNAMIC_DRAW);
116+
ctx.vertexAttribPointer(timeAttrLocation, 1, ctx.FLOAT, false, 0, 0);
117+
118+
ctx.clearColor(0, 0, 0, 1);
119+
ctx.clear(ctx.COLOR_BUFFER_BIT);
120+
ctx.drawArrays(ctx.POINTS, 0, MAX_PARTICLE_COUNT);
121+
}
122+
123+
runRenderLoop(performance.now());
124+
125+
return {
126+
destroy: () => {
127+
if (unsubscriber !== null) {
128+
unsubscriber();
129+
}
130+
131+
ctx.deleteBuffer(positionBuffer);
132+
ctx.deleteBuffer(sizeBuffer);
133+
ctx.deleteBuffer(timeBuffer);
134+
135+
ctx.deleteProgram(program.program);
136+
ctx.deleteShader(vertexShader);
137+
ctx.deleteShader(fragmentShader);
138+
139+
ctx.bindBuffer(ctx.ARRAY_BUFFER, null);
140+
ctx.useProgram(null);
141+
},
142+
};
143+
}
144+
145+
export { renderApp };

src/Helpers/FrameLoop.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
class FrameLoop {
2+
oneFrameMs;
3+
msCounter;
4+
subscribers = new Set();
5+
prevTimestamp = 0;
6+
7+
constructor(fps) {
8+
this.oneFrameMs = 1000 / fps;
9+
this.msCounter = 0;
10+
11+
this.#startLoop(0);
12+
}
13+
14+
subscribe(callback) {
15+
this.subscribers.add(callback);
16+
17+
return () => {
18+
this.subscribers.delete(callback);
19+
};
20+
}
21+
22+
#play(delta, timestamp) {
23+
this.msCounter += delta;
24+
25+
if (this.msCounter > this.oneFrameMs) {
26+
this.msCounter = this.msCounter % this.oneFrameMs;
27+
28+
this.subscribers.forEach(
29+
(it) => it(timestamp),
30+
);
31+
}
32+
}
33+
34+
#startLoop(timestamp) {
35+
const delta = (timestamp - this.prevTimestamp);
36+
37+
this.prevTimestamp = timestamp;
38+
39+
this.#play(delta, timestamp);
40+
41+
requestAnimationFrame(this.#startLoop.bind(this));
42+
}
43+
}
44+
45+
export { FrameLoop };

src/index.css

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,38 @@
1+
html {
2+
height: 100%;
3+
}
4+
15
body {
6+
height: 100%;
27
margin: 0;
38
overflow: hidden;
49
}
510

611
canvas {
7-
width: 100dvw;
8-
height: 100dvh;
12+
width: 100%;
13+
height: 100%;
914
display: block;
1015
}
1116

17+
#info-block {
18+
width: 70px;
19+
position: absolute;
20+
right: 0;
21+
top: 0;
22+
display: flex;
23+
flex-direction: column;
24+
gap: 2px;
25+
padding: 4px;
26+
font-family: sans-serif;
27+
font-weight: bold;
28+
font-size: 8px;
29+
line-height: 8px;
30+
text-align: right;
31+
background: black;
32+
opacity: 0.8;
33+
color: #00ff0b;
34+
}
35+
1236
#flames-count-input-wrapper {
1337
width: 150px;
1438
position: absolute;
@@ -25,35 +49,3 @@ canvas {
2549
margin: 0;
2650
cursor: pointer;
2751
}
28-
29-
#flames-count-view {
30-
width: 70px;
31-
position: absolute;
32-
right: 0;
33-
top: 20px;
34-
padding: 4px;
35-
font-family: sans-serif;
36-
font-weight: bold;
37-
font-size: 10px;
38-
line-height: 12px;
39-
text-align: center;
40-
color: #00ff0b;
41-
background: black;
42-
opacity: 0.8;
43-
}
44-
45-
#fps-count-view {
46-
width: 50px;
47-
position: absolute;
48-
right: 0;
49-
top: 0;
50-
padding: 4px;
51-
font-family: sans-serif;
52-
font-weight: bold;
53-
font-size: 10px;
54-
line-height: 12px;
55-
text-align: center;
56-
color: #00ff0b;
57-
background: black;
58-
opacity: 0.8;
59-
}

src/index.html

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@
1010
<canvas></canvas>
1111

1212
<div id="flames-count-input-wrapper">
13-
<input id="flames-count-input" type="range" min="100" max="1000" />
13+
<input id="flames-count-input" type="range" min="100" max="1500" />
1414
</div>
1515

16-
<div id="fps-count-view"></div>
16+
<div id="info-block">
17+
<div id="fps-count-view">...</div>
1718

18-
<div id="flames-count-view"></div>
19+
<div id="flames-count-view"></div>
20+
21+
<div id="particles-count-view"></div>
22+
</div>
1923

2024
<script src="./index.js" type="module"></script>
2125
</body>

0 commit comments

Comments
 (0)