//==============================================================================
// 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;
threeworld.scene.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();
}
}