Skip to content

Instantly share code, notes, and snippets.

@alecperkins
Last active August 29, 2015 14:07
Show Gist options
  • Save alecperkins/54e63051921ab501404d to your computer and use it in GitHub Desktop.
Save alecperkins/54e63051921ab501404d to your computer and use it in GitHub Desktop.
script.
// https://github.com/chandlerprall/ThreeCSG/
window.ThreeBSP = (function() {
var ThreeBSP,
EPSILON = 1e-5,
COPLANAR = 0,
FRONT = 1,
BACK = 2,
SPANNING = 3;
ThreeBSP = function( geometry ) {
// Convert THREE.Geometry to ThreeBSP
var i, _length_i,
face, vertex, faceVertexUvs, uvs,
polygon,
polygons = [],
tree;
if ( geometry instanceof THREE.Geometry ) {
this.matrix = new THREE.Matrix4;
} else if ( geometry instanceof THREE.Mesh ) {
// #todo: add hierarchy support
geometry.updateMatrix();
this.matrix = geometry.matrix.clone();
geometry = geometry.geometry;
} else if ( geometry instanceof ThreeBSP.Node ) {
this.tree = geometry;
this.matrix = new THREE.Matrix4;
return this;
} else {
throw 'ThreeBSP: Given geometry is unsupported';
}
for ( i = 0, _length_i = geometry.faces.length; i < _length_i; i++ ) {
face = geometry.faces[i];
faceVertexUvs = geometry.faceVertexUvs[0][i];
polygon = new ThreeBSP.Polygon;
if ( face instanceof THREE.Face3 ) {
vertex = geometry.vertices[ face.a ];
uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;
vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs );
vertex.applyMatrix4(this.matrix);
polygon.vertices.push( vertex );
vertex = geometry.vertices[ face.b ];
uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;
vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );
vertex.applyMatrix4(this.matrix);
polygon.vertices.push( vertex );
vertex = geometry.vertices[ face.c ];
uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;
vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );
vertex.applyMatrix4(this.matrix);
polygon.vertices.push( vertex );
} else if ( typeof THREE.Face4 ) {
vertex = geometry.vertices[ face.a ];
uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;
vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs );
vertex.applyMatrix4(this.matrix);
polygon.vertices.push( vertex );
vertex = geometry.vertices[ face.b ];
uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;
vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[1], uvs );
vertex.applyMatrix4(this.matrix);
polygon.vertices.push( vertex );
vertex = geometry.vertices[ face.c ];
uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;
vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );
vertex.applyMatrix4(this.matrix);
polygon.vertices.push( vertex );
vertex = geometry.vertices[ face.d ];
uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[3].x, faceVertexUvs[3].y ) : null;
vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[3], uvs );
vertex.applyMatrix4(this.matrix);
polygon.vertices.push( vertex );
} else {
throw 'Invalid face type at index ' + i;
}
polygon.calculateProperties();
polygons.push( polygon );
};
this.tree = new ThreeBSP.Node( polygons );
};
ThreeBSP.prototype.subtract = function( other_tree ) {
var a = this.tree.clone(),
b = other_tree.tree.clone();
a.invert();
a.clipTo( b );
b.clipTo( a );
b.invert();
b.clipTo( a );
b.invert();
a.build( b.allPolygons() );
a.invert();
a = new ThreeBSP( a );
a.matrix = this.matrix;
return a;
};
ThreeBSP.prototype.union = function( other_tree ) {
var a = this.tree.clone(),
b = other_tree.tree.clone();
a.clipTo( b );
b.clipTo( a );
b.invert();
b.clipTo( a );
b.invert();
a.build( b.allPolygons() );
a = new ThreeBSP( a );
a.matrix = this.matrix;
return a;
};
ThreeBSP.prototype.intersect = function( other_tree ) {
var a = this.tree.clone(),
b = other_tree.tree.clone();
a.invert();
b.clipTo( a );
b.invert();
a.clipTo( b );
b.clipTo( a );
a.build( b.allPolygons() );
a.invert();
a = new ThreeBSP( a );
a.matrix = this.matrix;
return a;
};
ThreeBSP.prototype.toGeometry = function() {
var i, j,
matrix = new THREE.Matrix4().getInverse( this.matrix ),
geometry = new THREE.Geometry(),
polygons = this.tree.allPolygons(),
polygon_count = polygons.length,
polygon, polygon_vertice_count,
vertice_dict = {},
vertex_idx_a, vertex_idx_b, vertex_idx_c,
vertex, face,
verticeUvs;
for ( i = 0; i < polygon_count; i++ ) {
polygon = polygons[i];
polygon_vertice_count = polygon.vertices.length;
for ( j = 2; j < polygon_vertice_count; j++ ) {
verticeUvs = [];
vertex = polygon.vertices[0];
verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
vertex.applyMatrix4(matrix);
if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
vertex_idx_a = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
} else {
geometry.vertices.push( vertex );
vertex_idx_a = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
}
vertex = polygon.vertices[j-1];
verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
vertex.applyMatrix4(matrix);
if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
vertex_idx_b = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
} else {
geometry.vertices.push( vertex );
vertex_idx_b = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
}
vertex = polygon.vertices[j];
verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
vertex.applyMatrix4(matrix);
if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
vertex_idx_c = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
} else {
geometry.vertices.push( vertex );
vertex_idx_c = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
}
face = new THREE.Face3(
vertex_idx_a,
vertex_idx_b,
vertex_idx_c,
new THREE.Vector3( polygon.normal.x, polygon.normal.y, polygon.normal.z )
);
geometry.faces.push( face );
geometry.faceVertexUvs[0].push( verticeUvs );
}
}
return geometry;
};
ThreeBSP.prototype.toMesh = function( material ) {
var geometry = this.toGeometry(),
mesh = new THREE.Mesh( geometry, material );
mesh.position.setFromMatrixPosition( this.matrix );
mesh.rotation.setFromRotationMatrix( this.matrix );
return mesh;
};
ThreeBSP.Polygon = function( vertices, normal, w ) {
if ( !( vertices instanceof Array ) ) {
vertices = [];
}
this.vertices = vertices;
if ( vertices.length > 0 ) {
this.calculateProperties();
} else {
this.normal = this.w = undefined;
}
};
ThreeBSP.Polygon.prototype.calculateProperties = function() {
var a = this.vertices[0],
b = this.vertices[1],
c = this.vertices[2];
this.normal = b.clone().subtract( a ).cross(
c.clone().subtract( a )
).normalize();
this.w = this.normal.clone().dot( a );
return this;
};
ThreeBSP.Polygon.prototype.clone = function() {
var i, vertice_count,
polygon = new ThreeBSP.Polygon;
for ( i = 0, vertice_count = this.vertices.length; i < vertice_count; i++ ) {
polygon.vertices.push( this.vertices[i].clone() );
};
polygon.calculateProperties();
return polygon;
};
ThreeBSP.Polygon.prototype.flip = function() {
var i, vertices = [];
this.normal.multiplyScalar( -1 );
this.w *= -1;
for ( i = this.vertices.length - 1; i >= 0; i-- ) {
vertices.push( this.vertices[i] );
};
this.vertices = vertices;
return this;
};
ThreeBSP.Polygon.prototype.classifyVertex = function( vertex ) {
var side_value = this.normal.dot( vertex ) - this.w;
if ( side_value < -EPSILON ) {
return BACK;
} else if ( side_value > EPSILON ) {
return FRONT;
} else {
return COPLANAR;
}
};
ThreeBSP.Polygon.prototype.classifySide = function( polygon ) {
var i, vertex, classification,
num_positive = 0,
num_negative = 0,
vertice_count = polygon.vertices.length;
for ( i = 0; i < vertice_count; i++ ) {
vertex = polygon.vertices[i];
classification = this.classifyVertex( vertex );
if ( classification === FRONT ) {
num_positive++;
} else if ( classification === BACK ) {
num_negative++;
}
}
if ( num_positive > 0 && num_negative === 0 ) {
return FRONT;
} else if ( num_positive === 0 && num_negative > 0 ) {
return BACK;
} else if ( num_positive === 0 && num_negative === 0 ) {
return COPLANAR;
} else {
return SPANNING;
}
};
ThreeBSP.Polygon.prototype.splitPolygon = function( polygon, coplanar_front, coplanar_back, front, back ) {
var classification = this.classifySide( polygon );
if ( classification === COPLANAR ) {
( this.normal.dot( polygon.normal ) > 0 ? coplanar_front : coplanar_back ).push( polygon );
} else if ( classification === FRONT ) {
front.push( polygon );
} else if ( classification === BACK ) {
back.push( polygon );
} else {
var vertice_count,
i, j, ti, tj, vi, vj,
t, v,
f = [],
b = [];
for ( i = 0, vertice_count = polygon.vertices.length; i < vertice_count; i++ ) {
j = (i + 1) % vertice_count;
vi = polygon.vertices[i];
vj = polygon.vertices[j];
ti = this.classifyVertex( vi );
tj = this.classifyVertex( vj );
if ( ti != BACK ) f.push( vi );
if ( ti != FRONT ) b.push( vi );
if ( (ti | tj) === SPANNING ) {
t = ( this.w - this.normal.dot( vi ) ) / this.normal.dot( vj.clone().subtract( vi ) );
v = vi.interpolate( vj, t );
f.push( v );
b.push( v );
}
}
if ( f.length >= 3 ) front.push( new ThreeBSP.Polygon( f ).calculateProperties() );
if ( b.length >= 3 ) back.push( new ThreeBSP.Polygon( b ).calculateProperties() );
}
};
ThreeBSP.Vertex = function( x, y, z, normal, uv ) {
this.x = x;
this.y = y;
this.z = z;
this.normal = normal || new THREE.Vector3;
this.uv = uv || new THREE.Vector2;
};
ThreeBSP.Vertex.prototype.clone = function() {
return new ThreeBSP.Vertex( this.x, this.y, this.z, this.normal.clone(), this.uv.clone() );
};
ThreeBSP.Vertex.prototype.add = function( vertex ) {
this.x += vertex.x;
this.y += vertex.y;
this.z += vertex.z;
return this;
};
ThreeBSP.Vertex.prototype.subtract = function( vertex ) {
this.x -= vertex.x;
this.y -= vertex.y;
this.z -= vertex.z;
return this;
};
ThreeBSP.Vertex.prototype.multiplyScalar = function( scalar ) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
return this;
};
ThreeBSP.Vertex.prototype.cross = function( vertex ) {
var x = this.x,
y = this.y,
z = this.z;
this.x = y * vertex.z - z * vertex.y;
this.y = z * vertex.x - x * vertex.z;
this.z = x * vertex.y - y * vertex.x;
return this;
};
ThreeBSP.Vertex.prototype.normalize = function() {
var length = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
this.x /= length;
this.y /= length;
this.z /= length;
return this;
};
ThreeBSP.Vertex.prototype.dot = function( vertex ) {
return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;
};
ThreeBSP.Vertex.prototype.lerp = function( a, t ) {
this.add(
a.clone().subtract( this ).multiplyScalar( t )
);
this.normal.add(
a.normal.clone().sub( this.normal ).multiplyScalar( t )
);
this.uv.add(
a.uv.clone().sub( this.uv ).multiplyScalar( t )
);
return this;
};
ThreeBSP.Vertex.prototype.interpolate = function( other, t ) {
return this.clone().lerp( other, t );
};
ThreeBSP.Vertex.prototype.applyMatrix4 = function ( m ) {
// input: THREE.Matrix4 affine matrix
var x = this.x, y = this.y, z = this.z;
var e = m.elements;
this.x = e[0] * x + e[4] * y + e[8] * z + e[12];
this.y = e[1] * x + e[5] * y + e[9] * z + e[13];
this.z = e[2] * x + e[6] * y + e[10] * z + e[14];
return this;
}
ThreeBSP.Node = function( polygons ) {
var i, polygon_count,
front = [],
back = [];
this.polygons = [];
this.front = this.back = undefined;
if ( !(polygons instanceof Array) || polygons.length === 0 ) return;
this.divider = polygons[0].clone();
for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
this.divider.splitPolygon( polygons[i], this.polygons, this.polygons, front, back );
}
if ( front.length > 0 ) {
this.front = new ThreeBSP.Node( front );
}
if ( back.length > 0 ) {
this.back = new ThreeBSP.Node( back );
}
};
ThreeBSP.Node.isConvex = function( polygons ) {
var i, j;
for ( i = 0; i < polygons.length; i++ ) {
for ( j = 0; j < polygons.length; j++ ) {
if ( i !== j && polygons[i].classifySide( polygons[j] ) !== BACK ) {
return false;
}
}
}
return true;
};
ThreeBSP.Node.prototype.build = function( polygons ) {
var i, polygon_count,
front = [],
back = [];
if ( !this.divider ) {
this.divider = polygons[0].clone();
}
for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
this.divider.splitPolygon( polygons[i], this.polygons, this.polygons, front, back );
}
if ( front.length > 0 ) {
if ( !this.front ) this.front = new ThreeBSP.Node();
this.front.build( front );
}
if ( back.length > 0 ) {
if ( !this.back ) this.back = new ThreeBSP.Node();
this.back.build( back );
}
};
ThreeBSP.Node.prototype.allPolygons = function() {
var polygons = this.polygons.slice();
if ( this.front ) polygons = polygons.concat( this.front.allPolygons() );
if ( this.back ) polygons = polygons.concat( this.back.allPolygons() );
return polygons;
};
ThreeBSP.Node.prototype.clone = function() {
var node = new ThreeBSP.Node();
node.divider = this.divider.clone();
node.polygons = this.polygons.map( function( polygon ) { return polygon.clone(); } );
node.front = this.front && this.front.clone();
node.back = this.back && this.back.clone();
return node;
};
ThreeBSP.Node.prototype.invert = function() {
var i, polygon_count, temp;
for ( i = 0, polygon_count = this.polygons.length; i < polygon_count; i++ ) {
this.polygons[i].flip();
}
this.divider.flip();
if ( this.front ) this.front.invert();
if ( this.back ) this.back.invert();
temp = this.front;
this.front = this.back;
this.back = temp;
return this;
};
ThreeBSP.Node.prototype.clipPolygons = function( polygons ) {
var i, polygon_count,
front, back;
if ( !this.divider ) return polygons.slice();
front = [], back = [];
for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
this.divider.splitPolygon( polygons[i], front, back, front, back );
}
if ( this.front ) front = this.front.clipPolygons( front );
if ( this.back ) back = this.back.clipPolygons( back );
else back = [];
return front.concat( back );
};
ThreeBSP.Node.prototype.clipTo = function( node ) {
this.polygons = node.clipPolygons( this.polygons );
if ( this.front ) this.front.clipTo( node );
if ( this.back ) this.back.clipTo( node );
};
return ThreeBSP;
})();
DEBUG = false
{
Shape
DirectionalLight
AmbientLight
MeshLambertMaterial
MeshPhongMaterial
Mesh
BoxGeometry
mergeGeometry
CylinderGeometry
SphereGeometry
WebGLRenderer
Scene
PerspectiveCamera
} = THREE
scene = new Scene()
camera = new PerspectiveCamera(
70
window.innerWidth / window.innerHeight
0.1
1000
)
if DEBUG
camera.position.set(400,200,400)
camera.lookAt(x:0,y:0,z:0)
else
camera.position.z = 600
scene.add(camera)
renderer = new WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setClearColor(0xD8E6F7,1)
document.body.appendChild(renderer.domElement)
origin =
x: window.innerWidth / 2
y: window.innerHeight / 2
unless DEBUG
renderer.domElement.addEventListener 'mousemove', (e) ->
# Convert the screen coordinates to world
rel_x = origin.x - e.pageX
rel_y = e.pageY - origin.y
# Adjust camera position
SCALE_FACTOR = 0.05
camera.position.x = rel_x * SCALE_FACTOR
camera.position.y = rel_y * SCALE_FACTOR
requestAnimationFrame(render)
WINDOW_WIDTH = 40
WINDOW_HEIGHT = 80
WINDOW_DEPTH = 10 # Depth of window from surface of facade.
WINDOW_X_SPACING = 1.5 * WINDOW_WIDTH
WINDOW_Y_SPACING = 1 * WINDOW_WIDTH
WINDOW_X_MARGIN = WINDOW_WIDTH
WINDOW_Y_MARGIN = WINDOW_WIDTH
SILL_WIDTH = WINDOW_WIDTH * 1.25
SILL_HEIGHT = 10
SILL_DEPTH = 5
generateWindow = ->
space = new Mesh(
new BoxGeometry(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_DEPTH * 2)
)
sill = new Mesh(
new BoxGeometry(SILL_WIDTH, SILL_HEIGHT, SILL_DEPTH)
)
sill.position.y -= WINDOW_HEIGHT / 2 + SILL_HEIGHT / 2
sill.position.z += SILL_DEPTH
# Custom set position to keep the window components together.
_setPosition = (x,y,z) ->
space.position.x = x
space.position.y = y
space.position.z = z
sill.position.x = x
sill.position.y = y - (WINDOW_HEIGHT / 2)
sill.position.z = z + SILL_DEPTH / 2
return {
# The space is used to subtract the gap for the window from the building.
space: space
frame: null
sill: sill
setPosition: _setPosition
}
generateBuilding = (width_n=2) ->
num_window_rows = 3
# Dimensions of the base. The width is based on the window width and spacing.
height = 400
depth = 180
width = width_n * WINDOW_WIDTH + 2 * WINDOW_X_MARGIN + (width_n - 1) * WINDOW_X_SPACING
roof_height = 80
win_origin_x = width / 2
win_origin_y = height / 2
# Generate the windows for the given unit width.
window_bsps = []
sill_bsps = []
# window_meshes = []
[0...width_n].forEach (col) ->
[0...num_window_rows].forEach (row) ->
_y = win_origin_y - WINDOW_Y_MARGIN - (WINDOW_HEIGHT / 2) * (row + 1) - (WINDOW_Y_SPACING + (WINDOW_HEIGHT / 2)) * row
_x = win_origin_x - WINDOW_X_MARGIN - (WINDOW_WIDTH / 2) * (col + 1) - (WINDOW_X_SPACING + (WINDOW_WIDTH / 2)) * col
_z = depth / 2
_window = generateWindow()
_window.setPosition(_x, _y, _z)
# window_meshes.push(_window.)
window_bsps.push(new ThreeBSP(_window.space))
sill_bsps.push(new ThreeBSP(_window.sill))
base = new BoxGeometry(width, height, depth)
base_materal = new MeshLambertMaterial(color: 0xFFFFFF)
base_mesh = new Mesh(base)
base_bsp = new ThreeBSP(base_mesh)
window_bsps.forEach (w) -> base_bsp = base_bsp.subtract(w)
sill_bsps.forEach (s) -> base_bsp = base_bsp.union(s)
if width_n > 2
chimney_mesh = new Mesh(new BoxGeometry(10, 100, 20))
chimney_mesh.position.x = -1 * (width / 2) + 5 + 0.1
chimney_mesh.position.y = height / 2 + 50
chimney_mesh.position.z = depth / 5
console.log 'chimney_mesh', chimney_mesh
chimney_bsp = new ThreeBSP(chimney_mesh)
base_bsp = base_bsp.union(chimney_bsp)
if Math.random() > 0.5
chimney_mesh = new Mesh(new BoxGeometry(10, 100, 20))
chimney_mesh.position.x = (width / 2) - 5 - 0.1
chimney_mesh.position.y = height / 2 + 50
chimney_mesh.position.z = depth / 5
chimney_bsp = new ThreeBSP(chimney_mesh)
base_bsp = base_bsp.union(chimney_bsp)
base_mesh = base_bsp.toMesh(base_materal)
base_mesh.receiveShadow = true
roof_section = new Shape()
roof_section.moveTo(depth / -2, height / 2)
roof_section.lineTo(0, height / 2 + roof_height)
roof_section.lineTo(depth / 2, height / 2)
roof_section.lineTo(depth / -2, height / 2)
roof = roof_section.extrude
curveSegments: 1
steps: 1
amount: width
bevelEnabled: false
roof_material = new MeshLambertMaterial(color: 0x7C4F55)
roof_mesh = new Mesh(roof, roof_material)
roof_mesh.receiveShadow = true
roof_mesh.rotation.y = 0.5 * Math.PI
roof_mesh.position.x = width / -2
base_mesh.add(roof_mesh)
building =
width: width
mesh: base_mesh
adjustPosition: (_x, _y, _z) ->
base_mesh.position.x += _x
base_mesh.position.y += _y
base_mesh.position.z += _z
addToScene: (_scene) ->
_scene.add(base_mesh)
return building
total_width = 0
buildings = [0...6].map (i) ->
b = generateBuilding(Math.floor(Math.random() * 4) + 2)
total_width += b.width
return b
console.log 'total_width', total_width
previous_center = 0
previous_width = 0
buildings.forEach (b, i) ->
_direction = if i % 2 is 0 then 1 else -1
console.log previous_center, previous_width / 2, b.width / 2, total_width / 2
_x = previous_center + previous_width / 2 + b.width / 2
_y = -300 + _direction * Math.floor(Math.random() * 25 + 20)
previous_width = b.width
previous_center = _x
# Apply offset after to avoid a mess with signs.
_x -= total_width / 2
b.adjustPosition(_x, _y, 100)
b.addToScene(scene)
close_building = generateBuilding(2)
close_building.adjustPosition(250,0,510)
close_building.mesh.rotation.y -= Math.PI / 3 * 1.5
close_building.addToScene(scene)
# Add the abducted/invading people
PERSON_X_SPACING = 100
PERSON_Y_SPACING = 100
PERSON_Z_SPACING = 200
person_clothing_material = new MeshLambertMaterial(color: 0x111111)
person_head_material = new MeshLambertMaterial(color: 0xd3af8e)
# shoe_material = new MeshPhongMaterial(color: 0x000000)
person_torso = new CylinderGeometry(4.5, 4.5, 24, 32)
person_shoulders = new CylinderGeometry(1, 4.5, 2, 32)
person_leg = new CylinderGeometry(1.25, 1.25, 8, 32)
person_head = new SphereGeometry(2)
person_hat = new CylinderGeometry(2, 2, 0.5, 32)
person_hat_bowl = new SphereGeometry(2)
person_hat_brim = new CylinderGeometry(3, 3, 0.1, 32)
generatePerson = ->
_person = new Mesh(
person_torso
person_clothing_material
)
_person_shoulders = new Mesh(person_shoulders, person_clothing_material)
_person_shoulders.position.y += 12 + 1
_person.add(_person_shoulders)
_person.scale.set(1,1,0.5)
_person_head = new Mesh(
person_head
person_head_material
)
_person_hat = new Mesh(person_hat, person_clothing_material)
_person_hat_bowl = new Mesh(person_hat_bowl, person_clothing_material)
_person_hat_brim = new Mesh(person_hat_brim, person_clothing_material)
_person_hat_bowl.position.y += 0.25
_person_hat_brim.position.y -= 0.25 + 0.1
_person_hat.add(_person_hat_bowl)
_person_hat.add(_person_hat_brim)
_person_hat.position.y += 1
_person_head.add(_person_hat)
_person_head.position.y += 15
_person.add(_person_head)
_person_leg_1 = new Mesh(
person_leg
person_clothing_material
)
_person_leg_1.position.y += -16
_person_leg_1.position.x += 1.3
_person.add(_person_leg_1)
_person_leg_2 = new Mesh(
person_leg
person_clothing_material
)
_person_leg_2.position.y += -16
_person_leg_2.position.x += -1.3
_person.add(_person_leg_2)
return _person
[-10..10].forEach (x) ->
[-10..10].forEach (y) ->
[0...3].forEach (z) ->
person = generatePerson()
offset = if y % 2 then 0.5 else 0
person.position.set(
(x + offset) * PERSON_X_SPACING
y * PERSON_Y_SPACING
z * PERSON_Z_SPACING
)
_dir = if Math.random() > 0.5 then 1 else -1
person.rotation.y += Math.random() * 0.2 * _dir
# Only have the one layer cast shadows, to avoid needing a more
# expensive lighting system that would correctly fade shadows.
person.castShadow = z is 1
scene.add(person)
sun = new DirectionalLight(0xffffff, 1)
sun.position.set(-220, 150, 200)
sun.castShadow = true
scene.add(sun)
sky_light = new THREE.AmbientLight( 0x222222)
sky_light.castShadow = true
scene.add( sky_light )
renderer.shadowMapEnabled = true
renderer.shadowMapSoft = true
renderer.shadowCameraNear = 3
renderer.shadowCameraFar = camera.far
renderer.shadowCameraFov = 50
renderer.shadowMapBias = 0.0039
renderer.shadowMapDarkness = 0.5
renderer.shadowMapWidth = 1024
renderer.shadowMapHeight = 1024
render = ->
renderer.render(scene, camera)
render()
{
"name": "magritte-golconda",
"proto_version": "1.5.1",
"script_libraries": [
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r68/three.min.js"
],
"style_libraries": [
],
"extra_head_markup": "<meta name='viewport' content='width=device-width'>"
}
html,
body
margin: 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment