Thinking about camera control.
5 var cameraSpeed = {"x": 0.1, "y": 0.1, "z": 0.5, "pitch": 0.5, "yaw": 0.5};
6 var keyActionMap = {'A': moveCameraLeft,
10 var mouseActionMap = {"pitch": [[true, false, false], pitchCamera],
11 "yaw": [[true, false, false], yawCamera],
16 var canvas = document.getElementById("machine");
17 var context = new Context(canvas);
20 var object = new Cube(1, context);
21 gl.clearColor(0.0, 0.0, 0.0, 1.0);
22 gl.enable(gl.DEPTH_TEST);
23 machine = new Machine(object);
24 camera = new Camera(cameraSpeed);
25 renderer = new Renderer(camera, context);
26 controller = new Controller(keyActionMap, mouseActionMap, camera, machine, renderer);
28 window.onresize = expandContext;
29 document.onkeydown = handleKeyDown;
30 document.onkeyup = handleKeyUp;
31 canvas.onmousedown = handleMouseDown;
32 document.onmouseup = handleMouseUp;
33 document.onmousemove = handleMouseMove;
34 document.onmousewheel = handleMouseWheel;
35 //read("config/camera", configureCamera);
38 function configureCamera(config) {
44 requestAnimFrame(update);
46 machine.scene.rotation.x += (random(0, 2) - 2) * 0.001;
47 machine.scene.rotation.y += (random(0, 3) - 1) * 0.001;
48 machine.scene.rotation.z += (random(0, 2) - 1) * 0.001;
49 machine.update(new Date().getTime());
50 renderer.update(machine.scene);
53 function random(min, max)
55 return (min + Math.random() * (max - min));
58 function Camera(speed)
66 this.rotMatrix = mat4.create();
67 this.transMatrix = mat4.create();
68 this.matrix = mat4.create();
69 mat4.identity(this.rotMatrix);
70 mat4.identity(this.transMatrix);
71 mat4.identity(this.matrix);
73 Camera.prototype.moveLeft = function()
75 this.x -= this.speed.x;
77 Camera.prototype.moveRight = function()
79 this.x += this.speed.x;
81 Camera.prototype.moveUp = function()
83 this.y += this.speed.y;
85 Camera.prototype.moveDown = function()
87 this.y -= this.speed.y;
89 Camera.prototype.zoom = function(delta)
91 this.z += this.speed.z * delta;
93 Camera.prototype.changePitch = function(delta)
95 this.pitch += this.speed.pitch * delta;
97 Camera.prototype.changeYaw = function(delta)
99 this.yaw += this.speed.yaw * delta;
101 Camera.prototype.update = function()
103 //mat4.rotate(this.rotMatrix, -this.pitch, [1, 0, 0]);
104 //mat4.rotate(this.rotMatrix, -this.yaw, [0, 1, 0]);
105 //mat4.translate(this.transMatrix, [-this.x, -this.y, -this.z]);
106 mat4.translate(this.matrix, [-this.x, -this.y, -this.z]);
107 //mat4.rotate(this.matrix, -this.pitch, [1, 0, 0]);
108 //mat4.rotate(this.matrix, -this.yaw, [0, 1, 0]);
112 //mat4.rotate(mvMatrix, -camera.pitch, [1, 0, 0]);
113 //mat4.rotate(mvMatrix, -camera.yaw, [0, 1, 0]);
114 //mat4.translate(mvMatrix, [-camera.x, -camera.y, -camera.z]);
116 Camera.prototype.clear = function()
118 this.x = this.y = this.z = 0;
119 //this.pitch = this.yaw = 0;
122 function Controller(keyActionMap, mouseActionMap, camera, machine, renderer)
124 this.keyboard = new Keyboard(keyActionMap);
125 this.mouse = new Mouse(mouseActionMap);
128 Controller.prototype.update = function()
130 this.keyboard.handle();
134 function Mouse(actionMap)
136 this.actionMap = actionMap;
137 this.pressed = [false, false, false];
140 Mouse.prototype.buttonDown = function(event)
142 this.pressed[event.which-1] = true;
144 Mouse.prototype.buttonUp = function(event)
146 this.pressed[event.which-1] = false;
148 Mouse.prototype.move = function(event)
150 this.currentPos = [event.clientX, event.clientY];
152 Mouse.prototype.moveWheel = function(event)
154 this.wheelDelta += event.wheelDelta;
156 Mouse.prototype.handle = function()
158 var pos = this.currentPos;
159 if (pos && this.lastPos)
161 var delta = [pos[0] - this.lastPos[0], pos[1] - this.lastPos[1]];
162 delta = normaliseMoveDelta(delta);
163 var action = this.actionMap["pitch"];
164 if (delta[1] != 0 && action)
166 for (var i = 0; i < 3; i++)
168 if (this.pressed[i] != action[0][i]) break;
169 if (i == 2) action[1](delta[1]);
172 action = this.actionMap["yaw"];
173 if (delta[0] != 0 && action)
175 for (var i = 0; i < 3; i++)
177 if (this.pressed[i] != action[0][i]) break;
178 if (i == 2) action[1](delta[0]);
184 if (this.wheelDelta != 0)
186 var delta = normaliseWheelDelta(this.wheelDelta);
187 var action = this.actionMap["wheel"];
188 if (delta > 0 && action) action(delta);
189 else if (delta < 0 && action) action(delta);
195 function normaliseMoveDelta(delta)
197 var width = renderer.context.canvas.width;
198 var height = renderer.context.canvas.height;
199 return [delta[0] / width, delta[1] / height];
202 function normaliseWheelDelta(delta)
207 function Keyboard(actionMap)
209 this.actionMap = actionMap;
212 Keyboard.prototype.keyDown = function(event)
214 this.pressed[event.keyCode] = true;
216 Keyboard.prototype.keyUp = function(event)
218 this.pressed[event.keyCode] = false;
220 Keyboard.prototype.handle = function()
222 for (key in this.pressed)
224 if (this.pressed[key])
226 key = String.fromCharCode(key);
227 if (this.actionMap[key]) this.actionMap[key]();
232 function Renderer(camera, context)
234 this.camera = camera;
235 this.context = context;
236 this.gl = context.gl;
237 this.matrixStack = [];
239 Renderer.prototype.update = function(scene)
241 var gl = this.context.gl;
242 var viewport = this.context.viewport;
243 var shader = this.context.shader;
244 var mvMatrix = this.context.mvMatrix;
245 var pMatrix = this.context.pMatrix;
246 var camera = this.camera;
249 gl.viewport(0, 0, viewport.width, viewport.height);
250 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
251 mat4.perspective(45, viewport.width / viewport.height, 0.1, 100.0, pMatrix);
253 mat4.identity(mvMatrix);
254 //mat4.multiply(mvMatrix, camera.rotMatrix);
255 //mat4.multiply(mvMatrix, camera.transMatrix);
256 mat4.rotate(mvMatrix, -camera.pitch, [1, 0, 0]);
257 mat4.rotate(mvMatrix, -camera.yaw, [0, 1, 0]);
258 mat4.multiply(mvMatrix, camera.matrix);
259 //mat4.rotate(camera.matrix, -camera.pitch, [1, 0, 0]);
260 //mat4.rotate(camera.matrix, -camera.yaw, [0, 1, 0]);
261 //camera.yaw = camera.pitch = 0;
264 this.pushMatrix(mvMatrix);
266 mat4.rotate(mvMatrix, scene.rotation.x, [1, 0, 0]);
267 mat4.rotate(mvMatrix, scene.rotation.y, [0, 1, 0]);
268 mat4.rotate(mvMatrix, scene.rotation.z, [0, 0, 1]);
270 gl.bindBuffer(gl.ARRAY_BUFFER, scene.positionBuffer);
271 gl.vertexAttribPointer(shader.vertexPosition, scene.positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
273 gl.bindBuffer(gl.ARRAY_BUFFER, scene.colourBuffer);
274 gl.vertexAttribPointer(shader.vertexColour, scene.colourBuffer.itemSize, gl.FLOAT, false, 0, 0);
276 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, scene.indexBuffer);
277 this.context.updateMatrixUniforms();
278 gl.drawElements(gl.TRIANGLES, scene.indexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
279 //gl.drawArrays(gl.TRIANGLE_STRIP, 0, scene.positionBuffer.numItems);
281 mvMatrix = this.popMatrix();
283 if (this.next) this.next.draw(scene);
285 Renderer.prototype.pushMatrix = function(matrix)
287 var copy = mat4.create();
288 mat4.set(matrix, copy);
289 this.matrixStack.push(copy);
291 Renderer.prototype.popMatrix = function()
293 if (this.matrixStack.length > 0) return this.matrixStack.pop();
296 function Machine(scene)
301 Machine.prototype.update = function(time)
303 if (this.lastUpdate != 0)
305 var diff = time - this.lastUpdate;
308 this.lastUpdate = time;
311 function Context(canvas)
313 this.canvas = canvas;
316 this.gl = canvas.getContext("experimental-webgl");
317 this.viewport = {'width': canvas.width,
318 'height': canvas.height};
324 if (!this.gl) alert("Failed: WebGL init.");
325 this.mvMatrix = mat4.create();
326 this.pMatrix = mat4.create();
327 this.shader = new Shader(this);
329 Context.prototype.updateMatrixUniforms = function()
332 var program = this.shader;
333 var pMatrix = this.pMatrix;
334 var mvMatrix = this.mvMatrix;
335 gl.uniformMatrix4fv(program.pMatrixUniform, false, pMatrix);
336 gl.uniformMatrix4fv(program.mvMatrixUniform, false, mvMatrix);
338 Context.prototype.expand = function()
340 var width = window.innerWidth;
341 var height = window.innerHeight;
342 this.canvas.width = width;
343 this.canvas.height = height;
344 this.viewport.width = width;
345 this.viewport.height = height;
348 function Shader(context)
351 var fragment = loadShader(gl, "fragment-shader");
352 var vertex = loadShader(gl, "vertex-shader");
353 this.program = gl.createProgram();
354 gl.attachShader(this.program, vertex);
355 gl.attachShader(this.program, fragment);
356 gl.linkProgram(this.program);
358 if (!gl.getProgramParameter(this.program, gl.LINK_STATUS))
360 alert("Failed: Shader init.");
363 gl.useProgram(this.program);
364 this.vertexPosition = gl.getAttribLocation(this.program, "aVertexPosition");
365 gl.enableVertexAttribArray(this.vertexPosition);
367 this.vertexColour = gl.getAttribLocation(this.program, "aVertexColour");
368 gl.enableVertexAttribArray(this.vertexColour);
370 this.pMatrixUniform = gl.getUniformLocation(this.program, "uPMatrix");
371 this.mvMatrixUniform = gl.getUniformLocation(this.program, "uMVMatrix");
373 function loadShader(gl, id)
375 var script = document.getElementById(id);
376 if (!script) return null;
379 var child = script.firstChild;
382 if (child.nodeType == 3) str += child.textContent;
383 child = child.nextSibling;
387 var common = "x-shader/x-";
388 if (script.type == common + "fragment") shader = gl.createShader(gl.FRAGMENT_SHADER);
389 else if (script.type == common + "vertex") shader = gl.createShader(gl.VERTEX_SHADER);
392 gl.shaderSource(shader, str);
393 gl.compileShader(shader);
395 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
397 alert(gl.getShaderInfoLog(shader));
406 function Cube(size, context)
409 this.size = size || 1;
410 this.rotation = {'x': 0.0, 'y': 0.0, 'z': 0.0};
412 this.positionBuffer = gl.createBuffer();
413 gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
450 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
451 this.positionBuffer.itemSize = 3;
452 this.positionBuffer.numItems = 24;
454 this.colourBuffer = gl.createBuffer();
455 gl.bindBuffer(gl.ARRAY_BUFFER, this.colourBuffer);
457 var colours = [[1.0, 0.0, 0.0, alpha],
458 [0.0, 1.0, 0.0, alpha],
459 [0.0, 0.0, 1.0, alpha],
460 [1.0, 0.0, 1.0, alpha],
461 [1.0, 1.0, 0.0, alpha],
462 [0.0, 1.0, 1.0, alpha]];
463 var unpackedColours = [];
464 for (var i in colours)
466 var colour = colours[i];
467 for (var j = 0; j < 4; j++)
469 unpackedColours = unpackedColours.concat(colour);
471 //colours = colours.concat([0.5, 0.5, 1.0, 1.0]);
473 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColours), gl.STATIC_DRAW);
474 this.colourBuffer.itemSize = 4;
475 this.colourBuffer.numItems = 24;
477 this.indexBuffer = gl.createBuffer();
478 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
479 var indices = [0, 1, 2, 0, 2, 3,
482 12, 13, 14, 12, 14, 15,
483 16, 17, 18, 16, 18, 19,
484 20, 21, 22, 20, 22, 23];
485 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
486 this.indexBuffer.itemSize = 1;
487 this.indexBuffer.numItems = 36;
490 function read(file, handler)
492 var request = new XMLHttpRequest();
493 request.open("GET", file);
494 request.onreadystatechange = function() {
495 alert(request.readyState);
496 if (request.readyState == 4) {
497 handler(request.responseText);
504 function expandContext()
506 renderer.context.expand();
509 function moveCameraLeft()
514 function moveCameraRight()
519 function moveCameraUp()
524 function moveCameraDown()
529 function zoomCamera(delta)
534 function pitchCamera(delta)
536 camera.changePitch(delta);
539 function yawCamera(delta)
541 camera.changeYaw(delta);
544 function handleKeyDown(event)
546 controller.keyboard.keyDown(event);
549 function handleKeyUp(event)
551 controller.keyboard.keyUp(event);
554 function handleMouseDown(event)
556 controller.mouse.buttonDown(event);
559 function handleMouseUp(event)
561 controller.mouse.buttonUp(event);
564 function handleMouseMove(event)
566 controller.mouse.move(event);
569 function handleMouseWheel(event)
571 controller.mouse.moveWheel(event);