// Cloned by Mathias Bazin on 26 Jun 2018 from World "Day/Night cycle" by Mathias Bazin
// Please leave this clone trail here.
// Cloned by Mathias Bazin on 20 Jun 2018 from World "First Person Night" by Mathias Bazin
// Please leave this clone trail here.
// Cloned by Mathias Bazin on 19 Jun 2018 from World "First Person Controls" by Mathias Bazin
// Please leave this clone trail here.
// Customise AB run parameters (optional).
// The following parameters can be customised. (They have default values.)
AB.clockTick = 20;
// Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps = 65545;
// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep = 1200;
// For automatic generation of World images.
// Take screenshot on this step. (All resources should have finished loading.) Default 50.
AB.runReady = false;
AB.drawRunControls = false;
threeworld.drawCameraControls = false;
const MOVESPEED = 3;
const boxtexturefile = "/uploads/mathias/wood.png";
const floorTextureFile = "/uploads/mathias/sand.jpg";
const skytexturefile = "/uploads/mathias/nightSky2.jpg";
const waterTextureFile = "/uploads/mathias/water1.jpg";
const SKYDISTANCE = 10000;
const NBSTARS = 100;
//==============================================================================
// Define the THREE.PointerLockControls class, source at https://threejs.org/
//==============================================================================
THREE.PointerLockControls = function ( camera ) {
var scope = this;
camera.rotation.set( 0, 0, 0 );
var pitchObject = new THREE.Object3D();
pitchObject.add( camera );
var yawObject = new THREE.Object3D();
yawObject.position.y = 10;
yawObject.add( pitchObject );
var PI_2 = Math.PI / 2;
var onMouseMove = function ( event ) {
if ( scope.enabled === false ) return;
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
yawObject.rotation.y -= movementX * 0.002;
pitchObject.rotation.x -= movementY * 0.002;
pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
};
this.dispose = function () {
document.removeEventListener( 'mousemove', onMouseMove, false );
};
document.addEventListener( 'mousemove', onMouseMove, false );
this.enabled = false;
this.getObject = function () {
return yawObject;
};
this.getDirection = function () {
// assumes the camera itself is not rotated
var direction = new THREE.Vector3( 0, 0, - 1 );
var rotation = new THREE.Euler( 0, 0, 0, 'YXZ' );
return function ( v ) {
rotation.set( pitchObject.rotation.x, yawObject.rotation.y, 0 );
v.copy( direction ).applyEuler( rotation );
return v;
};
}();
};
//=====================================================================
function WavesWater(x,y,z,width,height,amplitude,opacity)
{
this.w1;
this.w2;
this.offset = 0;
this.yPos = y;
this.amplitude = amplitude;
let waterGeometry = new THREE.PlaneBufferGeometry( width, height, 32 );
waterGeometry.rotateX( - Math.PI / 2 );
let waterTexture1 = new THREE.ImageUtils.loadTexture ( waterTextureFile ); //I used "/uploads/mathias/water1.jpg"
waterTexture1.minFilter = THREE.LinearFilter;
waterTexture1.wrapS = waterTexture1.wrapT = THREE.RepeatWrapping;
waterTexture1.offset.set( 0, 0 );
waterTexture1.repeat.set( width*4/100, height*4/100 );
let waterTexture2 = new THREE.ImageUtils.loadTexture ( waterTextureFile ); //I used "/uploads/mathias/water1.jpg"
waterTexture2.minFilter = THREE.LinearFilter;
waterTexture2.wrapS = waterTexture2.wrapT = THREE.RepeatWrapping;
waterTexture2.offset.set( 0.5, 0 );
waterTexture2.repeat.set( width*4/100, height*4/100 );
let waterMaterial1 = new THREE.MeshBasicMaterial({map : waterTexture1});
waterMaterial1.transparent = true;
waterMaterial1.opacity = opacity;
let waterMaterial2 = new THREE.MeshBasicMaterial({map : waterTexture2});
waterMaterial2.transparent = true;
waterMaterial2.opacity = opacity;
this.w1 = new THREE.Mesh(waterGeometry, waterMaterial1);
this.w1.position.set(x,y,z);
this.w2 = new THREE.Mesh(waterGeometry, waterMaterial2);
this.w2.position.set(x,y - 0.001,z);
threeworld.scene.add( this.w1 );
threeworld.scene.add( this.w2 );
}
WavesWater.prototype.animate = function()
{
this.w1.material.map.offset.set(Math.sin(this.offset)*this.amplitude+0.2,0);
this.w2.material.map.offset.set(Math.sin(this.offset+0.4)*this.amplitude,0);
this.w1.position.y = this.yPos + Math.sin(this.offset)*2.2;
this.w2.position.y = this.yPos - 0.001 + Math.sin(this.offset)*2.2;
this.offset+=0.012;
}
// --- some useful random functions -------------------------------------------
function randomfloatAtoB ( A, B )
{
return ( A + ( Math.random() * (B-A) ) );
}
function randomintAtoB ( A, B )
{
return ( Math.round ( randomfloatAtoB ( A, B ) ) );
}
function randomBoolean()
{
if ( Math.random() < 0.5 ) { return false; }
else { return true; }
}
function randomPick ( a, b )
{
if ( randomBoolean() )
return a;
else
return b;
}
/**
* 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) {
var 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;
}
function World() {
var camera, controls;
var objects = [];
var raycaster;
var controlsEnabled = true;
var moveForward = false;
var moveBackward = false;
var moveLeft = false;
var moveRight = false;
var canJump = false;
var prevTime = performance.now();
var velocity = new THREE.Vector3();
var direction = new THREE.Vector3();
var vertex = new THREE.Vector3();
var color = new THREE.Color();
var models = [];
let animatedObjects = [];
var Sun = function()
{
this.angle = Math.PI*7/16;
this.object = new THREE.DirectionalLight(0xffffff, 2);
this.object.position.set(0, Math.cos(this.angle)*SKYDISTANCE, Math.sin(this.angle)*SKYDISTANCE);
this.object.castShadow = true;
this.stars = [];
this.starMaterial = new THREE.MeshBasicMaterial({color : "white"}) ;
this.starMaterial.transparent = true;
this.starMaterial.opacity = 0;
threeworld.scene.add(this.object);
// let pointLightHelper = new THREE.PointLightHelper( this.object, 1000 );
// threeworld.scene.add( pointLightHelper );
let sunball = new THREE.Mesh( new THREE.SphereGeometry(1000,32,32), new THREE.MeshBasicMaterial({color : "yellow"}) );
this.object.add( sunball );
for (let i = 0; i<NBSTARS; i++)
{
let radius = randomfloatAtoB ( 40, 60 );
let star = new THREE.Mesh( new THREE.SphereGeometry(radius,8,8), this.starMaterial);
let s = randomfloatAtoB ( 0, Math.PI*2 );
let t = 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);
threeworld.scene.add(star);
}
this.animate = function()
{
this.angle+=0.0001;
//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(0, Math.cos(this.angle)*SKYDISTANCE, Math.sin(this.angle)*SKYDISTANCE);
this.object.intensity = this.getSunIntensity();
threeworld.scene.background = new THREE.Color(this.getSkyColor());
this.setStarsOpacity();
}
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);
}
}
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*3/4)
{
// console.log("Sunset 2");
return lerpColor(0xfd5e53, 0x0c3166, map(this.angle, Math.PI/2,Math.PI*3/4,0,1));
}
else if (this.angle > Math.PI*3/4 || 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));
}
}
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);
}
}
}
var Bird = function(x,y,z)
{
this.object = models[0].clone();
this.height = randomintAtoB(0,50);
this.isRising = randomBoolean();
this.object.position.x = x;
this.object.position.y = y;
this.object.position.z = z;
threeworld.scene.add(this.object);
this.animate = function()
{
// console.log("height : ", this.height);
if(this.height > 50) this.isRising = false;
else if (this.height < 0) this.isRising = true;
if (this.isRising)
{
this.height += 2;
this.object.position.y += 2;
}
else
{
this.height -= 0.2;
this.object.position.y -= 0.2;
}
this.object.position.z += 8;
if (this.object.position.z > SKYDISTANCE) this.object.position.z = randomintAtoB(-SKYDISTANCE/10, -2*SKYDISTANCE/10);
}
}
var loader = new THREE.OBJLoader();
// load a resource
loader.load(
// resource URL
'/uploads/mathias/bird.obj',
// called when resource is loaded
function ( object ) {
models.push( object);
AB.runReady = true;
},
// called when loading is in progresses
function ( xhr ) {
console.log( "bird " + ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened with bird' );
}
);
this.newRun = function()
{
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 100000 );
threeworld.camera = camera;
threeworld.init3d ( 0,0, 0x7ec0ee );
threeworld.renderer.shadowMap.enabled = true;
threeworld.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
var light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 0.2 );
threeworld.scene.add( light );
controls = new THREE.PointerLockControls( camera );
threeworld.scene.add( controls.getObject() );
// controls.getObject().rotateY(Math.PI);
var onKeyDown = function ( event ) {
switch ( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = true;
break;
case 37: // left
case 65: // a
moveLeft = true; break;
case 40: // down
case 83: // s
moveBackward = true;
break;
case 39: // right
case 68: // d
moveRight = true;
break;
case 32: // space
if ( canJump === true ) velocity.y += 350;
canJump = false;
break;
}
};
var onKeyUp = function ( event ) {
switch( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = false;
break;
case 37: // left
case 65: // a
moveLeft = false;
break;
case 40: // down
case 83: // s
moveBackward = false;
break;
case 39: // right
case 68: // d
moveRight = false;
break;
}
};
document.addEventListener( 'keydown', onKeyDown, false );
document.addEventListener( 'keyup', onKeyUp, false );
raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, - 1, 0 ), 0, 10 );
//Scene
//birds
for(let i = 0; i<10; i++)
{
let x = randomfloatAtoB(-2000,2000);
let y = randomfloatAtoB(50,1200);
let z = randomfloatAtoB(2000,4000);
animatedObjects.push(new Bird(x,y,z));
}
// threeworld.scene.add(models[0]);
//floor
var floorGeometry = new THREE.PlaneBufferGeometry( 6000, 6000, 200, 200 );
floorGeometry.rotateX( - Math.PI*1.85 / 4 );
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( 100, 100 );
var floor = new THREE.Mesh(floorGeometry, new THREE.MeshLambertMaterial({map : floorTexture}));
floor.position.set(0,-20,0);
floor.receiveShadow = true;
floor.castShadow = true;
threeworld.scene.add(floor);
var floorGeometry2 = new THREE.PlaneBufferGeometry( 9000, 9000, 200, 200 );
floorGeometry2.rotateX( - Math.PI / 2 );
var floor2 = new THREE.Mesh(floorGeometry2, new THREE.MeshLambertMaterial({map : floorTexture}));
floor2.position.set(0,-100,0);
threeworld.scene.add(floor2);
//wateer
let water = new WavesWater(0,-30,0,10000,10000,1,0.6);
water.w1.rotateY(-Math.PI/2);
water.w2.rotateY(-Math.PI/2);
animatedObjects.push(water);
//Objects
//box
var boxGeometry = new THREE.BoxBufferGeometry( 20, 60, 20 );
var boxTexture = new THREE.ImageUtils.loadTexture ( boxtexturefile );
var boxMaterial = new THREE.MeshLambertMaterial ( { map: boxTexture } );
var box = new THREE.Mesh ( boxGeometry, boxMaterial);
box.castShadow = true;
box.receiveShadow = true;
box.metalness = 0.0;
box.position.set(-60,0,-150);
box.name = "Box";
threeworld.scene.add(box);
objects.push(box);
//Sun
animatedObjects.push(new Sun());
$("#user_span1").html(" Use WASD or Arrows to move, mouse to look around and space to jump.");
var blocker = $("#user_span2");
blocker.html("<p><b>Click screen to enable mouse controls</b></p>");
//lock pointer
var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;
console.log(havePointerLock);
if ( havePointerLock ) {
var element = document.body;
var pointerlockchange = function ( event ) {
if ( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ) {
controlsEnabled = true;
controls.enabled = true;
blocker.html("");
} else
{
controls.enabled = false;
blocker.html("<p><b>Click screen to enable mouse controls</b></p>");
}
};
var pointerlockerror = function ( event ) {
console.error("pointerlockerror");
};
// Hook pointer lock state change events
document.addEventListener( 'pointerlockchange', pointerlockchange, false );
document.addEventListener( 'mozpointerlockchange', pointerlockchange, false );
document.addEventListener( 'webkitpointerlockchange', pointerlockchange, false );
document.addEventListener( 'pointerlockerror', pointerlockerror, false );
document.addEventListener( 'mozpointerlockerror', pointerlockerror, false );
document.addEventListener( 'webkitpointerlockerror', pointerlockerror, false );
document.addEventListener( 'click', function ( event ) {
// Ask the browser to lock the pointer
element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
element.requestPointerLock();
}, false );
} else {
$("#user_span1").html('<p>Your browser doesn\'t seem to support Pointer Lock API</p>');
}
};
this.nextStep = function()
{
// console.log("camera pos : ", camera.getWorldPosition().x, camera.getWorldPosition().y, camera.getWorldPosition().z);
// for (let o of objects)
// {
// console.log(o.name, " pos :", o.getWorldPosition().x,o.getWorldPosition().y,o.getWorldPosition().z);
// }
for (let o of animatedObjects)
{
o.animate();
}
previousPosition = controls.getObject().position;
if ( controlsEnabled === true ) {
raycaster.ray.origin.copy( controls.getObject().position );
raycaster.ray.origin.y -= 10;
var intersections = raycaster.intersectObjects( objects );
var onObject = intersections.length > 0;
var time = performance.now();
var delta = ( time - prevTime ) / 1000;
velocity.x -= velocity.x * 10.0 * delta;
velocity.z -= velocity.z * 10.0 * delta;
velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass
direction.z = Number( moveForward ) - Number( moveBackward );
direction.x = Number( moveLeft ) - Number( moveRight );
direction.normalize(); // this ensures consistent movements in all directions
if ( moveForward || moveBackward ) velocity.z -= direction.z * 400.0 * MOVESPEED * delta;
if ( moveLeft || moveRight ) velocity.x -= direction.x * 400.0 * MOVESPEED * delta;
if ( onObject === true ) {
velocity.y = Math.max( 0, velocity.y );
canJump = true;
}
// console.log("velocity ", velocity.x, " ", velocity.z)
controls.getObject().translateX( velocity.x * delta );
controls.getObject().translateY( velocity.y * delta );
controls.getObject().translateZ( velocity.z * delta );
if ( controls.getObject().position.y < 10 ) {
velocity.y = 0;
controls.getObject().position.y = 10;
canJump = true;
}
prevTime = time;
if (previousPosition.distanceTo(controls.getObject().position) > 30)
{
console.error("Teleport occured");
}
}
};
this.endRun = function()
{
};
}
//=====================================================================================
//IMPORTS source : https://threejs.org
//=====================================================================================
//====================================================================================