From ecde0af25609bcc783811244c4c93400c2054bd5 Mon Sep 17 00:00:00 2001 From: Robert Kaiser Date: Sun, 9 Mar 2014 01:56:44 +0100 Subject: [PATCH] add first pieces of support for webGL maps --- index.html | 5 ++ js/map.js | 135 +++++++++++++++++++++++++++++++++++++++++++++-- js/ui.js | 12 ++++- style/lantea.css | 13 ++++- 4 files changed, 160 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 393a368..2b8fb97 100644 --- a/index.html +++ b/index.html @@ -102,6 +102,9 @@ +
+ Unable to initialize WebGL. You need a browser that supports it. +

@@ -111,6 +114,8 @@

+ + Please use a browser that supports <canvas> elements. diff --git a/js/map.js b/js/map.js index 361420b..9d2ecdb 100644 --- a/js/map.js +++ b/js/map.js @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -var gMapCanvas, gMapContext, gTrackCanvas, gTrackContext, gGeolocation; +var gMapCanvas, gMapContext, gGLMapCanvas, gMapGL, gTrackCanvas, gTrackContext, gGeolocation; var gDebug = false; var gTileSize = 256; @@ -72,8 +72,23 @@ var gCurPosMapCache; function initMap() { gGeolocation = navigator.geolocation; + // Set up canvas contexts. TODO: Remove 2D map once GL support works. gMapCanvas = document.getElementById("map"); gMapContext = gMapCanvas.getContext("2d"); + gGLMapCanvas = document.getElementById("glmap"); + try { + // Try to grab the standard context. If it fails, fallback to experimental. + // We also try to tell it we do not need a depth buffer. + gMapGL = gGLMapCanvas.getContext("webgl", {depth: false}) || + gGLMapCanvas.getContext("experimental-webgl", {depth: false}); + gMapGL.viewport(0, 0, gMapGL.drawingBufferWidth, gMapGL.drawingBufferHeight); + } + catch(e) {} + // If we don't have a GL context, give up now + if (!gMapGL) { + showGLWarningDialog(); + gMapGL = null; + } gTrackCanvas = document.getElementById("track"); gTrackContext = gTrackCanvas.getContext("2d"); if (!gActiveMap) @@ -89,6 +104,8 @@ function initMap() { } } + gAction.addEventListener("prefload-done", initGL, false); + console.log("map vars set, loading prefs..."); loadPrefs(); } @@ -122,7 +139,7 @@ function loadPrefs(aEvent) { gLoadingTile = new Image(); gLoadingTile.src = "style/loading.png"; gLoadingTile.onload = function() { - var throwEv = new CustomEvent("mapinit-done"); + var throwEv = new CustomEvent("prefload-done"); gAction.dispatchEvent(throwEv); }; } @@ -190,12 +207,124 @@ function loadPrefs(aEvent) { } } +function initGL() { + if (gMapGL) { + gMapGL.clearColor(0.0, 0.0, 0.0, 0.5); // Set clear color to black, fully opaque. + gMapGL.clear(gMapGL.COLOR_BUFFER_BIT|gMapGL.DEPTH_BUFFER_BIT); // Clear the color. + + // Create and initialize the shaders. + var vertShader = gMapGL.createShader(gMapGL.VERTEX_SHADER); + var vertShaderSource = + 'attribute vec2 aVertexPosition;\n' + + 'attribute vec2 aTextureCoord;\n\n' + + 'uniform vec2 uResolution;\n\n' + + 'varying highp vec2 vTextureCoord;\n\n' + + 'void main(void) {\n' + + // convert the rectangle from pixels to -1.0 to +1.0 (clipspace) 0.0 to 1.0 + ' vec2 clipSpace = aVertexPosition * 2.0 / uResolution - 1.0;\n' + + ' gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);\n' + + ' vTextureCoord = aTextureCoord;\n' + + '}'; + var fragShader = gMapGL.createShader(gMapGL.FRAGMENT_SHADER); + var fragShaderSource = + 'varying highp vec2 vTextureCoord;\n\n' + + 'uniform sampler2D uImage;\n\n' + + 'void main(void) {\n' + + ' gl_FragColor = texture2D(uImage, vTextureCoord);\n' + + '}'; + + gMapGL.shaderSource(vertShader, vertShaderSource); + // Compile the shader program. + gMapGL.compileShader(vertShader); + // See if it compiled successfully. + if (!gMapGL.getShaderParameter(vertShader, gMapGL.COMPILE_STATUS)) { + console.log("An error occurred compiling the vertix shader: " + gMapGL.getShaderInfoLog(vertShader)); + return null; + } + gMapGL.shaderSource(fragShader, fragShaderSource); + // Compile the shader program. + gMapGL.compileShader(fragShader); + // See if it compiled successfully. + if (!gMapGL.getShaderParameter(fragShader, gMapGL.COMPILE_STATUS)) { + console.log("An error occurred compiling the fragment shader: " + gMapGL.getShaderInfoLog(fragShader)); + return null; + } + + var shaderProgram = gMapGL.createProgram(); + gMapGL.attachShader(shaderProgram, vertShader); + gMapGL.attachShader(shaderProgram, fragShader); + gMapGL.linkProgram(shaderProgram); + // If creating the shader program failed, alert + if (!gMapGL.getProgramParameter(shaderProgram, gMapGL.LINK_STATUS)) { + alert("Unable to initialize the shader program."); + } + gMapGL.useProgram(shaderProgram); + var vertexPositionAttribute = gMapGL.getAttribLocation(shaderProgram, "aVertexPosition"); + var textureCoordAttribute = gMapGL.getAttribLocation(shaderProgram, "aTextureCoord"); + var resolutionAttribute = gMapGL.getUniformLocation(shaderProgram, "uResolution"); + + var tileVerticesBuffer = gMapGL.createBuffer(); + gMapGL.bindBuffer(gMapGL.ARRAY_BUFFER, tileVerticesBuffer); + // The vertices are the coordinates of the corner points of the square. + var vertices = [ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 1.0, 1.0]; + gMapGL.bufferData(gMapGL.ARRAY_BUFFER, new Float32Array(vertices), gMapGL.STATIC_DRAW); + gMapGL.enableVertexAttribArray(textureCoordAttribute); + gMapGL.vertexAttribPointer(textureCoordAttribute, 2, gMapGL.FLOAT, false, 0, 0); + + // Map Texture + var mapTexture = gMapGL.createTexture(); + gMapGL.bindTexture(gMapGL.TEXTURE_2D, mapTexture); + // Set the parameters so we can render any size image. + gMapGL.texParameteri(gMapGL.TEXTURE_2D, gMapGL.TEXTURE_WRAP_S, gMapGL.CLAMP_TO_EDGE); + gMapGL.texParameteri(gMapGL.TEXTURE_2D, gMapGL.TEXTURE_WRAP_T, gMapGL.CLAMP_TO_EDGE); + gMapGL.texParameteri(gMapGL.TEXTURE_2D, gMapGL.TEXTURE_MIN_FILTER, gMapGL.NEAREST); + gMapGL.texParameteri(gMapGL.TEXTURE_2D, gMapGL.TEXTURE_MAG_FILTER, gMapGL.NEAREST); + // Upload the image into the texture. + gMapGL.texImage2D(gMapGL.TEXTURE_2D, 0, gMapGL.RGBA, gMapGL.RGBA, gMapGL.UNSIGNED_BYTE, gLoadingTile); + + gMapGL.uniform2f(resolutionAttribute, gGLMapCanvas.width, gGLMapCanvas.height); + + // Create a buffer for the position of the rectangle corners. + var mapVerticesTextureCoordBuffer = gMapGL.createBuffer(); + gMapGL.bindBuffer(gMapGL.ARRAY_BUFFER, mapVerticesTextureCoordBuffer); + var x_start = 10; + var i_width = 512; + var y_start = 10; + var i_height = 512; + 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]; + gMapGL.bufferData(gMapGL.ARRAY_BUFFER, new Float32Array(textureCoordinates), gMapGL.STATIC_DRAW); + gMapGL.enableVertexAttribArray(vertexPositionAttribute); + gMapGL.vertexAttribPointer(vertexPositionAttribute, 2, gMapGL.FLOAT, false, 0, 0); + + // There are 6 indices in textureCoordinates. + gMapGL.drawArrays(gMapGL.TRIANGLES, 0, 6); + } + + var throwEv = new CustomEvent("mapinit-done"); + gAction.dispatchEvent(throwEv); +} + function resizeAndDraw() { var viewportWidth = Math.min(window.innerWidth, window.outerWidth); var viewportHeight = Math.min(window.innerHeight, window.outerHeight); - if (gMapCanvas && gTrackCanvas) { + if (gMapCanvas && gGLMapCanvas && gTrackCanvas) { gMapCanvas.width = viewportWidth; gMapCanvas.height = viewportHeight; + gGLMapCanvas.width = viewportWidth; + gGLMapCanvas.height = viewportHeight; + gMapGL.viewport(0, 0, gMapGL.drawingBufferWidth, gMapGL.drawingBufferHeight); gTrackCanvas.width = viewportWidth; gTrackCanvas.height = viewportHeight; drawMap(); diff --git a/js/ui.js b/js/ui.js index d76f915..2ee2272 100644 --- a/js/ui.js +++ b/js/ui.js @@ -23,7 +23,7 @@ window.onload = function() { mSel.add(opt, null); } - var areas = document.getElementsByClassName('overlayArea'); + var areas = document.getElementsByClassName("overlayArea"); for (var i = 0; i <= areas.length - 1; i++) { areas[i].addEventListener("mouseup", uiEvHandler, false); areas[i].addEventListener("mousemove", uiEvHandler, false); @@ -208,6 +208,16 @@ function showUploadDialog() { dia.classList.remove("hidden"); } +function showGLWarningDialog() { + var dia = document.getElementById("dialogArea"); + var areas = dia.children; + for (var i = 0; i <= areas.length - 1; i++) { + areas[i].style.display = "none"; + } + document.getElementById("noGLwarning").style.display = "block"; + dia.classList.remove("hidden"); +} + function cancelDialog() { document.getElementById("dialogArea").classList.add("hidden"); document.getElementById("uploadTrackButton").disabled = false; diff --git a/style/lantea.css b/style/lantea.css index f5364ca..d05d1cc 100644 --- a/style/lantea.css +++ b/style/lantea.css @@ -78,16 +78,27 @@ h1 { border-radius: 3px; } -#map, #track { +#map, #glmap, #track { position: fixed; border: 0; top: 0; left: 0; right: 0; bottom: 0; +} + +#map { z-index: 1; } +#glmap { + z-index: 2; +} + +#track { + z-index: 3; +} + #action { position: absolute; bottom: 5px; -- 2.35.3