forked from totalorder/citygen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
threex.proceduralcity.js
115 lines (105 loc) · 4.47 KB
/
threex.proceduralcity.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// from @mrdoob http://www.mrdoob.com/lab/javascript/webgl/city/01/
var THREEx = THREEx || {}
THREEx.ProceduralCity = function(){
// build the base geometry for each building
var geometry = new THREE.CubeGeometry( 1, 1, 1 );
// translate the geometry to place the pivot point at the bottom instead of the center
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );
// get rid of the bottom face - it is never seen
geometry.faces.splice( 3, 1 );
geometry.faceVertexUvs[0].splice( 3, 1 );
// change UVs for the top face
// - it is the roof so it wont use the same texture as the side of the building
// - set the UVs to the single coordinate 0,0. so the roof will be the same color
// as a floor row.
geometry.faceVertexUvs[0][2][0].set( 0, 0 );
geometry.faceVertexUvs[0][2][1].set( 0, 0 );
geometry.faceVertexUvs[0][2][2].set( 0, 0 );
geometry.faceVertexUvs[0][2][3].set( 0, 0 );
// buildMesh
var buildingMesh= new THREE.Mesh( geometry );
// base colors for vertexColors. light is for vertices at the top, shaddow is for the ones at the bottom
var light = new THREE.Color( 0xffffff )
var shadow = new THREE.Color( 0x303050 )
var lights = [];
var cityGeometry= new THREE.Geometry();
for( var i = 0; i < 20; i ++ ){
// put a random position
buildingMesh.position.x = Math.floor( Math.random() * 200 - 100 ) * 10;
buildingMesh.position.z = Math.floor( Math.random() * 200 - 100 ) * 10;
// put a random rotation
buildingMesh.rotation.y = Math.random()*Math.PI*2;
// put a random scale
buildingMesh.scale.x = Math.random() * Math.random() * Math.random() * Math.random() * 50 + 10;
buildingMesh.scale.y = (Math.random() * Math.random() * Math.random() * buildingMesh.scale.x) * 8 + 8;
buildingMesh.scale.z = buildingMesh.scale.x
if (i % 10000 === 0) {
var spotLight = new THREE.PointLight( 0xffffff, 5, 100 );
spotLight.position.set(buildingMesh.position.x, buildingMesh.position.y, buildingMesh.position.z);
lights.push(spotLight);
}
// establish the base color for the buildingMesh
var value = 1 - Math.random() * Math.random();
var baseColor = new THREE.Color().setRGB( value + Math.random() * 0.1, value, value + Math.random() * 0.1 );
// set topColor/bottom vertexColors as adjustement of baseColor
var topColor = baseColor.clone().multiply( light );
var bottomColor = baseColor.clone().multiply( shadow );
// set .vertexColors for each face
var geometry = buildingMesh.geometry;
for ( var j = 0, jl = geometry.faces.length; j < jl; j ++ ) {
if ( j === 2 ) {
// set face.vertexColors on root face
geometry.faces[ j ].vertexColors = [ baseColor, baseColor, baseColor, baseColor ];
} else {
// set face.vertexColors on sides faces
geometry.faces[ j ].vertexColors = [ topColor, bottomColor, bottomColor, topColor ];
}
}
// merge it with cityGeometry - very important for performance
THREE.GeometryUtils.merge( cityGeometry, buildingMesh );
}
// generate the texture
var texture = new THREE.Texture( generateTextureCanvas() );
texture.anisotropy = renderer.getMaxAnisotropy();
texture.needsUpdate = true;
// build the mesh
var material = new THREE.MeshLambertMaterial({
map : texture,
vertexColors : THREE.VertexColors
});
var mesh = new THREE.Mesh(cityGeometry, material );
mesh.lights = lights;
return mesh
function generateTextureCanvas(){
// build a small canvas 32x64 and paint it in white
var canvas = document.createElement( 'canvas' );
canvas.width = 32;
canvas.height = 64;
var context = canvas.getContext( '2d' );
// plain it in white
context.fillStyle = '#ffffff';
context.fillRect( 0, 0, 32, 64 );
// draw the window rows - with a small noise to simulate light variations in each room
for( var y = 2; y < 64; y += 2 ){
for( var x = 0; x < 32; x += 2 ){
var value = Math.floor( Math.random() * 255 );
context.fillStyle = 'rgb(' + [value, value, 00].join( ',' ) + ')';
context.fillRect( x, y, 2, 1 );
}
}
// build a bigger canvas and copy the small one in it
// This is a trick to upscale the texture without filtering
var canvas2 = document.createElement( 'canvas' );
canvas2.width = 512;
canvas2.height = 1024;
var context = canvas2.getContext( '2d' );
// disable smoothing
context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
// then draw the image
context.drawImage( canvas, 0, 0, canvas2.width, canvas2.height );
// return the just built canvas2
return canvas2;
}
}