<body bgcolor=#80a0ff>
<center><canvas id=canvas width=800 height=800></canvas>
<script src=webgl2.js></script>
<script src=library.js></script>
<script>
 
function Scene() {
 
/*
   BELOW IS AN EXAMPLE OF HOW A PHONG-SHADED MATERIAL MIGHT BE DESCRIBED.
 
   ********** TO DO FOR HOMEWORK: **********
 
   INCORPORATE THIS KIND OF MATERIAL INFORMATION INTO YOUR FRAGMENT
   SHADER SO THAT YOU CAN RENDER DIFFERENT MATERIALS. SEE HOW MANY
   DIFFERENT MATERIALS (EG: TYPES OF METAL, PLASTIC) YOU CAN CREATE.
*/
 
   let gold = { ambient: [.3,.2,.0], diffuse: [.3,.2,.0], specular: [.6,.4,0,20] };
 
   this.vertexSize = 8;
   this.vertexAttributes = {
      aPos: { size: 3, position: 0 },
      aNor: { size: 3, position: 3 },
      aUV:  { size: 2, position: 6 },
   };
 
/*
   ********** TO DO FOR HOMEWORK: **********
 
   DEFINE A SPHERE TYPE BY IMPLEMENTING A createSphere() FUNCTION.
   AS WE DISCUSSED IN CLASS, YOU CAN DO THIS IN ONE OF TWO WAYS:
 
   (1) LOOP OVER u AND v TO DEFINE LONGITUDE AND LONGITUDE FOR A GLOBE:
 
      theta = 2 * PI * u   , where 0 <= u <= 1
      phi   = PI * v - PI/2, where 0 <= v <= 1
 
      ANY POINT [x,y,z] ON THE SPHERE CAN THEN BE DEFINED AS:
 
         [ cos(theta) * cos(phi) , sin(theta) * cos(phi) , sin(phi ]
 
   (2) USE THE INFLATED CUBE METHOD:
 
       (2a) DIVIDE EACH FACE OF THE UNIT CUBE INTO A GRID OF SUMALL SQUARES.
 
       (2b) NORMALIZE ALL VERTICES (THAT IS, SET THEM TO UNIT LENGTH).
 
   IN BOTH OF THE ABOVE CASES, THE SURFACE NORMAL IS THE SAME AS THE POINT,
   BECAUSE THIS IS A UNIT SPHERE.
*/
 
   let createCylinder = n => {
      let vertices = [];
      for (let i = 0 ; i < n ; i++) {
         let u0 = i / n;
         let u1 = (i+1) / n;
	 let x0 = Math.cos(2 * Math.PI * u0);
	 let y0 = Math.sin(2 * Math.PI * u0);
	 let x1 = Math.cos(2 * Math.PI * u1);
	 let y1 = Math.sin(2 * Math.PI * u1);
	 vertices.push(
	    x0,y0,-1, x0,y0,0, u0,0,  x0,y0, 1, x0,y0,0, u0,1,  x1,y1, 1, x1,y1,0, u1,1,
	    x1,y1,-1, x1,y1,0, u1,0,  x0,y0,-1, x0,y0,0, u0,0,  x1,y1, 1, x1,y1,0, u1,1,
 
	    x0,y0, 1, 0,0, 1, 0,1,   x1,y1, 1, 0,0, 1, 0,1,   0, 0, 1, 0,0, 1, 0,1,
	    x0,y0,-1, 0,0,-1, 0,0,   x1,y1,-1, 0,0,-1, 0,0,   0, 0,-1, 0,0,-1, 0,0,
         );
      }
      return vertices;
   }
 
   let createCube = () => [
       1,-1, 1,  1,0,0, 0,1,   1, 1,-1,  1,0,0, 1,0,   1,-1,-1,  1,0,0, 0,0,
       1,-1, 1,  1,0,0, 0,1,   1, 1,-1,  1,0,0, 1,0,   1, 1, 1,  1,0,0, 1,1,
      -1,-1, 1, -1,0,0, 0,1,  -1, 1,-1, -1,0,0, 1,0,  -1,-1,-1, -1,0,0, 0,0,
      -1,-1, 1, -1,0,0, 0,1,  -1, 1,-1, -1,0,0, 1,0,  -1, 1, 1, -1,0,0, 1,1,
 
      -1, 1, 1, 0, 1,0, 0,1,   1, 1,-1, 0, 1,0, 1,0,  -1, 1,-1, 0, 1,0, 0,0,
      -1, 1, 1, 0, 1,0, 0,1,   1, 1,-1, 0, 1,0, 1,0,   1, 1, 1, 0, 1,0, 1,1,
      -1,-1, 1, 0,-1,0, 0,1,   1,-1,-1, 0,-1,0, 1,0,  -1,-1,-1, 0,-1,0, 0,0,
      -1,-1, 1, 0,-1,0, 0,1,   1,-1,-1, 0,-1,0, 1,0,   1,-1, 1, 0,-1,0, 1,1,
 
      -1, 1, 1, 0,0, 1, 0,1,   1,-1, 1, 0,0, 1, 1,0,  -1,-1, 1, 0,0, 1, 0,0,
      -1, 1, 1, 0,0, 1, 0,1,   1,-1, 1, 0,0, 1, 1,0,   1, 1, 1, 0,0, 1, 1,1,
      -1, 1,-1, 0,0,-1, 0,1,   1,-1,-1, 0,0,-1, 1,0,  -1,-1,-1, 0,0,-1, 0,0,
      -1, 1,-1, 0,0,-1, 0,1,   1,-1,-1, 0,0,-1, 1,0,   1, 1,-1, 0,0,-1, 1,1,
   ];
 
   this.shapes = [
      {
         material: gold,
         vertices: createCube(),
      },
      {
         vertices: createCylinder(8),
      },
   ];
 
   this.vertexShader = `#version 300 es
   in  vec3 aPos, aNor;
   in  vec2 aUV;
   out vec3 vPos, vNor;
   out vec2 vUV;
   uniform mat4 uMF, uMI;
   void main() {
      vec4 pos = uMF * vec4(aPos, 1.);
      vec4 nor = vec4(aNor, 0.) * uMI;
      vPos = pos.xyz / pos.w;
      vPos.z *= -1.;
      vNor = nor.xyz;
      vUV = aUV;
      gl_Position = vec4(vPos, 1.);
   }`;
 
   this.fragmentShader = `#version 300 es
   precision highp float;
 
   in  vec3 vPos, vNor;
   in  vec2 vUV;
   out vec4 fragColor;
 
   void main() {
      vec3 nor = normalize(vNor);
      float a = .5 * dot(nor,vec3(-.7));
      float d = .5 + max(0.,a) + max(0.,-a);
//    d *= .5 + .5 * noise(10.*vec3(vUV,.5));
      fragColor = vec4(d,d,d,1.);
   }`;
 
   let m = new Matrix();
 
   let startTime = Date.now() / 1000;
 
   this.update = () => {
      let time = Date.now() / 1000 - startTime;
 
/*
      WHAT YOU REALLY WANT IS TO BE ABLE TO ANIMATE
      EACH SHAPE INDEPENDENTLY.
 
      ********** TO DO FOR HOMEWORK: **********
 
      GIVE EACH SHAPE ITS OWN matrix PROPERTY. THEN IN
      FILE webgl2.js, MODIFY setInterval() SO THAT IT
      CALLS setUniform() TO SET THE VALUES OF uMF AND
      uMI FOR EACH SHAPE BEFORE IT CALLS drawArrays()
      FOR THAT SHAPE.
*/
 
      m.identity();
      m.perspective(10);
      m.turnX(time);
      m.turnY(2*time);
      m.turnZ(3*time);
      m.scale(.1,.1,.5);
 
      setUniform('Matrix4fv', 'uMF', false, m.get());
      m.inverse();
      setUniform('Matrix4fv', 'uMI', false, m.get());
   }
}
 
gl_start(canvas, new Scene());
</script>