littlejsengine 1.7.12 → 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.
@@ -99,7 +99,7 @@ function gameUpdatePost()
99
99
  function gameRender()
100
100
  {
101
101
  // draw a grey square in the background without using webgl
102
- drawRect(cameraPos, tileCollisionSize.add(vec2(5)), new Color(.2,.2,.2), 0, 0);
102
+ drawRect(cameraPos, tileCollisionSize.add(vec2(5)), new Color(.4,.4,.4), 0, 0);
103
103
  }
104
104
 
105
105
  ///////////////////////////////////////////////////////////////////////////////
@@ -7,18 +7,18 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- LittleJS Engine -->
10
- <script src=../../src/engineDebug.js?1712></script>
11
- <script src=../../src/engineUtilities.js?1712></script>
12
- <script src=../../src/engineSettings.js?1712></script>
13
- <script src=../../src/engineObject.js?1712></script>
14
- <script src=../../src/engineDraw.js?1712></script>
15
- <script src=../../src/engineInput.js?1712></script>
16
- <script src=../../src/engineAudio.js?1712></script>
17
- <script src=../../src/engineTileLayer.js?1712></script>
18
- <script src=../../src/engineParticles.js?1712></script>
19
- <script src=../../src/engineMedals.js?1712></script>
20
- <script src=../../src/engineWebGL.js?1712></script>
21
- <script src=../../src/engine.js?1712></script>
10
+ <script src=../../src/engineDebug.js?1713></script>
11
+ <script src=../../src/engineUtilities.js?1713></script>
12
+ <script src=../../src/engineSettings.js?1713></script>
13
+ <script src=../../src/engineObject.js?1713></script>
14
+ <script src=../../src/engineDraw.js?1713></script>
15
+ <script src=../../src/engineInput.js?1713></script>
16
+ <script src=../../src/engineAudio.js?1713></script>
17
+ <script src=../../src/engineTileLayer.js?1713></script>
18
+ <script src=../../src/engineParticles.js?1713></script>
19
+ <script src=../../src/engineMedals.js?1713></script>
20
+ <script src=../../src/engineWebGL.js?1713></script>
21
+ <script src=../../src/engine.js?1713></script>
22
22
 
23
23
  <!-- Add your game scripts here -->
24
- <script src=game.js?1712></script>
24
+ <script src=game.js?1713></script>
@@ -6,7 +6,7 @@
6
6
  <link rel=icon type=image/png href=../favicon.png>
7
7
  </head><body>
8
8
 
9
- <script src=../../build/littlejs.min.js?1712></script>
9
+ <script src=../../build/littlejs.min.js?1713></script>
10
10
  <script>
11
11
 
12
12
  /*
@@ -4,6 +4,8 @@
4
4
  * LittleJS Build System
5
5
  */
6
6
 
7
+ 'use strict';
8
+
7
9
  const PROGRAM_NAME = 'game';
8
10
  const BUILD_FOLDER = 'build';
9
11
  const ROOT_FOLDER = 'examples/typescript';
