Files
three.js/examples/webgl_geometry_minecraft_ao.html
T
alteredq cf2e8c1bbf Refactored everything to use CamelCase naming for properties. Plus some smaller fixes here and there.
Went through all examples, all should work.

JSON files exported from Blender / converted from OBJ files still use underscored property names internally. I don't know, should these also be changed?
2011-02-28 14:52:04 +01:00

812 lines
20 KiB
HTML

<!DOCTYPE HTML>
<html lang="en">
<head>
<title>three.js webgl - geometry - minecraft - ao</title>
<meta charset="utf-8">
<style type="text/css">
body {
color: #61443e;
font-family:Monospace;
font-size:13px;
text-align:center;
/* background-color: #bfd1e5; */
background-color: #ffffff;
margin: 0px;
overflow: hidden;
}
a { color: #a06851; }
#info {
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
#oldie {
background:rgb(100,0,0) !important;
color:#fff !important;
margin-top:10em !important;
}
#oldie a { color:#fff }
button { border:0; background:rgba(0,0,0,0.5); color:orange; cursor:pointer }
button:hover { color:gold }
</style>
</head>
<body>
<div id="container"><br /><br /><br /><br /><br />Generating world...</div>
<div id="info">
<a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> - <a href="http://www.minecraft.net/" target="_blank">minecraft</a> demo [ambient occlusion]. featuring <a href="http://painterlypack.net/" target="_blank">painterly pack</a><br />(left click: forward, right click: backward)
<br/><br/>
<button id="bt">texture</button>
<button id="bao">ao</button>
<button id="baot">texture + ao</button>
</div>
<script type="text/javascript" src="../build/Three.js"></script>
<script type="text/javascript" src="js/ImprovedNoise.js"></script>
<script type="text/javascript" src="js/Detector.js"></script>
<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
<script type="text/javascript" src="js/Stats.js"></script>
<script type="text/javascript">
if ( ! Detector.webgl ) {
Detector.addGetWebGLMessage();
document.getElementById( 'container' ).innerHTML = "";
}
var fogExp2 = true;
var container, stats;
var camera, scene, renderer;
var mesh;
var mat;
var worldWidth = 128, worldDepth = 128,
worldHalfWidth = worldWidth / 2, worldHalfDepth = worldDepth / 2,
data = generateHeight( worldWidth, worldDepth );
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.QuakeCamera( { fov: 60, aspect: window.innerWidth / window.innerHeight, near: 1, far: 20000,
movementSpeed: 15, lookSpeed: 0.005, noFly: false, lookVertical: true } );
camera.target.position.z = - 100;
camera.position.y = getY( worldHalfWidth, worldHalfDepth ) * 100 + 100;
camera.target.position.y = camera.position.y;
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xffffff, 0.00025 );
var debug_texture = false,
debug_numbers = false,
debug_corner_colors = false,
strength = 2,
textures = { side: 'textures/minecraft/grass_dirt.png',
top: 'textures/minecraft/grass.png',
bottom: 'textures/minecraft/dirt.png'
},
m_aot = generateMegamaterialAO( textures, strength, debug_texture, debug_numbers, debug_corner_colors ),
m_ao = generateMegamaterialAO( textures, strength, true, debug_numbers, debug_corner_colors ),
m_t = generateMegamaterialPlain( textures ),
//m_d = generateMegamaterialDebug(),
mat = generateMegamaterialAO( textures, strength, debug_texture, debug_numbers, debug_corner_colors ),
materials = [ mat, mat, mat, mat, mat, mat ];
var i, j, x, z, h, h2, uv,
px, nx, pz, nz, sides,
right, left, bottom, top,
nright, nleft, nback, nfront,
nleftup, nrightup, nbackup, nfrontup,
nrb, nrf, nlb, nlf,
nrbup, nrfup,
face_px, face_nx, face_py, face_ny, face_pz, face_nz,
ti, ri, li, bi, fi, ci, mi, mm, column, row,
cube,
unit = 1/16 * 0.95, padding = 1/16 * 0.025, p, s, t, hash, N = -1,
// map of UV indices for faces of partially defined cubes
uv_index_map = {
0: { px: N, nx: N, py: 0, ny: N, pz: N, nz: N },
1: { px: N, nx: N, py: 0, ny: N, pz: N, nz: 1 },
2: { px: N, nx: N, py: 0, ny: N, pz: 1, nz: N },
3: { px: N, nx: N, py: 0, ny: N, pz: 1, nz: 2 },
4: { px: N, nx: 0, py: 1, ny: N, pz: N, nz: N },
5: { px: N, nx: 0, py: 1, ny: N, pz: N, nz: 2 },
6: { px: N, nx: 0, py: 1, ny: N, pz: 2, nz: N },
7: { px: N, nx: 0, py: 1, ny: N, pz: 2, nz: 3 },
8: { px: 0, nx: N, py: 1, ny: N, pz: N, nz: N },
9: { px: 0, nx: N, py: 1, ny: N, pz: N, nz: 2 },
10: { px: 0, nx: N, py: 1, ny: N, pz: 2, nz: N },
11: { px: 0, nx: N, py: 1, ny: N, pz: 2, nz: 3 },
12: { px: 0, nx: 1, py: 2, ny: N, pz: N, nz: N },
13: { px: 0, nx: 1, py: 2, ny: N, pz: N, nz: 3 },
14: { px: 0, nx: 1, py: 2, ny: N, pz: 3, nz: N },
15: { px: 0, nx: 1, py: 2, ny: N, pz: 3, nz: 4 }
},
// all possible combinations of corners and sides
// mapped to mixed tiles
// (including corners overlapping sides)
// (excluding corner alone and sides alone)
// looks ugly, but allows to squeeze all
// combinations for one texture into just 3 rows
// instead of 16
mixmap = {
"1_1": 0,
"1_3": 0,
"1_9": 0,
"1_11": 0,
"1_4": 1,
"1_6": 1,
"1_12": 1,
"1_14": 1,
"2_2": 2,
"2_3": 2,
"2_6": 2,
"2_7": 2,
"2_8": 3,
"2_9": 3,
"2_12": 3,
"2_13": 3,
"4_1": 4,
"4_5": 4,
"4_9": 4,
"4_13": 4,
"4_2": 5,
"4_6": 5,
"4_10": 5,
"4_14": 5,
"8_4": 6,
"8_5": 6,
"8_6": 6,
"8_7": 6,
"8_8": 7,
"8_9": 7,
"8_10": 7,
"8_11": 7,
"1_5": 8,
"1_7": 8,
"1_13": 8,
"1_15": 8,
"2_10": 9,
"2_11": 9,
"2_14": 9,
"2_15": 9,
"4_3": 10,
"4_7": 10,
"4_11": 10,
"4_15": 10,
"8_12": 11,
"8_13": 11,
"8_14": 11,
"8_15": 11,
"5_1": 12,
"5_3": 12,
"5_7": 12,
"5_9": 12,
"5_11": 12,
"5_13": 12,
"5_15": 12,
"6_2": 13,
"6_3": 13,
"6_6": 13,
"6_7": 13,
"6_10": 13,
"6_11": 13,
"6_14": 13,
"6_15": 13,
"9_4": 14,
"9_5": 14,
"9_6": 14,
"9_7": 14,
"9_12": 14,
"9_13": 14,
"9_14": 14,
"9_15": 14,
"10_8": 15,
"10_9": 15,
"10_10": 15,
"10_11": 15,
"10_12": 15,
"10_13": 15,
"10_14": 15,
"10_15": 15
},
tilemap = {},
top_row_corners = 0,
top_row_mixed = 1,
top_row_sides = 2,
sides_row = 3,
bottom_row = 4,
geometry = new THREE.Geometry();
// mapping from 256 possible corners + sides combinations
// into 3 x 16 tiles
for ( i = 0; i < 16; i++ ) {
for ( j = 0; j < 16; j++ ) {
mm = i + "_" + j;
if ( i == 0 )
row = top_row_corners;
else if ( mixmap[ mm ] != undefined )
row = top_row_mixed;
else
row = top_row_sides;
tilemap[ mm ] = row;
}
}
function setUVTile( face, s, t ) {
var j, uv = cube.uvs[ face ];
for ( j = 0; j < uv.length; j++ ) {
uv[ j ].u += s * (unit+2*padding);
uv[ j ].v += t * (unit+2*padding);
}
}
for ( z = 0; z < worldDepth; z ++ ) {
for ( x = 0; x < worldWidth; x ++ ) {
h = getY( x, z );
// direct neighbors
h2 = getY( x - 1, z );
nleft = h2 == h || h2 == h + 1;
h2 = getY( x + 1, z );
nright = h2 == h || h2 == h + 1;
h2 = getY( x, z + 1 );
nback = h2 == h || h2 == h + 1;
h2 = getY( x, z - 1 );
nfront = h2 == h || h2 == h + 1;
// corner neighbors
nrb = getY( x - 1, z + 1 ) == h && x > 0 && z < worldDepth - 1 ? 1 : 0;
nrf = getY( x - 1, z - 1 ) == h && x > 0 && z > 0 ? 1 : 0;
nlb = getY( x + 1, z + 1 ) == h && x < worldWidth - 1 && z < worldDepth - 1 ? 1 : 0;
nlf = getY( x + 1, z - 1 ) == h && x < worldWidth - 1 && z > 0 ? 1 : 0;
// up neighbors
nleftup = getY( x - 1, z ) > h && x > 0 ? 1 : 0;
nrightup = getY( x + 1, z ) > h && x < worldWidth - 1 ? 1 : 0;
nbackup = getY( x, z + 1 ) > h && z < worldDepth - 1 ? 1 : 0;
nfrontup = getY( x, z - 1 ) > h && z > 0 ? 1 : 0;
// up corner neighbors
nrbup = getY( x - 1, z + 1 ) > h && x > 0 && z < worldDepth - 1 ? 1 : 0;
nrfup = getY( x - 1, z - 1 ) > h && x > 0 && z > 0 ? 1 : 0;
nlbup = getY( x + 1, z + 1 ) > h && x < worldWidth - 1 && z < worldDepth - 1 ? 1 : 0;
nlfup = getY( x + 1, z - 1 ) > h && x < worldWidth - 1 && z > 0 ? 1 : 0;
// textures
ti = nleftup * 8 + nrightup * 4 + nfrontup * 2 + nbackup * 1;
ri = nrf * 8 + nrb * 4 + 1;
li = nlb * 8 + nlf * 4 + 1;
bi = nrb * 8 + nlb * 4 + 1;
fi = nlf * 8 + nrf * 4 + 1;
ci = nlbup * 8 + nlfup * 4 + nrbup * 2 + nrfup * 1;
// cube sides
px = nx = pz = nz = 0;
px = !nleft || x == 0 ? 1 : 0;
nx = !nright || x == worldWidth - 1 ? 1 : 0;
pz = !nback || z == worldDepth - 1 ? 1 : 0;
nz = !nfront || z == 0 ? 1 : 0;
sides = { px: px, nx: nx, py: true, ny: false, pz: pz, nz: nz };
cube = new Cube( 100, 100, 100, 1, 1, materials, false, sides );
// set UV tiles
for ( i = 0; i < cube.uvs.length; i++ ) {
uv = cube.uvs[ i ];
for ( j = 0; j < uv.length; j++ ) {
p = uv[j].u == 0 ? padding : -padding;
uv[j].u = uv[j].u * unit + p;
p = uv[j].v == 0 ? padding : -padding;
uv[j].v = uv[j].v * unit + p;
}
}
hash = px * 8 + nx * 4 + pz * 2 + nz;
face_px = uv_index_map[ hash ].px;
face_nx = uv_index_map[ hash ].nx;
face_py = uv_index_map[ hash ].py;
face_ny = uv_index_map[ hash ].ny;
face_pz = uv_index_map[ hash ].pz;
face_nz = uv_index_map[ hash ].nz;
if( face_px != N ) setUVTile( face_px, ri, sides_row );
if( face_nx != N ) setUVTile( face_nx, li, sides_row );
if( face_py != N ) {
mm = ti + "_" + ci;
switch ( tilemap[ mm ] ) {
case top_row_sides: column = ti; break;
case top_row_corners: column = ci; break;
case top_row_mixed: column = mixmap[ mm ]; break;
}
setUVTile( face_py, column, tilemap[ mm ] );
}
if( face_ny != N ) setUVTile( face_ny, 0, bottom_row );
if( face_pz != N ) setUVTile( face_pz, bi, sides_row );
if( face_nz != N ) setUVTile( face_nz, fi, sides_row );
mesh = new THREE.Mesh( cube );
mesh.position.x = x * 100 - worldHalfWidth * 100;
mesh.position.y = h * 100;
mesh.position.z = z * 100 - worldHalfDepth * 100;
GeometryUtils.merge( geometry, mesh );
}
}
geometry.sortFacesByMaterial();
mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial() );
scene.addObject( mesh );
var ambientLight = new THREE.AmbientLight( 0xcccccc );
scene.addLight( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.5 );
directionalLight.position.x = 1;
directionalLight.position.y = 1;
directionalLight.position.z = 0.5;
directionalLight.position.normalize();
scene.addLight( directionalLight );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.innerHTML = "";
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
document.getElementById( "bao" ).addEventListener( "click", function() { mat.map = m_ao.map; }, false );
document.getElementById( "baot" ).addEventListener( "click", function() { mat.map = m_aot.map; }, false );
document.getElementById( "bt" ).addEventListener( "click", function() { mat.map = m_t.map; }, false );
}
function generateMegamaterialAO( textures, strength, debug_texture, debug_numbers, debug_corner_colors ) {
var count = 0,
tex_side = loadTexture( textures.side, function() { count++; generateTexture() } ),
tex_top = loadTexture( textures.top, function() { count++; generateTexture() } ),
tex_bottom = loadTexture( textures.bottom, function() { count++; generateTexture() } ),
canvas = document.createElement( 'canvas' ),
ctx = canvas.getContext( '2d' ),
size = 256, tile = 16;
canvas.width = canvas.height = size;
var texture = new THREE.Texture( canvas, new THREE.UVMapping(), THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping, THREE.NearestFilter, THREE.LinearMipMapLinearFilter );
function generateTexture() {
if( count == 3 ) {
for( var i = 0; i < 16; i++ ) {
drawAOCorners( ctx, tex_top, 0, i, i, tile, strength, debug_texture, debug_numbers, debug_corner_colors );
drawAOMixed ( ctx, tex_top, 1, i, i, tile, strength, debug_texture, debug_numbers, debug_corner_colors );
drawAOSides ( ctx, tex_top, 2, i, i, tile, strength, debug_texture, debug_numbers );
drawAOSides ( ctx, tex_side, 3, i, i, tile, strength, debug_texture, debug_numbers );
drawAOSides ( ctx, tex_bottom, 4, i, i, tile, strength, debug_texture, debug_numbers );
}
texture.needsUpdate = true;
}
}
return new THREE.MeshLambertMaterial( { map: texture } );
}
function generateMegamaterialPlain( textures ) {
var count = 0,
tex_side = loadTexture( textures.side, function() { count++; generateTexture() } ),
tex_top = loadTexture( textures.top, function() { count++; generateTexture() } ),
tex_bottom = loadTexture( textures.bottom, function() { count++; generateTexture() } ),
canvas = document.createElement( 'canvas' ),
ctx = canvas.getContext( '2d' ),
size = 256, tile = 16;
canvas.width = canvas.height = size;
var texture = new THREE.Texture( canvas, new THREE.UVMapping(), THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping, THREE.NearestFilter, THREE.LinearMipMapLinearFilter );
function generateTexture() {
if( count == 3 ) {
var i, sx;
for( i = 0; i < 16; i++ ) {
sx = i * tile;
drawBase( ctx, tex_top, sx, 0 * tile, tile, false );
drawBase( ctx, tex_top, sx, 1 * tile, tile, false );
drawBase( ctx, tex_top, sx, 2 * tile, tile, false );
drawBase( ctx, tex_side, sx, 3 * tile, tile, false );
drawBase( ctx, tex_bottom, sx, 4 * tile, tile, false );
}
texture.needsUpdate = true;
}
}
return new THREE.MeshLambertMaterial( { map: texture } );
}
function generateMegamaterialDebug() {
var canvas = document.createElement( 'canvas' ),
ctx = canvas.getContext( "2d" ),
size = 256, tile = 16,
i, j, h, s;
canvas.width = size;
canvas.height = size;
ctx.textBaseline = "top";
ctx.font = "8pt arial";
for ( i = 0; i < tile; i++ ) {
for ( j = 0; j < tile; j++ ) {
h = i * tile + j;
ctx.fillStyle = "hsl(" + h + ",90%, 50%)";
ctx.fillRect( i * tile, j * tile, tile, tile );
drawHex( ctx, h, i * tile + 2, j * tile + 2 );
}
}
var texture = new THREE.Texture( canvas, new THREE.UVMapping(), THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping, THREE.NearestFilter, THREE.LinearMipMapLinearFilter );
texture.needsUpdate = true;
return new THREE.MeshLambertMaterial( { map: texture } );
}
function drawHex( ctx, n, x, y ) {
ctx.fillStyle = "black";
ctx.font = "8pt arial";
ctx.textBaseline = "top";
var s = n.toString( 16 );
s = n < 16 ? "0" + s : s;
ctx.fillText( s, x, y );
}
function drawBase( ctx, image, sx, sy, tile, debug_texture ) {
if ( debug_texture ) {
ctx.fillStyle = "#888";
ctx.fillRect( sx, sy, tile, tile );
} else {
ctx.drawImage( image, sx, sy, tile, tile );
}
}
function drawCorner( ctx, sx, sy, sa, ea, color, step, n ) {
for( var i = 0; i < n; i++ ) {
ctx.strokeStyle = color + step * ( n - i ) + ")";
ctx.beginPath();
ctx.arc( sx, sy, i, sa, ea, 0 ) ;
ctx.stroke();
}
}
function drawSide( ctx, sx, sy, a, b, n, width, height, color, step ) {
for( var i = 0; i < n; i++ ) {
ctx.fillStyle = color + step * ( n - i ) + ")";
ctx.fillRect( sx + a * i, sy + b * i, width, height );
}
}
function drawAOSides( ctx, image, row, column, sides, tile, strength, debug_texture, debug_numbers ) {
var sx = column * tile, sy = row * tile;
drawBase( ctx, image, sx, sy, tile, debug_texture );
drawAOSidesImp( ctx, image, row, column, sides, tile, strength );
if ( debug_numbers ) drawHex( ctx, row * tile + sides, sx + 2, sy + 2 );
}
function drawAOCorners( ctx, image, row, column, corners, tile, strength, debug_texture, debug_numbers, debug_corner_colors ) {
var sx = column * tile, sy = row * tile;
drawBase( ctx, image, sx, sy, tile, debug_texture );
drawAOCornersImp( ctx, image, row, column, corners, tile, strength, debug_corner_colors );
if ( debug_numbers ) drawHex( ctx, row * tile + corners, sx + 2, sy + 2 );
}
function drawAOMixed( ctx, image, row, column, elements, tile, strength, debug_texture, debug_numbers, debug_corner_colors ) {
var sx = column * tile, sy = row * tile,
mmap = {
0: [ 1, 1 ],
1: [ 1, 4 ],
2: [ 2, 2 ],
3: [ 2, 8 ],
4: [ 4, 1 ],
5: [ 4, 2 ],
6: [ 8, 4 ],
7: [ 8, 8 ],
8: [ 1, 5 ],
9: [ 2, 10 ],
10: [ 4, 3 ],
11: [ 8, 12 ],
12: [ 5, 1 ],
13: [ 6, 2 ],
14: [ 9, 4 ],
15: [ 10, 8 ]
};
drawBase( ctx, image, sx, sy, tile, debug_texture );
drawAOCornersImp( ctx, image, row, column, mmap[ elements ][1], tile, strength, debug_corner_colors );
drawAOSidesImp( ctx, image, row, column, mmap[ elements ][0], tile, strength );
if ( debug_numbers ) drawHex( ctx, row * tile + elements, sx + 2, sy + 2 );
}
function drawAOSidesImp( ctx, image, row, column, sides, tile, strength ) {
var sx = column * tile, sy = row * tile,
full = tile, step = 1 / full, half = full / 2 + strength,
color = "rgba(0, 0, 0, ",
left = (sides & 8) == 8,
right = (sides & 4) == 4,
bottom = (sides & 2) == 2,
top = (sides & 1) == 1;
if ( bottom ) drawSide( ctx, sx, sy, 0, 1, half, tile, 1, color, step );
if ( top ) drawSide( ctx, sx, sy + full - 1, 0, -1, half, tile, 1, color, step );
if ( left ) drawSide( ctx, sx, sy, 1, 0, half, 1, tile, color, step );
if ( right ) drawSide( ctx, sx + full - 1, sy, -1, 0, half, 1, tile, color, step );
}
function drawAOCornersImp( ctx, image, row, column, corners, tile, strength, debug_corner_colors ) {
var sx = column * tile, sy = row * tile,
full = tile, step = 1 / full, half = full / 2 + strength,
color = "rgba(0, 0, 0, ",
bottomright = (corners & 8) == 8,
topright = (corners & 4) == 4,
bottomleft = (corners & 2) == 2,
topleft = (corners & 1) == 1;
if ( topleft ) {
if ( debug_corner_colors ) color = "rgba(200, 0, 0, ";
drawCorner( ctx, sx, sy, 0, 1.57, color, step, half );
}
if ( bottomleft ) {
if ( debug_corner_colors ) color = "rgba(0, 200, 0, ";
drawCorner( ctx, sx, sy + full, 4.71, 6.28, color, step, half );
}
if ( bottomright ) {
if ( debug_corner_colors ) color = "rgba(0, 0, 200, ";
drawCorner( ctx, sx + full, sy + full, 3.14, 4.71, color, step, half );
}
if ( topright ) {
if ( debug_corner_colors ) color = "rgba(200, 0, 200, ";
drawCorner( ctx, sx + full, sy, 1.57, 3.14, color, step, half );
}
}
function loadTexture( path, callback ) {
var image = new Image();
image.onload = function () { callback(); };
image.src = path;
return image;
}
function generateHeight( width, height ) {
var data = [], perlin = new ImprovedNoise(),
size = width * height, quality = 2, z = Math.random() * 100;
for ( var j = 0; j < 4; j ++ ) {
if ( j == 0 ) for ( var i = 0; i < size; i ++ ) data[ i ] = 0;
for ( var i = 0; i < size; i ++ ) {
var x = i % width, y = ~~ ( i / width );
data[ i ] += perlin.noise( x / quality, y / quality, z ) * quality;
}
quality *= 4
}
return data;
}
function getY( x, z ) {
return ~~( data[ x + z * worldWidth ] * 0.2 );
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
renderer.render( scene, camera );
}
</script>
</body>
</html>