+ // Create a buffer for the position of the rectangle corners.
+ var mapVerticesTextureCoordBuffer = gMap.gl.createBuffer();
+ gMap.gl.bindBuffer(gMap.gl.ARRAY_BUFFER, mapVerticesTextureCoordBuffer);
+ gMap.gl.enableVertexAttribArray(gMap.glVertexPositionAttr);
+ gMap.gl.vertexAttribPointer(gMap.glVertexPositionAttr, 2, gMap.gl.FLOAT, false, 0, 0);
+ }
+
+ var throwEv = new CustomEvent("mapinit-done");
+ gAction.dispatchEvent(throwEv);
+ },
+
+ draw: function() {
+ gMap.assembleGL();
+ drawTrack();
+ },
+
+ assembleGL: function() {
+ if (!gMap.gl) { return; }
+
+ document.getElementById("zoomLevel").textContent = gMap.pos.z;
+ gMap.zoomFactor = Math.pow(2, gMap.maxZoom - gMap.pos.z);
+ var wid = gMap.gl.drawingBufferWidth * gMap.zoomFactor; // Width in level 18 pixels.
+ var ht = gMap.gl.drawingBufferHeight * gMap.zoomFactor; // Height in level 18 pixels.
+ var size = gMap.tileSize * gMap.zoomFactor; // Tile size in level 18 pixels.
+
+ var xMin = gMap.pos.x - wid / 2; // Corners of the window in level 18 pixels.
+ var yMin = gMap.pos.y - ht / 2;
+ var xMax = gMap.pos.x + wid / 2;
+ var yMax = gMap.pos.y + ht / 2;
+
+ if (gMapPrefsLoaded && mainDB)
+ gPrefs.set("position", gMap.pos);
+
+ // Go through all the tiles in the map, find out if to draw them and do so.
+ for (var x = Math.floor(xMin / size); x < Math.ceil(xMax / size); x++) {
+ for (var y = Math.floor(yMin / size); y < Math.ceil(yMax / size); y++) { // slow script warnings on the tablet appear here!
+ // Only go to loading step if we haven't loaded the texture.
+ var coords = {x: x, y: y, z: gMap.pos.z};
+ var tileKey = getTileKey(gMap.activeMap, normalizeCoords(coords));
+ if (!gMap.glTextures[tileKey]) {
+ // Initiate loading/drawing of the actual tile.
+ gTileService.get(gMap.activeMap, coords,
+ function(aImage, aStyle, aCoords, aTileKey) {
+ // Only actually load if this still applies for the current view.
+ if ((aStyle == gMap.activeMap) && (aCoords.z == gMap.pos.z)) {
+ var URL = window.URL;
+ var imgURL = URL.createObjectURL(aImage);
+ var imgObj = new Image();
+ imgObj.onload = function() {
+ gMap.loadImageToTexture(imgObj, aTileKey);
+ requestAnimationFrame(function(aTimestamp) { gMap.drawGL() });
+ URL.revokeObjectURL(imgURL);
+ }
+ imgObj.src = imgURL;
+ }
+ });
+ }
+ }
+ }
+ requestAnimationFrame(function(aTimestamp) { gMap.drawGL() });
+ },
+
+ drawGL: function() {
+ var wid = gMap.gl.drawingBufferWidth * gMap.zoomFactor; // Width in level 18 pixels.
+ var ht = gMap.gl.drawingBufferHeight * gMap.zoomFactor; // Height in level 18 pixels.
+ var size = gMap.tileSize * gMap.zoomFactor; // Tile size in level 18 pixels.
+
+ var xMin = gMap.pos.x - wid / 2; // Corners of the window in level 18 pixels.
+ var yMin = gMap.pos.y - ht / 2;
+ var xMax = gMap.pos.x + wid / 2;
+ var yMax = gMap.pos.y + ht / 2;
+
+ // Go through all the tiles in the map, find out if to draw them and do so.
+ for (var x = Math.floor(xMin / size); x < Math.ceil(xMax / size); x++) {
+ for (var y = Math.floor(yMin / size); y < Math.ceil(yMax / size); y++) { // slow script warnings on the tablet appear here!
+ // Round here is **CRUCIAL** otherwise the images are filtered
+ // and the performance sucks (more than expected).
+ var xoff = Math.round((x * size - xMin) / gMap.zoomFactor);
+ var yoff = Math.round((y * size - yMin) / gMap.zoomFactor);
+ // Draw the tile, first find out the actual texture to use.
+ var norm = normalizeCoords({x: x, y: y, z: gMap.pos.z});
+ var tileKey = getTileKey(gMap.activeMap, norm);
+ if (!gMap.glTextures[tileKey]) {
+ tileKey = getTileKey("loading", {x: 0, y: 0, z: 0});
+ }
+ gMap.drawTileGL(xoff, yoff, tileKey);
+ }
+ }
+ },
+
+ resizeAndDraw: function() {
+ var viewportWidth = Math.min(window.innerWidth, window.outerWidth);
+ var viewportHeight = Math.min(window.innerHeight, window.outerHeight);
+ if (gGLMapCanvas && gTrackCanvas) {
+ gGLMapCanvas.width = viewportWidth;
+ gGLMapCanvas.height = viewportHeight;
+ gTrackCanvas.width = viewportWidth;
+ gTrackCanvas.height = viewportHeight;
+ if (gMap.gl) {
+ // Size viewport to canvas size.
+ gMap.gl.viewport(0, 0, gMap.gl.drawingBufferWidth, gMap.gl.drawingBufferHeight);
+ // Clear the color.
+ gMap.gl.clear(gMap.gl.COLOR_BUFFER_BIT);
+ // Make sure the vertex shader get the right resolution.
+ gMap.gl.uniform2f(gMap.glResolutionAttr, gGLMapCanvas.width, gGLMapCanvas.height);
+ }
+ gMap.draw();
+ showUI();
+ }
+ },
+
+ drawTileGL: function(aLeft, aRight, aTileKey) {
+ gMap.gl.activeTexture(gMap.gl.TEXTURE0);
+ gMap.gl.bindTexture(gMap.gl.TEXTURE_2D, gMap.glTextures[aTileKey]);
+ // Set uImage to refer to TEXTURE0
+ gMap.gl.uniform1i(gMap.gl.getUniformLocation(gMap.glShaderProgram, "uImage"), 0);
+ var x_start = aLeft;
+ var i_width = gMap.tileSize;
+ var y_start = aRight;
+ var i_height = gMap.tileSize;
+ var textureCoordinates = [
+ x_start, y_start,
+ x_start + i_width, y_start,
+ x_start, y_start + i_height,
+ x_start, y_start + i_height,
+ x_start + i_width, y_start,
+ x_start + i_width, y_start + i_height,
+ ];
+ gMap.gl.bufferData(gMap.gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), gMap.gl.STATIC_DRAW);
+
+ // There are 6 indices in textureCoordinates.
+ gMap.gl.drawArrays(gMap.gl.TRIANGLES, 0, 6);
+ },
+
+ loadImageToTexture: function(aImage, aTileKey) {
+ // TODO: Get rid of old textures.
+ // Create and bind texture.
+ gMap.glTextures[aTileKey] = gMap.gl.createTexture();
+ gMap.gl.bindTexture(gMap.gl.TEXTURE_2D, gMap.glTextures[aTileKey]);
+ // Set params for how the texture minifies and magnifies (wrap params are not needed as we're power-of-two).
+ gMap.gl.texParameteri(gMap.gl.TEXTURE_2D, gMap.gl.TEXTURE_MIN_FILTER, gMap.gl.NEAREST);
+ gMap.gl.texParameteri(gMap.gl.TEXTURE_2D, gMap.gl.TEXTURE_MAG_FILTER, gMap.gl.NEAREST);
+ // Upload the image into the texture.
+ gMap.gl.texImage2D(gMap.gl.TEXTURE_2D, 0, gMap.gl.RGBA, gMap.gl.RGBA, gMap.gl.UNSIGNED_BYTE, aImage);
+ },