body {
background-color: #f0f0f0;
color: #444;
margin: 0;
}
a {
color: #08f;
}
canvas, html, body {
height: 100% !important;
width: 100% !important;
}
let camera, scene, renderer;
let plane;
let pointer, raycaster, isShiftDown = false;
let rollOverMesh, rollOverMaterial;
let cubeGeo, cubeMaterial;
const objects = [];
const position = {
itemSize: 3,
type: 'Float32Array',
array: [
-0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
-0.5,
0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.5,
0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
0.5,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.5,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
-0.5,
0.5,
0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
0.5,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
0.5,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.5,
-0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.5,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
-0.5,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.5,
0.5,
0.46000000834465027,
0.46000000834465027,
0.5,
-0.46000000834465027,
0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
-0.46000000834465027,
0.5,
0.46000000834465027,
0.46000000834465027,
0.5,
-0.46000000834465027,
-0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
-0.46000000834465027,
-0.5,
-0.46000000834465027,
0.46000000834465027,
-0.5,
0.46000000834465027,
-0.46000000834465027,
],
normalized: false,
};
const normal = {
itemSize: 3,
type: 'Float32Array',
array: [
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
-0.5773502588272095,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.5773502588272095,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0.7071067690849304,
0,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
-0.7071067690849304,
0,
0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
-0.7071067690849304,
-0.7071067690849304,
0,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
-1,
0,
0,
],
normalized: false,
};
init();
render();
function init() {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 30, 30, 30 );
camera.lookAt( 0, 0, 0 );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf0f0f0 );
// roll-over helpers
const rollOverGeo = new THREE.BufferGeometry( 1, 1, 1 );
rollOverGeo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(position.array), 3));
rollOverGeo.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normal.array), 3));
rollOverGeo.computeVertexNormals();
rollOverGeo.computeBoundingBox();
rollOverGeo.center();
rollOverGeo.normalizeNormals();
rollOverMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.5, transparent: true } );
rollOverMesh = new THREE.Mesh( rollOverGeo, rollOverMaterial );
scene.add( rollOverMesh );
// cubes
cubeGeo = new THREE.BufferGeometry( 1, 1, 1 );
cubeGeo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(position.array), 3));
cubeGeo.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normal.array), 3));
cubeGeo.computeVertexNormals();
cubeGeo.computeBoundingBox();
cubeGeo.center();
cubeGeo.normalizeNormals();
cubeMaterial = new THREE.MeshLambertMaterial( { color: 0xfeb74c} );
// grid
const gridHelper = new THREE.GridHelper( 100, 100 );
gridHelper.position.y = -0.5;
scene.add( gridHelper );
//
raycaster = new THREE.Raycaster();
pointer = new THREE.Vector2();
const geometry = new THREE.PlaneGeometry( 100, 100 );
geometry.rotateX( - Math.PI / 2 );
plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
plane.name = 'plane';
plane.position.y = -0.5;
scene.add( plane );
objects.push( plane );
// lights
const ambientLight = new THREE.AmbientLight( 0x606060 );
scene.add( ambientLight );
const directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 1, 0.75, 0.5 ).normalize();
scene.add( directionalLight );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
orbitControls.update();
document.addEventListener( 'pointermove', onPointerMove );
document.addEventListener( 'pointerdown', onPointerDown );
document.addEventListener( 'keydown', onDocumentKeyDown );
document.addEventListener( 'keyup', onDocumentKeyUp );
//
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onPointerMove( event ) {
const rect = renderer.domElement.getBoundingClientRect();
pointer.x = ( ( event.clientX - rect.left ) / ( rect.width - rect.left ) ) * 2 - 1;
pointer.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;
raycaster.setFromCamera( pointer, camera );
const intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
const intersect = intersects[ 0 ];
const intersectNormal = intersect.face.normal.clone().floor();
if (intersect.object.name === 'plane') {
rollOverMesh.position.copy(intersect.point).floor().add(new THREE.Vector3(0.5, 1, 0.5));
} else {
if (!intersectNormal.equals(intersect.face.normal)) return;
const faceNormal = new THREE.Vector3().copy(intersect.face.normal);
faceNormal.x = faceNormal.x > 0 ? Math.ceil(faceNormal.x) : Math.floor(faceNormal.x);
faceNormal.y = faceNormal.y > 0 ? Math.ceil(faceNormal.y) : Math.floor(faceNormal.y);
faceNormal.z = faceNormal.z > 0 ? Math.ceil(faceNormal.z) : Math.floor(faceNormal.z);
const intersectPos = intersect.object.position.clone();
rollOverMesh.position.copy(intersectPos.add(faceNormal));
}
}
}
function onPointerDown( event ) {
const rect = renderer.domElement.getBoundingClientRect();
pointer.x = ( ( event.clientX - rect.left ) / ( rect.width - rect.left ) ) * 2 - 1;
pointer.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;
raycaster.setFromCamera( pointer, camera );
const intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
const intersect = intersects[ 0 ];
// delete cube
if ( isShiftDown ) {
if ( intersect.object !== plane ) {
scene.remove( intersect.object );
objects.splice( objects.indexOf( intersect.object ), 1 );
}
// create cube
} else {
const voxel = new THREE.Mesh( cubeGeo, cubeMaterial );
const intersectNormal = intersect.face.normal.clone().floor();
if (intersect.object.name === 'plane') {
voxel.position.copy(intersect.point).floor().add(new THREE.Vector3(0.5, 1, 0.5));
} else {
const faceNormal = new THREE.Vector3().copy(intersect.face.normal);
faceNormal.x = faceNormal.x > 0 ? Math.ceil(faceNormal.x) : Math.floor(faceNormal.x);
faceNormal.y = faceNormal.y > 0 ? Math.ceil(faceNormal.y) : Math.floor(faceNormal.y);
faceNormal.z = faceNormal.z > 0 ? Math.ceil(faceNormal.z) : Math.floor(faceNormal.z);
const intersectPos = intersect.object.position.clone();
voxel.position.copy(intersectPos.add(faceNormal));
}
console.log(voxel);
objects.push(voxel);
scene.add(voxel);
}
}
}
function onDocumentKeyDown( event ) {
switch ( event.keyCode ) {
case 16: isShiftDown = true; break;
}
}
function onDocumentKeyUp( event ) {
switch ( event.keyCode ) {
case 16: isShiftDown = false; break;
}
}
function render() {
requestAnimationFrame(render);
renderer.render( scene, camera );
}
This Pen doesn't use any external CSS resources.