Lights

Color Red Green Blue
Ambient
Diffuse
Specular

Value of alpha:

Key ActioN
S Swap Shaders
x rotate Item about X axis
y rotate Item about Y axis
z rotate Item about Z axis
s Toggle Specular
a Toggle Ambient
d Toggle Diffuse

The HTML file:

<html>
<head>

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

attribute vec4 attributePosition;
attribute float attributeBC;

attribute vec3 surfaceNormal;
attribute vec3 vertexNormal;

uniform mat4 uniformTransform;
uniform mat4 worldTransform;
uniform mat4 uniformProject;

uniform float shadingType;
uniform float uAmbient;
uniform float uSpecular;
uniform float uDiffuse;
uniform float uAlpha;

uniform vec3 uSurfaceAmbient;
uniform vec3 uSurfaceSpecular;
uniform vec3 uSurfaceDiffuse;

varying vec3 varyingBC;
varying vec4 color;

varying vec3 varNormal;
varying vec3 lightVector;
varying vec3 halfAngle;
varying float fade;

void main() {

    vec4 here = worldTransform * uniformTransform *  attributePosition;
    gl_Position = uniformProject * here;
    vec4 tmp = worldTransform * uniformTransform * vec4(surfaceNormal,1);
    vec3 normal = normalize(tmp.xyz);

    varNormal = normal.xyz;
    here = -here;

    vec4 light = vec4(0.0 ,-1.0 ,4.0, 0.0);
    light = worldTransform * light;
    
    lightVector = normalize(light-here).xyz;
    halfAngle = normalize(vec4(0.0, 0.0, -4.0, 0.0)+ light).xyz;
/*
    float a = 1.0;
    float b = 0.0;
    float c = 0.0;

    float dist = distance(light.xyz,here.xyz);
    fade = 1.0/(a+b*dist + c *dist*dist);
*/
    fade = 1.0;
    if (attributeBC == 0.0) {
         varyingBC = vec3(1.0, 0.0, 0.0);
    } else if (attributeBC == 1.0) {
         varyingBC = vec3(0.0, 1.0, 0.0);
    } else {
         varyingBC = vec3(0.0, 0.0, 1.0);
    }

    if (shadingType < 0.5) { 

	vec3 lightAmbient = vec3(1.0,1.0, 1.0);
	vec3 lightDiffuse = vec3(1.0, 1.0, 1.0);
	vec3 lightSpecular = vec3(1.0, 1.0, 1.0);
	vec3 zero = vec3(0.0, 0.0, 0.0);
	vec3 one = vec3(1.0, 1.0, 1.0);

	vec3  ambient = lightAmbient * uSurfaceAmbient;
	vec3 diffuse = max(dot(lightVector, normal),0.0)
	                  * lightDiffuse * uSurfaceDiffuse ;
        diffuse = clamp(diffuse, zero, one);
	vec3 specular = max(pow(dot(halfAngle,normal),uAlpha),0.0) 
	                  * lightSpecular * uSurfaceSpecular;
        specular = clamp(specular,zero,one);

        if (uAmbient > .5) {
	   ambient = zero;
	}
	if (uDiffuse > .5) {
	    diffuse = zero;
	}
	if (uSpecular > .5) {
	    specular = zero;
	}

        color = vec4(clamp(ambient + fade*(diffuse + specular), zero,one),1.0);
    }  else {
        vec4 tmp = worldTransform * uniformTransform * vec4(vertexNormal,1);
        varNormal = normalize(tmp.xyz);
    }
}
</script>

