//==============================================================================// Welcome to the Infinite world !//==============================================================================// This program was made by Nathan Bonnard.// In this world, you can generate an infinite world ! // But this isn't really infinite, it would be impossible, // the world do a simple loop when the player reach the border. Like in real life !// This world isn't a kind of game with specific goal, just enjoy the landscapes and all objects !// // The first purpose of this world was to create an exploration world to just enjoy good views and the fact that it is infinite // Finally it became a sort of Minecraft, I wanted to create that type of game but with complex 3D Objects, not cubes.// I wanted to demonstrate the power of javascript and Three.js : we can create big world with a lot of objects even in a browser// Also, the aim is to encourage people using the first person view on worlds or the sun that you can find on Enhanced World.// If you want to change the world by yourself, it would be a bit hard to get into 2000 lines of code, // but you can simply change some letiables and see the effects. Here are those letiables ://------------------------------ TWEAKER BOX ------------------------------//const squaresize =5;// The world is like a infinite grid of square. squaresize is the size of a squarevar MOVESPEED =7;// Speed of the playervar viewDistance =2;// determinate the maximum distance to see ground (viewDistance * squaresize * groundMultiplier)var groundMultiplier =551;// The world is divided by zone (grounds) that size groundMultiplier*squaresizevar worldSize =13;// dimension of the world, it is not really infinite but big and can loop. There is worldSize * worldSize Grounds const SKYDISTANCE =3000;// Distance of stars and sunconst NBSTARS =100;//number of stars//Where all textures and objects areconst PATHTEXTURES ="/uploads/meak/";//Limit between rare and small objects (in term of size, obviously here a castle size is bigger than 4 but a rock is not)//Care it depends on the squaresize letiable. If you increase squaresize, you should also decrease this letiableconst limitSizeForRareObjects =2;//Number of maximum element that can be pose in each ground. You can change this after running the worldvar maxElementsForest =300;var maxElementsSnow =90;var maxElementsSand =60;//Determine if the world can or not generate different type of ground. You can change this after running the worldvar enableSand =true;var enableSnow =true;var enableForest =true;//Path of sounds that are played during the game depending on what type of ground you are walking onconst SOUNDFOREST ="/uploads/meak/forestsound.mp3";const SOUNDSNOW ="/uploads/meak/snowsound.mp3";const SOUNDSAND ="/uploads/meak/sandsound.mp3";const SOUNDNIGHT ="/uploads/meak/nightsound.mp3";//Objects that are loaded for each type of ground. Add some Objects that you found on the internet and see !//For that it need to be an Object3D (.obj) with a material (.mtl).//If the material need some texture, upload those textures too.const ARRAYOBJECTSFOREST ={"tree2":40,"lowpolytree":30,"castle-tower":0.5,"WoodenCabinObj":1,"houseA_obj":1,"tree13":30};const ARRAYOBJECTSSNOW ={"castle-tower":0.5,"Stone":15,"kardanadam":2,"WoodenCabinObj":1,"Rock2":1};const ARRAYOBJECTSSAND ={"castle-tower":0.5,"Cactus":10,"Stone":15,"WoodenCabinObj":1,"houseA_obj":1,"Rock2":1};//------------------------------ END OF TWEAKER BOX ------------------------------//var newviewDistance = viewDistance;// letiables to re-generate the world with new letiables var newGroundMultiplier = groundMultiplier;var newworldSize = worldSize;const MAXPOS =4000;const startRadiusConst = MAXPOS *0.5;// distance from centre to start the camera atconst maxRadiusConst = MAXPOS *5;// maximum distance from camera we will render things const SKYCOLOR =0x6495ED;const LIGHTCOLOR =0xffffff;//all type of Groundsconst TYPEGROUND =["SNOW","FOREST","SAND"];//used to manage the click and give back control to the user when you click on a button/text box //and don't want to enter into first person modevar HUDEVENT =false;var mouse;// vector2 : position of the mouse on the screen
threehandler.MAXCAMERAPOS = MAXPOS *10;// allow camera go far away
threeworld.drawCameraControls =false;
AB.clockTick =20;// Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps =1000000;// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep =50;// Take screenshot on this step. (All resources should have finished loading.) Default 50./**
* 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;}//==============================================================================// World Definition//==============================================================================functionWorld(){//==============================================================================// All variables//==============================================================================var objects;// Stock all the prefabs of objectsvar groundTexture;// Texture of the groundvar snowTexture;// Texture of the groundvar sandTexture;// Texture of the groundvar transitionTextureSnow;var transitionTextureSand;var transitionTextureForest;var loaded =false;// True when everything is loadedvar grounds;// Array2d : Stock all ground of the world, with objects on it (recurrentObjects...)var ai, aj;// position in grounds of the playervar pai, paj;// past position in grounds of the playervar modeOfView =0;//0 : first person --- 1 : third personvar timeRunning =0;// time updated var raycaster;// to determine direction of a clickvar camera, controls;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 sun;//sun and all functions with it (also stars)var loadingManager;//loading manager to load everything and thenrun the programvar soundManager;//Object that manage the sound depending of the position of the playervar saving;//Object that store all informations to save the worldvar offsetGround =0.001;//letiable to avoid that grounds "bug" when they to parcels are overlappingvar userObjects =[];//store all objects that were pose by the player in edit modevar allObjects =[];//allTypeOfObjects tht can be created by the player in edit modevar indexObjects =0;//Usefull to manage userObjectsvar rayToDestroy;var selectionBox =new THREE.Mesh();var selectionObject =null;var editMode =null;this.endCondition =false;//==============================================================================//==============================================================================//==============================================================================// All Class//==============================================================================//==============================================================================// Defines the THREE.PointerLockControls class, source at https://threejs.org///==============================================================================
THREE.PointerLockControls=function(camera){
let scope =this;
camera.rotation.set(0,0,0);
let pitchObject =new THREE.Object3D();
pitchObject.add( camera );
let yawObject =new THREE.Object3D();
yawObject.position.y =10;
yawObject.add( pitchObject );
let attachedObject =new THREE.Object3D();
yawObject.add(attachedObject);
attachedObject.position.set(0,-yawObject.position.y,-150);
let PI_2 =Math.PI /2;
let onMouseMove =function( event ){if( scope.enabled ===false)return;if(editMode ==1){if(controls.getAttachedObject().children.length !=0){
controls.getAttachedObject().remove(controls.getAttachedObject().children[1]);
controls.getAttachedObject().remove(controls.getAttachedObject().children[0]);}DestroyObject(event);}
let movementX = event.movementX || event.mozMovementX || event.webkitMovementX ||0;
let 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.getAttachedObject =function(){return attachedObject;};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
let direction =new THREE.Vector3(0,0,-1);
let rotation =new THREE.Euler(0,0,0,'YXZ');returnfunction( v ){
rotation.set( pitchObject.rotation.x, yawObject.rotation.y,0);
v.copy( direction ).applyEuler( rotation );return v;};}();};//==============================================================================//Class to save the worldclassSaveWorld{
constructor(dim, mult, viewDistance){this.worldSize = dim;this.groundMultiplier = mult;this.viewDistance = viewDistance;this.grounds =newArray(dim);for(let i =0; i <this.grounds.length; i++){this.grounds[i]=newArray(dim);for(let j =0; j <this.grounds[i].length; j++){this.grounds[i][j]={biom:"", elements:[]};}}this.userObjects =[];}}// Class to stock Each Objects that were loaded // with their combobox size to place them in space// and to avoid collision ( objects at same place)classObj{
constructor(nameDoc, sX, sY, sZ){this.path = nameDoc;this.sizeX = sX;this.sizeY = sY;this.sizeZ = sZ;this.obj;}}//Class that determine if a ground is active or not//And stock the ground with all the objects attachedclassGround{
constructor(act, pos){this.active = act;this.ground;this.type;}}//Class that stock all the objects of all typesclassObjectsConteneur{
constructor(){this.objectsForest =newObjectsOfType("FOREST");this.objectsSand =newObjectsOfType("SAND");this.objectsSnow =newObjectsOfType("SNOW");}}//Class that stock all the objects of the different typeclassObjectsOfType{
constructor(type){this.type = type;this.recurrentObjects =[];this.rareObjects =[];}}//Class that deal with the soundclassSoundManager{
constructor(){
let listener =new THREE.AudioListener();
threeworld.camera.add( listener );// create a global audio sourcethis.soundForest =new THREE.Audio( listener );this.soundSand =new THREE.Audio( listener );this.soundSnow =new THREE.Audio( listener );this.soundNight =new THREE.Audio( listener );this.currentSound =null;this.isNight =false;}
playCurrentSound(){if(this.currentSound !=null){if(this.isNight){this.soundNight.setVolume(0);this.soundNight.play();}elseif(this.currentSound =="FOREST"){this.soundForest.setVolume(0);this.soundForest.play();}elseif(this.currentSound =="SAND"){this.soundSand.setVolume(0);this.soundSand.play();}elseif(this.currentSound =="SNOW"){this.soundSnow.setVolume(0);this.soundSnow.play();}}}
updateSound(){if(this.currentSound !=null){if(this.isNight &&this.soundNight.getVolume()<0.5){this.soundNight.setVolume(this.soundNight.getVolume()+0.01);}elseif(this.currentSound =="FOREST"&&this.soundForest.getVolume()<0.5){this.soundForest.setVolume(this.soundForest.getVolume()+0.01);}elseif(this.currentSound =="SAND"&&this.soundSand.getVolume()<0.5){this.soundSand.setVolume(this.soundSand.getVolume()+0.01);}elseif(this.currentSound =="SNOW"&&this.soundSnow.getVolume()<0.5){this.soundSnow.setVolume(this.soundSnow.getVolume()+0.01);}if((this.currentSound !="FOREST"||this.isNight)&&this.soundForest.getVolume()>0){this.soundForest.setVolume(this.soundForest.getVolume()-0.01);if(this.soundForest.getVolume()<=0){this.soundForest.stop();}}if((this.currentSound !="SAND"||this.isNight)&&this.soundSand.getVolume()>0){this.soundSand.setVolume(this.soundSand.getVolume()-0.01);if(this.soundSand.getVolume()<=0){this.soundSand.stop();}}if((this.currentSound !="SNOW"||this.isNight)&&this.soundSnow.getVolume()>0){this.soundSnow.setVolume(this.soundSnow.getVolume()-0.01);if(this.soundSnow.getVolume()<=0){this.soundSnow.stop();}}if(!this.isNight &&this.soundNight.getVolume()>0){this.soundNight.setVolume(this.soundNight.getVolume()-0.01);if(this.soundNight.getVolume()<=0){this.soundNight.stop();}}}}}//Sun, used like a class
let Sun=function(){this.angle =0;this.object =new THREE.DirectionalLight(0xffffff,0.1);this.object.position.set(0,Math.cos(this.angle)*3000,Math.sin(this.angle)*3000);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;this.object.target = controls.getObject();
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(100,32,32),new THREE.MeshBasicMaterial({color :"yellow", fog:false}));this.object.add( sunball );this.starGyroscope =new THREE.Mesh();
controls.getObject().add(this.starGyroscope );for(let i =0; i<NBSTARS; i++){
let radius = AB.randomFloatAtoB (5,20);
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 movethis.animate =function(){this.angle+=0.001;//normalize angle between -PI and PIwhile(this.angle <=-Math.PI)this.angle +=Math.PI*2;while(this.angle >Math.PI)this.angle -=Math.PI*2;this.object.position.set(controls.getObject().position.x,Math.cos(this.angle)*3000,Math.sin(this.angle)*3000+ controls.getObject().position.z);this.object.intensity =this.getSunIntensity();
let c =new THREE.Color(this.getSkyColor());
threeworld.scene.background = c;
threeworld.scene.fog.color = c;this.setStarsOpacityAndFog();this.starGyroscope.rotation.set(-controls.getObject().rotation.x,-controls.getObject().rotation.y,-controls.getObject().rotation.z);}//change star opacity and fog depending of the position of the sunthis.setStarsOpacityAndFog =function(){if(this.angle >Math.PI/2&&this.angle <Math.PI*3/4){
soundManager.isNight =true;
soundManager.playCurrentSound();this.starMaterial.opacity = map(this.angle,Math.PI/2,Math.PI*3/4,0,0.8);
threeworld.scene.fog.far =1000+ SKYDISTANCE *(1- map(this.angle,Math.PI/2,Math.PI*3/4,0,0.8));}elseif(this.angle >-Math.PI*3/4&&this.angle <-Math.PI/2){
soundManager.isNight =false;this.starMaterial.opacity = map(this.angle,-Math.PI*3/4,-Math.PI/2,0.8,0);
threeworld.scene.fog.far =1000+ SKYDISTANCE *(1- 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");return0x7ec0ee;}elseif(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));}elseif(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));}elseif(this.angle >Math.PI*5/8||this.angle <-Math.PI*3/4){// console.log("night");return0x0c3166;}elseif(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));}elseif(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 sunthis.getSunIntensity =function(){if(this.angle >-Math.PI*3/8&&this.angle <Math.PI*3/8){return2;}elseif(this.angle >Math.PI*3/8&&this.angle <Math.PI/2){return map(this.angle,Math.PI*3/8,Math.PI/2,2,1);}elseif(this.angle >Math.PI/2&&this.angle <Math.PI*3/4){return map(this.angle,Math.PI/2,Math.PI*3/4,1,0);}elseif(this.angle >Math.PI*3/4||this.angle <-Math.PI*3/4){return0;}elseif(this.angle >-Math.PI*3/4&&this.angle <-Math.PI/2){return map(this.angle,-Math.PI*3/4,-Math.PI/2,0,1);}elseif(this.angle >-Math.PI/2&&this.angle <-Math.PI*3/8){return map(this.angle,-Math.PI/2,-Math.PI*3/8,1,2);}}}//==============================================================================//==============================================================================//==============================================================================// Functions to create/generate infinite world//==============================================================================//return a random element of the argument type. If wantRareObject is true, it will return a rare Objectfunction getRandomElement(type, wantRareObject){if(type ==="FOREST"){if(wantRareObject){return objects.objectsForest.rareObjects[AB.randomIntAtoB(0,objects.objectsForest.rareObjects.length -1)];}else{return objects.objectsForest.recurrentObjects[AB.randomIntAtoB(0,objects.objectsForest.recurrentObjects.length -1)];}}elseif(type ==="SAND"){if(wantRareObject){return objects.objectsSand.rareObjects[AB.randomIntAtoB(0,objects.objectsSand.rareObjects.length -1)];}else{return objects.objectsSand.recurrentObjects[AB.randomIntAtoB(0,objects.objectsSand.recurrentObjects.length -1)];}}elseif(type ==="SNOW"){if(wantRareObject){return objects.objectsSnow.rareObjects[AB.randomIntAtoB(0,objects.objectsSnow.rareObjects.length -1)];}else{return objects.objectsSnow.recurrentObjects[AB.randomIntAtoB(0,objects.objectsSnow.recurrentObjects.length -1)];}}}//Detect what type is in Position x,y. If those coordinates are not //in the dimension of the world, it change so that it loop like in the infinite worldfunction checkTypeGroundAround(x, y, type){if(x <0) x = worldSize -1;if(y <0) y = worldSize -1;if(x >= worldSize) x =0;if(y >= worldSize) y =0;return grounds[x][y].type == type;}//Everytime the player move to a ground that is not yet created, //it calls this function to create a Ground in a specific position pos.//x and y are for the coordinates in the array2d grounds.//type is to determine the type of the ground.//elements is when we load a world that alrdy had a ground with elements at this position.function createGround(pos, type, x, y, elements =null){
let conteneur =new THREE.Mesh();
conteneur.name ="ground";
let texture;
let transitionTexture;
let numberElement =0;
let hasRareObject =(AB.randomIntAtoB(0,20)>18);
saving.grounds[x][y].biom = type;if(type ==="FOREST"){
transitionTexture = transitionTextureForest;
texture = groundTexture;
numberElement = AB.randomIntAtoB(0,maxElementsForest);}elseif(type ==="SAND"){
transitionTexture = transitionTextureSand;
texture = sandTexture;
numberElement = AB.randomIntAtoB(0,maxElementsSand);}elseif(type ==="SNOW"){
transitionTexture = transitionTextureSnow;
texture = snowTexture;
numberElement = AB.randomIntAtoB(0,maxElementsSnow);}
let gr =new THREE.Mesh(new THREE.PlaneGeometry( squaresize*groundMultiplier, squaresize*groundMultiplier ),
texture );
gr.receiveShadow =true;
gr.rotation.x =(Math.PI /2)*3;
gr.position = pos;if(!checkTypeGroundAround(x -1, y, type)){
let a1 =new THREE.Mesh(new THREE.PlaneGeometry( squaresize*groundMultiplier, squaresize*groundMultiplier/8),transitionTexture );
conteneur.add(a1);
a1.position.set(-squaresize*groundMultiplier/2, offsetGround,0);
a1.rotation.x =(Math.PI /2)*3;
a1.rotation.z =Math.PI/2;
a1.receiveShadow =true;
offsetGround +=0.01;
a1.name ="ground";}if(!checkTypeGroundAround(x +1, y, type)){
let a2 =new THREE.Mesh(new THREE.PlaneGeometry( squaresize*groundMultiplier, squaresize*groundMultiplier/8),transitionTexture);
conteneur.add(a2);
a2.position.set(squaresize*groundMultiplier/2, offsetGround,0);
a2.rotation.z =Math.PI/2;
a2.rotation.x =(Math.PI /2)*3;
a2.receiveShadow =true;
offsetGround +=0.01;
a2.name ="ground";}if(!checkTypeGroundAround(x, y -1, type)){
let a3 =new THREE.Mesh(new THREE.PlaneGeometry( squaresize*groundMultiplier, squaresize*groundMultiplier/8), transitionTexture);
conteneur.add(a3);
a3.position.set(0, offsetGround,-squaresize*groundMultiplier/2);
a3.rotation.x =(Math.PI /2)*3;
a3.receiveShadow =true;
offsetGround +=0.01;
a3.name ="ground";}if(!checkTypeGroundAround(x, y +1, type)){
let a4 =new THREE.Mesh(new THREE.PlaneGeometry( squaresize*groundMultiplier, squaresize*groundMultiplier/8), transitionTexture);
conteneur.add(a4);
a4.position.set(0, offsetGround , squaresize*groundMultiplier/2);
a4.rotation.x =(Math.PI /2)*3;
a4.receiveShadow =true;
offsetGround +=0.01;
a4.name ="ground";}if(offsetGround >0.2){
offsetGround =0.001;}
gr.add(conteneur);
conteneur.rotation.x =-(Math.PI /2)*3;if(elements ==null){//add all elements to a group,
let placeAvailables =[];
let tmp =[];for(let i =0; i < groundMultiplier; i++){
tmp[i]=[];for(let j =0; j < groundMultiplier; j++){
tmp[i][j]=false;}}
let numberOfTry =0;while(numberElement >0&& numberOfTry !=500){
let randomX = AB.randomIntAtoB(0,groundMultiplier-1);
let randomY = AB.randomIntAtoB(0,groundMultiplier-1);
let wasPosed =false;while(!wasPosed && numberOfTry !=500){
numberOfTry++;if(tmp[randomX][randomY]===false){
let t = getRandomElement(type, hasRareObject);
let randomScale = AB.randomFloatAtoB(0.5,1.5);if(t !=null){
let canPoseObject =true;for(let i = randomX -Math.ceil(((t.sizeX * randomScale)-1)/2); i <= randomX +Math.ceil(((t.sizeX* randomScale)-1)/2); i++){for(let j = randomY -Math.ceil(((t.sizeZ* randomScale)-1)/2); j <= randomY +Math.ceil(((t.sizeZ* randomScale)-1)/2); j++){if(i >=0&& j >=0&& i < groundMultiplier && j < groundMultiplier){if(tmp[i][j]){
canPoseObject =false;break;}}else{
canPoseObject =false;break;}}}if(canPoseObject){
let obj = t.obj.clone();if(!hasRareObject) obj.rotation.y = AB.randomIntAtoB(0,360);
obj.scale.multiplyScalar(randomScale);
obj.position.set((randomX - parseInt(groundMultiplier/2))* squaresize, t.sizeY* randomScale,(randomY - parseInt(groundMultiplier/2))* squaresize);
conteneur.add(obj);for(let i = randomX -Math.ceil(((t.sizeX* randomScale)-1)/2); i <= randomX +Math.ceil(((t.sizeX* randomScale)-1)/2); i++){for(let j = randomY -Math.ceil(((t.sizeZ* randomScale)-1)/2); j <= randomY +Math.ceil(((t.sizeZ* randomScale)-1)/2); j++){if(i >=0&& j >=0&& i < groundMultiplier && j < groundMultiplier){
tmp[i][j]=true;}}}if(hasRareObject){
hasRareObject =false;}
let o ={
s:Number((randomScale).toFixed(1)),
x:Number(((randomX - parseInt(groundMultiplier/2))* squaresize).toFixed(1)),
y:Number((t.sizeY* randomScale).toFixed(1)),
z:Number(((randomY - parseInt(groundMultiplier/2))* squaresize).toFixed(1)),
o: t.path,}
saving.grounds[x][y].elements.push(o);
wasPosed =true;
numberElement--;
numberOfTry =0;}else{
randomX = AB.randomIntAtoB(0,groundMultiplier-1);
randomY = AB.randomIntAtoB(0,groundMultiplier-1);}}else{if(hasRareObject){
hasRareObject =false;}}}else{
randomX = AB.randomIntAtoB(0,groundMultiplier-1);
randomY = AB.randomIntAtoB(0,groundMultiplier-1);}}}}else{for(let i =0; i < elements.length; i++){
let t = findObjectLoad(elements[i].o);if(t !==null){
let obj = t.obj.clone();
obj.scale.multiplyScalar(elements[i].s);
obj.position.set(elements[i].x, elements[i].y, elements[i].z);
conteneur.add(obj);}}}
gr.name ="ground";return gr;}//With the name of an object, it find the object to recreate it.//If it wasn't loaded(code changed) it returns nullfunction findObjectLoad(path){for(let i =0; i < allObjects.length; i++){if(path == allObjects[i].path)return allObjects[i];}
console.log("The Object", path,"was not find in allObjects array that stock all objects that have been loaded");returnnull;}//Return the type that is the most present around the current position.function getTypeAround(array,x,y){
let sand =0;
let snow =0;
let forest =0;if(x -1>=0&& array[x -1][y]!==null){if(array[x -1][y]=="SAND") sand++;if(array[x -1][y]=="FOREST") forest++;if(array[x -1][y]=="SNOW") snow++;}if(y -1>=0&& array[x][y -1]!==null){if(array[x][y -1]=="SAND") sand++;if(array[x][y -1]=="FOREST") forest++;if(array[x][y -1]=="SNOW") snow++;}if(x +1< array.length && array[x +1][y]!==null){if(array[x +1][y]=="SAND") sand++;if(array[x +1][y]=="FOREST") forest++;if(array[x +1][y]=="SNOW") snow++;}if(y +1< array.length && array[x][y +1]!==null){if(array[x][y +1]=="SAND") sand++;if(array[x][y +1]=="FOREST") forest++;if(array[x][y +1]=="SNOW") snow++;}if(sand ==0&& snow ==0&& forest ==0){returnnull;}elseif(sand > forest && sand > snow && enableSand){return"SAND";}elseif(snow > forest && snow > sand && enableSnow){return"SNOW";}elseif(forest > snow && forest > sand && enableForest){return"FOREST";}else{returnnull;}}//initialize the ground of the world, to determine all type nd create fake grounds//around the world to fake a loopfunction initGround(){
let tmp =newArray(worldSize);for(let i =0; i < worldSize; i++){
tmp[i]=newArray(worldSize);for(let j =0; j < worldSize; j++){
tmp[i][j]=null;}}for(let i =0; i < worldSize; i++){for(let j =0; j < worldSize; j++){
let typeAround = getTypeAround(tmp,i,j);
let rand = AB.randomIntAtoB(0,5);if(rand <3&& typeAround !==null){
tmp[i][j]= typeAround;}else{
let t = TYPEGROUND[AB.randomIntAtoB(0,TYPEGROUND.length-1)];while(!((t =="FOREST"&& enableForest)||(t =="SAND"&& enableSand)||(t =="SNOW"&& enableSnow))){
t = TYPEGROUND[AB.randomIntAtoB(0,TYPEGROUND.length-1)];}
tmp[i][j]= t;}}}for(let i =0; i < worldSize; i++){for(let j =0; j < worldSize; j++){
grounds[i][j]=newGround(false,new THREE.Vector3(0,0,0));
grounds[i][j].type = tmp[i][j];
grounds[i][j].active =false;
saving.grounds[i][j].biom = tmp[i][j];}}for(let i =0; i < worldSize; i++){for(let j =0; j < worldSize; j++){if(i < viewDistance +1|| i >= worldSize - viewDistance || j >= worldSize - viewDistance || j < viewDistance +1){
grounds[i][j].ground = createGround(new THREE.Vector3(0,0,0), grounds[i][j].type, i, j);
grounds[i][j].ground.position.set((i-Math.trunc(worldSize/2))* squaresize * groundMultiplier,0,(j-Math.trunc(worldSize/2))* squaresize*groundMultiplier)}}}for(let i =- viewDistance; i < worldSize + viewDistance; i++){for(let j =- viewDistance; j < worldSize + viewDistance; j++){
let b =(worldSize + viewDistance);if(i <0|| j <0|| i >= worldSize || j >= worldSize){
let tmp;if( i <0){if(j <0){
tmp = grounds[worldSize + i][worldSize + j].ground.clone();}elseif(j >= worldSize){
tmp = grounds[worldSize + i][j - worldSize].ground.clone();}else{
tmp = grounds[worldSize + i][j].ground.clone();}}elseif(i >= worldSize){if(j <0){
tmp = grounds[i - worldSize][worldSize + j].ground.clone();}elseif(j >= worldSize){
tmp = grounds[i - worldSize][j - worldSize].ground.clone();}else{
tmp = grounds[i - worldSize][j].ground.clone();}}elseif(i >= worldSize){if(j <0){
tmp = grounds[i - worldSize][worldSize + j].ground.clone();}elseif(j >= worldSize){
tmp = grounds[i - worldSize][j - worldSize].ground.clone();}else{
tmp = grounds[i - worldSize][j].ground.clone();}}elseif( j <0){
tmp = grounds[i][worldSize + j].ground.clone();}elseif(j >= worldSize){
tmp = grounds[i][j - worldSize].ground.clone();}
tmp.position.set((i -Math.trunc(worldSize/2))* squaresize * groundMultiplier,0,(j -Math.trunc(worldSize/2))* squaresize*groundMultiplier);
threeworld.scene.add(tmp);}}}}//This function is called each time the player moves to another ground to check if //there is a need to create a ground depending on the viewDistancefunction updateGround(){for(let i = ai - viewDistance -1; i < ai + viewDistance +2; i++){for(let j = aj - viewDistance -1; j < aj + viewDistance +2; j++){if(!(i <0|| i >= worldSize || j >= worldSize || j <0)){if(i == ai - viewDistance -1|| j == aj - viewDistance -1|| i == ai + viewDistance +1|| j == aj + viewDistance +1){if(grounds[i][j]!==null&& grounds[i][j].active){
grounds[i][j].active =false;}}else{if(typeof grounds[i][j].ground =='undefined'|| grounds[i][j].ground ===null){
grounds[i][j].ground = createGround(new THREE.Vector3(0,0,0), grounds[i][j].type, i, j);
grounds[i][j].ground.position.set((i-Math.trunc(worldSize/2))* squaresize * groundMultiplier,0,(j-Math.trunc(worldSize/2))* squaresize*groundMultiplier);
grounds[i][j].active =true;
threeworld.scene.add(grounds[i][j].ground);}elseif(!grounds[i][j].active){
grounds[i][j].active =true;
threeworld.scene.add(grounds[i][j].ground);}}}}}}//==============================================================================//==============================================================================//==============================================================================// Functions link to an event//==============================================================================//Create a new infinite world. First destroy the previous one and then recreate one.function createNewInfiniteWorld(){
viewDistance = newviewDistance;
worldSize = newworldSize;
groundMultiplier = newGroundMultiplier;
saving =newSaveWorld(worldSize,groundMultiplier, viewDistance);while(threeworld.scene.children.length){
threeworld.scene.remove(threeworld.scene.children[0]);}
threeworld.scene.add(controls.getObject());
threeworld.scene.fog =new THREE.Fog(SKYCOLOR,0, SKYDISTANCE);// LIGHTS
let hemiLight =new THREE.HemisphereLight( SKYCOLOR, SKYCOLOR,0.4);
hemiLight.position.set(0,50,0);
threeworld.scene.add( hemiLight );
init();}function onDocumentTouchStart( event ){
console.log("onDocumentTouchStart");
event.preventDefault();
event.clientX = event.touches[0].clientX;
event.clientY = event.touches[0].clientY;
onDocumentMouseDown( event );}//Called when a key is upfunction handleKeyUp (e){if((e.keyCode ==38)||(e.keyCode ==40)){
velocity =0;}if(e.keyCode ==37|| e.keyCode ==39){
speedRotation =0;}if(e.keyCode ==37|| e.keyCode ==39){
speedRotation =0;}}//Called on scrool of the wheel's mousefunction onScroll(e){if(controls.getAttachedObject().children.length !==0){
controls.getAttachedObject().remove(controls.getAttachedObject().children[1]);
controls.getAttachedObject().remove(controls.getAttachedObject().children[0]);
let delta =Math.max(-1,Math.min(1,(e.wheelDelta ||-e.detail)));if(delta ==1&& indexObjects +1< allObjects.length){
indexObjects++;}elseif(delta ==1){
indexObjects =0;}elseif(delta ==-1&& indexObjects -1>=0){
indexObjects--;}elseif(delta ==-1){
indexObjects = allObjects.length-1;}
let t = allObjects[indexObjects];
let o = t.obj.clone();
let bbox = getBoundingBoxObject(o,0x008000);
controls.getAttachedObject().add(bbox);
controls.getAttachedObject().add(o);
o.position.y = t.sizeY;}}functionOnClick(e){if(editMode !==null){if(editMode ===0){
selectionObject =null;CreateObject(e);}elseif(editMode ==1){if(controls.getAttachedObject().children.length !=0){
controls.getAttachedObject().remove(controls.getAttachedObject().children[1]);
controls.getAttachedObject().remove(controls.getAttachedObject().children[0]);}if(e !=null&& e.which ==3){
editMode =null;}if(selectionObject !==null){
removeObject(threeworld.scene, selectionObject);while(selectionBox.children.length){
selectionBox.remove(selectionBox.children[0]);}}}}}functionDestroyObject(e){if(e.type !=='click'){
rayToDestroy =new THREE.Raycaster( controls.getObject().position, threeworld.camera.getWorldDirection(),0,800);
let inter = rayToDestroy.intersectObjects(threeworld.scene.children,true);if(inter.length >0){
let i =0;while(i < inter.length &&(inter[i].object.name =="ground"|| inter[i].object == selectionBox)) i++;if(i < inter.length && inter[i].object != selectionObject){
let o = getParent(inter[0].object);if(selectionObject != o && o != selectionBox){while(selectionBox.children.length){
selectionBox.remove(selectionBox.children[0]);}
let bbox = getBoundingBoxObject(o,0xFF0000);
selectionBox.add(bbox);
bbox.position.x = o.getWorldPosition().x;
bbox.position.z = o.getWorldPosition().z;
selectionObject = o;}}}}}function removeObject(objConteneur, objToDestroy){for(let i =0; i < objConteneur.children.length; i++){if(objConteneur.children[i]== objToDestroy){
objConteneur.remove(objToDestroy);}else{
removeObject(objConteneur.children[i], objToDestroy);}}}//Called when you click left with the nosue after having entered the edit mode with a left click.functionCreateObject(e =null){
editMode =0;if(e !=null&& e.which ==3&& controls.getAttachedObject().children.length !==0){
controls.getAttachedObject().remove(controls.getAttachedObject().children[1]);
controls.getAttachedObject().remove(controls.getAttachedObject().children[0]);
editMode =null;}elseif(e !=null&& e.which ==3){//TODO Maybe ?}else{if(controls.getAttachedObject().children.length !==0){
let o = controls.getAttachedObject().children[1].clone();
userObjects.push(o);
threeworld.scene.add(o);
let pos = controls.getAttachedObject().children[1].getWorldPosition();
let rot = controls.getAttachedObject().children[1].getWorldRotation();
o.rotation.set();
o.position.set(pos.x, pos.y, pos.z);
o.rotation.set(rot.x, rot.y, rot.z);
let t = allObjects[indexObjects];
let s ={
s:1,
x:Number((pos.x).toFixed(1)),
y:Number((pos.y).toFixed(1)),
z:Number((pos.z).toFixed(1)),
o: t.path,}
saving.userObjects.push(s);}else{
let t = allObjects[indexObjects];
let o = t.obj.clone();
let bbox = getBoundingBoxObject(o,0x008000);
controls.getAttachedObject().add(bbox);
controls.getAttachedObject().add(o);
o.position.y = t.sizeY;}}}//Fonction that return an cube that determine the bouding box of an objectfunction getBoundingBoxObject(object, colorBox){
let bbox =new THREE.Box3().setFromObject(object);
let cube =new THREE.Mesh(new THREE.BoxGeometry(bbox.size().x,bbox.size().y,bbox.size().z),new THREE.MeshBasicMaterial({
color: colorBox,
transparent:true,
opacity:0.5}));
cube.position.y = bbox.size().y/2;return cube;}//Return highest parent in the hierarchy that is not a ground or the scene.//Useful for models that were not well designed.function getParent(object){if(object.parent !==null&& object.parent.name !="ground"&& object.parent != threeworld.scene){return getParent(object.parent);}return object;}//==============================================================================//==============================================================================//==============================================================================// Function that load Initialize the world//==============================================================================//Load all objects3D and textures before initialazing the worldfunction loaderObjects(){
soundManager =newSoundManager();// load a sound and set it as the Audio object's buffer
let audioLoaderForest =new THREE.AudioLoader(loadingManager);
audioLoaderForest.load( SOUNDFOREST,function(buffer){
soundManager.soundForest.setBuffer(buffer);
soundManager.soundForest.setLoop(true);
soundManager.soundForest.setVolume(0);});
let audioLoaderSand =new THREE.AudioLoader(loadingManager);
audioLoaderSand.load( SOUNDSAND,function( buffer ){
soundManager.soundSand.setBuffer( buffer );
soundManager.soundSand.setLoop(true);
soundManager.soundSand.setVolume(0);});
let audioLoaderSnow =new THREE.AudioLoader(loadingManager);
audioLoaderSnow.load( SOUNDSNOW,function( buffer ){
soundManager.soundSnow.setBuffer( buffer );
soundManager.soundSnow.setLoop(true);
soundManager.soundSnow.setVolume(0);});
let audioLoaderNight =new THREE.AudioLoader(loadingManager);
audioLoaderNight.load( SOUNDSNOW,function( buffer ){
soundManager.soundNight.setBuffer( buffer );
soundManager.soundNight.setLoop(true);
soundManager.soundNight.setVolume(0);});
let planeTex = THREE.ImageUtils.loadTexture("/uploads/meak/transitionsnow.png");
planeTex.wrapS = planeTex.wrapT = THREE.RepeatWrapping;
planeTex.repeat.set(8,8);
transitionTextureSnow =new THREE.MeshPhongMaterial({map: planeTex, transparent:true, dithering:true});
transitionTextureSnow.wrapAround =true;
transitionTextureSnow.receiveShadow =true;
planeTex = THREE.ImageUtils.loadTexture("/uploads/meak/transitionsand.png");
planeTex.wrapS = planeTex.wrapT = THREE.RepeatWrapping;
planeTex.repeat.set(8,8);
transitionTextureSand =new THREE.MeshPhongMaterial({map: planeTex, transparent:true, dithering:true});
transitionTextureSand.wrapAround =true;
transitionTextureSand.receiveShadow =true;
planeTex = THREE.ImageUtils.loadTexture("/uploads/meak/transitiongrass.png");
planeTex.wrapS = planeTex.wrapT = THREE.RepeatWrapping;
planeTex.repeat.set(8,8);
transitionTextureForest =new THREE.MeshPhongMaterial({map: planeTex, transparent:true, dithering:true});
transitionTextureForest.wrapAround =true;
transitionTextureForest.receiveShadow =true;
planeTex = THREE.ImageUtils.loadTexture("/uploads/meak/grass.jpg");
planeTex.wrapS = planeTex.wrapT = THREE.RepeatWrapping;
planeTex.repeat.set(8,8);
groundTexture =new THREE.MeshPhongMaterial({map: planeTex, dithering:true});
groundTexture.wrapAround =true;
groundTexture.castShadow =true;
groundTexture.receiveShadow =true;
planeTex = THREE.ImageUtils.loadTexture("/uploads/meak/sand.jpg");
planeTex.wrapS = planeTex.wrapT = THREE.RepeatWrapping;
planeTex.repeat.set(8,8);
sandTexture =new THREE.MeshPhongMaterial({map: planeTex, dithering:true});
sandTexture.wrapAround =true;
sandTexture.castShadow =true;
sandTexture.receiveShadow =true;
snowTexture =new THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture("/uploads/meak/snow.png"), dithering:true});
snowTexture.wrapAround =true;
snowTexture.castShadow =true;
snowTexture.receiveShadow =true;
console.log("loading "+Object.keys(ARRAYOBJECTSFOREST).length +" elements ");for(let key in ARRAYOBJECTSFOREST){
let loaderMat =new THREE.MTLLoader(loadingManager);
let loaderObj =new THREE.OBJLoader(loadingManager);
loaderMat.setTexturePath(PATHTEXTURES);
let pathMat = PATHTEXTURES + key +'.mtl';
let pathObj = PATHTEXTURES + key +'.obj';
let value = ARRAYOBJECTSFOREST[key];
let nameObj = key;
console.log("loading "+ pathMat +" and "+ pathObj);
loaderMat.load
(// resource URL
pathMat,// called when resource is loadedfunction(materials){
materials.preload();
loaderObj.setMaterials(materials);// load a resource
loaderObj.load
(// resource URL
pathObj,// called when resource is loadedfunction( object ){
let boundingBox =new THREE.Box3().setFromObject(object);
object.traverse(function( child ){if( child instanceof THREE.Mesh){
child.castShadow =true;
object.receiveShadow =true;}});
object.position.set(0,0,0);
let sizeX =Math.abs(boundingBox.min.x)+Math.abs(boundingBox.max.x);
let sizeY =Math.abs(boundingBox.min.y)+Math.abs(boundingBox.max.y);
let sizeZ =Math.abs(boundingBox.min.z)+Math.abs(boundingBox.max.z);
let tmp =newObj(nameObj,1+Math.ceil((value * sizeX / squaresize)*2),Math.abs(boundingBox.min.y)*value,1+Math.ceil((value * sizeZ / squaresize)*2));
object.scale.multiplyScalar(value);
tmp.obj = object;if(Math.ceil((sizeZ-1)/2)< limitSizeForRareObjects &&Math.ceil((sizeX-1)/2)< limitSizeForRareObjects ){
objects.objectsForest.recurrentObjects.push(tmp);}else{
objects.objectsForest.rareObjects.push(tmp);}
allObjects.push(tmp);});});}
console.log("loading "+Object.keys(ARRAYOBJECTSSAND).length +" elements ");for(let key in ARRAYOBJECTSSAND){
let loaderMat =new THREE.MTLLoader(loadingManager);
let loaderObj =new THREE.OBJLoader(loadingManager);
loaderMat.setTexturePath(PATHTEXTURES);
let pathMat = PATHTEXTURES + key +'.mtl';
let pathObj = PATHTEXTURES + key +'.obj';
let value = ARRAYOBJECTSSAND[key];
let nameObj = key;
console.log("loading "+ pathMat +" and "+ pathObj);
loaderMat.load
(// resource URL
pathMat,// called when resource is loadedfunction( materials ){
materials.preload();
loaderObj.setMaterials(materials);// load a resource
loaderObj.load
(// resource URL
pathObj,// called when resource is loadedfunction( object ){
let boundingBox =new THREE.Box3().setFromObject(object);
object.traverse(function( child ){if( child instanceof THREE.Mesh){
child.castShadow =true;
object.receiveShadow =true;}});
object.position.set(0,0,0);
let sizeX =Math.abs(boundingBox.min.x)+Math.abs(boundingBox.max.x);
let sizeY =Math.abs(boundingBox.min.y)+Math.abs(boundingBox.max.y);
let sizeZ =Math.abs(boundingBox.min.z)+Math.abs(boundingBox.max.z);
let tmp =newObj(nameObj,1+Math.ceil((value * sizeX / squaresize)*2),Math.abs(boundingBox.min.y)*value,1+Math.ceil((value * sizeZ / squaresize)*2));
object.scale.multiplyScalar(value);
tmp.obj = object;if(Math.ceil((sizeZ-1)/2)< limitSizeForRareObjects &&Math.ceil((sizeX-1)/2)< limitSizeForRareObjects ){
objects.objectsSand.recurrentObjects.push(tmp);}else{
objects.objectsSand.rareObjects.push(tmp);}
allObjects.push(tmp);});});}
console.log("loading "+Object.keys(ARRAYOBJECTSSNOW).length +" elements ");for(let key in ARRAYOBJECTSSNOW){
let loaderMat =new THREE.MTLLoader(loadingManager);
let loaderObj =new THREE.OBJLoader(loadingManager);
loaderMat.setTexturePath(PATHTEXTURES);
let pathMat = PATHTEXTURES + key +'.mtl';
let pathObj = PATHTEXTURES + key +'.obj';
let value = ARRAYOBJECTSSNOW[key];
let nameObj = key;
console.log("loading "+ pathMat +" and "+ pathObj);
loaderMat.load
(// resource URL
pathMat,// called when resource is loadedfunction( materials ){
materials.preload();
loaderObj.setMaterials(materials);// load a resource
loaderObj.load
(// resource URL
pathObj,// called when resource is loadedfunction( object ){
let boundingBox =new THREE.Box3().setFromObject(object);
object.traverse(function( child ){if( child instanceof THREE.Mesh){
child.castShadow =true;
object.receiveShadow =true;}});
object.position.set(0,0,0);
let sizeX =Math.abs(boundingBox.min.x)+Math.abs(boundingBox.max.x);
let sizeY =Math.abs(boundingBox.min.y)+Math.abs(boundingBox.max.y);
let sizeZ =Math.abs(boundingBox.min.z)+Math.abs(boundingBox.max.z);
let tmp =newObj(nameObj,1+Math.ceil((value * sizeX / squaresize)*2),Math.abs(boundingBox.min.y)*value,1+Math.ceil((value * sizeZ / squaresize)*2));
object.scale.multiplyScalar(value);
tmp.obj = object;if(Math.ceil((sizeZ-1)/2)< limitSizeForRareObjects &&Math.ceil((sizeX-1)/2)< limitSizeForRareObjects ){
objects.objectsSnow.recurrentObjects.push(tmp);}else{
objects.objectsSnow.rareObjects.push(tmp);}
allObjects.push(tmp);});});}}//Initalization before running the worldfunction init(){
grounds =newArray(worldSize);for(let i =0; i < worldSize; i++){
grounds[i]=newArray(worldSize);for(let j =0; j < worldSize; j++){
grounds[i][j]=null;}}
ai = parseInt(worldSize/2+ camera.getWorldPosition().x/(squaresize*groundMultiplier));
aj = parseInt(worldSize/2+ camera.getWorldPosition().z/(squaresize*groundMultiplier));
pai = ai;
paj = aj;
initGround();
updateGround();
soundManager.currentSound = grounds[ai][aj].type;
soundManager.playCurrentSound();
threeworld.scene.add(selectionBox);}//Initialization of the loading Manager. When everything is loaded, create all HUD elements and start to initialize the worldfunction initLoadingManager(){
loadingManager =new THREE.LoadingManager();
AB.loadingScreen();
loadingManager.onLoad =function(){
console.log('Loading complete!');if( AB.onDesktop()){
$("#user_span1").html("<p>Use WASD or Arrows to move, mouse to look around and space to jump.</p>"+"<p>Tap '1' to enter first edit mode, then click left to create Objects</p>"+"<p>You can change the object by scrolling !</p>"+"<p>Tap '2' to enter second edit mode, then click left to destroy objects</p>"+"<p>click right to cancel each edit mode. (Creation or Destruction)</p>"+"<p> If you want to re-generate the world, you can press R !</p>");if(AB.runloggedin){// Definitely can save, not sure if can restore:
$("#user_span2").html (" <button onclick='AB.saveData();' >Save your work</button> ");// Check if any data exists, if so make restore button:
AB.queryDataExists();// will call World.queryDataExists when done }else{
$("#user_span2").html(" <p> <b> To save your work, go to the World page and run this \"logged in\". </b> </p> ");}
$("#user_span10").html("<p><b>Click screen to enable mouse controls</b></p>");
$("#user_span4").html("<p> Size of each parcel of the world (1 to 1000) <input type=\"number\" min=\"1\" max=\"1000\" value=\""+ newGroundMultiplier +"\" class=\"slider\" id=\"groundMultiplierHtml\"></p>");
document.getElementById("groundMultiplierHtml").onchange =function(){
newGroundMultiplier =+this.value ;
console.log("groundMultiplierHtml "+ newGroundMultiplier);
HUDEVENT =true;}
document.getElementById("groundMultiplierHtml").onkeypress =function(){
newGroundMultiplier =+this.value ;
console.log("groundMultiplierHtml "+ newGroundMultiplier);
HUDEVENT =true;}
$("#user_span5").html("<p> Size of the world (1 to 50) <input type=\"number\" min=\"1\" max=\"50\" value=\""+ newworldSize +"\" class=\"slider\" id=\"worldSizeHtml\"></p>");
document.getElementById("worldSizeHtml").onchange =function(){
newworldSize =+this.value;if((newworldSize -1)/2< newviewDistance){
newviewDistance =0;
document.getElementById("viewDistanceHtml").value =0;}
HUDEVENT =true;
console.log("worldSizeHtml "+ newworldSize);}
document.getElementById("worldSizeHtml").onkeypress =function(){
newworldSize =+this.value;if((newworldSize -1)/2< newviewDistance){
newviewDistance =0;
document.getElementById("viewDistanceHtml").value =0;}
HUDEVENT =true;
console.log("worldSizeHtml "+ newworldSize);}
$("#user_span6").html("<p> viewDistance in term of parcel (0 to 10) <input type=\"number\" min=\"0\" max=\"10\" value=\""+ newviewDistance +"\" class=\"slider\" id=\"viewDistanceHtml\"></p>");
document.getElementById("viewDistanceHtml").onchange =function(){if(newworldSize/2>=+this.value){
newviewDistance =+this.value ;}
console.log("viewDistanceHtml "+ newviewDistance);
HUDEVENT =true;}
document.getElementById("viewDistanceHtml").onkeypress =function(){if(newworldSize/2>=+this.value){
newviewDistance =+this.value ;}
console.log("viewDistanceHtml "+ newviewDistance);
HUDEVENT =true;}
$("#user_span7").html("<p>Enable snow <input type=\"checkBox\" id=\"enableSnowHtml\" checked> Max element for one section <input type=\"number\" id=\"maxElementsSnowHtml\" min=\"0\" max=\"300\" value=\""+ maxElementsSnow +"\"></p>");
document.getElementById("maxElementsSnowHtml").onchange =function(){
maxElementsSnow =this.value;
HUDEVENT =true;}
document.getElementById("maxElementsSnowHtml").onkeypress =function(){
maxElementsSnow =this.value;
HUDEVENT =true;}
document.getElementById("enableSnowHtml").onchange =function(){if(!enableSand &&!enableForest){this.checked =true;}else{
enableSnow =!enableSnow;}this.releasePointerCapture();
HUDEVENT =true;}
$("#user_span8").html("<p>Enable sand <input type=\"checkBox\" id=\"enableSandHtml\" checked> Max element for one section <input type=\"number\" id=\"maxElementsSandHtml\" min=\"0\" max=\"300\" value=\""+ maxElementsSand +"\"></p>");
document.getElementById("maxElementsSandHtml").onchange =function(){
maxElementsSand =this.value;
HUDEVENT =true;}
document.getElementById("maxElementsSandHtml").onkeypress =function(){
maxElementsSand =this.value;
HUDEVENT =true;}
document.getElementById("enableSandHtml").onchange =function(){if(!enableSnow &&!enableForest){this.checked =true;}else{
enableSand =!enableSand;}
HUDEVENT =true;}
$("#user_span9").html("<p>Enable forest <input type=\"checkBox\" id=\"enableForestHtml\" checked> Max element for one section <input type=\"number\" id=\"maxElementsForestHtml\" min=\"0\" max=\"300\" value=\""+ maxElementsForest +"\"></p>");
document.getElementById("maxElementsForestHtml").onchange =function(){
maxElementsForest =this.value;
HUDEVENT =true;}
document.getElementById("maxElementsForestHtml").onkeypress =function(){
maxElementsForest =this.value;
HUDEVENT =true;}
document.getElementById("enableForestHtml").onchange =function(){if(!enableSnow &&!enableSand){this.checked =true;}else{
enableForest =!enableForest;}
HUDEVENT =true;}}else{
$("#user_span1").html("<p> <b> This World currently only works on desktop. </b> </p>");}
loaded =true;
AB.removeLoading();
createNewInfiniteWorld();
sun =newSun();};
loadingManager.onProgress =function( url, itemsLoaded, itemsTotal ){
let s ='Loading file: '+ itemsLoaded +' of '+ itemsTotal +' files.';
$("#user_span1").html( s );};}//==============================================================================//==============================================================================//==============================================================================// Function newRun ,init and endRun//==============================================================================this.newRun =function(){
camera =new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight,1, SKYDISTANCE );
threeworld.camera = camera;
let vector =new THREE.Vector3(0,0,-1);
vector = camera.localToWorld(vector);
vector.sub(camera.position);// Now vector is a unit vector with the same direction as the camera
rayToDestroy =new THREE.Raycaster( camera.position, vector);
threeworld.init3d (0,0, SKYCOLOR );// can adjust renderer:
threeworld.renderer.shadowMap.enabled =true;
threeworld.renderer.shadowMap.type = THREE.PCFSoftShadowMap;//Initialize the Object that will contain all possible 3D Objects in the world
objects =newObjectsConteneur();//position of mouse in the canvas
mouse =new THREE.Vector2();//First person controller
controls =new THREE.PointerLockControls( camera );//Start loading objects
initLoadingManager();
loaderObjects();//This will handle key presses
let onKeyDown =function( event ){switch( event.keyCode ){case38:// upcase87:// w
moveForward =true;break;case37:// leftcase65:// a
moveLeft =true;break;case40:// downcase83:// s
moveBackward =true;break;case39:// rightcase68:// d
moveRight =true;break;case32:// spaceif( canJump ===true||true) velocity.y +=350;
canJump =false;break;case49:case97:// Create Object (1)CreateObject();break;case50:case98:// Destroy Object (2)
editMode =1;break;case82://Re-generateTheworld (R)
createNewInfiniteWorld();
threeworld.scene.add(sun.object);break;}};
let onKeyUp =function( event ){switch( event.keyCode ){case38:// upcase87:// w
moveForward =false;break;case37:// leftcase65:// a
moveLeft =false;break;case40:// downcase83:// s
moveBackward =false;break;case39:// rightcase68:// d
moveRight =false;break;}};
document.addEventListener('keydown', onKeyDown,false);
document.addEventListener('keyup', onKeyUp,false);
document.addEventListener('click',OnClick,false);
document.addEventListener('mousewheel', onScroll,false);
document.addEventListener('DOMMouseScroll', onScroll,false);
document.addEventListener('onmousewheel', onScroll,false);
raycaster =new THREE.Raycaster(new THREE.Vector3(),new THREE.Vector3(0,-1,0),0,10);//The following handles pointer locking when clicking the window
let havePointerLock ='pointerLockElement' in document ||'mozPointerLockElement' in document ||'webkitPointerLockElement' in document;
console.log(havePointerLock);if( havePointerLock ){
let element = document.body;
let pointerlockchange =function( event ){if( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ){if(HUDEVENT){
document.exitPointerLock();
HUDEVENT =false;}else{if(!controls.enabled && controls.getAttachedObject().children.length !==0){
controls.getAttachedObject().remove(controls.getAttachedObject().children[1]);
controls.getAttachedObject().remove(controls.getAttachedObject().children[0]);}if(!controls.enabled){
controls.enabled =true;
$("#user_span10").html("");}}}else{
controls.enabled =false;
$("#user_span10").html("<p><b>Click screen to enable mouse controls</b></p>");}};
let 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(){if(!loaded){
console.warn('Charging');}else{//======================================================================//This will handle moving the player and the camera//======================================================================
sun.animate();
let time = performance.now();
let 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 directionsif( moveForward || moveBackward ) velocity.z -= direction.z *400.0* MOVESPEED * delta;if( moveLeft || moveRight ) velocity.x -= direction.x *400.0* MOVESPEED * delta;
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;//Update the position of the payer in the world so that it can generate ground around the player, depending on the viewDistanceif(worldSize/2+ camera.getWorldPosition().x/(squaresize*groundMultiplier)<0){
ai =-Math.ceil(-(worldSize/2+ camera.getWorldPosition().x/(squaresize*groundMultiplier)));}else{
ai =Math.trunc(worldSize/2+ camera.getWorldPosition().x/(squaresize*groundMultiplier));}if(worldSize/2+ camera.getWorldPosition().z/(squaresize*groundMultiplier)<0){
aj =-Math.ceil(-(worldSize/2+ camera.getWorldPosition().z/(squaresize*groundMultiplier)));}else{
aj =Math.trunc(worldSize/2+ camera.getWorldPosition().z/(squaresize*groundMultiplier));}if(ai <0){
controls.getObject().position.x =(worldSize/2)* groundMultiplier * squaresize -0.01;}if(ai >= worldSize){
controls.getObject().position.x =- worldSize/2* groundMultiplier * squaresize;}if(aj >= worldSize){
controls.getObject().position.z =- worldSize/2* groundMultiplier * squaresize;}if(aj <0){
controls.getObject().position.z =(worldSize/2)* groundMultiplier * squaresize -0.01;}if(ai != pai || aj != paj){
pai = ai;
paj = aj;
updateGround();if(grounds[ai][aj].type != soundManager.currentSound){
soundManager.currentSound = grounds[ai][aj].type;
soundManager.playCurrentSound();}}/////Update of the sound depending of the actual zone where the player is.
soundManager.updateSound();}};this.endRun =function(){};//==============================================================================//==============================================================================//==============================================================================// Functions to store/restore data//==============================================================================this.restoreData =function( a )// process object returned from server {
console.log ("Restore Data");//We store saving data so that the player can update his save
saving = a;//We re-generate the world that was savedRecreateInfiniteWorld(a);};this.queryDataExists =function( exists ){if( exists )
$("#user_span3").html ("<p> <button onclick='AB.restoreData();' >Restore your work</button> </p>");};this.saveData =function()// defines what object to save to server{
HUDEVENT =true;return(saving);};//Function to recreate a world that was saved on the server.//a functionRecreateInfiniteWorld(data){
worldSize = data.worldSize;
groundMultiplier = data.groundMultiplier;
viewDistance = data.viewDistance;while(threeworld.scene.children.length){
threeworld.scene.remove(threeworld.scene.children[0]);}
threeworld.scene.add(controls.getObject());
threeworld.scene.add(sun.object);
threeworld.scene.fog =new THREE.Fog(SKYCOLOR,0, SKYDISTANCE);// LIGHTS
let hemiLight =new THREE.HemisphereLight( SKYCOLOR, SKYCOLOR,0.4);
hemiLight.position.set(0,50,0);
threeworld.scene.add( hemiLight );for(let i =0; i < worldSize; i++){for(let j =0; j < worldSize; j++){
grounds[i][j]=newGround(false,new THREE.Vector3(0,0,0));
grounds[i][j].type = data.grounds[i][j].biom;
grounds[i][j].active =false;
saving.grounds[i][j].biom = data.grounds[i][j].biom;}}for(let i =0; i < worldSize; i++){for(let j =0; j < worldSize; j++){if(data.grounds[i][j].elements.length !==0){
grounds[i][j].ground = createGround(new THREE.Vector3(0,0,0), grounds[i][j].type, i, j, data.grounds[i][j].elements);
grounds[i][j].ground.position.set((i-Math.trunc(worldSize/2))* squaresize * groundMultiplier,0,(j-Math.trunc(worldSize/2))* squaresize*groundMultiplier)
threeworld.scene.add(grounds[i][j].ground);}}}for(let i =0; i < data.userObjects.length; i++){
let t = findObjectLoad(data.userObjects[i].o);if(t !==null){
let obj = t.obj.clone();
obj.scale.multiplyScalar(data.userObjects[i].s);
obj.position.set(data.userObjects[i].x, data.userObjects[i].y, data.userObjects[i].z);
threeworld.scene.add(obj);}}for(let i =- viewDistance; i < worldSize + viewDistance; i++){for(let j =- viewDistance; j < worldSize + viewDistance; j++){
let b =(worldSize + viewDistance);if(i <0|| j <0|| i >= worldSize || j >= worldSize){
let tmp;if( i <0){if(j <0){
tmp = grounds[worldSize + i][worldSize + j].ground.clone();}elseif(j >= worldSize){
tmp = grounds[worldSize + i][j - worldSize].ground.clone();}else{
tmp = grounds[worldSize + i][j].ground.clone();}}elseif(i >= worldSize){if(j <0){
tmp = grounds[i - worldSize][worldSize + j].ground.clone();}elseif(j >= worldSize){
tmp = grounds[i - worldSize][j - worldSize].ground.clone();}else{
tmp = grounds[i - worldSize][j].ground.clone();}}elseif(i >= worldSize){if(j <0){
tmp = grounds[i - worldSize][worldSize + j].ground.clone();}elseif(j >= worldSize){
tmp = grounds[i - worldSize][j - worldSize].ground.clone();}else{
tmp = grounds[i - worldSize][j].ground.clone();}}elseif( j <0){
tmp = grounds[i][worldSize + j].ground.clone();}elseif(j >= worldSize){
tmp = grounds[i][j - worldSize].ground.clone();}
tmp.position.set((i -Math.trunc(worldSize/2))* squaresize * groundMultiplier,0,(j -Math.trunc(worldSize/2))* squaresize*groundMultiplier);
threeworld.scene.add(tmp);}}}}//==============================================================================//==============================================================================}