Code viewer for World: Sun
//==============================================================================
//  Explanation Sun Made by Mathias Bazin & Nathan Bonnard
//==============================================================================

// This world was designed to help you create a sun in you world !
// For this, you just have to copy and paste 3 functions (lerpcolor, map and Sun).
// Then, instantiate an object with the sun class (var obj = new Sun(...))
// Don't forget the parameters of the sun : there are informations
// about those in the declaration of the function.
// And if you want the sun to move, you need to call obj.animate() in the nextStep function 

// Enjoy the Sunrise !

//==============================================================================
//==============================================================================

const skycolor          = 'LightBlue';          // sky color

const objectsize        = 20;                  // size of object   

const startRadius       = 100;                 // distance from centre we start the camera at

const maxRadius         = startRadius * 100;     // maximum distance from camera we render things 

const skycolorObject    = new THREE.Color ( skycolor.toLowerCase() );    

const SKYDISTANCE = 3000;

const NBSTARS = 100;

const floorTextureFile = "/uploads/mathias/grass.jpg"

//==============================================================================
//  Functions
//==============================================================================

/**
 * A linear interpolator for hexadecimal colors
 * @param {Int} a
 * @param {Int} b
 * @param {Number} amount
 * @example
 * // returns 0x7F7F7F
 * lerpColor(0x000000, 0xffffff, 0.5)
 * @returns {Int}
 */
function lerpColor(a, b, amount) { 

    let ah = a;
        ar = ah >> 16, ag = ah >> 8 & 0xff, ab = ah & 0xff,
        bh = b;
        br = bh >> 16, bg = bh >> 8 & 0xff, bb = bh & 0xff,
        rr = ar + amount * (br - ar),
        rg = ag + amount * (bg - ag),
        rb = ab + amount * (bb - ab);

    return  ((1 << 24) + (rr << 16) + (rg << 8) + rb | 0);
}

/**/
function map(n, start1, stop1, start2, stop2) 
{
    return ((n-start1)/(stop1-start1))*(stop2-start2)+start2;
}


//==============================================================================
//  Sun Class
//==============================================================================

// Here is the Sun Object. Explanation of parameters :