<script id="fragment-shader" type="x-shader/x-fragment">

    precision mediump float;
    varying vec3 varyingBC;
    uniform vec4 uniformEdgeColor;

    uniform float shadingType;
    uniform float uAmbient;
    uniform float uSpecular;
    uniform float uDiffuse;
    uniform float uAlpha;

    uniform vec3 uSurfaceAmbient;
    uniform vec3 uSurfaceSpecular;
    uniform vec3 uSurfaceDiffuse;

    varying vec4 color;
    varying vec3 varNormal;
    varying vec3 lightVector;
    varying vec3 halfAngle;
    varying float fade;


    void main(){

        if (shadingType <   0.5) {
            gl_FragColor= color;
	} else {

	    vec3 lightAmbient = vec3(1.0,1.0, 1.0);
            vec3 lightDiffuse = vec3(1.0, 1.0, 1.0);
            vec3 lightSpecular = vec3(1.0, 1.0, 1.0);
	    vec3 zero = vec3(0.0, 0.0, 0.0);
	    vec3 one = vec3(1.0, 1.0, 1.0);

            // because they may have become denormailzed in interpolation
	    vec3 L = normalize(lightVector);
	    vec3 N = normalize (varNormal);
	    vec3 H = normalize(halfAngle);

	    vec3  ambient = lightAmbient * uSurfaceAmbient;
	    vec3 diffuse = max(dot(L,N),0.0) * lightDiffuse * uSurfaceDiffuse ;
            diffuse = clamp(diffuse, zero, one);

	    vec3 specular = max(pow(dot(H,N),uAlpha),0.0) 
	                  * lightSpecular * uSurfaceSpecular;
            specular = clamp(specular,zero,one);

            if (uAmbient > .5) {
	       ambient = zero;
	    }
	    if (uDiffuse > .5) {
	        diffuse = zero;
	    }
	    if (uSpecular > .5) {
	        specular = zero;
	    }

            gl_FragColor = vec4(clamp(ambient + fade*(diffuse + specular),
	                                                        zero,one),1.0);
	}
    }
</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/teapot.js"></script>
<script type="text/javascript" src="../Models/bunny.js"> </script>
<script type="text/javascript" src="../Models/cone.js"> </script>
<script type="text/javascript" src="../Models/dragon.js"> </script>
<script type="text/javascript" src="../Models/head.js"></script>
<script type="text/javascript" src="../Models/stego.js"></script>
<script type="text/javascript" src="../Models/xwing.js"></script>
<script type="text/javascript" src="../Models/butterfly.js"></script>
<script type="text/javascript" src="../Models/copter.js"></script>
<script type="text/javascript" src="../Models/enterprise.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/cube.js"></script>
<script type="text/javascript" src="../Models/epcot.js"></script>
<script type="text/javascript" src="../Models/plane.js"></script>

<script type="text/javascript" src="GLCanvas.js"></script>
<script type="text/javascript" src="utils.js"></script>
<script type="text/javascript" src="Widget.js"></script>


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

</head>

<body>
     <h1>Lights</h1>
<select onchange="MakeItem(this.value);Redisplay()">
   <option value="teapot">Teapot</option>
   <option value="lizard">lizard</option>
   <option value="bunny">bunny</option>
   <option value="epcot">epcot</option>
</select>
<script>
'use strict'

    var canvas = new Canvas(400, 400, Keypress);

    var item;
    var camera, at, up, theta, phi;
    var orx=0, ory=0,orz=0;

    MakeItem("teapot")

    ResetCamera();
    canvas.NewView(camera, at, up);
    Redisplay();
</script>
<p>
<table>
<tr><th>Color</th><th> Red</th><th> Green</th><th> Blue</th></td>
<tr><td> Ambient</td>
<td>  
   <input type="range" min="1" max="100" value="5" class="slider"
          id="ambientRed" onchange="FixColor()" list="ticks">
   <output for="ambientRedInput" id="ambientRedInputValue"></output>
</td>
<td>  
   <input type="range" min="1" max="100" value="5" class="slider"
          id="ambientGreen" onchange="FixColor()" list="ticks">
   <output for="ambientGreenInput" id="ambientGreenInputValue"></output>
</td>
<td>  
   <input type="range" min="1" max="100" value="5" class="slider"
          id="ambientBlue" onchange="FixColor()" list="ticks">
   <output for="ambientBlueInput" id="ambientBlueInputValue"></output>
