Transforming the Teapot.

The HTML File

<html>
<head>

<script id="vertex-shader" type="x-shader/x-vertex" >
precision mediump float;

attribute vec4 aPosition;
attribute vec3 aBC;

uniform mat4 uMatrix1, uMatrix2, uWorld;

varying vec3 vBC;

void main() {

    gl_Position = uWorld *  uMatrix2 * uMatrix1 * aPosition;
    vBC = aBC;
}
</script>

<script id="fragment-shader" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec3 vBC;

uniform vec4 uEdgeColor;

float edgeFactor(){
    vec3 d = fwidth(vBC);
    vec3 a3 = smoothstep(vec3(0.0), d*1.5, vBC);
    return min(min(a3.x, a3.y), a3.z);
}

void main(){
   vec4 tmp;
   tmp = uEdgeColor;
   tmp.a = (1.0-edgeFactor())*0.95;

   if (gl_FrontFacing) {  
       gl_FragColor = tmp; 
   } else {
      discard;
   }
}
</script>

<script type="text/javascript" src="../../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../../Common/initShaders.js"></script>
<script type="text/javascript" src="../../Common/MV.js"></script>

<script type="text/javascript" src="../../Models/cone.js"></script>
<script type="text/javascript" src="../../Models/cube.js"></script>
<script type="text/javascript" src="../../Models/dragon.js"></script>
<script type="text/javascript" src="../../Models/epcot.js"></script>
<script type="text/javascript" src="../../Models/lizard.js"></script>
<script type="text/javascript" src="../../Models/tank.js"></script>
<script type="text/javascript" src="../../Models/teapot.js"></script>
<script type="text/javascript" src="../../Models/xwing.js"></script>

<script type="text/javascript" src="modelDisplay.js"></script>
<script type="text/javascript" src="canvas.js"></script>
<script type="text/javascript" src="main.js"></script>

<style>
    input {
        text-align: right;
    }
</style>
</head>

<body>
<h1>Transforming the Teapot.</h1>
<script>

    // set up the canvas 
    var canvas = new Canvas(500, 500);

    var xwing= new xwingModel();
    var model = new ModelDisplay(canvas, xwing);

    //add a callback for keypresses
    canvas.disp.addEventListener("keypress",
         function(evnt) {
            KeyFunc(evnt, canvas, model);
         }
    );

    // display it.
    DoRedisplay(canvas, model);
</script>
<ul>
  <Li> The red xwing  
     <pre class="prettyprint">
  var a = mult(scalem(0.25, 0.25, 0.25), rotate(-90, [1,0,0]));
  var b = translate(0.25, 0.25, 0.0);
  var c = rotate(90, [0.0, 0.0, 1.0]);
  var edge = vec4(1.0, 0.0, 0.0, 1.0);

  canvas.gl.uniformMatrix4fv(canvas.mat1Pos, false, flatten(a));
  canvas.gl.uniformMatrix4fv(canvas.mat2Pos, false, flatten(mat4(1.0)));
  canvas.gl.uniform4fv(canvas.edgeColorPos, flatten(edge));
  model.Display()
     </pre>
     <li> The green xwing
     <pre class="prettyprint">

   var d = mult(b, a);
   edge = vec4(0.0, 1.0, 0.0, 1.0);

   canvas.gl.uniformMatrix4fv(canvas.mat1Pos, false, flatten(d));
   canvas.gl.uniformMatrix4fv(canvas.mat2Pos, false, flatten(c));
   canvas.gl.uniform4fv(canvas.edgeColorPos, flatten(edge));

   model.Display();
     </pre>
    <li> The blue xwing
    <pre class="prettyprint">
   edge = vec4(0.0, 0.0, 1.0, 1.0);
   d = mult(c, a);

   canvas.gl.uniformMatrix4fv(canvas.mat1Pos, false, flatten(d));
   canvas.gl.uniformMatrix4fv(canvas.mat2Pos, false, flatten(b));
   canvas.gl.uniform4fv(canvas.edgeColorPos, flatten(edge));

    </pre>
</ul>
</body>

main.js

'use strict';

// counts on canvas and models being global.
function DoRedisplay(canvas, model) {
   var i;
   canvas.Redisplay();

   var a = mult(mult(scalem(0.35, 0.35, 0.35), rotate(-90, [0,1,0])), rotate(90,[1,0,0]));
   var b = translate(0.45, 0.45, 0.0);
   var c = rotate(90, [0.0, 0.0, 1.0]);
   //var c = scalem(2, 2, 2);

   var edge = vec4(1.0, 0.0, 0.0, 1.0);

   canvas.gl.uniformMatrix4fv(canvas.mat1Pos, false, flatten(a));
   canvas.gl.uniformMatrix4fv(canvas.mat2Pos, false, flatten(mat4(1.0)));
   canvas.gl.uniform4fv(canvas.edgeColorPos, flatten(edge));
   model.Display();

   var d = mult(b, a);
   edge = vec4(0.0, 1.0, 0.0, 1.0);
 
   canvas.gl.uniformMatrix4fv(canvas.mat1Pos, false, flatten(d));
   canvas.gl.uniformMatrix4fv(canvas.mat2Pos, false, flatten(c));
   canvas.gl.uniform4fv(canvas.edgeColorPos, flatten(edge));
   
   model.Display();

   edge = vec4(0.0, 0.0, 1.0, 1.0);
   d = mult(c, a);

   canvas.gl.uniformMatrix4fv(canvas.mat1Pos, false, flatten(d));
   canvas.gl.uniformMatrix4fv(canvas.mat2Pos, false, flatten(b));
   canvas.gl.uniform4fv(canvas.edgeColorPos, flatten(edge));

   model.Display();

   return;
};


function KeyFunc(evnt, canvas, models) {
   switch(evnt.key) {
       case 'X':  canvas.Rotate('x', 'r'); break;
       case 'Y':  canvas.Rotate('y', 'r'); break;
       case 'Z':  canvas.Rotate('z', 'r'); break;
       case 'x':  canvas.Rotate('x', 'f'); break;
       case 'y':  canvas.Rotate('y', 'f'); break;
       case 'z':  canvas.Rotate('z', 'f'); break;
       case 'r':  canvas.Reset(); break;
   }
   DoRedisplay(canvas, models);
};

function MakeScene(model) {
    var xwign= new xwingModel();
    model = new ModelDisplay(canvas, xwing);

    return;
}

canvas.js

'use strict';

function MakeCanvas(width, height, locID) {

    if (width == undefined || width < 0) {
       width = 300;
    }

    if (height == undefined || height < 0) {
       height = 300;
    }

    var canvas = document.createElement('canvas')
        canvas.tabIndex = 0;
        canvas.height = height;
        canvas.width = width;
	canvas.style.border = "1px solid #0000FF";

    if(locID == undefined) {
        document.body.appendChild(canvas);
    } else {
        div = document.getElementById(locID);
        if (null == div) {
            document.body.appendChild(canvas);
        } else {
            div.appendChild(canvas);
        }
    }

    document.body.appendChild(canvas);
    return canvas;
}

function InitGL(canvas) {
    var gl =  WebGLUtils.setupWebGL(canvas,'OES_standard_derivatives');
    if (!gl) {
        alert ("WebGL isn't available");
    }

    gl.getExtension('OES_standard_derivatives');
    return gl;
}

function Canvas(width, height, locID) {
    this.disp = MakeCanvas(width, height, locID);

    var gl = InitGL(this.disp);
    this.gl = gl;

    var tmpCanvas = this;
    this.x = this.disp.offsetLeft;
    this.y = this.disp.offsetTop;

    gl.viewport(0,0, width, height);

    this.program = initShaders(gl, "vertex-shader","fragment-shader");
    gl.useProgram(this.program);

    this.mat1Pos = gl.getUniformLocation(this.program, "uMatrix1");
    this.mat2Pos = gl.getUniformLocation(this.program, "uMatrix2");
    this.worldMatPos = gl.getUniformLocation(this.program, "uWorld");
    this.edgeColorPos = gl.getUniformLocation(this.program, "uEdgeColor");

    this.Init();
    return this;
}

Canvas.prototype = {

    Init: function() {
        this.gl.clearColor(1.0, 1.0, 1.0, 1.0);

        this.gl.enable(this.gl.BLEND);
	this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);

        this.gl.enable(this.gl.DEPTH_TEST);
	this.gl.depthFunc(this.gl.LESS);
	this.gl.depthMask(this.gl.TRUE);

        this.shaderChoice = false;
        this.gl.uniform1f(this.shaderLoc, 0.0);

        this.gl.enable(this.gl.CULL_FACE);
        this.gl.frontFace(this.gl.CCW);
	this.gl.cullFace(this.gl.BACK);

	this.Reset();
    },

    Rotate: function(axis, dir) {
        var change = 5;
	if (dir =='r') {
	    change = -5;
	}
        switch(axis) {
	   case 'x': this.xr += change; break;
	   case 'y': this.yr += change; break;
	   case 'z': this.zr += change; break;
	}
    },

    Reset: function() {
	this.xr = 0;
	this.yr = 0;
	this.zr = 0;
    },


    Redisplay: function() {
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);

	var transform = mat4(1);
	transform = mult(transform, rotate(this.xr, [1,0,0]));
	transform = mult(transform, rotate(this.yr, [0,1,0]));
	transform = mult(transform, rotate(this.zr, [0,0,1]));
        this.gl.uniformMatrix4fv(this.worldMatPos,false, flatten(transform ));
        return;
    }
};

modelDisplay.js

'use strict';


/*
   ModelDisplay is responsible for
           Maintaining a connection between the vertex buffer and the model
	   Displaying the model when called.
        
*/
function ModelDisplay(canvas, model) {
     this.gl = canvas.gl;
     var gl = canvas.gl;

     this.vboTriangles = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, this.vboTriangles);
     this.pointCount = model.Triangles.length;

     this.vPos = gl.getAttribLocation(canvas.program, "aPosition");
     gl.vertexAttribPointer(this.vPos,3,gl.FLOAT, false,0,0);
     gl.enableVertexAttribArray(this.vPos);

     gl.bufferData(gl.ARRAY_BUFFER,flatten(model.Triangles),gl.STATIC_DRAW);

     this.vboBC = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, this.vboBC);

     this.vBC = gl.getAttribLocation(canvas.program, "aBC");
     gl.vertexAttribPointer(this.vBC,3,gl.FLOAT, false,0,0);
     gl.enableVertexAttribArray(this.vBC);

     gl.bufferData(gl.ARRAY_BUFFER,flatten(model.BC),gl.STATIC_DRAW);
};

ModelDisplay.prototype= {
     Display() {
        var gl = this.gl;

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vboTriangles);
        gl.vertexAttribPointer(this.vPos,3,gl.FLOAT, false,0,0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vboBC);
        gl.vertexAttribPointer(this.vBC,3,gl.FLOAT, false,0,0);

        this.gl.drawArrays(this.gl.TRIANGLES, 0, this.pointCount);
     }
};