212 lines
5.7 KiB
JavaScript
212 lines
5.7 KiB
JavaScript
import * as THREE from 'three';
|
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
import { makeNoise3D } from 'open-simplex-noise';
|
|
import { makeCuboid } from 'fractal-noise';
|
|
import { cornerIndexAFromEdge, cornerIndexBFromEdge, triangulationTable } from './table';
|
|
import { mkslider, mkcheckbox, mkspacer } from './interface';
|
|
|
|
const noise3D = makeNoise3D(55);
|
|
|
|
let DIM = 24;
|
|
let xMax = DIM + 1;
|
|
let yMax = DIM + 1;
|
|
let isoLevel = 7;
|
|
let grid = [];
|
|
let mesh;
|
|
let bbox;
|
|
|
|
function to1D( x, y, z ) {
|
|
return (z * xMax * yMax) + (y * xMax) + x;
|
|
}
|
|
|
|
function to3D( idx ) {
|
|
const z = idx / (xMax * yMax);
|
|
idx -= (z * xMax * yMax);
|
|
const y = idx / xMax;
|
|
const x = idx % xMax;
|
|
return [ x, y, z ];
|
|
}
|
|
|
|
let frequency = 0.15;
|
|
let octaves = 8;
|
|
let persistence = 0.2;
|
|
let amplitude = 1;
|
|
let depthScale = 16;
|
|
|
|
function generateGrid() {
|
|
grid.length = 0;
|
|
const cube = makeCuboid(DIM + 1, DIM + 1, DIM + 1, noise3D, { frequency, octaves, persistence, amplitude })
|
|
for (let x = 0; x < DIM + 1; x++) {
|
|
for (let y = 0; y < DIM + 1; y++) {
|
|
for (let z = 0; z < DIM + 1; z++) {
|
|
grid[to1D(x, y, z)] = (cube[x][y][z] + 0.5) * depthScale;
|
|
}
|
|
}
|
|
}
|
|
|
|
// const center = new THREE.Vector3(DIM / 2, DIM / 2, DIM / 2);
|
|
// for (let x = 0; x < DIM; x++) {
|
|
// for (let y = 0; y < DIM; y++) {
|
|
// for (let z = 0; z < DIM; z++) {
|
|
// const dist = new THREE.Vector3(x, y, z).distanceTo(center);
|
|
// grid[to1D(x, y, z)] = dist;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// console.log(Math.min(...grid));
|
|
// console.log(Math.max(...grid));
|
|
}
|
|
|
|
function dim4(x, y, z) {
|
|
return new THREE.Vector4(x, y, z, grid[to1D(x, y, z)]);
|
|
}
|
|
|
|
function interpolateVerts(v1, v2) {
|
|
const t = (isoLevel - v1.w) / (v2.w - v1.w);
|
|
const v1xyz = new THREE.Vector3(v1.x, v1.y, v1.z);
|
|
const v2xyz = new THREE.Vector3(v2.x, v2.y, v2.z);
|
|
const subtract = v2xyz.clone().sub(v1xyz);
|
|
|
|
return v1xyz.add(subtract.multiplyScalar(t))
|
|
}
|
|
|
|
function marchCube() {
|
|
const geometry = new THREE.BufferGeometry();
|
|
const positions = [];
|
|
for (let x = 0; x < DIM; x += 1) {
|
|
for (let y = 0; y < DIM; y += 1) {
|
|
for (let z = 0; z < DIM; z += 1) {
|
|
let cube = [
|
|
dim4(x, y, z), dim4(x + 1, y, z), dim4(x + 1, y, z + 1), dim4(x, y, z + 1),
|
|
dim4(x, y + 1, z), dim4(x + 1, y + 1, z), dim4(x + 1, y + 1, z + 1), dim4(x, y + 1, z + 1),
|
|
];
|
|
|
|
let cubeIndex = 0;
|
|
for (let i = 0; i < 8; i++) {
|
|
if (cube[i].w < isoLevel) {
|
|
cubeIndex |= (1 << i);
|
|
}
|
|
}
|
|
|
|
for (let i = 0; triangulationTable[cubeIndex][i] != -1; i += 3) {
|
|
for (let tri = 0; tri < 3; tri++) {
|
|
const a0 = cornerIndexAFromEdge[triangulationTable[cubeIndex][i + tri]];
|
|
const b0 = cornerIndexBFromEdge[triangulationTable[cubeIndex][i + tri]];
|
|
const tri0 = interpolateVerts(cube[a0], cube[b0]);
|
|
positions.push(tri0.x, tri0.y, tri0.z);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
|
|
geometry.computeVertexNormals();
|
|
|
|
return geometry;
|
|
}
|
|
|
|
function update(setGrid) {
|
|
if (setGrid) {
|
|
generateGrid();
|
|
}
|
|
mesh.geometry.dispose();
|
|
const geom = marchCube();
|
|
mesh.geometry = geom;
|
|
}
|
|
|
|
function guitools() {
|
|
const gui = document.createElement('div');
|
|
gui.classList.add('overlay');
|
|
document.body.appendChild(gui);
|
|
|
|
const dimensions = mkslider('Dimensions', 8, 64, 8, DIM, (newval) => {
|
|
DIM = newval;
|
|
xMax = newval + 1;
|
|
yMax = newval + 1;
|
|
const vhdim = new THREE.Vector3(DIM, DIM, DIM);
|
|
bbox.max = vhdim;
|
|
update(true);
|
|
});
|
|
gui.appendChild(dimensions);
|
|
|
|
const isoset = mkslider('Iso level', 1, depthScale, 1, isoLevel, (newval) => {
|
|
isoLevel = newval;
|
|
update(false);
|
|
});
|
|
gui.appendChild(isoset);
|
|
gui.appendChild(mkspacer());
|
|
|
|
const freqset = mkslider('Frequency', 0.01, 1, 0.01, frequency, (newval) => {
|
|
frequency = newval;
|
|
update(true);
|
|
});
|
|
gui.appendChild(freqset);
|
|
|
|
const ampset = mkslider('Amplitude', 0.01, 1, 0.01, amplitude, (newval) => {
|
|
amplitude = newval;
|
|
update(true);
|
|
});
|
|
gui.appendChild(ampset);
|
|
|
|
const perset = mkslider('Persistence', 0.01, 1, 0.01, persistence, (newval) => {
|
|
persistence = newval;
|
|
update(true);
|
|
});
|
|
gui.appendChild(perset);
|
|
|
|
const octset = mkslider('Octaves', 1, 16, 1, octaves, (newval) => {
|
|
octaves = newval;
|
|
update(true);
|
|
});
|
|
gui.appendChild(octset);
|
|
gui.appendChild(mkspacer());
|
|
|
|
const wireframe = mkcheckbox('Wireframe', false, (newval) => {
|
|
mesh.material.wireframe = newval;
|
|
});
|
|
gui.appendChild(wireframe);
|
|
}
|
|
|
|
function init() {
|
|
const scene = new THREE.Scene();
|
|
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
|
|
const renderer = new THREE.WebGLRenderer();
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
renderer.setClearColor(0x00aaff);
|
|
document.body.appendChild( renderer.domElement );
|
|
|
|
const controls = new OrbitControls(camera, renderer.domElement);
|
|
camera.position.set( 48, 48, 48 );
|
|
|
|
controls.update();
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
controls.update();
|
|
renderer.render(scene, camera);
|
|
}
|
|
|
|
generateGrid();
|
|
const geom = marchCube();
|
|
mesh = new THREE.Mesh(geom, new THREE.MeshNormalMaterial());
|
|
scene.add(mesh);
|
|
|
|
const light = new THREE.DirectionalLight(0xffffff);
|
|
light.position.set(10, 10, 0);
|
|
scene.add(light);
|
|
|
|
const ambient = new THREE.AmbientLight(0x424141);
|
|
scene.add(ambient);
|
|
|
|
const vhdim = new THREE.Vector3(DIM, DIM, DIM);
|
|
bbox = new THREE.Box3(new THREE.Vector3(0, 0, 0), vhdim);
|
|
const helper = new THREE.Box3Helper(bbox, 0xfcdf00);
|
|
scene.add(helper);
|
|
|
|
guitools();
|
|
animate();
|
|
}
|
|
|
|
init();
|