This repository has been archived by the owner on Jul 29, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathApp.js
151 lines (130 loc) · 3.31 KB
/
App.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
import {ProgramLoader} from "./program/index.js"
import {MouseEventHandler, KeyboardEventHandler} from "./event/index.js"
/*
* Abstract class App
*
* Subclasses must implement update() and draw() methods
*/
export class App {
/*
* Creates an instance of App and
* gets the webGL rendering context
* associated with the canvas
*/
constructor(canvas, timeStep = 0) {
this.glContext = canvas.getContext("webgl2");
if(!this.glContext)
throw new Error("No WebGL2 context");
this.timeStep = timeStep;
this._programs = new Map();
this._lag = 0;
this._t_old = 0;
this._running = false;
this._loopBinded = this._loop.bind(this);
this._mouse = null;
this._keyboard = null;
}
/* Get the current canvas */
get canvas() {
return this.glContext.canvas;
}
/* Get the time ahead of update.
*
* If time step is non zero, when draw is called
* lag is the residual time of the frame
* lower than the time step hence not updated.
* If time step is variable (`timeStep == 0`)
* when draw is called lag is always 0.
*/
get lag() {
return this._lag;
}
/* Get the time step */
get timeStep() {
return this._timeStep;
}
/* Set the time step to a non negative value */
set timeStep(dt) {
const ndt = +dt;
if(!(ndt >= 0))
throw new Error("Time step must be a non negative number in milliseconds");
this._timeStep = ndt;
}
/* Get the instance of ProgramWrapper from the name */
getProgram(name) {
return this._programs.get(name);
}
/* Create a ProgramLoader instance for the current app */
getProgramLoader() {
return new ProgramLoader(this);
}
/* Initialize the mouse event listener for the current canvas */
initMouse() {
if(this._mouse == null)
this._mouse = new MouseEventHandler(this.canvas);
}
/* Get the mouse event handler if present */
get mouse() {
return this._mouse;
}
/* Initialize the keyboard event listener for the current canvas */
initKeyboard() {
if(this._keyboard == null)
this._keyboard = new KeyboardEventHandler(this.canvas);
}
/* Get the keyboard event handler if present */
get keyboard() {
return this._keyboard;
}
/* Initialize and run the loop */
run() {
if(this._running)
return;
this._t_old = performance.now();
this._lag = 0;
this._running = true;
requestAnimationFrame(this._loopBinded);
}
/* Stop the loop */
stop() {
this._running = false;
}
/* Resize the canvas */
resize(width, height, hdpi=true) {
let pixr = hdpi && window.devicePixelRatio || 1;
this.canvas.width = Math.floor(width * pixr);
this.canvas.height = Math.floor(height * pixr);
}
/*
* Logic update function
* Called every `timeStep` milliseconds
* with fixed time step if it is non zero,
* otherwise it is called every frame
* with a variable time step.
*
* `dt` : time elapsed since last update
*/
update(dt) {}
/* Rendering function called once per frame */
draw() {}
/*
* Internal loop function
*/
_loop(time) {
const dt = time - this._t_old;
this._t_old = time;
this._lag += dt;
if(this.timeStep)
while(this._lag >= this._timeStep) {
this.update(this._timeStep);
this._lag -= this._timeStep;
}
else {
this.update(this._lag);
this._lag = 0;
}
this.draw();
if(this._running)
requestAnimationFrame(this._loopBinded);
}
}