diff --git a/index.html b/index.html
index 37d1caf..b5c108b 100644
--- a/index.html
+++ b/index.html
@@ -1,5 +1,4 @@
-
WebGL test application
@@ -13,16 +12,15 @@
+
-
-
-
\ No newline at end of file
diff --git a/main.js b/main.js
index 17e5eaf..38765dc 100644
--- a/main.js
+++ b/main.js
@@ -25,6 +25,8 @@
/* default fragment and vertex shaders, if we were clever we would import these
* from a file, rather than just hard-coding them here */
+
+/* Texture Shader */
var fragmentShaderSource =
"precision mediump float; \
uniform sampler2D sampler; \
@@ -46,18 +48,17 @@
vUV = uv; \
}";
- var camera;
- var shaderProgram;
- var projectionMatrix;
- var viewMatrix;
- var squareVertexPositionBuffer;
- var squareTexture;
- var gl;
+var camera;
+var shaderProgram;
+var projectionMatrix;
+var viewMatrix;
+var terrain;
+var gl;
- /* compile the provided shaderCode and return a shader which we can then
- * include in the shader program */
- function compileShader(gl, type, shaderCode)
- {
+/* compile the provided shaderCode and return a shader which we can then
+ * include in the shader program */
+function compileShader(gl, type, shaderCode)
+{
var shader = gl.createShader(type);
gl.shaderSource(shader, shaderCode);
gl.compileShader(shader);
@@ -67,15 +68,15 @@
return null;
}
return shader;
- }
+}
- /* compile a shader program from the provided shader source
- * @param vertexShaderCode vertex shader source code to compile
- * @param fragmentShaderCode fragment shader source code to compile
- * @returns shaderProgram object, or null if failed.
- */
- function createShaderProgram(gl, vertexShaderCode, fragmentShaderCode)
- {
+/* compile a shader program from the provided shader source
+ * @param vertexShaderCode vertex shader source code to compile
+ * @param fragmentShaderCode fragment shader source code to compile
+ * @returns shaderProgram object, or null if failed.
+ */
+function createShaderProgram(gl, vertexShaderCode, fragmentShaderCode)
+{
var fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentShaderCode);
var vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexShaderCode);
var shader = gl.createProgram();
@@ -111,144 +112,85 @@
}
return shader;
- }
+}
- function initGL(canvas)
- {
- var _gl;
- try {
- _gl = canvas.getContext("experimental-webgl") || canvas.getConext("webgl");
- }
- catch(e) {}
+function initGL(canvas)
+{
+ var _gl;
+ try {
+ _gl = canvas.getContext("experimental-webgl") || canvas.getConext("webgl");
+ }
+ catch(e) {}
- if (!_gl) {
- alert("Could not initialise WebGL. Your browser may not support it");
+ if (!_gl) {
+ alert("Could not initialise WebGL. Your browser may not support it");
return null;
}
- _gl.viewport(0,0, canvas.width, canvas.height);
- //gl.clearDepth(1.0);
- //gl.cullFace(gl.FRONT);
- _gl.clearColor(0.0, 0.0, 0.0, 1.0);
- _gl.enable(_gl.DEPTH_TEST);
+ _gl.viewport(0,0, canvas.width, canvas.height);
+ _gl.clearDepth(1.0);
+ _gl.cullFace(_gl.FRONT);
+ _gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ _gl.enable(_gl.DEPTH_TEST);
- return _gl;
- }
+ return _gl;
+}
- function initBuffers(gl)
- {
- var vertices = [
- /* x, y, z, u, v */
- 2.0, 2.0, 0.0, 0.0, 0.0,
- -2.0, 2.0, 0.0, 1.0, 0.0,
- 2.0, -2.0, 0.0, 0.0, 1.0,
- -2.0, -2.0, 0.0, 1.0, 1.0,
- ];
+function loadImage(gl, fileURL)
+{
+ var image = new Image();
+ image.src = fileURL;
+ image.texture = null;
- squareVertexPositionBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
- }
-
- /*
- function initTerrainMesh(gl, heightMap)
- {
- var vertices = []
-
- for (var x = 0; x < 512; x++) {
- for (var z = 0; z < 512; z++) {
- var vertex = [];
- vertex[0] = x;
- vertex[1] = heightMap[x,z];
- vertex[2] = z;
- vertex[3] = 0;
- vertex[4] = 0;
- vertices.push(vertex);
- }
- }
- squareVertexPositionBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
- }
-
-
- function loadTextures(gl)
- {
- //squareTexture = loadImage(gl, "terrain.png");
-
- }
-
- function loadImage(gl, fileURL)
- {
- var image = new Image();
- image.src = fileURL;
- image.texture = null;
-
- image.onload = function(e) {
- var texture = gl.createTexture();
- // gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
-
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.bindTexture(gl.TEXTURE_2D, null);
- image.texture = texture;
- }
- return image;
- }
- */
-
- function createTextureFromArray(gl, data, width, height)
- {
- var image = new Image();
- var texture = gl.createTexture();
-
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, new Uint8Array(data));
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.bindTexture(gl.TEXTURE_2D, null);
- image.texture = texture;
-
- return image;
- }
+ image.onload = function(e) {
+ var texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ image.texture = texture;
+ }
+ return image;
+}
+
+/* Given R,G,B data in an array convert this to a WebGL texture */
+function createTextureFromArray(gl, data, width, height)
+{
+ var texture = gl.createTexture();
+
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, new Uint8Array(data));
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ return texture;
+}
- function drawFrame(gl)
- {
- viewMatrix = camera.view;
-
+function drawFrame(gl)
+{
+ viewMatrix = camera.view;
+
gl.useProgram(shaderProgram);
-
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.uniformMatrix4fv(shaderProgram.uniform.uPMatrix, false, projectionMatrix);
- gl.uniformMatrix4fv(shaderProgram.uniform.uMVMatrix, false, viewMatrix);
+ gl.uniformMatrix4fv(shaderProgram.uniform.uMVMatrix, false, viewMatrix);
gl.uniform1i(shaderProgram.uniform.sampler, 0);
- if (squareTexture.texture) {
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D,squareTexture.texture);
- }
-
- gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
-
- gl.enableVertexAttribArray(shaderProgram.attribute.position);
- gl.enableVertexAttribArray(shaderProgram.attribute.uv);
-
- gl.vertexAttribPointer(shaderProgram.attribute.position, 3, gl.FLOAT, false, 4*(3+2), 0);
- gl.vertexAttribPointer(shaderProgram.attribute.uv, 2, gl.FLOAT, false, 4*(3+2), 3*4);
-
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
- }
+
+ terrain.render(gl);
+}
- function tick()
- {
+function tick()
+{
requestAnimFrame(tick);
drawFrame(gl);
- }
+}
- function main()
- {
+function main()
+{
var canvas = document.getElementById("viewport");
gl = initGL(canvas);
@@ -258,19 +200,17 @@
mat4.perspective(45, canvas.width / canvas.height, 0.1, 100.0, projectionMatrix);
shaderProgram = createShaderProgram(gl, vertexShaderSource, fragmentShaderSource);
-
- initBuffers(gl);
- //loadTextures(gl);
-
+
camera = Object.create(Camera).init(canvas);
camera.position = [0.0, 0.0, 7.0];
- var heightMap = Object.create(DiamondSquare).init(513, 0).image;
- squareTexture = createTextureFromArray(gl, heightMap, 512,512);
- //initTerrainMesh(gl, heightMap);
+ var heightMap = Object.create(DiamondSquare).init(129, 0);
+ terrain = Object.create(Terrain).init(gl, heightMap.data, 129, 129);
+ terrain.texture = createTextureFromArray(gl, heightMap.image, 128, 128);
+ terrain.shaderProgram = shaderProgram;
requestAnimFrame(tick);
- }
-
+}
+
/* once the page has finished loading, kick off the main() function */
window.addEventListener("load", main);
\ No newline at end of file
diff --git a/terrain.js b/terrain.js
new file mode 100644
index 0000000..bb99c86
--- /dev/null
+++ b/terrain.js
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2014, Chris Dewbery
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgement in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
+
+var Terrain = Object.create(Object, {
+
+ _gl : {
+ writable: true,
+ value : null,
+ },
+ _verticesBuffer: {
+ writable: true,
+ value: null,
+ },
+ _indicesBuffer : {
+ writable: true,
+ value: null,
+ },
+ _texture : {
+ writable: true,
+ value: null,
+ },
+ width : {
+ value: 0,
+ },
+ length : {
+ value: 0,
+ },
+ scale : {
+ value : 10,
+ },
+
+ generateVerticesFromHeightMap: {
+ value: function (heightMap, width, length) {
+
+ var vertices = [];
+ var halfWidth = width / 2.0;
+ var halfLength = length / 2.0;
+ var index = 0;
+
+ for (var z = 0; z < length; z++) {
+ for (var x = 0; x < width; x++) {
+ vertices[index++] = x - halfWidth; // x
+ vertices[index++] = heightMap[x][z] / this.scale; // y
+ vertices[index++] = z - halfLength; // z
+ vertices[index++] = x / (width - 1); // u
+ vertices[index++] = z / (length - 1); // v
+ }
+ }
+ return vertices;
+ }
+ },
+
+ generateTriangleStripIndices: {
+ value: function(width, length) {
+
+ var indices = [];
+ var index = 0;
+
+ for (var z = 0; z < length - 1; z++) {
+ if (z % 2 == 0) {
+ // even row, move from left to right
+ for (var x = 0; x < width; x++) {
+ indices[index++] = x + (z * width);
+ indices[index++] = x + (z * width) + width;
+ }
+ // insert degenerate vertex if this isn't the last row
+ if (z != length - 2) {
+ indices[index++] = --x + (z * width);
+ }
+ }
+ else {
+
+ // Odd row, move from right to left
+ for ( var x = width - 1; x >= 0; x-- ) {
+ indices[index++] = x + (z * width);
+ indices[index++] = x + (z * width) + width;
+ }
+ // Insert degenerate vertex if this isn't the last row
+ if (z != length - 2) {
+ index[index++] = ++x + (z * width);
+ }
+ }
+ }
+ return indices;
+ }
+ },
+
+ texture : {
+ get : function() {
+ return this._texture;
+ },
+ set : function(texture) {
+ this._texture = texture;
+ },
+ },
+
+ shaderProgram : {
+ get : function() {
+ return this._shaderProgram;
+ },
+ set : function(shaderProgram) {
+ this._shaderProgram = shaderProgram;
+ },
+ },
+
+ /* generate terrain mesh from the provided height map */
+ init : {
+ value : function(gl, heightMap, width, length) {
+ var vertices = this.generateVerticesFromHeightMap(heightMap, width, length);
+ var indices = this.generateTriangleStripIndices(width, length);
+
+ this._verticesBuffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
+ this._verticesBuffer.numIndices = vertices.length;
+
+ this._indicesBuffer = gl.createBuffer();
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indicesBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
+ this._indicesBuffer.numIndices = indices.length;
+
+ this._gl = gl;
+ this.width = width;
+ this.length = length;
+
+ return this;
+ }
+ },
+
+ /* render terrain mesh */
+ render : {
+ value : function (gl) {
+
+ if (this._texture) {
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, this._texture);
+ }
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer);
+
+ gl.enableVertexAttribArray(this._shaderProgram.attribute.position);
+ gl.enableVertexAttribArray(this._shaderProgram.attribute.uv);
+
+ /* vertices are in the format (x,y,z) (u,v) */
+ gl.vertexAttribPointer(this._shaderProgram.attribute.position, 3, gl.FLOAT, false, 20, 0);
+ gl.vertexAttribPointer(this._shaderProgram.attribute.uv, 2, gl.FLOAT, false, 20, 12);
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indicesBuffer);
+ //gl.drawElements(gl.TRIANGLE_STRIP, this._indicesBuffer.numIndices, gl.UNSIGNED_SHORT, 0);
+ gl.drawElements(gl.TRIANGLE_STRIP, this._indicesBuffer.numIndices, gl.UNSIGNED_SHORT, 0);
+ }
+ },
+
+ });
\ No newline at end of file