Caves!!
This commit is contained in:
parent
d0f1749ef2
commit
19fb285d18
20
game.js
20
game.js
@ -1,6 +1,8 @@
|
||||
import { makeBufferFromFaces } from "./geometry";
|
||||
import { memoize } from "./memoize";
|
||||
import * as se3 from './se3';
|
||||
import {
|
||||
blockLookup,
|
||||
BlockType,
|
||||
castRay,
|
||||
checkCollision,
|
||||
@ -123,19 +125,6 @@ function handleKeys(params) {
|
||||
});
|
||||
}
|
||||
|
||||
function memoize(f) {
|
||||
const memo = {};
|
||||
|
||||
function g(...args) {
|
||||
if (!(args in memo)) {
|
||||
memo[args] = f(...args);
|
||||
}
|
||||
return memo[args];
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/** From far to near. */
|
||||
function _getChunkOrder(chunki, chunkj) {
|
||||
return [...function* () {
|
||||
@ -193,6 +182,10 @@ function tagABlock(gl, params, objects) {
|
||||
const ori = se3.inverse(se3.rotxyz(-camori[0], -camori[1], -camori[2]));
|
||||
const viewDirection = se3.apply(ori, dir).slice(0, 3);
|
||||
|
||||
if (blockLookup(params.world, ...params.camera.position).type !== BlockType.AIR) {
|
||||
return;
|
||||
}
|
||||
|
||||
const face = markBlock(params.world, params.camera.position, viewDirection, params.blockSelectDistance);
|
||||
if (face === undefined) {
|
||||
return;
|
||||
@ -242,6 +235,7 @@ function tagABlock(gl, params, objects) {
|
||||
// [ ] inventory
|
||||
// [ ] monsters
|
||||
// [ ] multi player
|
||||
// [ ] chat
|
||||
|
||||
export function setupParamPanel(params) {
|
||||
document.querySelector('#lightx').oninput = e => {
|
||||
|
13
geometry.js
13
geometry.js
@ -1,15 +1,4 @@
|
||||
function memoize(f) {
|
||||
const memo = {};
|
||||
|
||||
function g(...args) {
|
||||
if (!(args in memo)) {
|
||||
memo[args] = f(...args);
|
||||
}
|
||||
return memo[args];
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
import { memoize } from "./memoize";
|
||||
|
||||
function _makeTextureFace(texture) {
|
||||
const textMul = 0.0625;
|
||||
|
12
memoize.js
Normal file
12
memoize.js
Normal file
@ -0,0 +1,12 @@
|
||||
export function memoize(f) {
|
||||
const memo = {};
|
||||
|
||||
function g(...args) {
|
||||
if (!(args in memo)) {
|
||||
memo[args] = f(...args);
|
||||
}
|
||||
return memo[args];
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
89
terrain.js
89
terrain.js
@ -1,3 +1,5 @@
|
||||
import { memoize } from "./memoize";
|
||||
|
||||
const smoothstep = x => x*x*(3 - 2*x);
|
||||
function interpolate(a, b, x, f = smoothstep) {
|
||||
const val = a + f(x) * (b - a);
|
||||
@ -21,6 +23,10 @@ export function random(seed, z, x) {
|
||||
return xorshift(1337 * z + seed + 80085 * x);
|
||||
}
|
||||
|
||||
export function random3d(seed, z, x, y) {
|
||||
return xorshift(1337 * z + seed + 80085 * x + 13 * y);
|
||||
}
|
||||
|
||||
function ghettoPerlinNoise(seed, x, y, gridSize = 16) {
|
||||
const dot = (vx, vy) => vx[0] * vy[0] + vx[1] * vy[1];
|
||||
|
||||
@ -42,6 +48,39 @@ function ghettoPerlinNoise(seed, x, y, gridSize = 16) {
|
||||
return 1.5 * interpolate(interpolate(n0, n1, sx), interpolate(n2, n3, sx), sy);
|
||||
}
|
||||
|
||||
function gpn3d(seed, x, y, z, gridSize = 16) {
|
||||
const dot = (u, v) => u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
|
||||
|
||||
const randGrad = (x0, y0, z0) => {
|
||||
const rand0 = random3d(seed, x0, y0, z0);
|
||||
const rand1 = random3d(seed+1, x0, y0, z0);
|
||||
return [Math.cos(rand0), Math.sin(rand1) * Math.sin(rand0), Math.cos(rand1) * Math.sin(rand0)];
|
||||
};
|
||||
|
||||
const x0 = Math.floor(x / gridSize);
|
||||
const y0 = Math.floor(y / gridSize);
|
||||
const z0 = Math.floor(z / gridSize);
|
||||
const sx = x / gridSize - x0;
|
||||
const sy = y / gridSize - y0;
|
||||
const sz = z / gridSize - z0;
|
||||
|
||||
const n0 = dot(randGrad(x0, y0, z0), [sx, sy, sz]);
|
||||
const n1 = dot(randGrad(x0 + 1, y0, z0), [sx - 1, sy, sz]);
|
||||
const n2 = dot(randGrad(x0, y0 + 1, z0), [sx, sy - 1, sz]);
|
||||
const n3 = dot(randGrad(x0 + 1, y0 + 1, z0), [sx - 1, sy - 1, sz]);
|
||||
|
||||
const n4 = dot(randGrad(x0, y0, z0 + 1), [sx, sy, sz - 1]);
|
||||
const n5 = dot(randGrad(x0 + 1, y0, z0 + 1), [sx - 1, sy, sz - 1]);
|
||||
const n6 = dot(randGrad(x0, y0 + 1, z0 + 1), [sx, sy - 1, sz - 1]);
|
||||
const n7 = dot(randGrad(x0 + 1, y0 + 1, z0 + 1), [sx - 1, sy - 1, sz - 1]);
|
||||
|
||||
return 1.5 * interpolate(
|
||||
interpolate(interpolate(n0, n1, sx), interpolate(n2, n3, sx), sy),
|
||||
interpolate(interpolate(n4, n5, sx), interpolate(n6, n7, sx), sy),
|
||||
sz,
|
||||
);
|
||||
}
|
||||
|
||||
function cliffPerlin(seed, x, y) {
|
||||
const noise1 = ghettoPerlinNoise(seed, x, y);
|
||||
const noise2 = ghettoPerlinNoise(seed+1, x, y);
|
||||
@ -55,6 +94,56 @@ function cliffPerlin(seed, x, y) {
|
||||
return interpolate(-1, 1, 0.5 * (1 + Math.atan2(noise1, noise2) / Math.PI), softerEdge);
|
||||
}
|
||||
|
||||
function _fractalGpn3d(seed, x, y, z) {
|
||||
const lacunarity = 3.4;
|
||||
const persistence = 0.28;
|
||||
|
||||
let value = 0;
|
||||
let power = 0.6;
|
||||
let scale = 0.7;
|
||||
const noises = [gpn3d, gpn3d, gpn3d];
|
||||
|
||||
for (const noiseFun of noises) {
|
||||
const noise = noiseFun(seed, scale * x, scale * y, scale * z);
|
||||
|
||||
value += noise * power;
|
||||
|
||||
power *= persistence;
|
||||
scale *= lacunarity;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
const fractalGpn3d = memoize(_fractalGpn3d);
|
||||
|
||||
export function checkCave(seed, x, y, z) {
|
||||
const nx = Math.floor(x / 8) * 8;
|
||||
const ny = Math.floor(y / 4) * 4;
|
||||
const nz = Math.floor(z / 8) * 8;
|
||||
|
||||
const sx = (x - nx) / 8;
|
||||
const sy = (y - ny) / 4;
|
||||
const sz = (z - nz) / 8;
|
||||
|
||||
const itp = interpolate;
|
||||
const n = (x, y, z) => fractalGpn3d(seed, x / 3, y, z / 3);
|
||||
|
||||
const noise = itp(
|
||||
itp(itp(n(nx, ny, nz), n(nx + 8, ny, nz), sx),
|
||||
itp(n(nx, ny + 4, nz), n(nx + 8, ny + 4, nz), sx),
|
||||
sy
|
||||
),
|
||||
itp(itp(n(nx, ny, nz + 8), n(nx + 8, ny, nz + 8), sx),
|
||||
itp(n(nx, ny + 4, nz + 8), n(nx + 8, ny + 4, nz + 8), sx),
|
||||
sy
|
||||
),
|
||||
sz);
|
||||
|
||||
//if (n(nx, ny, nz) > 0.3) throw Error('break!');
|
||||
|
||||
return noise > 0.28 && noise < 0.55;
|
||||
}
|
||||
|
||||
export function makeTerrain(seed, x, y) {
|
||||
const lacunarity = 2.1;
|
||||
const persistence = 0.35;
|
||||
|
52
world.js
52
world.js
@ -1,7 +1,7 @@
|
||||
import { makeBufferFromFaces, makeFace } from "./geometry";
|
||||
import { loadTexture, makeProgram } from "./gl";
|
||||
import * as se3 from './se3';
|
||||
import { makeTerrain, random } from "./terrain";
|
||||
import { checkCave, makeTerrain, random } from "./terrain";
|
||||
|
||||
const VSHADER = `
|
||||
attribute vec3 aPosition;
|
||||
@ -132,6 +132,54 @@ function makeChunk(z, x) {
|
||||
makeATree(data, tree, seed, z, x);
|
||||
}
|
||||
|
||||
// caves
|
||||
// [ ] 3d perlin noise up to 64
|
||||
// [ ] sample 4x4x4 to check for caves
|
||||
// [ ] fill with air where appropriate
|
||||
|
||||
function propagateCave(i, j, k) {
|
||||
const neighbors = (i, j, k) => [
|
||||
[i - 1, j, k],
|
||||
[i + 1, j, k],
|
||||
[i, j - 1, k],
|
||||
[i, j + 1, k],
|
||||
[i, j, k - 1],
|
||||
[i, j, k + 1],
|
||||
];
|
||||
|
||||
const queue = neighbors(i, j, k);
|
||||
|
||||
while (queue.length > 0) {
|
||||
const [ni, nj, nk] = queue.pop();
|
||||
if (ni < 0 || ni > 15 || nj < 0 || nj > 15 || nk < 0 || nk > 255) {
|
||||
continue;
|
||||
}
|
||||
const bi = 256 * (16 * ni + nj) + nk;
|
||||
if (data[bi] === BlockType.AIR || data[bi] === BlockType.WATER) {
|
||||
continue;
|
||||
}
|
||||
if (checkCave(seed, x + nj, nk, z + ni)) {
|
||||
data[bi] = BlockType.AIR;
|
||||
queue.push(...neighbors(ni, nj, nk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 16; i += 4) {
|
||||
for (let j = 0; j < 16; j += 4) {
|
||||
let bi = 256 * (16 * i + j);
|
||||
for (let k = 0; k < 58; k += 4, bi += 4) {
|
||||
if (data[bi] === BlockType.AIR) {
|
||||
continue;
|
||||
}
|
||||
if (checkCave(seed, x + j, k, z + i)) {
|
||||
data[bi] = BlockType.AIR;
|
||||
propagateCave(i, j, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
position: {z, x},
|
||||
data,
|
||||
@ -302,7 +350,7 @@ function makeFaces(chunk, blocks, neighbors) {
|
||||
.filter(({ block }) => isTransparent(block) && block !== BlockType.WATER)
|
||||
) {
|
||||
if (chunk.data[bi] === BlockType.WATER) {
|
||||
faceCenter[1] -= 0.15;
|
||||
faceCenter[1] -= 0.15; // TODO: lower face should be normal
|
||||
}
|
||||
chunk.transparentFaces.push({
|
||||
dir,
|
||||
|
Loading…
Reference in New Issue
Block a user