@@ -7,4 +7,4 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- Add your game scripts here -->
10
- <script src=game.js?1712 type=module></script>
10
+ <script src=game.js?1713 type=module></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.7.12",
3
+ "version": "1.7.21",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "build/littlejs.esm.js",
6
6
  "repository": {
package/src/engine.js CHANGED
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
30
30
  * @type {String}
31
31
  * @default
32
32
  * @memberof Engine */
33
- const engineVersion = '1.7.12';
33
+ const engineVersion = '1.7.21';
34
34
 
35
35
  /** Frames per second to update objects
36
36
  * @type {Number}
@@ -100,11 +100,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
100
100
  debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
101
101
 
102
102
  // setup html
103
- const styleBody = 'margin:0;overflow:hidden;' + // fill the window
104
- 'background:#000;' + // set background color
105
- 'touch-action:none;' + // prevent mobile pinch to resize
106
- 'user-select:none;' + // prevent mobile hold to select
107
- '-webkit-user-select:none'; // compatibility for ios
103
+ const styleBody =
104
+ 'margin:0;overflow:hidden;' + // fill the window
105
+ 'background:#000;' + // set background color
106
+ 'touch-action:none;' + // prevent mobile pinch to resize
107
+ 'user-select:none;' + // prevent mobile hold to select
108
+ '-webkit-user-select:none;' + // compatibility for ios
109
+ '-webkit-touch-callout:none'; // compatibility for ios
108
110
  document.body.style = styleBody;
109
111
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
110
112
  mainContext = mainCanvas.getContext('2d');
@@ -118,7 +120,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
118
120
  overlayContext = overlayCanvas.getContext('2d');
119
121
 
120
122
  // set canvas style
121
- const styleCanvas = 'position:absolute;' +
123
+ const styleCanvas =
124
+ 'position:absolute;' + // position canvas
122
125
  'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
123
126
  (canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
124
127
  (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
@@ -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.cachedSamples = zzfxSound && zzfxG(...zzfxSound);
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
- * @return {AudioBufferSourceNode} - The audio, can be used to stop sound later
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.cachedSamples) return;
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([this.cachedSamples], volume, playbackRate, pan);
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, can be used to stop sound later
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} waveFilename - Filename of wave file to load
105
- * @param {Number} [randomness=.05] - How much to randomize frequency each time sound plays
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(waveFilename, randomness=.05, range, taper)
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 (!soundWaveDecoderContext)
148
+ if (!soundDecoderContext)
116
149
  soundDecoderContext = new AudioContext;
117
150
 
118
- fetch(waveFilename)
151
+ fetch(filename)
119
152
  .then(response => response.arrayBuffer())
120
- .then(arrayBuffer => soundWaveDecoderContext.decodeAudioData(arrayBuffer))
121
- .then(audioBuffer => this.cachedSamples = audioBuffer.getChannelData(0));
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
- if (!soundEnable) return;
202
+ super();
164
203
 
165
- this.cachedSamples = zzfxM(...zzfxMusic);
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 when it reaches the end
171
- * @return {AudioBufferSourceNode} - The audio node, can be used to stop sound later
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 when it reaches the end
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, zzfxR),
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
@@ -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
- [], license
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
- [], license
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=[], topOfFileText)
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 (topOfFileText)
105
- buffer += topOfFileText + '\n';
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
- buffer += fs.readFileSync(file) + '\n';
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+'});
@@ -211,7 +211,11 @@ function debugRender()
211
211
  mainContext.drawImage(overlayCanvas, 0, 0);
212
212
  overlayCanvas.width |= 0;
213
213
 
214
- debugSaveCanvas(mainCanvas);
214
+ // remove alpha and save
215
+ const w = mainCanvas.width, h = mainCanvas.height;
216
+ overlayContext.fillRect(0,0,w,h);
217
+ overlayContext.drawImage(mainCanvas, 0, 0);
218
+ debugSaveCanvas(overlayCanvas);
215
219
  debugTakeScreenshot = 0;
216
220
  }
217
221
 
package/src/engineDraw.js CHANGED
@@ -191,7 +191,7 @@ function drawPoly(points, color=new Color, useWebGL=glEnable, screenSpace)
191
191
  * @param {Boolean} [useWebGL=glEnable]
192
192
  * @param {Boolean} [screenSpace=0]
193
193
  * @memberof Draw */
194
- function drawLine(posA, posB, thickness=.1, color, useWebGL)
194
+ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace)
195
195
  {
196
196
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
197
197
  const size = vec2(thickness, halfDelta.length()*2);
@@ -211,12 +211,12 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
211
211
  {
212
212
  if (!screenSpace)
213
213
  {
214
- // create canvas transform from world space to screen space
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|0, pos.y+.5|0);
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);
@@ -191,18 +191,26 @@ function mouseToScreen(mousePos)
191
191
  const stickData = [];
192
192
  function gamepadsUpdate()
193
193
  {
194
- if (touchGamepadEnable && touchGamepadTimer.isSet())
194
+ // update touch gamepad if enabled
195
+ if (touchGamepadEnable && isTouchDevice)
195
196
  {
196
- // read virtual analog stick
197
- const sticks = stickData[0] || (stickData[0] = []);
198
- sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
197
+ // create the touch gamepad if it doesn't exist
198
+ if (!touchGamepadButtons)
199
+ createTouchGamepad();
199
200
 
200
- // read virtual gamepad buttons
201
- const data = inputData[1] || (inputData[1] = []);
202
- for (let i=10; i--;)
201
+ if (touchGamepadTimer.isSet())
203
202
  {
204
- const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
205
- data[j] = touchGamepadButtons[i] ? 1 + 2*!gamepadIsDown(j,0) : 4*gamepadIsDown(j,0);
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
- if (touchGamepadEnable)
326
+ function createTouchGamepad()
315
327
  {
316
328
  // touch input internal variables
317
329
  touchGamepadButtons = [];
@@ -31,7 +31,7 @@ let glActiveTexture, glShader, glArrayBuffer, glPositionData, glColorData, glBat
31
31
 
32
32
  ///////////////////////////////////////////////////////////////////////////////
33
33
 
34
- // Init WebGL, called automatically by the engine
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()