</td></tr>
<tr><td> Diffuse</td>
<td>  
   <input type="range" min="1" max="100" value="80" class="slider"
          id="diffuseRed" onchange="FixColor()" list="ticks">
   <output for="diffuseRedInput" id="diffuseRedInputValue"></output>
</td>
<td>  
   <input type="range" min="1" max="100" value="0" class="slider"
          id="diffuseGreen" onchange="FixColor()" list="ticks">
   <output for="diffuseGreenInput" id="diffuseGreenInputValue"></output>
</td>
<td>  
   <input type="range" min="1" max="100" value="0" class="slider"
          id="diffuseBlue" onchange="FixColor()" list="ticks">
   <output for="diffuseBlueInput" id="diffuseBlueInputValue"></output>
</td></tr>
<tr><td> Specular</td>
<td>  
   <input type="range" min="1" max="100" value="70" class="slider"
          id="specularRed" onchange="FixColor()" list="ticks">
   <output for="specularRedInput" id="specularRedInputValue"></output>
</td>
<td>  
   <input type="range" min="1" max="100" value="10" class="slider"
          id="specularGreen" onchange="FixColor()" list="ticks">
   <output for="specularGreenInput" id="specularGreenInputValue"></output>
</td>
<td>  
   <input type="range" min="1" max="100" value="10" class="slider"
          id="specularBlue" onchange="FixColor()" list="ticks">
   <output for="specularBlueInput" id="specularBlueInputValue"></output>
</td></tr>

</table>
<p>
   Value of alpha:
   <input type="range" min="1" max="500" value="100" class="slider"
          id="alphaInput" onchange="FixAlpha()" list="ticks">
   <output for="alphaInput" id="alphaInputValue"></output>
   <datalist id="ticks">
   <option value="1"></option>
   <option value="120"></option>
   <option value="250"></option>
   <option value="360"></option>
   <option value="500"></option>
   </datalist>
<p>
<table>
<tr><th> Key</td><td> ActioN</td></tr>
<tr><td> S</td><td> Swap Shaders</td></tr>
<tr><td> x</td><td> rotate Item about X axis</td></tr>
<tr><td> y</td><td> rotate Item about Y axis</td></tr>
<tr><td> z</td><td> rotate Item about Z axis</td></tr>
<tr><td> s</td><td> Toggle Specular</td></tr>
<tr><td> a</td><td> Toggle Ambient</td></tr>
<tr><td> d</td><td> Toggle Diffuse</td></tr>
</table>

</body>

GLCanvas.js

'use strict'

class Canvas {
    constructor (width, height, Keypress) {
        this.height = height;
        this.width = width;

        this.MakeCanvas();
        this.canvas.addEventListener("keypress", Keypress);

        this.SetupGL();
        this.MakeShaders();

        this.Init();

        this.NewView([0,0,-1], [0,0,0],[0,1,0]);
    }

    NewView( camera, at, up) {
        var eye = lookAt(camera, at, up);
	var proj =  this.Frustum(-1, 1, -1, 1,2 ,20);
        this.gl.uniformMatrix4fv(this.worldTrans, false,flatten(eye));
        this.gl.uniformMatrix4fv(this.projLoc, false,flatten(proj));
    }

    Frustum(l,r,b,t,n,f) {
       var m =  mat4(1);
       m[0][0] = 2 * n / (r - l);
       m[0][1] = 0;
       m[0][2] = (r + l) / (r - l);
       m[0][3] = 0;

       m[1][0] = 0;
       m[1][1] = 2 * n / (t - b);
       m[1][2] = (t + b) / (t - b);
       m[1][3] = 0;

       m[2][0] = 0;
       m[2][1] = 0;
       m[2][2] = -(f + n) / (f - n);
       m[2][3] = -2 * f * n / (f - n);

       m[3][0] = 0;
       m[3][1] = 0;
       m[3][2] = -1;
       m[3][3] = 0;

       return m;
    }

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

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