// target (Object3D) : the player, or camera if you are in first person view. The sun need to be relative to a point
// size (number): the size of the sun & stars
// speed (number): the speed of the rotation of the sun
// skyDistance (number): the distance of the sky from the target.
function Sun(target, size, speed, skyDistance)
{
        this.angle = 0;
    	this.object = new THREE.DirectionalLight( 0xffffff, 0.1 );
        this.object.position.set(0, Math.cos(this.angle)*skyDistance, Math.sin(this.angle)*skyDistance);
        this.object.castShadow = true;
		this.object.shadow.mapSize.width = 1024;
		this.object.shadow.mapSize.height = 1024;
		this.object.shadow.camera.near = 10;
		this.object.shadow.camera.far = 4000;
        
		let d = 1000;

		this.object.shadow.camera.left = -d;
		this.object.shadow.camera.right = d;
		this.object.shadow.camera.top = d;
		this.object.shadow.camera.bottom = -d;
		this.object.shadow.bias = -0.0001;
		
        target.add(this.object);
        this.stars = [];
        this.starMaterial = new THREE.MeshBasicMaterial({color : "white", fog: false}) ;
        this.starMaterial.transparent = true;
        this.starMaterial.opacity = 0;
        
        let sunball = new THREE.Mesh( new THREE.SphereGeometry(size,32,32), new THREE.MeshBasicMaterial({color : "yellow", fog: false}) );
        this.object.add( sunball );
        this.starGyroscope = new THREE.Mesh();
        target.add(this.starGyroscope);
        
        //Create all stars
        for (let i = 0; i<NBSTARS; i++)
        {
            let radius = AB.randomFloatAtoB ( size/25, size/8 );
            let star = new THREE.Mesh( new THREE.SphereGeometry(radius,8,8), this.starMaterial);
            let s = AB.randomFloatAtoB ( 0, Math.PI*2 );
            let t = AB.randomFloatAtoB ( 0, Math.PI/2 );
            star.position.set(SKYDISTANCE*Math.cos(s)*Math.sin(t), skyDistance*Math.cos(t), skyDistance*Math.sin(s)*Math.sin(t));
            this.stars.push(star);
            this.starGyroscope.add(star);
        }
        
        //Use this in nextStep to make the sun move
        this.animate = function()
        {
            this.angle+= 0.001 * speed;
            
            //normalize angle between -PI and PI
            while (this.angle <= -Math.PI) this.angle += Math.PI*2;
            while (this.angle > Math.PI) this.angle -= Math.PI*2;
            
            this.object.position.set(target.position.x, Math.cos(this.angle)*skyDistance, Math.sin(this.angle)*skyDistance + target.position.z);
            
            this.object.intensity = this.getSunIntensity();
            
            let c = new THREE.Color(this.getSkyColor());
		    threeworld.scene.background = c;
		    
		    this.setStarsOpacity();
		    this.starGyroscope.rotation.set(-target.rotation.x, -target.rotation.y, -target.rotation.z);
        }
    
        //change star opacity and fog depending of the position of the sun
        this.setStarsOpacity = function()
        {
            if (this.angle > Math.PI/2  &&  this.angle < Math.PI*3/4)
            {
                this.starMaterial.opacity = map(this.angle, Math.PI/2, Math.PI*3/4, 0, 0.8);
            }
            else if (this.angle > -Math.PI*3/4  &&  this.angle < -Math.PI/2)
            {
                this.starMaterial.opacity = map(this.angle, -Math.PI*3/4, -Math.PI/2, 0.8, 0);
            }
        }
        
        //return the color of the sky depending of the position of the sun (to get the sunrise)
        this.getSkyColor = function()
        {
            if (this.angle > -Math.PI*3/8  &&  this.angle < Math.PI*3/8)
            {
                // console.log("day");
                return 0x7ec0ee;
            }
            else if (this.angle > Math.PI*3/8  &&  this.angle < Math.PI/2)
            {
                // console.log("Sunset 1");
                return lerpColor(0x7ec0ee, 0xfd5e53, map(this.angle, Math.PI*3/8,Math.PI/2,0,1));
            }
            else if (this.angle > Math.PI/2  &&  this.angle < Math.PI*5/8)
            {
                // console.log("Sunset 2");
                return lerpColor(0xfd5e53, 0x0c3166, map(this.angle, Math.PI/2,Math.PI*5/8,0,1));
            }
            else if (this.angle > Math.PI*5/8  ||  this.angle < -Math.PI*3/4)
            {
                // console.log("night");
                return 0x0c3166;
            }
            else if (this.angle > -Math.PI*3/4  &&  this.angle < -Math.PI/2)
            {
                // console.log("Sunrise 1");
                return lerpColor(0x0c3166, 0xfd5e53, map(this.angle, -Math.PI*3/4,-Math.PI/2,0,1));
            }
            else if (this.angle > -Math.PI/2  &&  this.angle < -Math.PI*3/8)
            {
                // console.log("Sunrise 2");
                return lerpColor(0xfd5e53, 0x7ec0ee, map(this.angle, -Math.PI/2, -Math.PI*3/8, 0, 1));
            }
        }
        
        //return intensity of the sun
        this.getSunIntensity = function()
        {
            
            if (this.angle > -Math.PI*3/8  &&  this.angle < Math.PI*3/8)
            {
                return 2;
            }
            else if (this.angle > Math.PI*3/8  &&  this.angle < Math.PI/2)
            {
                return map(this.angle, Math.PI*3/8,Math.PI/2,2,1);
            }
            else if (this.angle > Math.PI/2  &&  this.angle < Math.PI*3/4)
            {
                return map(this.angle, Math.PI/2,Math.PI*3/4,1,0);
            }
            else if (this.angle > Math.PI*3/4  ||  this.angle < -Math.PI*3/4)
            {
                return 0;
            }
            else if (this.angle > -Math.PI*3/4  &&  this.angle < -Math.PI/2)
            {
                return map(this.angle, -Math.PI*3/4,-Math.PI/2,0,1);
            }
            else if (this.angle > -Math.PI/2  &&  this.angle < -Math.PI*3/8)
            {
                return map(this.angle, -Math.PI/2, -Math.PI*3/8, 1, 2);
            }
        }
}


function World() 
{ 
  // the object is a cube (each dimension equal): 
  var shape             = new THREE.BoxGeometry ( objectsize, objectsize, objectsize );
  var theobject         = new THREE.Mesh ( shape );
  var sun;

  this.newRun = function() 
  {
    // start a 3D scene: 
    threeworld.init3d ( startRadius, maxRadius, skycolorObject ); 
    
    //floor
    var floorGeometry = new THREE.PlaneBufferGeometry( 20000, 20000, 1000, 1000 );
    floorGeometry.rotateX( - Math.PI / 2 );
    var floorTexture = new THREE.ImageUtils.loadTexture ( floorTextureFile );
    floorTexture.minFilter = THREE.LinearFilter;
    floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
    floorTexture.offset.set( 0, 0 );
    floorTexture.repeat.set( 40, 40 );
    var floor = new THREE.Mesh(floorGeometry, new THREE.MeshBasicMaterial({map : floorTexture}));
    floor.position.set(0,-objectsize/2,0);
    threeworld.scene.add(floor);
        
    // add the object to the scene:
    threeworld.scene.add(theobject);
    
    //Instantiate sun
    sun = new Sun(theobject, 120, 10, SKYDISTANCE);


  };
  
  this.nextStep = function()
  {
      sun.animate();
  }

}