Three.js - Voxel Geometry - UI
by Lazaro Contreras
HTML
<canvas id="c"></canvas>
<div id="ui">
<div class="tiles">
<input type="radio" name="voxel" id="voxel1" value="1"><label for="voxel1" style="background-position: -0% -0%"></label>
<input type="radio" name="voxel" id="voxel2" value="2"><label for="voxel2" style="background-position: -100% -0%"></label>
<input type="radio" name="voxel" id="voxel3" value="3"><label for="voxel3" style="background-position: -200% -0%"></label>
<input type="radio" name="voxel" id="voxel4" value="4"><label for="voxel4" style="background-position: -300% -0%"></label>
<input type="radio" name="voxel" id="voxel5" value="5"><label for="voxel5" style="background-position: -400% -0%"></label>
<input type="radio" name="voxel" id="voxel6" value="6"><label for="voxel6" style="background-position: -500% -0%"></label>
<input type="radio" name="voxel" id="voxel7" value="7"><label for="voxel7" style="background-position: -600% -0%"></label>
<input type="radio" name="voxel" id="voxel8" value="8"><label for="voxel8" style="background-position: -700% -0%"></label>
</div>
<div class="tiles">
<input type="radio" name="voxel" id="voxel9" value="9"><label for="voxel9" style="background-position: -800% -0%"></label>
<input type="radio" name="voxel" id="voxel10" value="10"><label for="voxel10" style="background-position: -900% -0%"></label>
<input type="radio" name="voxel" id="voxel11" value="11"><label for="voxel11" style="background-position: -1000% -0%"></label>
<input type="radio" name="voxel" id="voxel12" value="12"><label for="voxel12" style="background-position: -1100% -0%"></label>
<input type="radio" name="voxel" id="voxel13" value="13"><label for="voxel13" style="background-position: -1200% -0%"></label>
<input type="radio" name="voxel" id="voxel14" value="14"><label for="voxel14" style="background-position: -1300% -0%"></label>
<input type="radio" name="voxel" id="voxel15" value="15"><label for="voxel15"...
CSS
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
#ui {
position: absolute;
left: 10px;
top: 10px;
background: rgba(0, 0, 0, 0.8);
padding: 5px;
}
#ui input[type=radio] {
width: 0;
height: 0;
display: none;
}
#ui input[type=radio]+label {
background-image: url('https://threejsfundamentals.org/threejs/resources/images/minecraft/flourish-cc-by-nc-sa.png');
background-size: 1600% 400%;
image-rendering: pixelated;
width: 64px;
height: 64px;
display: inline-block;
}
#ui input[type=radio]:checked+label {
outline: 3px solid red;
}
@media (max-width: 600px),
(max-height: 600px) {
#ui input[type=radio]+label {
width: 32px;
height: 32px;
}
}
JavaScript
// Three.js - Voxel Geometry - UI
// from https://threejsfundamentals.org/threejs/threejs-voxel-geometry-culled-faces-ui.html
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js';
import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js';
class VoxelWorld {
constructor(options) {
this.cellSize = options.cellSize;
this.tileSize = options.tileSize;
this.tileTextureWidth = options.tileTextureWidth;
this.tileTextureHeight = options.tileTextureHeight;
const {cellSize} = this;
this.cellSliceSize = cellSize * cellSize;
this.cells = {};
}
computeVoxelOffset(x, y, z) {
const {cellSize, cellSliceSize} = this;
const voxelX = THREE.Math.euclideanModulo(x, cellSize) | 0;
const voxelY = THREE.Math.euclideanModulo(y, cellSize) | 0;
const voxelZ = THREE.Math.euclideanModulo(z, cellSize) | 0;
return voxelY * cellSliceSize +
voxelZ * cellSize +
voxelX;
}
computeCellId(x, y, z) {
const {cellSize} = this;
const cellX = Math.floor(x / cellSize);
const cellY = Math.floor(y / cellSize);
const cellZ = Math.floor(z / cellSize);
return `${cellX},${cellY},${cellZ}`;
}
addCellForVoxel(x, y, z) {
const cellId = this.computeCellId(x, y, z);
let cell = this.cells[cellId];
if (!cell) {
const {cellSize} = this;
cell = new Uint8Array(cellSize * cellSize * cellSize);
this.cells[cellId] = cell;
}
return cell;
}
getCellForVoxel(x, y, z) {
return this.cells[this.computeCellId(x, y, z)];
}
setVoxel(x, y, z, v, addCell = true) {
let cell = this.getCellForVoxel(x, y, z);
if (!cell) {
if (!addCell) {
return;
}
cell = this.addCellForVoxel(x, y, z);
}
const voxelOffset = this.computeVoxelOffset(x, y, z);
cell[voxelOffset] = v;
}
getVoxel(x, y, z) {
const cell =...