         this.canvas = document.createElement('canvas')
	 this.canvas.tabIndex=0;
         this.canvas.height = this.height;
         this.canvas.width = this.width;
	 this.canvas.style.border = '1px solid #000';
         document.body.appendChild(this.canvas);
    }

    SetupGL() {
        this.gl = WebGLUtils.setupWebGL(this.canvas);
        if (!this.gl) {
            alert ("WebGL isn't available");
	    return;
        }
	this.gl.getExtension('OES_standard_derivatives');
    }

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

	this.projLoc = gl.getUniformLocation(this.program, "uniformProject");
	this.worldTrans = gl.getUniformLocation(this.program, "worldTransform");
	this.transLoc = gl.getUniformLocation(this.program, "uniformTransform");
	this.edgeColorLoc = gl.getUniformLocation(this.program, "uniformEdgeColor");
	this.surfaceColorLoc = gl.getUniformLocation(this.program, "uniformSurfaceColor");
	this.shadingType = gl.getUniformLocation(this.program,"shadingType");
	this.uAmbient = gl.getUniformLocation(this.program,"uAmbient");
	this.uSpecular = gl.getUniformLocation(this.program,"uSpecular");
	this.uDiffuse = gl.getUniformLocation(this.program,"uDiffuse");
	this.uAlpha = gl.getUniformLocation(this.program,"uAlpha");
	this.uSurfaceAmbient = gl.getUniformLocation(this.program,"uSurfaceAmbient");
	this.uSurfaceSpecular = gl.getUniformLocation(this.program,"uSurfaceSpecular");
	this.uSurfaceDiffuse = gl.getUniformLocation(this.program,"uSurfaceDiffuse");
    }

    SurfaceAmbient(r,g,b) {
	this.gl.uniform3f(this.uSurfaceAmbient,r,g,b);
    }

    SurfaceSpecular(r,g,b) {
	this.gl.uniform3f(this.uSurfaceSpecular,r,g,b);
    }

    SurfaceDiffuse(r,g,b) {
	this.gl.uniform3f(this.uSurfaceDiffuse,r,g,b);
    }

    ToggleAmbient() {
        if (this.ambientV == 1) {
	   this.ambientV = 0;
	} else {
	   this.ambientV = 1;
	}
	this.gl.uniform1f(this.uAmbient,this.ambientV);
    }

    ToggleSpecular() {
        if (this.specularV == 1) {
	   this.specularV = 0;
	} else {
	   this.specularV = 1;
	}
	this.gl.uniform1f(this.uSpecular,this.specularV);
    }

    ToggleDiffuse() {
        if (this.diffuseV == 1) {
	   this.diffuseV = 0;
	} else {
	   this.diffuseV = 1;
	}
	this.gl.uniform1f(this.uDiffuse,this.diffuseV);
    }

    SwapShaders() {
        if (this.shadingTypeV == 1.0) {
	   this.shadingTypeV = 0.0;
	} else {
	   this.shadingTypeV = 1.0;
	}
	this.gl.uniform1f(this.shadingType,this.shadingTypeV);
    }

    SetAlpha(d) {
        if (d >0 && d <= 500) {
	    this.gl.uniform1f(this.uAlpha,d);
	}
    }

    Init() {
        var gl = this.gl;

        gl.clearColor(1.0, 1.0, 1.0, 1.0);
        gl.viewport(0,0, this.width, this.height);

	gl.enable(gl.BLEND);
	gl.blendFuncSeparate(gl.SRC_ALPHA,
	               gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

        gl.enable(gl.DEPTH_TEST);
        gl.frontFace(gl.CW);

        // set the default edge color for everything
	this.NewEdgeColor([0.0, 0.0, 0.0, 1.0]);
	this.NewSurfaceColor([1.0, 0.0, 0.0, 1.0]);

	this.shadingTypeV =1;
        this.SwapShaders();	

	this.ambientV = 1;
	this.ToggleAmbient();
	this.specularV = 1;
	this.ToggleSpecular();
	this.diffuseV = 1;
	this.ToggleDiffuse();

	this.SetAlpha(100);

	this.SurfaceSpecular(0.7, 0.1, 0.1);
	this.SurfaceAmbient(0.05, 0.05, 0.05);
	this.SurfaceDiffuse(0.8, 0.0, 0.0);
    }

    NewSurfaceColor(c) {
        this.gl.uniform4fv(this.surfaceColorLoc, c);
    }

    NewEdgeColor(c) {
        this.gl.uniform4fv(this.edgeColorLoc, c);
    }

    Program() {
       return this.program;
    }

    GL() {
       return this.gl;
    }

    Translate() {
       return this.transLoc;
    }

    Clear() {
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
    }
};

