MyScene.js

/**
 * @class Represents the {@link CGFapplication} scene. 
 */
class MyScene extends CGFscene {
    /**
     * Instantiates a new scene instance.
     */
    constructor() {
        super();
        this.canvas = []
    }

    /**
     * Initializes the scene.
     * @param {CGFapplication} application - the application
     */
    init(application) {
        super.init(application);
        this.initCameras();
        this.initLights();
        this.initSkins();

        //Background color
        this.gl.clearColor(0.0, 0.0, 0.0, 1.0);

        this.gl.clearDepth(100.0);
        this.gl.enable(this.gl.DEPTH_TEST);
        this.gl.enable(this.gl.CULL_FACE);
        this.gl.depthFunc(this.gl.LEQUAL);

        this.setUpdatePeriod(1);
        this.last = performance.now();

        this.enableTextures(true);

        this.initSuppliesSkins();
        this.initTerrains();
        this.initCubeMap();

        this.axis = new CGFaxis(this);
        this.vehicle = new MyVehicle(this);
        this.supplyManager = new MySupplyManager(this);
        this.billboard = new MyBillboard(this, this.supplyManager.numberOfSupplies);

        // Interface
        this.displayAxis = false;
        this.speedFactor = 1;
        this.scaleFactor = 1;
        this.autoPilot = false;
        this.customMovement = false;
        this.volume = 20.0;
    }

    /**
     * Initializes the scene lights.
     */
    initLights() {
        this.lights[0].setPosition(15, 15, 5, 1);
        this.lights[0].setDiffuse(1, 1, 1, 1);
        this.lights[0].setConstantAttenuation(0.8);
        this.lights[0].enable();
        this.lights[0].update();
    }

    /**
     * Initializes the scene cameras.
     */
    initCameras() {
        this.selectedCamera = 0;

        this.cameraList = {
            'Default (Right)': 0,
            'Left': 1,
            'Left Back': 2,
            'Right Back': 3,
            'Top': 4,
            'Third person': 5
        };

        this.cameras = [new CGFcamera(0.7, 0.1, 500, vec3.fromValues(26, 20, 26), vec3.fromValues(0, 5, 0)),
            new CGFcamera(0.7, 0.1, 500, vec3.fromValues(-26, 20, 26), vec3.fromValues(0, 5, 0)),
            new CGFcamera(0.7, 0.1, 500, vec3.fromValues(-26, 20, -26), vec3.fromValues(0, 5, 0)),
            new CGFcamera(0.7, 0.1, 500, vec3.fromValues(26, 20, -26), vec3.fromValues(0, 5, 0)),
            new CGFcamera(0.7, 0.1, 500, vec3.fromValues(0, 50, 1), vec3.fromValues(0, 0, 0))
        ];

        this.camera = this.cameras[this.selectedCamera];
    }

    /**
     * Initializes the scene terrains.
     */
    initTerrains() {
        this.terrains = [
            '../resources/textures/terrain/lake1.png',
            '../resources/textures/terrain/lake2.png',
            '../resources/textures/terrain/canyon.png',
            '../resources/textures/terrain/given.png'
        ];

        this.heightMaps = [
            '../resources/textures/terrain/lake1_h.png',
            '../resources/textures/terrain/lake2_h.png',
            '../resources/textures/terrain/canyon_h.png',
            '../resources/textures/terrain/given_h.png'
        ];

        this.terrainsTex = [];
        this.heightMapsTex = [];

        for (var value in this.terrains) {
            this.terrainsTex.push(new CGFtexture(this, this.terrains[value]));
        }

        for (var value in this.heightMaps) {
            this.heightMapsTex.push(new CGFtexture(this, this.heightMaps[value]));
        }

        // cube map interface variables
        this.terrainList = {
            'Lake 1': 0,
            'Lake 2': 1,
            'Canyon': 2,
            'Given': 3
        };

        this.selectedTerrain = 0;

        this.terrain = new MyTerrain(this);

        this.terrain.material.setTexture(this.terrainsTex[this.selectedTerrain]);
    }

    /**
     * Initializes the scene cube maps.
     */
    initCubeMap() {
        this.cubeMaps = [
            new CGFtexture(this, '../resources/textures/cubemap/lake1.png'),
            new CGFtexture(this, '../resources/textures/cubemap/lake2.png'),
            new CGFtexture(this, '../resources/textures/cubemap/canyon.png'),
            new CGFtexture(this, '../resources/textures/cubemap/given.png')
        ];

        this.cubeMap = new MyCubeMap(this, 50, this.cubeMaps[this.selectedTerrain]);
    }

    /**
     * Initializes the scene blimp skins.
     */
    initSkins() {
        this.selectedSkin = 0;

        this.skins = [
            '../resources/textures/up/',
            '../resources/textures/github/',
            '../resources/textures/google/'
        ];

        this.skinList = {
            'U.P.': 0,
            'GitHub': 1,
            'Google': 2
        };
    }

