littlejsengine 1.7.13 → 1.7.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/littlejs.d.ts +152 -18
- package/build/littlejs.esm.js +158 -115
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +158 -115
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +158 -115
- package/examples/electron/build.js +2 -0
- package/examples/js13k/build.js +2 -0
- package/examples/screenshot.jpg +0 -0
- package/examples/starter/build.js +2 -0
- package/examples/starter/game.js +1 -1
- package/examples/typescript/build.js +2 -0
- package/package.json +1 -1
- package/src/engine.js +10 -7
- package/src/engineAudio.js +67 -41
- package/src/engineBuild.js +23 -7
- package/src/engineDraw.js +2 -2
- package/src/engineInput.js +22 -10
- package/src/engineWebGL.js +43 -43
package/src/engineAudio.js
CHANGED
|
@@ -45,8 +45,9 @@ class Sound
|
|
|
45
45
|
if (zzfxSound)
|
|
46
46
|
{
|
|
47
47
|
// generate zzfx sound now for fast playback
|
|
48
|
-
this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
|
|
49
|
-
this.
|
|
48
|
+
this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
|
|
49
|
+
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
50
|
+
this.sampleRate = zzfxR;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -55,11 +56,12 @@ class Sound
|
|
|
55
56
|
* @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
|
|
56
57
|
* @param {Number} [pitch=1] - How much to scale pitch by (also adjusted by this.randomness)
|
|
57
58
|
* @param {Number} [randomnessScale=1] - How much to scale randomness
|
|
58
|
-
* @
|
|
59
|
+
* @param {Boolean} [loop=0] - Should the sound loop
|
|
60
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
59
61
|
*/
|
|
60
|
-
play(pos, volume=1, pitch=1, randomnessScale=1)
|
|
62
|
+
play(pos, volume=1, pitch=1, randomnessScale=1, loop=0)
|
|
61
63
|
{
|
|
62
|
-
if (!soundEnable || !this.
|
|
64
|
+
if (!soundEnable || !this.sampleChannels) return;
|
|
63
65
|
|
|
64
66
|
let pan;
|
|
65
67
|
if (pos)
|
|
@@ -82,43 +84,80 @@ class Sound
|
|
|
82
84
|
|
|
83
85
|
// play the sound
|
|
84
86
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
85
|
-
return playSamples(
|
|
87
|
+
return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Stop the last instance of this sound that was played */
|
|
91
|
+
stop()
|
|
92
|
+
{
|
|
93
|
+
if (this.source)
|
|
94
|
+
this.source.stop();
|
|
95
|
+
this.source = 0;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
/** Play the sound as a note with a semitone offset
|
|
89
99
|
* @param {Number} semitoneOffset - How many semitones to offset pitch
|
|
90
100
|
* @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
|
|
91
101
|
* @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
|
|
92
|
-
* @return {AudioBufferSourceNode} - The audio
|
|
102
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
93
103
|
*/
|
|
94
104
|
playNote(semitoneOffset, pos, volume)
|
|
95
105
|
{ return this.play(pos, volume, 2**(semitoneOffset/12), 0); }
|
|
106
|
+
|
|
107
|
+
/** Get how long this sound is in seconds
|
|
108
|
+
* @return {Number} - How long the sound is in seconds (undefined if loading)
|
|
109
|
+
*/
|
|
110
|
+
getDuration()
|
|
111
|
+
{ return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
|
|
112
|
+
|
|
113
|
+
/** Check if the last instance of this sound is playing
|
|
114
|
+
* @return {Boolean} - True if the sound is playing
|
|
115
|
+
*/
|
|
116
|
+
isPlaying() { return this.source && !this.source.ended; }
|
|
117
|
+
|
|
118
|
+
/** Check if sound is loading, for sounds fetched from a url
|
|
119
|
+
* @return {Boolean} - True if sound is loading and not ready to play
|
|
120
|
+
*/
|
|
121
|
+
isLoading() { return !this.sampleChannels; }
|
|
96
122
|
}
|
|
97
123
|
|
|
98
124
|
/**
|
|
99
125
|
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
126
|
+
* - this can be used to play wave, mp3, and ogg files
|
|
127
|
+
* @example
|
|
128
|
+
* // create a sound
|
|
129
|
+
* const sound_example = new SoundWave('sound.mp3');
|
|
130
|
+
*
|
|
131
|
+
* // play the sound
|
|
132
|
+
* sound_example.play();
|
|
100
133
|
*/
|
|
101
134
|
class SoundWave extends Sound
|
|
102
135
|
{
|
|
103
136
|
/** Create a sound object and cache the wave file for later use
|
|
104
|
-
* @param {String}
|
|
105
|
-
* @param {Number} [randomness
|
|
137
|
+
* @param {String} filename - Filename of audio file to load
|
|
138
|
+
* @param {Number} [randomness=0] - How much to randomize frequency each time sound plays
|
|
106
139
|
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
107
140
|
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
|
|
108
141
|
*/
|
|
109
|
-
constructor(
|
|
142
|
+
constructor(filename, randomness=0, range, taper)
|
|
110
143
|
{
|
|
111
144
|
super(0, range, taper);
|
|
112
145
|
this.randomness = randomness;
|
|
113
146
|
|
|
114
147
|
if (!soundEnable) return;
|
|
115
|
-
if (!
|
|
148
|
+
if (!soundDecoderContext)
|
|
116
149
|
soundDecoderContext = new AudioContext;
|
|
117
150
|
|
|
118
|
-
fetch(
|
|
151
|
+
fetch(filename)
|
|
119
152
|
.then(response => response.arrayBuffer())
|
|
120
|
-
.then(arrayBuffer =>
|
|
121
|
-
.then(audioBuffer =>
|
|
153
|
+
.then(arrayBuffer => soundDecoderContext.decodeAudioData(arrayBuffer))
|
|
154
|
+
.then(audioBuffer =>
|
|
155
|
+
{
|
|
156
|
+
this.sampleChannels = [];
|
|
157
|
+
for (let i = audioBuffer.numberOfChannels; i--;)
|
|
158
|
+
this.sampleChannels[i] = audioBuffer.getChannelData(i);
|
|
159
|
+
this.sampleRate = audioBuffer.sampleRate;
|
|
160
|
+
});
|
|
122
161
|
}
|
|
123
162
|
}
|
|
124
163
|
let soundDecoderContext; // audio context used only to decode audio files
|
|
@@ -153,48 +192,34 @@ let soundDecoderContext; // audio context used only to decode audio files
|
|
|
153
192
|
* // play the music
|
|
154
193
|
* music_example.play();
|
|
155
194
|
*/
|
|
156
|
-
class Music
|
|
195
|
+
class Music extends Sound
|
|
157
196
|
{
|
|
158
197
|
/** Create a music object and cache the zzfx music samples for later use
|
|
159
198
|
* @param {Array} zzfxMusic - Array of zzfx music parameters
|
|
160
199
|
*/
|
|
161
200
|
constructor(zzfxMusic)
|
|
162
201
|
{
|
|
163
|
-
|
|
202
|
+
super();
|
|
164
203
|
|
|
165
|
-
|
|
204
|
+
if (!soundEnable) return;
|
|
205
|
+
this.randomness = 0;
|
|
206
|
+
this.sampleChannels = zzfxM(...zzfxMusic);
|
|
207
|
+
this.sampleRate = zzfxR;
|
|
166
208
|
}
|
|
167
209
|
|
|
168
210
|
/** Play the music
|
|
169
211
|
* @param {Number} [volume=1] - How much to scale volume by
|
|
170
|
-
* @param {Boolean} [loop=1] - True if the music should loop
|
|
171
|
-
* @return {AudioBufferSourceNode} - The audio node
|
|
212
|
+
* @param {Boolean} [loop=1] - True if the music should loop
|
|
213
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
172
214
|
*/
|
|
173
215
|
play(volume, loop = 1)
|
|
174
|
-
{
|
|
175
|
-
if (!soundEnable) return;
|
|
176
|
-
|
|
177
|
-
return this.source = playSamples(this.cachedSamples, volume, 1, 0, loop);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Stop the music */
|
|
181
|
-
stop()
|
|
182
|
-
{
|
|
183
|
-
if (this.source)
|
|
184
|
-
this.source.stop();
|
|
185
|
-
this.source = 0;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/** Check if music is playing
|
|
189
|
-
* @return {Boolean}
|
|
190
|
-
*/
|
|
191
|
-
isPlaying() { return this.source; }
|
|
216
|
+
{ return super.play(0, volume, 1, 1, loop); }
|
|
192
217
|
}
|
|
193
218
|
|
|
194
|
-
/** Play an mp3 or wav audio from a local file or url
|
|
219
|
+
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
195
220
|
* @param {String} url - Location of sound file to play
|
|
196
221
|
* @param {Number} [volume=1] - How much to scale volume by
|
|
197
|
-
* @param {Boolean} [loop=1] - True if the music should loop
|
|
222
|
+
* @param {Boolean} [loop=1] - True if the music should loop
|
|
198
223
|
* @return {HTMLAudioElement} - The audio element for this sound
|
|
199
224
|
* @memberof Audio */
|
|
200
225
|
function playAudioFile(url, volume=1, loop=1)
|
|
@@ -258,9 +283,10 @@ let audioContext;
|
|
|
258
283
|
* @param {Number} [rate=1] - The playback rate to use
|
|
259
284
|
* @param {Number} [pan=0] - How much to apply stereo panning
|
|
260
285
|
* @param {Boolean} [loop=0] - True if the sound should loop when it reaches the end
|
|
286
|
+
* @param {Number} [sampleRate=44100] - Sample rate for the sound
|
|
261
287
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
262
288
|
* @memberof Audio */
|
|
263
|
-
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
289
|
+
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate=zzfxR)
|
|
264
290
|
{
|
|
265
291
|
if (!soundEnable) return;
|
|
266
292
|
|
|
@@ -277,7 +303,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
277
303
|
}
|
|
278
304
|
|
|
279
305
|
// create buffer and source
|
|
280
|
-
const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length,
|
|
306
|
+
const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, sampleRate),
|
|
281
307
|
source = audioContext.createBufferSource();
|
|
282
308
|
|
|
283
309
|
// copy samples to buffer and setup source
|
package/src/engineBuild.js
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
* - Run custom build steps
|
|
7
7
|
* - Check for errors
|
|
8
8
|
* - Output to build folder
|
|
9
|
-
* @namespace Build
|
|
10
9
|
*/
|
|
11
10
|
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
12
13
|
const ENGINE_NAME = 'littlejs';
|
|
13
14
|
const BUILD_FOLDER = 'build';
|
|
14
15
|
const SOURCE_FOLDER = 'src';
|
|
@@ -54,7 +55,7 @@ Build
|
|
|
54
55
|
'Build Engine -- all',
|
|
55
56
|
`${BUILD_FOLDER}/${ENGINE_NAME}.js`,
|
|
56
57
|
[`${SOURCE_FOLDER}/engineDebug.js`, ...engineSourceFiles],
|
|
57
|
-
[],
|
|
58
|
+
[], true
|
|
58
59
|
);
|
|
59
60
|
|
|
60
61
|
Build
|
|
@@ -62,7 +63,7 @@ Build
|
|
|
62
63
|
'Build Engine -- release',
|
|
63
64
|
`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
|
|
64
65
|
[`${SOURCE_FOLDER}/engineRelease.js`, ...engineSourceFiles],
|
|
65
|
-
[],
|
|
66
|
+
[], true
|
|
66
67
|
);
|
|
67
68
|
|
|
68
69
|
Build
|
|
@@ -95,16 +96,31 @@ console.log(`Engine built in ${((Date.now() - startTime)/1e3).toFixed(2)} second
|
|
|
95
96
|
|
|
96
97
|
// A single build with its own source files, build steps, and output file
|
|
97
98
|
// - each build step is a callback that accepts a single filename
|
|
98
|
-
function Build(message, outputFile, files=[], buildSteps=[],
|
|
99
|
+
function Build(message, outputFile, files=[], buildSteps=[], isPrimaryBuild)
|
|
99
100
|
{
|
|
100
101
|
console.log(message);
|
|
101
102
|
|
|
102
103
|
// copy files into a buffer
|
|
103
104
|
let buffer = '';
|
|
104
|
-
if (
|
|
105
|
-
|
|
105
|
+
if (isPrimaryBuild)
|
|
106
|
+
{
|
|
107
|
+
// add license and strict mode to top
|
|
108
|
+
buffer += license + '\n';
|
|
109
|
+
buffer += "'use strict';\n\n";
|
|
110
|
+
}
|
|
111
|
+
|
|
106
112
|
for (const file of files)
|
|
107
|
-
|
|
113
|
+
{
|
|
114
|
+
// get file content
|
|
115
|
+
let fileContent = fs.readFileSync(file) + '\n';
|
|
116
|
+
|
|
117
|
+
// remove first 'use strict' from each file
|
|
118
|
+
if (isPrimaryBuild)
|
|
119
|
+
fileContent = fileContent.replace("'use strict';", '');
|
|
120
|
+
|
|
121
|
+
// add it to the buffer
|
|
122
|
+
buffer += fileContent;
|
|
123
|
+
}
|
|
108
124
|
|
|
109
125
|
// output file
|
|
110
126
|
fs.writeFileSync(outputFile, buffer, {flag: 'w+'});
|
package/src/engineDraw.js
CHANGED
|
@@ -211,12 +211,12 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
211
211
|
{
|
|
212
212
|
if (!screenSpace)
|
|
213
213
|
{
|
|
214
|
-
//
|
|
214
|
+
// transform from world space to screen space
|
|
215
215
|
pos = worldToScreen(pos);
|
|
216
216
|
size = size.scale(cameraScale);
|
|
217
217
|
}
|
|
218
218
|
context.save();
|
|
219
|
-
context.translate(pos.x+.5
|
|
219
|
+
context.translate(pos.x+.5, pos.y+.5);
|
|
220
220
|
context.rotate(angle);
|
|
221
221
|
context.scale(mirror ? -size.x : size.x, size.y);
|
|
222
222
|
drawFunction(context);
|
package/src/engineInput.js
CHANGED
|
@@ -191,18 +191,26 @@ function mouseToScreen(mousePos)
|
|
|
191
191
|
const stickData = [];
|
|
192
192
|
function gamepadsUpdate()
|
|
193
193
|
{
|
|
194
|
-
|
|
194
|
+
// update touch gamepad if enabled
|
|
195
|
+
if (touchGamepadEnable && isTouchDevice)
|
|
195
196
|
{
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
// create the touch gamepad if it doesn't exist
|
|
198
|
+
if (!touchGamepadButtons)
|
|
199
|
+
createTouchGamepad();
|
|
199
200
|
|
|
200
|
-
|
|
201
|
-
const data = inputData[1] || (inputData[1] = []);
|
|
202
|
-
for (let i=10; i--;)
|
|
201
|
+
if (touchGamepadTimer.isSet())
|
|
203
202
|
{
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
// read virtual analog stick
|
|
204
|
+
const sticks = stickData[0] || (stickData[0] = []);
|
|
205
|
+
sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
|
|
206
|
+
|
|
207
|
+
// read virtual gamepad buttons
|
|
208
|
+
const data = inputData[1] || (inputData[1] = []);
|
|
209
|
+
for (let i=10; i--;)
|
|
210
|
+
{
|
|
211
|
+
const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
|
|
212
|
+
data[j] = touchGamepadButtons[i] ? 1 + 2*!gamepadIsDown(j,0) : 4*gamepadIsDown(j,0);
|
|
213
|
+
}
|
|
206
214
|
}
|
|
207
215
|
}
|
|
208
216
|
|
|
@@ -299,6 +307,10 @@ if (isTouchDevice)
|
|
|
299
307
|
// set was touching
|
|
300
308
|
wasTouching = touching;
|
|
301
309
|
|
|
310
|
+
// prevent default handling like copy and magnifier lens
|
|
311
|
+
if (document.hasFocus()) // allow document to get focus
|
|
312
|
+
e.preventDefault();
|
|
313
|
+
|
|
302
314
|
// must return true so the document will get focus
|
|
303
315
|
return true;
|
|
304
316
|
}
|
|
@@ -311,7 +323,7 @@ if (isTouchDevice)
|
|
|
311
323
|
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
312
324
|
|
|
313
325
|
// create the touch gamepad, called automatically by the engine
|
|
314
|
-
|
|
326
|
+
function createTouchGamepad()
|
|
315
327
|
{
|
|
316
328
|
// touch input internal variables
|
|
317
329
|
touchGamepadButtons = [];
|
package/src/engineWebGL.js
CHANGED
|
@@ -31,7 +31,7 @@ let glActiveTexture, glShader, glArrayBuffer, glPositionData, glColorData, glBat
|
|
|
31
31
|
|
|
32
32
|
///////////////////////////////////////////////////////////////////////////////
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// Initalize WebGL, called automatically by the engine
|
|
35
35
|
function glInit()
|
|
36
36
|
{
|
|
37
37
|
// create the canvas and tile texture
|
|
@@ -70,6 +70,48 @@ function glInit()
|
|
|
70
70
|
glBatchCount = 0;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// Setup render each frame, called automatically by engine
|
|
74
|
+
function glPreRender()
|
|
75
|
+
{
|
|
76
|
+
// clear and set to same size as main canvas
|
|
77
|
+
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
78
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
79
|
+
|
|
80
|
+
// set up the shader
|
|
81
|
+
glContext.useProgram(glShader);
|
|
82
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
83
|
+
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
84
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
85
|
+
glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
86
|
+
glSetBlendMode();
|
|
87
|
+
|
|
88
|
+
// set vertex attributes
|
|
89
|
+
let offset = 0;
|
|
90
|
+
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
91
|
+
{
|
|
92
|
+
const location = glContext.getAttribLocation(glShader, name);
|
|
93
|
+
glContext.enableVertexAttribArray(location);
|
|
94
|
+
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
95
|
+
offset += size*typeSize;
|
|
96
|
+
}
|
|
97
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
98
|
+
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
99
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
100
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
101
|
+
|
|
102
|
+
// build the transform matrix
|
|
103
|
+
const sx = 2 * cameraScale / mainCanvas.width;
|
|
104
|
+
const sy = 2 * cameraScale / mainCanvas.height;
|
|
105
|
+
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
106
|
+
new Float32Array([
|
|
107
|
+
sx, 0, 0, 0,
|
|
108
|
+
0, sy, 0, 0,
|
|
109
|
+
1, 1, -1, 1,
|
|
110
|
+
-1-sx*cameraPos.x, -1-sy*cameraPos.y, 0, 0
|
|
111
|
+
])
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
73
115
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
74
116
|
* @param {Boolean} [additive=0]
|
|
75
117
|
* @memberof WebGL */
|
|
@@ -149,48 +191,6 @@ function glCreateTexture(image)
|
|
|
149
191
|
return texture;
|
|
150
192
|
}
|
|
151
193
|
|
|
152
|
-
// called automatically by engine before render
|
|
153
|
-
function glPreRender()
|
|
154
|
-
{
|
|
155
|
-
// clear and set to same size as main canvas
|
|
156
|
-
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
157
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
158
|
-
|
|
159
|
-
// set up the shader
|
|
160
|
-
glContext.useProgram(glShader);
|
|
161
|
-
glContext.activeTexture(gl_TEXTURE0);
|
|
162
|
-
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
163
|
-
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
164
|
-
glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
165
|
-
glSetBlendMode();
|
|
166
|
-
|
|
167
|
-
// set vertex attributes
|
|
168
|
-
let offset = 0;
|
|
169
|
-
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
170
|
-
{
|
|
171
|
-
const location = glContext.getAttribLocation(glShader, name);
|
|
172
|
-
glContext.enableVertexAttribArray(location);
|
|
173
|
-
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
174
|
-
offset += size*typeSize;
|
|
175
|
-
}
|
|
176
|
-
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
177
|
-
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
178
|
-
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
179
|
-
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
180
|
-
|
|
181
|
-
// build the transform matrix
|
|
182
|
-
const sx = 2 * cameraScale / mainCanvas.width;
|
|
183
|
-
const sy = 2 * cameraScale / mainCanvas.height;
|
|
184
|
-
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
185
|
-
new Float32Array([
|
|
186
|
-
sx, 0, 0, 0,
|
|
187
|
-
0, sy, 0, 0,
|
|
188
|
-
1, 1, -1, 1,
|
|
189
|
-
-1-sx*cameraPos.x, -1-sy*cameraPos.y, 0, 0
|
|
190
|
-
])
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
194
|
/** Draw all sprites and clear out the buffer, called automatically by the system whenever necessary
|
|
195
195
|
* @memberof WebGL */
|
|
196
196
|
function glFlush()
|