A scene and a Camera

Keys wasdUu to move the camera: u y down, U y up.

Keys hjkl to move the look at: hl: x, jk: y

  • 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, uWorld;
    uniform mat4 uCamera, uProject;
    
    varying vec3 vBC;
    
    void main() {
    
        gl_Position = uProject * uCamera * uWorld * 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>A scene and a Camera </h1>
    <script>
    
        // set up the canvas 
        var canvas = new Canvas(500, 500);
    
        var models = [];
     
        MakeScene(models);
        
        //add a callback for keypresses
        canvas.disp.addEventListener("keypress",
             function(evnt) {
                KeyFunc(evnt, canvas, models);
             }
        );
    
        // display it.
        DoRedisplay(canvas, models);
    </script>
    <p>
    Keys <b>wasdUu</b> to move the camera:  u y down, U y up.
    <p>
    Keys <b>hjkl</b> to move the look at:   hl: x, jk: y
    <p>
    <lI> <img src="map.png">
    
    </body>
    

    main.js

    'use strict';
    
    function BuildTransform(i) {
       var a = mat4(1);
       var edge = vec4(1,0,0,1);
    
       switch(i) {
           case 0:
                // lizard is at 0.
    	    a = translate(.2,.2,.0); 
    	    a = mult(rotate(-90,[1,0,0]),a);
    	    a = mult(scalem(3,3,3),a);
                edge = vec4(1,0,0,1);
    	    break;
           case 1:
                // cube is at x=-2
                a = translate(-2,0,0);
                edge = vec4(0,1,0,1);
    	    break;
           case 2:
                // teapot is 2 x = 2
                a = mult(translate(2,0,0), rotate(-90,[1,0,0]));
                edge = vec4(0,0,1,1);
    	    break;
           case 3:
                // cone is at z = 2
                a = mult(translate(0,0,2), scalem(2,2,2));
                edge = vec4(0,1,1,1);
    	    break;
           case 4:
                // epcot is at z= -2
                a = translate(0,0,-2);
                edge = vec4(1,0,1,1);
    	    break;
           default:
                edge = vec4(0,0,0,1);
    	    break;
       }
    
       canvas.gl.uniformMatrix4fv(canvas.mat1Pos, false, flatten(a));
    
       canvas.gl.uniform4fv(canvas.edgeColorPos, flatten(edge));
    }
    
    function DoRedisplay(canvas, models) {
       var i;
       canvas.Redisplay();
    
       for(i=0;i<models.length;i++) {
           BuildTransform(i);
           models[i].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;
    
           case 'w':  canvas.MoveCamera([0,0,1]); break;
           case 's':  canvas.MoveCamera([0,0,-1]); break;
           case 'a':  canvas.MoveCamera([-1,0,0]); break;
           case 'd':  canvas.MoveCamera([1,0,0]); break;
           case 'u':  canvas.MoveCamera([0,-1,0]); break;
           case 'U':  canvas.MoveCamera([0,1,0]); break;
    
           case 'h':  canvas.MoveEye([-1,0]); break;
           case 'j':  canvas.MoveEye([0,1]); break;
           case 'k':  canvas.MoveEye([0,-1]); break;
           case 'l':  canvas.MoveEye([1,0]); break;
       }
       DoRedisplay(canvas, models);
    };
    
    function MakeScene(models) {
        var thing= new lizardModel();
        models.push(new ModelDisplay(canvas, thing));
    
        thing= new cubeModel();
        models.push(new ModelDisplay(canvas, thing));
    
        thing= new teapotModel();
        models.push(new ModelDisplay(canvas, thing));
    
        thing= new coneModel();
        models.push(new ModelDisplay(canvas, thing));
    
        thing= new epcotModel();
        models.push(new ModelDisplay(canvas, thing));
    
        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.cameraMatPos = gl.getUniformLocation(this.program, "uCamera");
        this.projectionMatPos = gl.getUniformLocation(this.program, "uProject");
        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;
    
    	this.ex = 0;
    	this.ey = 0;
    	this.ez = -3;
    
    	this.atx = 0;
    	this.aty = 0;
    	this.atz = 0;
    	this.RedoCameraMat();
    
    	this.near = .5;
    	this.far = 5;
    	this.left = -4;
    	this.right = 4;
    	this.top = 4;
    	this.bottom = -4;
    	
    	this.RedoProjectionMatrix();
        },
    
        RedoProjectionMatrix: function() {
            var mat = ortho(this.left, this.right, this.bottom, this.top, 
    	                                              this.near, this.far);
            this.gl.uniformMatrix4fv(this.projectionMatPos,false,flatten(mat));
        },
    
        ChangeView: function(x,y,near, far){
             this.left = -x;
    	 this.right = x;
    	 this.near = near;
    	 this.far = far;
    	 this.top = y;
    	 this.bottom = -y;
    
    	this.RedoProjectionMatrix();
    
        },
    
        MoveCamera: function(dvec) {
            var posDelta = 0.1;
    
            this.ex += dvec[0]*posDelta; 
            this.ey += dvec[1]*posDelta; 
            this.ez += dvec[2]*posDelta; 
    
            this.atx += dvec[0]*posDelta; 
            this.aty += dvec[1]*posDelta; 
            this.atz += dvec[2]*posDelta; 
    
            this.RedoCameraMat(); 
        },
    
        MoveEye: function(dvec) {
            var posDelta = 0.01;
    
            this.atx += dvec[0]*posDelta; 
            this.aty += dvec[1]*posDelta; 
            this.RedoCameraMat(); 
        },
    
        RedoCameraMat: function() {
    	console.log("eye is ", this.ex, this.ey, this.ez);
    	console.log("Look at is ", this.atx, this.aty, this.atz);
            var cameraMatrix = lookAt([this.ex, this.ey, this.ez], 
    	                           [this.atx, this.aty, this.atz], 
    				   [0,1,0]);
            this.gl.uniformMatrix4fv(this.cameraMatPos,false,flatten(cameraMatrix));
        },
    
    
    
        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);
         }
    };