Widget.js

'use strict'

class Widget {
    constructor(gl, program, tris,cw) {
	this.size = tris.length;

        var bcs = []
	for (var i=0;i<tris.length/3;i++) {
	   bcs.push([0.0,1.0,2.0]); 
	}

	this.SetupVBO(gl, program, tris, bcs);

	this.SurfaceNormals(gl,program,tris,cw);
	this.VertexNormals(gl,program,tris,cw);

	this.Reset();
	this.Transform();
    }

    SurfaceNormals(gl, program,tris,cw){
        let i;
	let sn=[];

	for(i=0;i<tris.length;i+=3) {
	    let j=0;

	    let normal;

	    let a = subtract(vec3(tris[i]) , vec3(tris[i+1]));
	    let b = subtract(vec3(tris[i]) , vec3(tris[i+2]));
	    if (equal(a, vec3(0,0,0))) {
	       a = vec3(1,0,0);
	    }
	    a = normalize(a);
	    if (equal(b, vec3(0,0,0))) {
	       b = vec3(1,0,0);
	    }
	    b = normalize(b)
	    if (cw) {
	       normal = normalize(cross(a,b));
	    }  else {
	       normal = normalize(cross(b,a));
	    }


	    for(j=0;j<3;j++ ) {
	       sn.push(normal);
	    }
	}

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

        this.aSN = gl.getAttribLocation(program, "surfaceNormal");
	if (this.aSN ==-1) {
	   console.log("failed to get attribute for surfaceNormal")
	} else {
           gl.vertexAttribPointer(this.aSN,3,gl.FLOAT, false,0,0);
           gl.enableVertexAttribArray(this.aSN);
	   gl.bufferData(gl.ARRAY_BUFFER,flatten(sn),gl.STATIC_DRAW);
	}
    }

    VertexNormals(gl,program,tris,cw) {

        var verts = new Map();
        let i;
	let sn=[];

	for(i=0;i<tris.length;i+=3) {
	    let j=0;

	    let normal;

	    let a = subtract(vec3(tris[i]) , vec3(tris[i+1]));
	    let b = subtract(vec3(tris[i]) , vec3(tris[i+2]));
	    if (equal(a, vec3(0,0,0))) {
	       a = vec3(1,0,0);
	    }
	    a = normalize(a);
	    if (equal(b, vec3(0,0,0))) {
	       b = vec3(1,0,0);
	    }
	    b = normalize(b)
	    if (cw) {
	       normal = normalize(cross(a,b));
	    }  else {
	       normal = normalize(cross(b,a));
	    }

            for (let j=0;j<3;j++) {
	        if (verts.has(tris[i+j])) {
	            verts[tris[i+j]] = add(verts[tris[i+j]],normal);
	        } else {
	            verts[tris[i+j]] = normal;
	        }
	    }

	}

	for (let k of verts.keys()) {
	     verts[k] = normalize(verts[k]);
	}

	for(i=0;i<tris.length;i+=3) {
            for (let j=0;j<3;j++) {
	        sn.push(verts[tris[i+j]]); 
            }
	}

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

        this.aVN = gl.getAttribLocation(program, "vertexNormal");
	if (this.aVN ==-1) {
	   console.log("failed to get attribute for vertexNormal")
	} else {
           gl.vertexAttribPointer(this.aVN,3,gl.FLOAT, false,0,0);
           gl.enableVertexAttribArray(this.aVN);
	   gl.bufferData(gl.ARRAY_BUFFER,flatten(sn),gl.STATIC_DRAW);
	}

    }

