scripts/machine.js
author Eugen Sawin <sawine@me73.com>
Sun, 17 Apr 2011 01:27:49 +0200
changeset 16 fba447c2485c
parent 15 98191d5d5af6
child 17 8b187fa957ad
permissions -rwxr-xr-x
Thinking about camera control.
     1 var machine;
     2 var renderer;
     3 var controller;
     4 var camera;
     5 var cameraSpeed = {"x": 0.1, "y": 0.1, "z": 0.5, "pitch": 0.5, "yaw": 0.5};
     6 var keyActionMap = {'A': moveCameraLeft,
     7 		 'D': moveCameraRight,
     8 		 'W': moveCameraUp,
     9 		 'S': moveCameraDown};
    10 var mouseActionMap = {"pitch": [[true, false, false], pitchCamera],
    11 		      "yaw": [[true, false, false], yawCamera],
    12                       "wheel": zoomCamera};
    13 
    14 function main()
    15 {
    16     var canvas = document.getElementById("machine");
    17     var context = new Context(canvas);
    18     context.expand();
    19     var gl = context.gl;
    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);
    27     update();
    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);
    36 }
    37 
    38 function configureCamera(config) {
    39     alert(config);
    40 }
    41 
    42 function update()
    43 {    
    44     requestAnimFrame(update);
    45     controller.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);
    51 }
    52 
    53 function random(min, max)
    54 {
    55     return (min + Math.random() * (max - min));
    56 }
    57 
    58 function Camera(speed)
    59 {
    60     this.x = 0.0;
    61     this.y = 0.0;
    62     this.z = 7.0;
    63     this.pitch = 0.0;
    64     this.yaw = 0.0;
    65     this.speed = 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);
    72 }
    73 Camera.prototype.moveLeft = function()
    74 {
    75     this.x -= this.speed.x;
    76 }
    77 Camera.prototype.moveRight = function()
    78 {
    79     this.x += this.speed.x;
    80 }
    81 Camera.prototype.moveUp = function()
    82 {
    83     this.y += this.speed.y;
    84 }
    85 Camera.prototype.moveDown = function()
    86 {
    87     this.y -= this.speed.y;
    88 }
    89 Camera.prototype.zoom = function(delta) 
    90 {
    91     this.z += this.speed.z * delta;
    92 }
    93 Camera.prototype.changePitch = function(delta)
    94 {
    95     this.pitch += this.speed.pitch * delta;
    96 }
    97 Camera.prototype.changeYaw = function(delta)
    98 {
    99     this.yaw += this.speed.yaw * delta;
   100 }
   101 Camera.prototype.update = function()
   102 {
   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]);
   109     
   110     
   111     this.clear();
   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]);
   115 }
   116 Camera.prototype.clear = function()
   117 {
   118    this.x = this.y = this.z = 0;
   119    //this.pitch = this.yaw = 0;
   120 }
   121 
   122 function Controller(keyActionMap, mouseActionMap, camera, machine, renderer)
   123 { 
   124     this.keyboard = new Keyboard(keyActionMap);
   125     this.mouse = new Mouse(mouseActionMap);
   126    
   127 }
   128 Controller.prototype.update = function()
   129 {
   130     this.keyboard.handle();
   131     this.mouse.handle();
   132 }
   133 
   134 function Mouse(actionMap)
   135 {
   136     this.actionMap = actionMap;
   137     this.pressed = [false, false, false];   
   138     this.wheelDelta = 0;
   139 }
   140 Mouse.prototype.buttonDown = function(event)
   141 {
   142     this.pressed[event.which-1] = true;
   143 }
   144 Mouse.prototype.buttonUp = function(event)
   145 {
   146     this.pressed[event.which-1] = false;
   147 }
   148 Mouse.prototype.move = function(event)
   149 {
   150     this.currentPos = [event.clientX, event.clientY];
   151 }
   152 Mouse.prototype.moveWheel = function(event)
   153 { 
   154     this.wheelDelta += event.wheelDelta;
   155 }
   156 Mouse.prototype.handle = function()
   157 {
   158     var pos = this.currentPos;
   159     if (pos && this.lastPos)
   160     {
   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) 
   165 	{
   166 	    for (var i = 0; i < 3; i++)
   167 	    {
   168 		if (this.pressed[i] != action[0][i]) break;
   169 		if (i == 2) action[1](delta[1]);
   170 	    }
   171 	} 
   172 	action = this.actionMap["yaw"];
   173 	if (delta[0] != 0 && action) 
   174 	{
   175 	    for (var i = 0; i < 3; i++)
   176 	    {
   177 		if (this.pressed[i] != action[0][i]) break;
   178 		if (i == 2) action[1](delta[0]);
   179 	    }
   180 	}
   181     }
   182     this.lastPos = pos;
   183     
   184     if (this.wheelDelta != 0)
   185     {
   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);
   190 	this.wheelDelta = 0;
   191     }
   192     
   193 }
   194 
   195 function normaliseMoveDelta(delta)
   196 {
   197     var width = renderer.context.canvas.width;
   198     var height = renderer.context.canvas.height;
   199     return [delta[0] / width, delta[1] / height];
   200 }
   201 
   202 function normaliseWheelDelta(delta)
   203 {
   204     return delta / 60;
   205 }
   206 
   207 function Keyboard(actionMap)
   208 {
   209     this.actionMap = actionMap;
   210     this.pressed = {};
   211 }
   212 Keyboard.prototype.keyDown = function(event)
   213 {
   214     this.pressed[event.keyCode] = true;    
   215 }
   216 Keyboard.prototype.keyUp = function(event)
   217 {
   218     this.pressed[event.keyCode] = false;
   219 }
   220 Keyboard.prototype.handle = function()
   221 {
   222     for (key in this.pressed)
   223     {
   224 	if (this.pressed[key])
   225 	{
   226 	    key = String.fromCharCode(key);
   227 	    if (this.actionMap[key]) this.actionMap[key](); 
   228 	}
   229     }
   230 }
   231 
   232 function Renderer(camera, context)
   233 {
   234     this.camera = camera;
   235     this.context = context;
   236     this.gl = context.gl;
   237     this.matrixStack = [];
   238 }
   239 Renderer.prototype.update = function(scene)
   240 { 
   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;
   247     camera.update();
   248 
   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);
   252 
   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;
   262    
   263     
   264     this.pushMatrix(mvMatrix);
   265 
   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]);
   269 
   270     gl.bindBuffer(gl.ARRAY_BUFFER, scene.positionBuffer);
   271     gl.vertexAttribPointer(shader.vertexPosition, scene.positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
   272 
   273     gl.bindBuffer(gl.ARRAY_BUFFER, scene.colourBuffer);
   274     gl.vertexAttribPointer(shader.vertexColour, scene.colourBuffer.itemSize, gl.FLOAT, false, 0, 0);
   275     
   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);
   280     
   281     mvMatrix = this.popMatrix();
   282 
   283     if (this.next) this.next.draw(scene);
   284 }
   285 Renderer.prototype.pushMatrix = function(matrix)
   286 {
   287     var copy = mat4.create();
   288     mat4.set(matrix, copy);
   289     this.matrixStack.push(copy);
   290 }
   291 Renderer.prototype.popMatrix = function()    
   292 {
   293     if (this.matrixStack.length > 0) return this.matrixStack.pop();
   294 }
   295 
   296 function Machine(scene)
   297 {
   298     this.scene = scene;
   299     this.lastUpdate = 0;
   300 }
   301 Machine.prototype.update = function(time)
   302 {
   303     if (this.lastUpdate != 0)
   304     {
   305 	var diff = time - this.lastUpdate;
   306 	
   307     }
   308     this.lastUpdate = time;
   309 }
   310 
   311 function Context(canvas)
   312 {
   313     this.canvas = canvas;
   314     try 
   315     {
   316 	this.gl = canvas.getContext("experimental-webgl");
   317 	this.viewport = {'width': canvas.width,
   318 			 'height': canvas.height};
   319     } 
   320     catch(e) 
   321     {
   322 	alert(e);
   323     }
   324     if (!this.gl) alert("Failed: WebGL init.");
   325     this.mvMatrix = mat4.create();
   326     this.pMatrix = mat4.create();
   327     this.shader = new Shader(this);  
   328 }
   329 Context.prototype.updateMatrixUniforms = function()
   330 {
   331     var gl = this.gl;
   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);
   337 }
   338 Context.prototype.expand = function()
   339 { 
   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;
   346 }
   347 
   348 function Shader(context)
   349 {
   350     var gl = context.gl;
   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);
   357 
   358     if (!gl.getProgramParameter(this.program, gl.LINK_STATUS))
   359     {
   360 	alert("Failed: Shader init.");
   361     }
   362 
   363     gl.useProgram(this.program);
   364     this.vertexPosition = gl.getAttribLocation(this.program, "aVertexPosition");
   365     gl.enableVertexAttribArray(this.vertexPosition);
   366     
   367     this.vertexColour = gl.getAttribLocation(this.program, "aVertexColour");
   368     gl.enableVertexAttribArray(this.vertexColour);
   369 
   370     this.pMatrixUniform = gl.getUniformLocation(this.program, "uPMatrix");
   371     this.mvMatrixUniform = gl.getUniformLocation(this.program, "uMVMatrix");
   372 
   373     function loadShader(gl, id)
   374     {
   375 	var script = document.getElementById(id);
   376 	if (!script) return null;
   377 	
   378 	var str = "";
   379 	var child = script.firstChild;
   380 	while (child)
   381 	{
   382 	    if (child.nodeType == 3) str += child.textContent;
   383 	    child = child.nextSibling;
   384 	}
   385 	
   386 	var shader;
   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);
   390 	else return null;
   391 
   392 	gl.shaderSource(shader, str);
   393 	gl.compileShader(shader);
   394 
   395 	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
   396         {
   397 	    alert(gl.getShaderInfoLog(shader));
   398 	    return null;
   399 	}
   400 
   401 	return shader;
   402     }
   403 }
   404 
   405 
   406 function Cube(size, context)
   407 {
   408     var gl = context.gl;
   409     this.size = size || 1;
   410     this.rotation = {'x': 0.0, 'y': 0.0, 'z': 0.0};
   411     
   412     this.positionBuffer = gl.createBuffer();
   413     gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
   414     var vertices = [
   415 	     // Front face
   416             -1.0, -1.0,  1.0,
   417              1.0, -1.0,  1.0,
   418              1.0,  1.0,  1.0,
   419             -1.0,  1.0,  1.0,
   420  
   421             // Back face
   422             -1.0, -1.0, -1.0,
   423             -1.0,  1.0, -1.0,
   424              1.0,  1.0, -1.0,
   425              1.0, -1.0, -1.0,
   426  
   427             // Top face
   428             -1.0,  1.0, -1.0,
   429             -1.0,  1.0,  1.0,
   430              1.0,  1.0,  1.0,
   431              1.0,  1.0, -1.0,
   432  
   433             // Bottom face
   434             -1.0, -1.0, -1.0,
   435              1.0, -1.0, -1.0,
   436              1.0, -1.0,  1.0,
   437             -1.0, -1.0,  1.0,
   438  
   439             // Right face
   440              1.0, -1.0, -1.0,
   441              1.0,  1.0, -1.0,
   442              1.0,  1.0,  1.0,
   443              1.0, -1.0,  1.0,
   444  
   445             // Left face
   446             -1.0, -1.0, -1.0,
   447             -1.0, -1.0,  1.0,
   448             -1.0,  1.0,  1.0,
   449             -1.0,  1.0, -1.0];
   450     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
   451     this.positionBuffer.itemSize = 3;
   452     this.positionBuffer.numItems = 24;
   453 
   454     this.colourBuffer = gl.createBuffer();
   455     gl.bindBuffer(gl.ARRAY_BUFFER, this.colourBuffer);
   456     var alpha = 1.0;
   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)
   465     {
   466 	var colour = colours[i];
   467 	for (var j = 0; j < 4; j++)
   468 	{
   469 	    unpackedColours = unpackedColours.concat(colour);
   470 	}
   471 	//colours = colours.concat([0.5, 0.5, 1.0, 1.0]);
   472     }
   473     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColours), gl.STATIC_DRAW);
   474     this.colourBuffer.itemSize = 4;
   475     this.colourBuffer.numItems = 24;
   476 
   477     this.indexBuffer = gl.createBuffer();
   478     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
   479     var indices = [0, 1, 2, 0, 2, 3,
   480 		   4, 5, 6, 4, 6, 7,
   481 		   8, 9, 10, 8, 10, 11,
   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;
   488 }
   489 
   490 function read(file, handler)
   491 {
   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);
   498 	}
   499     }
   500     request.send();
   501 }		
   502 
   503 // event handler
   504 function expandContext()
   505 {
   506     renderer.context.expand();
   507 }
   508 
   509 function moveCameraLeft()
   510 {
   511     camera.moveLeft();
   512 }
   513 
   514 function moveCameraRight()
   515 {
   516     camera.moveRight();
   517 }
   518 
   519 function moveCameraUp()
   520 {
   521     camera.moveUp();
   522 }
   523 
   524 function moveCameraDown()
   525 {
   526     camera.moveDown();
   527 }
   528 
   529 function zoomCamera(delta)
   530 {
   531     camera.zoom(delta);
   532 }
   533 
   534 function pitchCamera(delta)
   535 {
   536     camera.changePitch(delta);
   537 }
   538 
   539 function yawCamera(delta)
   540 {
   541     camera.changeYaw(delta);
   542 }
   543 
   544 function handleKeyDown(event)
   545 {
   546     controller.keyboard.keyDown(event);
   547 }
   548 
   549 function handleKeyUp(event)
   550 {
   551     controller.keyboard.keyUp(event);
   552 }
   553 
   554 function handleMouseDown(event)
   555 {
   556     controller.mouse.buttonDown(event);
   557 }
   558 
   559 function handleMouseUp(event)
   560 {
   561     controller.mouse.buttonUp(event);
   562 }
   563 
   564 function handleMouseMove(event)
   565 {
   566     controller.mouse.move(event);
   567 }
   568 
   569 function handleMouseWheel(event)
   570 {
   571     controller.mouse.moveWheel(event);
   572 }