    /**
     * Initializes the scene supply skins.
     */
    initSuppliesSkins() {
        this.selectedSupplySkin = 0;

        this.supplySkin = [
            '../resources/textures/supply/crate1',
            '../resources/textures/supply/pig',
            '../resources/textures/supply/steve'
        ];

        this.supplySkinList = {
            'Crate 1': 0,
            'Pig': 1,
            'Steve': 2
        };
    }

    /**
     * Sets the default scene appearance.
     */
    setDefaultAppearance() {
        this.setAmbient(0.2, 0.4, 0.8, 1.0);
        this.setDiffuse(0.2, 0.4, 0.8, 1.0);
        this.setSpecular(0.2, 0.4, 0.8, 1.0);
        this.setEmission(0, 0, 0, 0);
        this.setShininess(10.0);
    }

    /**
     * Updates periodically each object in the scene (as per setUpdatePeriod() in init())
     * 
     * @param t {number}    - the current time
     */
    update(t) {
        this.vehicle.update(t);
        this.vehicle.accelerationMultiplier = this.speedFactor;
        this.vehicle.scale = this.scaleFactor;
        this.vehicle.customMovement = this.customMovement;
        this.vehicle.autoPilot = this.autoPilot;

        this.supplyManager.update(t);
        this.billboard.update(t, this.supplyManager.numberOfSuppliesDelivered);
    }

    /**
     * Event called when a terrain is changed in the interface.
     */
    onTerrainChanged() {
        // document.getElementById('heightmap').src = this.heightMaps[this.selectedTerrain];
        this.terrain.material.setTexture(this.terrainsTex[this.selectedTerrain]);
        this.cubeMap.setTexture(this.cubeMaps[this.selectedTerrain]);

        this.supplyManager.reset();
        this.billboard.updateHeight();
    }

    /**
     * Event called when a blimp skin is changed in the interface.
     */
    onSkinChanged() {
        this.vehicle.initTextures();
        this.vehicle.wings.initTextures();
        this.vehicle.flag.initTextures();
    }

    /**
     * Event called when a supply skin is changed in the interface.
     */
    onSupplySkinChanged() {
        this.supplyManager.initTextures();
        this.supplyManager.reset();
    }

    /**
     * Event called when the camera is changed in the inteface.
     */
    onCameraChanged() {
        this.camera = this.cameras[this.selectedCamera];
    }

    /**
     * Resets all scene objects.
     */
    resetAll() {
        this.gui.continuousActiveKeys["KeyP"] = false;
        this.autoPilot = false;
        this.vehicle.reset();
        this.supplyManager.reset();
    }

    /**
     * Method responsible for key handling.
     */
    checkKeys() {
        // Check for key codes e.g. in https://keycode.info/
        if (this.gui.isContinuousKeyPressed("KeyP"))
            this.autoPilot = true;
        else
            this.autoPilot = false;

        if (this.gui.isKeyPressed("KeyR"))
            this.resetAll();

        if (this.autoPilot)
            return;

        if (this.gui.isKeyPressed("KeyW"))
            this.vehicle.accelerate(0.01);
        if (this.gui.isKeyPressed("KeyS"))
            if (!this.vehicle.customMovement)
                this.vehicle.brake(0.08);
            else
                this.vehicle.accelerate(-0.02);
        if (!this.gui.isKeyPressed("KeyW") && !this.gui.isKeyPressed("KeyS"))
            this.vehicle.accelerate(0.0);
        if (this.gui.isKeyPressed("KeyA"))
            this.vehicle.turn(0.25);
        if (this.gui.isKeyPressed("KeyD"))
            this.vehicle.turn(-0.25);
        if (this.gui.isKeyPressed("Space"))
            this.vehicle.brake(0.15);
        if (this.gui.isSingleKeyPressed("KeyL"))
            this.supplyManager.drop(this.vehicle);
    }

    /**
     * Displays the objects in the scene.
     */
    display() {
        // Update fps in the html page
        this.now = performance.now();
        var fps = 1000 / (this.now - this.last);
        this.last = this.now;

        document.getElementById("fps-counter").firstElementChild.innerHTML = "FPS: " + Math.round(fps);

        // ---- BEGIN Background, camera and axis setup
        // Clear image and depth buffer everytime we update the scene
        this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);

        // Initialize Model-View matrix as identity (no transformation
        this.updateProjectionMatrix();
        this.loadIdentity();
        // Apply transformations corresponding to the camera position relative to the origin
        this.applyViewMatrix();

        this.lights[0].update();

        // Draw axis
        if (this.displayAxis)
            this.axis.display();

        this.setDefaultAppearance();

        // Key listening
        this.checkKeys();

        // ---- BEGIN Primitive drawing section
        this.cubeMap.display();
        this.supplyManager.display();
        this.billboard.display();
        this.vehicle.display();

        // ---- BEGIN Shader drawing section
        this.vehicle.displayWithShaders();
        this.terrain.displayWithShaders(this.terrainsTex[this.selectedTerrain], this.heightMapsTex[this.selectedTerrain]);
        this.billboard.displayWithShaders();

        this.setActiveShader(this.defaultShader);

        // ---- END Primitive drawing section
    }
}