    // things we might want to have to totally reset the item.
    Reset() {
        this.visible = true;
	this.rx = 0;
	this.ry = 0;
	this.rz = 0;
	this.sx = 1;
	this.sy = 1;
	this.sz = 1;
	this.tx = 0;
	this.ty = 0;
	this.tz = 0;
    }

    SetupVBO(gl, program, tris, bcs) {

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

	this.aPos =  gl.getAttribLocation(program, "attributePosition");
	if (this.aPos == -1) {
	   console.log("Faild to get attributePosition") ;
	} else { 
           gl.vertexAttribPointer(this.aPos, 3, gl.FLOAT, false, 0, 0);
           gl.enableVertexAttribArray(this.aPos);
	   gl.bufferData(gl.ARRAY_BUFFER,flatten(tris),gl.STATIC_DRAW);
	}

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

        this.aBC = gl.getAttribLocation(program, "attributeBC");
	if (this.aBC == -1) {
	   console.log("Faild to get attributeBC") ;
	} else {
            gl.vertexAttribPointer(this.aBC,1,gl.FLOAT, false,0,0);
            gl.enableVertexAttribArray(this.aBC);
	    gl.bufferData(gl.ARRAY_BUFFER,flatten(bcs),gl.STATIC_DRAW);
	}
    }

    Show() {
        this.visible = true;
    }

    Hide() {
        this.visible = false;
    }

    Visible() {
        return this.visible;
    }

    Transform() {
        var tmp = translate(this.tx, this.ty, this.tz);
	tmp = mult(tmp, scalem(this.sx, this.sy, this.sz));
	tmp = mult(tmp, rotate(this.rz, [0,0,1]));
	tmp = mult(tmp, rotate(this.ry, [0,1,0]));
	tmp = mult(tmp, rotate(this.rx, [1,0,0]));

	this.transform = tmp;
    }

