sawine@2: var machine; sawine@8: var renderer; sawine@11: var controller; sawine@11: var camera; sawine@19: var cameraSpeed = {"h": 0.1, "v": 0.1, "zoom": 0.5, "pitch": 0.5, "yaw": 0.5}; sawine@13: var keyActionMap = {'A': moveCameraLeft, sawine@13: 'D': moveCameraRight, sawine@13: 'W': moveCameraUp, sawine@13: 'S': moveCameraDown}; sawine@14: var mouseActionMap = {"pitch": [[true, false, false], pitchCamera], sawine@14: "yaw": [[true, false, false], yawCamera], sawine@14: "wheel": zoomCamera}; sawine@2: sawine@2: function main() sawine@2: { sawine@2: var canvas = document.getElementById("machine"); sawine@2: var context = new Context(canvas); sawine@10: context.expand(); sawine@2: var gl = context.gl; sawine@7: var object = new Cube(1, context); sawine@2: gl.clearColor(0.0, 0.0, 0.0, 1.0); sawine@2: gl.enable(gl.DEPTH_TEST); sawine@11: machine = new Machine(object); sawine@22: camera = new Camera(cameraSpeed, [0, 5, 20]); sawine@11: renderer = new Renderer(camera, context); sawine@13: controller = new Controller(keyActionMap, mouseActionMap, camera, machine, renderer); sawine@6: update(); sawine@13: window.onresize = expandContext; sawine@13: document.onkeydown = handleKeyDown; sawine@13: document.onkeyup = handleKeyUp; sawine@13: canvas.onmousedown = handleMouseDown; sawine@13: document.onmouseup = handleMouseUp; sawine@13: document.onmousemove = handleMouseMove; sawine@14: document.onmousewheel = handleMouseWheel; sawine@13: //read("config/camera", configureCamera); sawine@13: } sawine@13: sawine@13: function configureCamera(config) { sawine@13: alert(config); sawine@6: } sawine@6: sawine@6: function update() sawine@6: { sawine@7: requestAnimFrame(update); sawine@11: controller.update(); sawine@10: machine.scene.rotation.x += (random(0, 2) - 2) * 0.001; sawine@10: machine.scene.rotation.y += (random(0, 3) - 1) * 0.001; sawine@10: machine.scene.rotation.z += (random(0, 2) - 1) * 0.001; sawine@8: machine.update(new Date().getTime()); sawine@8: renderer.update(machine.scene); sawine@2: } sawine@2: sawine@9: function random(min, max) sawine@9: { sawine@10: return (min + Math.random() * (max - min)); sawine@9: } sawine@9: sawine@22: function normalize(vectors) sawine@22: { sawine@22: for (var v in vectors) vec3.normalize(v); sawine@22: } sawine@22: sawine@22: function Camera(speed, pos, up, right, target) sawine@4: { sawine@12: this.speed = speed; sawine@22: this.pos = vec3.create(pos || [0, 0, 7]); sawine@22: this.up = vec3.create(up || [0, 1, 0]); sawine@22: this.right = vec3.create(right || [1, 0, 0]); sawine@22: this.target = vec3.create(target || [0, 0, -1]); sawine@22: normalize([this.up, this.right, this.target]); sawine@16: this.matrix = mat4.create(); sawine@11: } sawine@11: Camera.prototype.moveLeft = function() sawine@11: { sawine@19: var dir = vec3.create(this.right); sawine@19: vec3.scale(dir, -this.speed.h); sawine@19: vec3.add(this.pos, dir); sawine@11: } sawine@11: Camera.prototype.moveRight = function() sawine@11: { sawine@19: var dir = vec3.create(this.right); sawine@19: vec3.scale(dir, this.speed.h); sawine@19: vec3.add(this.pos, dir); sawine@11: } sawine@11: Camera.prototype.moveUp = function() sawine@19: { sawine@19: var dir = vec3.create(this.up); sawine@19: vec3.scale(dir, this.speed.v); sawine@19: vec3.add(this.pos, dir); sawine@11: } sawine@11: Camera.prototype.moveDown = function() sawine@19: { sawine@19: var dir = vec3.create(this.up); sawine@19: vec3.scale(dir, -this.speed.v); sawine@19: vec3.add(this.pos, dir); sawine@13: } sawine@14: Camera.prototype.zoom = function(delta) sawine@13: { sawine@19: var dir = vec3.create(this.target); sawine@20: vec3.scale(dir, delta * this.speed.zoom); sawine@19: vec3.add(this.pos, dir); sawine@13: } sawine@19: Camera.prototype.pitch = function(delta) sawine@13: { sawine@21: delta *= -this.speed.pitch; sawine@21: vec3.scale(this.target, Math.cos(delta)); sawine@21: vec3.scale(this.up, Math.sin(delta)); sawine@21: vec3.add(this.target, this.up); sawine@21: vec3.normalize(this.target); sawine@21: vec3.cross(this.target, this.right, this.up); sawine@21: vec3.scale(this.up, -1); sawine@13: } sawine@19: Camera.prototype.yaw = function(delta) sawine@13: { sawine@21: delta *= -this.speed.yaw; sawine@21: vec3.scale(this.right, Math.cos(delta)); sawine@21: vec3.scale(this.target, Math.sin(delta)); sawine@21: vec3.add(this.right, this.target); sawine@21: vec3.normalize(this.right); sawine@21: vec3.cross(this.right, this.up, this.target); sawine@21: vec3.scale(this.target, -1); sawine@19: } sawine@19: Camera.prototype.roll = function(delta) sawine@19: { sawine@19: sawine@12: } sawine@15: Camera.prototype.update = function() sawine@15: { sawine@19: var x = vec3.dot(this.right, this.pos); sawine@19: var y = vec3.dot(this.up, this.pos); sawine@19: var z = vec3.dot(this.target, this.pos); sawine@19: this.matrix = mat4.create([this.right[0], this.up[0], -this.target[0], 0, sawine@19: this.right[1], this.up[1], -this.target[1], 0, sawine@19: this.right[2], this.up[2], -this.target[2], 0, sawine@22: -x, -y, z, 1]); sawine@15: } sawine@12: sawine@13: function Controller(keyActionMap, mouseActionMap, camera, machine, renderer) sawine@11: { sawine@13: this.keyboard = new Keyboard(keyActionMap); sawine@13: this.mouse = new Mouse(mouseActionMap); sawine@11: sawine@11: } sawine@11: Controller.prototype.update = function() sawine@11: { sawine@11: this.keyboard.handle(); sawine@13: this.mouse.handle(); sawine@11: } sawine@11: sawine@13: function Mouse(actionMap) sawine@11: { sawine@13: this.actionMap = actionMap; sawine@14: this.pressed = [false, false, false]; sawine@14: this.wheelDelta = 0; sawine@11: } sawine@13: Mouse.prototype.buttonDown = function(event) sawine@13: { sawine@13: this.pressed[event.which-1] = true; sawine@13: } sawine@13: Mouse.prototype.buttonUp = function(event) sawine@13: { sawine@13: this.pressed[event.which-1] = false; sawine@13: } sawine@13: Mouse.prototype.move = function(event) sawine@13: { sawine@14: this.currentPos = [event.clientX, event.clientY]; sawine@14: } sawine@14: Mouse.prototype.moveWheel = function(event) sawine@14: { sawine@14: this.wheelDelta += event.wheelDelta; sawine@14: } sawine@14: Mouse.prototype.handle = function() sawine@14: { sawine@14: var pos = this.currentPos; sawine@14: if (pos && this.lastPos) sawine@13: { sawine@13: var delta = [pos[0] - this.lastPos[0], pos[1] - this.lastPos[1]]; sawine@14: delta = normaliseMoveDelta(delta); sawine@13: var action = this.actionMap["pitch"]; sawine@13: if (delta[1] != 0 && action) sawine@13: { sawine@13: for (var i = 0; i < 3; i++) sawine@13: { sawine@13: if (this.pressed[i] != action[0][i]) break; sawine@13: if (i == 2) action[1](delta[1]); sawine@13: } sawine@13: } sawine@13: action = this.actionMap["yaw"]; sawine@13: if (delta[0] != 0 && action) sawine@13: { sawine@13: for (var i = 0; i < 3; i++) sawine@13: { sawine@13: if (this.pressed[i] != action[0][i]) break; sawine@13: if (i == 2) action[1](delta[0]); sawine@13: } sawine@13: } sawine@13: } sawine@13: this.lastPos = pos; sawine@14: sawine@14: if (this.wheelDelta != 0) sawine@14: { sawine@14: var delta = normaliseWheelDelta(this.wheelDelta); sawine@14: var action = this.actionMap["wheel"]; sawine@21: if (delta != 0 && action) action(delta); sawine@14: this.wheelDelta = 0; sawine@14: } sawine@14: sawine@13: } sawine@13: sawine@14: function normaliseMoveDelta(delta) sawine@13: { sawine@13: var width = renderer.context.canvas.width; sawine@13: var height = renderer.context.canvas.height; sawine@13: return [delta[0] / width, delta[1] / height]; sawine@13: } sawine@13: sawine@14: function normaliseWheelDelta(delta) sawine@14: { sawine@14: return delta / 60; sawine@14: } sawine@14: sawine@11: function Keyboard(actionMap) sawine@11: { sawine@11: this.actionMap = actionMap; sawine@11: this.pressed = {}; sawine@11: } sawine@11: Keyboard.prototype.keyDown = function(event) sawine@11: { sawine@11: this.pressed[event.keyCode] = true; sawine@11: } sawine@11: Keyboard.prototype.keyUp = function(event) sawine@11: { sawine@11: this.pressed[event.keyCode] = false; sawine@11: } sawine@11: Keyboard.prototype.handle = function() sawine@11: { sawine@11: for (key in this.pressed) sawine@11: { sawine@11: if (this.pressed[key]) sawine@11: { sawine@11: key = String.fromCharCode(key); sawine@11: if (this.actionMap[key]) this.actionMap[key](); sawine@11: } sawine@11: } sawine@11: } sawine@11: sawine@11: function Renderer(camera, context) sawine@11: { sawine@11: this.camera = camera; sawine@4: this.context = context; sawine@4: this.gl = context.gl; sawine@7: this.matrixStack = []; sawine@4: } sawine@8: Renderer.prototype.update = function(scene) sawine@5: { sawine@5: var gl = this.context.gl; sawine@5: var viewport = this.context.viewport; sawine@5: var shader = this.context.shader; sawine@5: var mvMatrix = this.context.mvMatrix; sawine@5: var pMatrix = this.context.pMatrix; sawine@13: var camera = this.camera; sawine@15: camera.update(); sawine@5: sawine@5: gl.viewport(0, 0, viewport.width, viewport.height); sawine@5: gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); sawine@18: mat4.perspective(45, viewport.width / viewport.height, 0.1, 100.0, pMatrix); sawine@5: sawine@5: mat4.identity(mvMatrix); sawine@21: mat4.multiply(mvMatrix, camera.matrix); sawine@15: sawine@17: //this.pushMatrix(mvMatrix); sawine@7: sawine@5: gl.bindBuffer(gl.ARRAY_BUFFER, scene.positionBuffer); sawine@5: gl.vertexAttribPointer(shader.vertexPosition, scene.positionBuffer.itemSize, gl.FLOAT, false, 0, 0); sawine@5: sawine@5: gl.bindBuffer(gl.ARRAY_BUFFER, scene.colourBuffer); sawine@5: gl.vertexAttribPointer(shader.vertexColour, scene.colourBuffer.itemSize, gl.FLOAT, false, 0, 0); sawine@9: sawine@9: gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, scene.indexBuffer); sawine@17: sawine@5: this.context.updateMatrixUniforms(); sawine@9: gl.drawElements(gl.TRIANGLES, scene.indexBuffer.numItems, gl.UNSIGNED_SHORT, 0); sawine@9: //gl.drawArrays(gl.TRIANGLE_STRIP, 0, scene.positionBuffer.numItems); sawine@7: sawine@17: //mvMatrix = this.popMatrix(); sawine@5: sawine@5: if (this.next) this.next.draw(scene); sawine@5: } sawine@8: Renderer.prototype.pushMatrix = function(matrix) sawine@7: { sawine@7: var copy = mat4.create(); sawine@7: mat4.set(matrix, copy); sawine@7: this.matrixStack.push(copy); sawine@7: } sawine@8: Renderer.prototype.popMatrix = function() sawine@7: { sawine@7: if (this.matrixStack.length > 0) return this.matrixStack.pop(); sawine@7: } sawine@5: sawine@5: function Machine(scene) sawine@4: { sawine@5: this.scene = scene; sawine@8: this.lastUpdate = 0; sawine@5: } sawine@8: Machine.prototype.update = function(time) sawine@5: { sawine@8: if (this.lastUpdate != 0) sawine@8: { sawine@9: var diff = time - this.lastUpdate; sawine@8: sawine@8: } sawine@8: this.lastUpdate = time; sawine@4: } sawine@4: sawine@2: function Context(canvas) sawine@2: { sawine@3: this.canvas = canvas; sawine@2: try sawine@2: { sawine@2: this.gl = canvas.getContext("experimental-webgl"); sawine@2: this.viewport = {'width': canvas.width, sawine@2: 'height': canvas.height}; sawine@2: } sawine@2: catch(e) sawine@2: { sawine@2: alert(e); sawine@2: } sawine@2: if (!this.gl) alert("Failed: WebGL init."); sawine@2: this.mvMatrix = mat4.create(); sawine@2: this.pMatrix = mat4.create(); sawine@10: this.shader = new Shader(this); sawine@2: } sawine@2: Context.prototype.updateMatrixUniforms = function() sawine@2: { sawine@2: var gl = this.gl; sawine@2: var program = this.shader; sawine@2: var pMatrix = this.pMatrix; sawine@2: var mvMatrix = this.mvMatrix; sawine@2: gl.uniformMatrix4fv(program.pMatrixUniform, false, pMatrix); sawine@2: gl.uniformMatrix4fv(program.mvMatrixUniform, false, mvMatrix); sawine@2: } sawine@10: Context.prototype.expand = function() sawine@10: { sawine@10: var width = window.innerWidth; sawine@10: var height = window.innerHeight; sawine@10: this.canvas.width = width; sawine@10: this.canvas.height = height; sawine@10: this.viewport.width = width; sawine@10: this.viewport.height = height; sawine@10: } sawine@10: sawine@2: function Shader(context) sawine@2: { sawine@2: var gl = context.gl; sawine@9: var fragment = loadShader(gl, "fragment-shader"); sawine@9: var vertex = loadShader(gl, "vertex-shader"); sawine@2: this.program = gl.createProgram(); sawine@2: gl.attachShader(this.program, vertex); sawine@2: gl.attachShader(this.program, fragment); sawine@2: gl.linkProgram(this.program); sawine@2: sawine@2: if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) sawine@2: { sawine@2: alert("Failed: Shader init."); sawine@2: } sawine@2: sawine@2: gl.useProgram(this.program); sawine@2: this.vertexPosition = gl.getAttribLocation(this.program, "aVertexPosition"); sawine@2: gl.enableVertexAttribArray(this.vertexPosition); sawine@4: sawine@4: this.vertexColour = gl.getAttribLocation(this.program, "aVertexColour"); sawine@4: gl.enableVertexAttribArray(this.vertexColour); sawine@4: sawine@2: this.pMatrixUniform = gl.getUniformLocation(this.program, "uPMatrix"); sawine@2: this.mvMatrixUniform = gl.getUniformLocation(this.program, "uMVMatrix"); sawine@2: sawine@9: function loadShader(gl, id) sawine@2: { sawine@2: var script = document.getElementById(id); sawine@2: if (!script) return null; sawine@2: sawine@2: var str = ""; sawine@2: var child = script.firstChild; sawine@2: while (child) sawine@2: { sawine@2: if (child.nodeType == 3) str += child.textContent; sawine@2: child = child.nextSibling; sawine@2: } sawine@2: sawine@2: var shader; sawine@2: var common = "x-shader/x-"; sawine@2: if (script.type == common + "fragment") shader = gl.createShader(gl.FRAGMENT_SHADER); sawine@2: else if (script.type == common + "vertex") shader = gl.createShader(gl.VERTEX_SHADER); sawine@2: else return null; sawine@2: sawine@2: gl.shaderSource(shader, str); sawine@2: gl.compileShader(shader); sawine@2: sawine@2: if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) sawine@2: { sawine@2: alert(gl.getShaderInfoLog(shader)); sawine@2: return null; sawine@2: } sawine@2: sawine@2: return shader; sawine@2: } sawine@2: } sawine@2: sawine@2: sawine@7: function Cube(size, context) sawine@2: { sawine@2: var gl = context.gl; sawine@2: this.size = size || 1; sawine@10: this.rotation = {'x': 0.0, 'y': 0.0, 'z': 0.0}; sawine@4: sawine@4: this.positionBuffer = gl.createBuffer(); sawine@4: gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer); sawine@9: var vertices = [ sawine@9: // Front face sawine@9: -1.0, -1.0, 1.0, sawine@9: 1.0, -1.0, 1.0, sawine@9: 1.0, 1.0, 1.0, sawine@9: -1.0, 1.0, 1.0, sawine@9: sawine@9: // Back face sawine@9: -1.0, -1.0, -1.0, sawine@9: -1.0, 1.0, -1.0, sawine@9: 1.0, 1.0, -1.0, sawine@9: 1.0, -1.0, -1.0, sawine@9: sawine@9: // Top face sawine@9: -1.0, 1.0, -1.0, sawine@9: -1.0, 1.0, 1.0, sawine@9: 1.0, 1.0, 1.0, sawine@9: 1.0, 1.0, -1.0, sawine@9: sawine@9: // Bottom face sawine@9: -1.0, -1.0, -1.0, sawine@9: 1.0, -1.0, -1.0, sawine@9: 1.0, -1.0, 1.0, sawine@9: -1.0, -1.0, 1.0, sawine@9: sawine@9: // Right face sawine@9: 1.0, -1.0, -1.0, sawine@9: 1.0, 1.0, -1.0, sawine@9: 1.0, 1.0, 1.0, sawine@9: 1.0, -1.0, 1.0, sawine@9: sawine@9: // Left face sawine@9: -1.0, -1.0, -1.0, sawine@9: -1.0, -1.0, 1.0, sawine@9: -1.0, 1.0, 1.0, sawine@9: -1.0, 1.0, -1.0]; sawine@2: gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); sawine@4: this.positionBuffer.itemSize = 3; sawine@9: this.positionBuffer.numItems = 24; sawine@4: sawine@4: this.colourBuffer = gl.createBuffer(); sawine@4: gl.bindBuffer(gl.ARRAY_BUFFER, this.colourBuffer); sawine@9: var alpha = 1.0; sawine@9: var colours = [[1.0, 0.0, 0.0, alpha], sawine@9: [0.0, 1.0, 0.0, alpha], sawine@9: [0.0, 0.0, 1.0, alpha], sawine@9: [1.0, 0.0, 1.0, alpha], sawine@9: [1.0, 1.0, 0.0, alpha], sawine@9: [0.0, 1.0, 1.0, alpha]]; sawine@9: var unpackedColours = []; sawine@9: for (var i in colours) sawine@4: { sawine@9: var colour = colours[i]; sawine@9: for (var j = 0; j < 4; j++) sawine@9: { sawine@9: unpackedColours = unpackedColours.concat(colour); sawine@9: } sawine@9: //colours = colours.concat([0.5, 0.5, 1.0, 1.0]); sawine@4: } sawine@9: gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColours), gl.STATIC_DRAW); sawine@4: this.colourBuffer.itemSize = 4; sawine@9: this.colourBuffer.numItems = 24; sawine@9: sawine@9: this.indexBuffer = gl.createBuffer(); sawine@9: gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); sawine@9: var indices = [0, 1, 2, 0, 2, 3, sawine@9: 4, 5, 6, 4, 6, 7, sawine@9: 8, 9, 10, 8, 10, 11, sawine@9: 12, 13, 14, 12, 14, 15, sawine@9: 16, 17, 18, 16, 18, 19, sawine@9: 20, 21, 22, 20, 22, 23]; sawine@9: gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); sawine@9: this.indexBuffer.itemSize = 1; sawine@9: this.indexBuffer.numItems = 36; sawine@13: } sawine@13: sawine@13: function read(file, handler) sawine@13: { sawine@13: var request = new XMLHttpRequest(); sawine@13: request.open("GET", file); sawine@13: request.onreadystatechange = function() { sawine@13: alert(request.readyState); sawine@13: if (request.readyState == 4) { sawine@13: handler(request.responseText); sawine@13: } sawine@13: } sawine@13: request.send(); sawine@13: } sawine@13: sawine@13: // event handler sawine@13: function expandContext() sawine@13: { sawine@13: renderer.context.expand(); sawine@13: } sawine@13: sawine@13: function moveCameraLeft() sawine@13: { sawine@13: camera.moveLeft(); sawine@13: } sawine@13: sawine@13: function moveCameraRight() sawine@13: { sawine@13: camera.moveRight(); sawine@13: } sawine@13: sawine@13: function moveCameraUp() sawine@13: { sawine@13: camera.moveUp(); sawine@13: } sawine@13: sawine@13: function moveCameraDown() sawine@13: { sawine@13: camera.moveDown(); sawine@13: } sawine@13: sawine@14: function zoomCamera(delta) sawine@14: { sawine@14: camera.zoom(delta); sawine@14: } sawine@14: sawine@13: function pitchCamera(delta) sawine@13: { sawine@19: camera.pitch(delta); sawine@13: } sawine@13: sawine@13: function yawCamera(delta) sawine@13: { sawine@19: camera.yaw(delta); sawine@13: } sawine@13: sawine@13: function handleKeyDown(event) sawine@13: { sawine@13: controller.keyboard.keyDown(event); sawine@13: } sawine@13: sawine@13: function handleKeyUp(event) sawine@13: { sawine@13: controller.keyboard.keyUp(event); sawine@13: } sawine@13: sawine@13: function handleMouseDown(event) sawine@13: { sawine@13: controller.mouse.buttonDown(event); sawine@13: } sawine@13: sawine@13: function handleMouseUp(event) sawine@13: { sawine@13: controller.mouse.buttonUp(event); sawine@13: } sawine@13: sawine@13: function handleMouseMove(event) sawine@13: { sawine@13: controller.mouse.move(event); sawine@13: } sawine@14: sawine@14: function handleMouseWheel(event) sawine@14: { sawine@14: controller.mouse.moveWheel(event); sawine@14: }