//generates noise to use on planet Math.dist2D=function(x1,y1,x2,y2){return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))} Math.dist3D=function(x1,y1,z1,x2,y2,z2){return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2))} var Noise3d={ genOctave:function(w,l,h,rad) { //Initialize arrays var bar=[];//Keeps track of poisson disk sample radii var grad=[];//Will be filled with gradient noise var wor1=[];var wor2=[];var wor3=[];//Wil be filled with different types of Worley noise for(var xx=0;xx<w;xx++)//Creates 3d array tospecified dimensions { bar.push([]);wor1.push([]);wor2.push([]);wor3.push([]);grad.push([]); for(var yy=0;yy<l;yy++) { bar[xx].push([]);wor1[xx].push([]);wor2[xx].push([]);wor3[xx].push([]);grad[xx].push([]); for(var zz=0;zz<h;zz++) { bar[xx][yy].push(0);wor1[xx][yy].push(rad);wor2[xx][yy].push(rad);wor3[xx][yy].push(rad);grad[xx][yy].push(rad); } } } //Fills array with noise var pointLimit=l*w*h/(.5436*rad*rad*rad); var points=0;var tries=0; while(points<=pointLimit&&tries<64) { var xx=Math.floor(Math.random()*w); var yy=Math.floor(Math.random()*l); var zz=Math.floor(Math.random()*h); if(bar[xx][yy][zz]==0) { Noise3d.placeCircle(xx,yy,zz,rad,bar,grad,wor1,wor2,wor3); tries=0; points++; } else{tries++} } //Rescales gradient noise var maxVal=0;var minVal=0; for(var xx=0;xx<w;xx++){ for(var yy=0;yy<l;yy++){ for(var zz=0;zz<h;zz++){ maxVal=Math.max(maxVal,grad[xx][yy][zz]); minVal=Math.min(minVal,grad[xx][yy][zz]); }}} for(var xx=0;xx<w;xx++){ for(var yy=0;yy<l;yy++){ for(var zz=0;zz<h;zz++){ grad[xx][yy][zz]=(grad[xx][yy][zz]-minVal)/(maxVal-minVal); }}} return [grad,wor1,wor2,wor3]; }, placeCircle:function(x,y,z,rad,bar,grad,wor1,wor2,wor3) { var thetaH=2*Math.PI*Math.random(); var thetaV=Math.PI*Math.random(); var gradVec=[Math.cos(thetaH)*Math.sin(thetaV),Math.sin(thetaH)*Math.sin(thetaV),Math.cos(thetaV)] for(var xx=-rad;xx<=rad;xx++) { var rad2=Math.floor(Math.sqrt(rad*rad-xx*xx)); for(var yy=-rad2;yy<rad;yy++) { var rad3=Math.floor(Math.sqrt(rad2*rad2-yy*yy)); for(var zz=-rad3;zz<=rad3;zz++) { var chx=x+xx-bar.length*Math.floor((x+xx)/bar.length); var chy=y+yy-bar[0].length*Math.floor((y+yy)/bar[0].length) var chz=z+zz-bar[0][0].length*Math.floor((z+zz)/bar[0][0].length) var dot=xx*gradVec[0]+yy*gradVec[1]+zz*gradVec[2]; var dist=(xx*xx+yy*yy+zz*zz)/(rad*rad); if(dist<1/4){bar[chx][chy][chz]=1} //if(dist<=1/4) { grad[chx][chy][chz]+=dot/Math.exp(11*dist*dist); if(Math.sqrt(dist)<=wor1[chx][chy][chz]) { wor3[chx][chy][chz]=wor2[chx][chy][chz]; wor2[chx][chy][chz]=wor1[chx][chy][chz]; wor1[chx][chy][chz]=Math.sqrt(dist); } else if(Math.sqrt(dist)<=wor2[chx][chy][chz]) { wor3[chx][chy][chz]=wor2[chx][chy][chz]; wor2[chx][chy][chz]=Math.sqrt(dist); } else if(Math.sqrt(dist)<=wor3[chx][chy][chz]) { wor3[chx][chy][chz]=Math.sqrt(dist); } } } } } }, display(noisemap,scale) { var disp=document.createElement('canvas'); disp.width=noisemap.length; disp.height=noisemap[0].length*noisemap[0][0].length; var ctx=disp.getContext('2d'); document.body.appendChild(disp); for(var zz=0;zz<noisemap[0][0].length;zz++) { for(var xx=0;xx<noisemap.length;xx++) { for(var yy=0;yy<noisemap[0].length;yy++) { var shade=Math.floor(100*scale*noisemap[xx][yy][zz]); ctx.fillStyle=`hsl(0,0%,${shade}%)`; ctx.fillRect(xx,yy+zz*noisemap[0].length,1,1); } } } } } var cells=[];//Will hold all the vertices. Why 'cells'? I plan to use this to make cellular automata. var chunks=[];//Will be used to optimize cell interactions var tris=[];//Will hold groups of 3 vertives which form triangles. //Cells on sphere. There is code for cellular automata in here. class Cell { constructor(x,y,z) { this.x=x; this.y=y; this.z=z; this.nbors=[]; this.color=`rgb(${Math.floor(256*Math.random())}, ${Math.floor(256*Math.random())}, ${Math.floor(256*Math.random())})`; this.rep=[0,0,0];//Repulsion values for ease function //For cellular automata test this.state0=Math.floor(3*Math.random()) this.state1=0; } static icosahedron(size) {//Creates cells arranged in an icosahedron var array=[]; var cardinals=[[0,0,1], [Math.sqrt(3)*Math.cos(Math.PI/2.5)/2, Math.sqrt(3)*Math.sin(Math.PI/2.5)/2,1/2], [Math.sqrt(3)*Math.cos(2*Math.PI/2.5)/2, Math.sqrt(3)*Math.sin(2*Math.PI/2.5)/2,1/2], [Math.sqrt(3)*Math.cos(3*Math.PI/2.5)/2, Math.sqrt(3)*Math.sin(3*Math.PI/2.5)/2,1/2], [Math.sqrt(3)*Math.cos(4*Math.PI/2.5)/2, Math.sqrt(3)*Math.sin(4*Math.PI/2.5)/2,1/2], [Math.sqrt(3)*Math.cos(5*Math.PI/2.5)/2, Math.sqrt(3)*Math.sin(5*Math.PI/2.5)/2,1/2], [Math.sqrt(3)*Math.cos(1.5*Math.PI/2.5)/2,Math.sqrt(3)*Math.sin(1.5*Math.PI/2.5)/2,-1/2], [Math.sqrt(3)*Math.cos(2.5*Math.PI/2.5)/2,Math.sqrt(3)*Math.sin(2.5*Math.PI/2.5)/2,-1/2], [Math.sqrt(3)*Math.cos(3.5*Math.PI/2.5)/2,Math.sqrt(3)*Math.sin(3.5*Math.PI/2.5)/2,-1/2], [Math.sqrt(3)*Math.cos(4.5*Math.PI/2.5)/2,Math.sqrt(3)*Math.sin(4.5*Math.PI/2.5)/2,-1/2], [Math.sqrt(3)*Math.cos(5.5*Math.PI/2.5)/2,Math.sqrt(3)*Math.sin(5.5*Math.PI/2.5)/2,-1/2], [0,0,-1]]; var diamonds=[[1,0,6,2],[2,0,7,3],[3,0,8,4],[4,0,9,5],[5,0,10,1], [6,2,11,7],[7,3,11,8],[8,4,11,9],[9,5,11,10],[10,1,11,6]]; for(var aa=0;aa<10;aa++) { var point0=cardinals[diamonds[aa][0]]; var point1=cardinals[diamonds[aa][1]]; var point2=cardinals[diamonds[aa][2]]; var point3=cardinals[diamonds[aa][3]]; for(var bb=0;bb<size;bb++) { for(var cc=0;cc<size;cc++) { var coord1=(size-Math.max(bb,cc))/size; var coord2=(bb-cc)/size; var coord3=1-coord1-Math.abs(coord2); if(coord2>=0) {array.push(new Cell((coord1*point0[0]+coord2*point1[0]+coord3*point3[0]), (coord1*point0[1]+coord2*point1[1]+coord3*point3[1]), (coord1*point0[2]+coord2*point1[2]+coord3*point3[2])));} else{array.push(new Cell((coord1*point0[0]-coord2*point2[0]+coord3*point3[0]), (coord1*point0[1]-coord2*point2[1]+coord3*point3[1]), (coord1*point0[2]-coord2*point2[2]+coord3*point3[2])));} } } } array.push(new Cell(0,0,1),new Cell(0,0,-1)) //Identifies neighbors for(var ind1=0;ind1<array.length-1;ind1++) { var cell1=array[ind1]; for(var ind2=ind1+1;ind2<array.length;ind2++) { var cell2=array[ind2]; if(((cell1.x-cell2.x)*(cell1.x-cell2.x)+(cell1.y-cell2.y)*(cell1.y-cell2.y)+(cell1.z-cell2.z)*(cell1.z-cell2.z))<=(1.2*1.2/size/size)) { cell1.nbors.push(cell2); cell2.nbors.push(cell1); } } } for(var ind1=0;ind1<array.length;ind1++) { var cell1=array[ind1]; var dist=Math.sqrt(cell1.x*cell1.x+cell1.y*cell1.y+cell1.z*cell1.z); cell1.x/=dist;cell1.y/=dist;cell1.z/=dist; } for(var ind1=0;ind1<array.length;ind1++)//Arranges the vertices of the cell polygons { var cell1=array[ind1]; var vertSort=[]; //Angles for conversion var distH=Math.sqrt(cell1.x*cell1.x+cell1.y*cell1.y); var sinH=-cell1.y/distH; var cosH=cell1.x/distH; for(var ind2=0;ind2<cell1.nbors.length;ind2++) { var vertex=cell1.nbors[ind2]; var nx=-vertex.z*distH+(vertex.x*cosH-vertex.y*sinH)*cell1.z; var ny=vertex.x*sinH+vertex.y*cosH; vertSort.push([vertex,Math.atan2(ny,nx)]); } vertSort.sort(function(a,b){return(a[1]-b[1])}); cell1.nbors=[]; for(var ind2=0;ind2<vertSort.length;ind2++) { cell1.nbors.push(vertSort[ind2][0]); } cell1.updateVerts() } return array; } static distribute(chunkArr,density) { var array=[];//Will hold all cells for(var ind1=0;ind1<chunkArr.length;ind1++) {chunkArr[ind1].contents=[];//Initializes chunk arrays chunkArr[ind1].checked=false} var dist1=2/Math.sqrt(chunkArr.length)//Distance between chunk points var dist2=3/Math.sqrt(chunkArr.length*density)//Distance between cell points var arc=2*Math.asin(dist1/2);//Maximum angle from chunk of a new point for(var ind1=0;ind1<chunkArr.length;ind1++)//Cycles all chunks { //Variables relevant to the chunk var chunk1=chunkArr[ind1]; var mag=Math.sqrt(chunk1.x*chunk1.x+chunk1.y*chunk1.y);//magnitude x/y var cosH=chunk1.x/mag; var sinH=chunk1.y/mag; if(mag==0){cosH=1;sinH=0} var cosV=chunk1.z; var sinV=mag; //Places random points until it can’t var tries=0;//How many failed tries to place a point while(tries<128) { //Generates the initial coordinatesof a random point var thetaH=Math.PI*2*Math.random();//Speherical coordinates var thetaV=arc*Math.sqrt(Math.random()); var x0=Math.cos(thetaH)*Math.sin(thetaV);//Initial cartesian coordinates var y0=Math.sin(thetaH)*Math.sin(thetaV); var z0=Math.cos(thetaV); var x1=(x0*cosV+z0*sinV)*cosH-y0*sinH//Cartesian coordinates transformed by chunk var y1=(x0*cosV+z0*sinV)*sinH+y0*cosH; var z1=z0*cosV-x0*sinV; //Checks this point against all other points in the neighborhood var canAdd=true; for(var ind2=-1;ind2<chunk1.nbors.length;ind2++) {//Cycles all chunks in neighborhood var chunk2=chunk1; if (ind2>0){chunk2=chunk1.nbors[ind2]} for(var ind3=0;ind3<chunk2.contents.length;ind3++) {//Cycles all other pointsin neighbiorhood var cell1=chunk2.contents[ind3]; if(Math.dist3D(x1,y1,z1,cell1.x,cell1.y,cell1.z)<dist2) {//Checks if new point is too close to existing points. If so, ends the loops. canAdd=false; ind3=chunk2.contents.length; ind2=chunk1.nbors.length; tries++; } } } //If the point isn’t too close to any others, adds it to the cell array if(canAdd) { var cell1=new Cell(x1,y1,z1); array.push(cell1); //Puts cell into a chunk var inChunk=chunk1;//First chunk to try var checkDist1=dist1;//Distance from centrral chunk for(var ind2=0;ind2<chunk1.nbors.length;ind2++) { var chunk2=chunk1.nbors[ind2]; var checkDist2=Math.dist3D(x1,y1,z1,chunk2.x,chunk2.y,chunk2.z) if(checkDist2<checkDist1) {//If chunk2 iscloser than inChunk, updates checkDist1=checkDist2; inChunk=chunk2; } } inChunk.contents.push(cell1);//Puts cell into chunk contents } } } return array; } static automaton(array) { for(var ind1=0;ind1<array.length;ind1++) { var cell1=array[ind1]; //var total=0; var rps=[0,0,0]; for(var ind2=0;ind2<cell1.nbors.length;ind2++) { rps[cell1.nbors[ind2].state0]++ //total+=cell1.nbors[ind2].state0/cell1.nbors.length; } //if ((Math.random()*rps[cell1.state0+1-3*Math.floor((cell1.state0+1)/3)])>1){cell1.state1=cell1.state0+1-3*Math.floor((cell1.state0+1)/3)} if ((rps[cell1.state0+1-3*Math.floor((cell1.state0+1)/3)])>1){cell1.state1=cell1.state0+1-3*Math.floor((cell1.state0+1)/3)} else{cell1.state1=cell1.state0} } for(var ind1=0;ind1<array.length;ind1++) { var cell1=array[ind1]; cell1.state0=cell1.state1; cell1.color=`hsl(${120*cell1.state0},100%,50%)`; } } static setChunks(chunkArr,cellArr) { var chunkRad=3.6/(chunkArr.length-2); for(var ind1=0;ind1<chunkArr.length;ind1++) {//Clears chunk arrays chunkArr[ind1].contents=[]; chunkArr[ind1].checked=false; } for(var ind1=0;ind1<cellArr.length;ind1++) {//Adds all cells to nearest chunk var cell1=cellArr[ind1]; var dist1=8;var chunk1=null; for(var ind2=0;ind2<chunkArr.length;ind2++) { var chunk2=chunkArr[ind2]; var dist2=((cell1.x-chunk2.x)*(cell1.x-chunk2.x)+(cell1.y-chunk2.y)*(cell1.y-chunk2.y)+(cell1.z-chunk2.z)*(cell1.z-chunk2.z)); if(dist2<dist1) { dist1=dist2; chunk1=chunk2; //if(dist2<chunkRad){break} } } chunk1.contents.push(cell1); } } static triangulateChunks(chunkArr,cellArr,useNewChunks) { var maxDist=(.3*cellArr.length+58)/(cellArr.length+27); if(useNewChunks){Cell.setChunks(chunkArr,cellArr);} for(var ind1=0;ind1<chunkArr.length;ind1++) { var chunk1=chunkArr[ind1];chunk1.checked=true;//Prevents excessive checking of this chunk var checkArray1=chunk1.contents;//All cells in the neighborhood to be triangulated var checkArray2=chunk1.contents;//All cells that must be checked in cones for(var ind2=0;ind2<chunk1.nbors.length;ind2++) {//Puts whole neighborhood into one array if(chunk1.nbors[ind2].checked==false){checkArray1=checkArray1.concat(chunk1.nbors[ind2].contents);} checkArray2=checkArray2.concat(chunk1.nbors[ind2].contents); } for(var ind2=0;ind2<chunk1.contents.length;ind2++) {//First vertex to check var cell1=checkArray1[ind2]; for(var ind3=ind2+1;ind3<checkArray1.length-1;ind3++) {//Second vertex to check var cell2=checkArray1[ind3]; if (((cell1.x-cell2.x)*(cell1.x-cell2.x)+(cell1.y-cell2.y)*(cell1.y-cell2.y)+(cell1.z-cell2.z)*(cell1.z-cell2.z))<maxDist*maxDist) { for(var ind4=ind3+1;ind4<checkArray1.length;ind4++) {//Third vertex to check var cell3=checkArray1[ind4]; if (((cell1.x-cell3.x)*(cell1.x-cell3.x)+(cell1.y-cell3.y)*(cell1.y-cell3.y)+(cell1.z-cell3.z)*(cell1.z-cell3.z))<maxDist*maxDist &&((cell2.x-cell3.x)*(cell2.x-cell3.x)+(cell2.y-cell3.y)*(cell2.y-cell3.y)+(cell2.z-cell3.z)*(cell2.z-cell3.z))<maxDist*maxDist) { //Finds a vector perpendicular to the 3 vertices var vector1=[cell2.x-cell1.x,cell2.y-cell1.y,cell2.z-cell1.z]; var vector2=[cell3.x-cell1.x,cell3.y-cell1.y,cell3.z-cell1.z]; var cross=[vector1[1]*vector2[2]-vector1[2]*vector2[1] ,vector1[2]*vector2[0]-vector1[0]*vector2[2] ,vector1[0]*vector2[1]-vector1[1]*vector2[0]]; //Dot product to compare all other cells against. var dot1=cell1.x*cross[0]+cell1.y*cross[1]+cell1.z*cross[2]; var charge=dot1/Math.abs(dot1); //Checks to see if any points are within circumscribed cone var makeTri=true; for(var ind5=0;ind5<checkArray2.length;ind5++) //for(var ind5=0;ind5<cellArr.length;ind5++) { var cell4=checkArray2[ind5]; //var cell4=cellArr[ind5] if(cell4!=cell1&&cell4!=cell2&&cell4!=cell3) { if((charge*(cell4.x*cross[0]+cell4.y*cross[1]+cell4.z*cross[2]))>Math.abs(dot1)) {makeTri=false;break;} } } if(makeTri)//If not, makes a triangle { //For drawing triangles tris.push([cell1,cell2,cell3]); //For adding neighbors if(!cell1.nbors.includes(cell2)){cell1.nbors.push(cell2)} if(!cell1.nbors.includes(cell3)){cell1.nbors.push(cell3)} if(!cell2.nbors.includes(cell1)){cell2.nbors.push(cell1)} if(!cell2.nbors.includes(cell3)){cell2.nbors.push(cell3)} if(!cell3.nbors.includes(cell2)){cell3.nbors.push(cell2)} if(!cell3.nbors.includes(cell1)){cell3.nbors.push(cell1)} } } } } } } } for(var ind1=0;ind1<cellArr.length;ind1++)//Arranges the vertices of the cell polygons { var cell1=cellArr[ind1]; var vertSort=[]; //Angles for conversion var distH=Math.sqrt(cell1.x*cell1.x+cell1.y*cell1.y); var sinH=-cell1.y/distH; var cosH=cell1.x/distH; for(var ind2=0;ind2<cell1.nbors.length;ind2++) { var vertex=cell1.nbors[ind2]; var nx=-vertex.z*distH+(vertex.x*cosH-vertex.y*sinH)*cell1.z; var ny=vertex.x*sinH+vertex.y*cosH; vertSort.push([vertex,Math.atan2(ny,nx)]); } vertSort.sort(function(a,b){return(a[1]-b[1])}); cell1.nbors=[]; for(var ind2=0;ind2<vertSort.length;ind2++) { cell1.nbors.push(vertSort[ind2][0]); } cell1.updateVerts() } } updateVerts() { this.verts=[]; var nbor1=this.nbors[this.nbors.length-1] for(var ind=0;ind<this.nbors.length;ind++) { var nbor2=this.nbors[ind]; this.verts.push([(this.x+nbor1.x+nbor2.x)/3, (this.y+nbor1.y+nbor2.y)/3, (this.z+nbor1.z+nbor2.z)/3]) nbor1=nbor2; } } static drawAll(array,x,y,scale,hor,vert,ctx) { var horizon=0//-Math.sin(2*Math.asin(1/Math.sqrt(array.length))); var cosH=Math.cos(hor); var sinH=Math.sin(hor); var cosV=Math.cos(vert); var sinV=Math.sin(vert); for(var ind1=0;ind1<array.length;ind1++) { var chunk1=array[ind1]; //var chunkZIndex=scale*(-(sinH*chunk1.x+cosH*chunk1.y)*sinV+cosV*chunk1.z) if(scale*(-(sinH*chunk1.x+cosH*chunk1.y)*sinV+cosV*chunk1.z)>=horizon) { for(var ind2=0;ind2<chunk1.contents.length;ind2++) { var cell1=chunk1.contents[ind2]; if(scale*(-(sinH*cell1.x+cosH*cell1.y)*sinV+cosV*cell1.z)>=horizon) { ctx.beginPath(); for(var ind3=0;ind3<cell1.verts.length;ind3++) { var nx=scale*(cosH*cell1.verts[ind3][0]-sinH*cell1.verts[ind3][1]) var ny=scale*((sinH*cell1.verts[ind3][0]+cosH*cell1.verts[ind3][1])*cosV+sinV*cell1.verts[ind3][2]) //var nz=scale*(-(sinH*cell1.verts[ind3][0]+cosH*cell1.verts[ind3][1])*sinV+cosV*cell1.verts[ind3][2]) if(ind3==0){ctx.moveTo(x+nx,y+ny)} else{ctx.lineTo(x+nx,y+ny)} } ctx.closePath(); ctx.fillStyle=cell1.color; ctx.fill(); } } } } } static terrain(array,w,l,h) { //Creates necessary noisemaps. Each returns an array:[gradient noise, worley 1, wor2, wor3]. //The number after w,l,h is the frequency. var octave1=Noise3d.genOctave(w,l,h,32); var octave2=Noise3d.genOctave(w,l,h,16); var octave3=Noise3d.genOctave(w,l,h,8); for(var ind1=0;ind1<array.length;ind1++) { var cell1=array[ind1]; var xx=Math.floor(w*(1+cell1.x)/4); var yy=Math.floor(l*(1+cell1.y)/4); var zz=Math.floor(h*(1+cell1.z)/4); var elevation=(4*octave2[0][xx][yy][zz]+2*octave2[0][xx][yy][zz]+octave3[0][xx][yy][zz])/7; if(elevation>.8){cell1.color=`hsl(187,78%,${Math.floor(50*(.87+elevation))}%)`;} else if(elevation>.7){cell1.color=`hsl(27,7%,${Math.floor(50*(.53+elevation))}%)`;} else if(elevation>.61){cell1.color=`hsl(103,74%,${Math.floor(50*(.31+elevation))}%)`;} else if(elevation>.6){cell1.color=`hsl(42,42%,${Math.floor(50*(.69+elevation))}%)`;} else{cell1.color=`hsl(209,77%,${Math.floor(50*(.43+elevation))}%)`;} } } } var t=0; //var start=setInterval(function(){if(t<1){t++}else{clearInterval(start);setup()}},1); var vert=Math.PI/2; var hor=0; //function setup(){ //Initializes canvas var canvas=document.createElement("canvas"); document.body.appendChild(canvas); canvas.width=window.innerWidth; canvas.height=window.innerHeight; canvas.style="position:absolute;left:0px;top:0px;background-color:black;" var ctx=canvas.getContext('2d'); //Initializes and calculates vertices console.time('gen') console.time('ico'); var chunks=Cell.icosahedron(12);console.timeEnd('ico'); console.time('dist'); var cells=Cell.distribute(chunks,8);console.log(cells.length) console.timeEnd('dist'); console.time('tri'); Cell.triangulateChunks(chunks,cells,false);console.timeEnd('tri') console.time('terra'); Cell.terrain(cells,64,64,64);console.timeEnd('terra') console.timeEnd('gen'); //Cell.setChunks(chunks,cells) var int=setInterval(function(){ hor+=.05; vert+=.005; ctx.clearRect(0,0,canvas.width,canvas.height); //Cell.automaton(); Cell.drawAll(chunks,canvas.width/2,canvas.height/2,canvas.height*3/8,hor,vert,ctx); },100) document.body.onresize=function(){canvas.width=window.innerWidth;canvas.height=window.innerHeight} //}

!