    Display(gl, transform, transLoc) {
          if (this.visible) {

              // make sure that the transform matrix is up to date.
              this.Transform();

	      // multiply it by any incoming transformation matrix
              let tx  =
	      mult(transform, this.transform);
	      // use it
	      gl.uniformMatrix4fv(transLoc, false, flatten(tx)); 

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

              gl.bindBuffer(gl.ARRAY_BUFFER, this.vBC);
              gl.vertexAttribPointer(this.aBC, 1, gl.FLOAT, false, 0, 0);

              gl.bindBuffer(gl.ARRAY_BUFFER, this.vSN);
              gl.vertexAttribPointer(this.aSN, 3, gl.FLOAT, false, 0, 0);

              gl.bindBuffer(gl.ARRAY_BUFFER, this.vVN);
              gl.vertexAttribPointer(this.aVN, 3, gl.FLOAT, false, 0, 0);

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

utils.js

'use strict'

const FA = [-10, -1, -10];
const FB = [ 10, -1.5, -10];
const FC = [0, -1.5, 0];
const FD = [ 10, -1.5,  10];
const FE = [-10, -1.5,  10];

const CA = [-10, 3, -10];
const CB = [ 10, 3, -10];
const CC = [  0, 3,   0];
const CD = [ 10, 3,  10];
const CE = [-10, 3,  10];

const Floor= [FA, FB, FC, FB, FD, FC, FD, FE, FC, FE, FA, FC];
const Roof = [CB, CA, CC, CD, CB, CC, CE, CD, CC, CA, CE, CC];

    function MakeTeapot() {
      let teapot =new Widget(canvas.GL(), canvas.Program(), 
                                            Teapot_Triangles,false);
          teapot.tz = .5 
          teapot.rx = -90
	  orx = -90;
	  teapot.sx = 2; teapot.sy = 2; teapot.sz = 2;
       return teapot;
    }

    function MakeLizard() {
      let lizard =new Widget(canvas.GL(), canvas.Program(), 
                                            Lizard_Triangles,false);
          lizard.tz = .5 
          lizard.ty = -1.3 
          lizard.rx = -90
	  orx = -90;
	  lizard.sx = 3.4; lizard.sy = 3.4; lizard.sz = 3.4;
       return lizard;
    }
    function MakeBunny() {
      let bunny =new Widget(canvas.GL(), canvas.Program(), 
                                            Bunny_Triangles,false);
          bunny.tz = 1.0 
          bunny.ty = 0
          bunny.ry = 90
	  orx = 90;
	  bunny.sx = 3.4; bunny.sy = 3.4; bunny.sz = 3.4;
       return bunny;
    }
    function MakeEpcot() {
      let epcot =new Widget(canvas.GL(), canvas.Program(), 
                                            Epcot_Triangles,false);
          epcot.tz = 1.0 
          epcot.ty = 0
	  epcot.sx = 3.4; epcot.sy = 3.4; epcot.sz = 3.4;
       return epcot;
    }

    function MakeItem(key) {
          if (key == "teapot") {
               item = MakeTeapot(); 
	  } else if (key == "lizard") {
	       item = MakeLizard();
	  } else if (key == "bunny") {
	       item = MakeBunny();
	  } else if (key == "epcot") {
	       item = MakeEpcot();
	  }
    }

    function ResetCamera() {
        up = [0,1,0];
        theta = 0;
        phi = 90;

        camera = [0,0,-3.0];
	MoveAt(0.5);
    }    

    function Fix(angle) {
       while (angle < 0) {
          angle += 360;
       }
       angle %= 360;
       return angle;
    }

    function Offset(delta) {
        let x,y,z;

        theta = Fix(theta);
        phi = Fix(phi);

        let rphi = phi * Math.PI/180;
        let rtheta = theta * Math.PI/180;

        z =  delta * Math.sin(rphi) * Math.cos(rtheta);
        y =  delta * Math.cos(rphi)
        x =  delta * Math.sin(rphi) * Math.sin(rtheta);

        return ([x,y,z]);
    }

    function MoveAt(delta) {
        let offset = Offset(delta);
        at = add(camera, offset);
    }

    function MoveAll(delta) {
        let offset = Offset(delta);
        camera = add(camera ,  offset);
        at = add(camera, offset);
    }

    function Keypress(evnt) {
       switch(evnt.key) {
          case 'S': canvas.SwapShaders();
	            break;
	  case 'R': item.rx = orx;
	            item.ry = ory;
		    item.rz = orz;
		    break;
          case 'x': item.rx ++;
	            break;
          case 'y': item.ry ++;
	            break;
          case 'z': item.rz ++;
	            break;
          case 'a': canvas.ToggleAmbient();
	            break;
          case 's': canvas.ToggleSpecular();
	            break;
	  case 'd': canvas.ToggleDiffuse();
	            break;
       }

       Redisplay();
    }

    function FixAlpha() {
       let alpha = document.getElementById('alphaInput').value
       canvas.SetAlpha(alpha);
       Redisplay();
    }

    function FixColor() {
       let aR = document.getElementById('ambientRed').value
       let aG = document.getElementById('ambientGreen').value
       let aB = document.getElementById('ambientBlue').value
       canvas.SurfaceAmbient(aR/100,aG/100,aB/100);
       let dR = document.getElementById('diffuseRed').value
       let dG = document.getElementById('diffuseGreen').value
       let dB = document.getElementById('diffuseBlue').value
       canvas.SurfaceDiffuse(dR/100,dG/100,dB/100);
       let sR = document.getElementById('specularRed').value
       let sG = document.getElementById('specularGreen').value
       let sB = document.getElementById('specularBlue').value
       canvas.SurfaceSpecular(sR/100,sG/100,sB/100);
       Redisplay();
    }


    function Redisplay() {

        canvas.Clear();
        canvas.NewEdgeColor([0.0, 0.0, 0.0, 1.0]);

        canvas.NewSurfaceColor([1.0, 0.0, 0.0, 1.0]);
        canvas.GL().frontFace(canvas.GL().CW);
        item.Display(canvas.GL(), mat4(), canvas.Translate());
    }