Add a few trees

This commit is contained in:
Paul Mathieu
2021-12-24 05:50:13 -08:00
parent 10b5109e21
commit 86a65fc39c
4 changed files with 127 additions and 44 deletions

128
world.js
View File

@@ -1,7 +1,7 @@
import { makeBufferFromFaces, makeFace} from "./geometry";
import { loadTexture, makeProgram } from "./gl";
import * as se3 from './se3';
import { makeTerrain } from "./terrain";
import { makeTerrain, random } from "./terrain";
const VSHADER = `
attribute vec3 aPosition;
@@ -56,35 +56,78 @@ export const BlockType = {
GRASS: 3,
STONE: 4,
WATER: 5,
TREE: 6,
LEAVES: 7,
};
function hasATree(seed, z, x) {
const rand = random(seed, z, x);
return (rand % 333 === 123);
}
function makeATree(data, pos, seed, chunkz, chunkx) {
const height = 3 + random(seed, chunkz + pos[0], chunkx + pos[1]) % 6;
const offset = 256 * (16 * pos[0] + pos[1]);
const firstBlock = pos[2];
for (let i = 0; i < height; i++) {
data[offset + firstBlock + i] = BlockType.TREE;
}
function setBlock(i, j, k, type) {
if (i < 0 || j < 0 || k < 0 || i > 15 || j > 15 || k > 255) return;
const offset = 256 * (16 * i + j) + k;
if (data[offset] !== BlockType.AIR) return;
data[offset] = type;
}
for (let i = pos[0] - 2; i < pos[0] + 3; i++) {
for (let j = pos[1] - 2; j < pos[1] + 3; j++) {
for (let k = firstBlock + height - 2; k < firstBlock + height + 2; k++) {
setBlock(i, j, k, BlockType.LEAVES);
}
}
}
return firstBlock + height;
}
function makeChunk(z, x) {
const terrain = makeTerrain(z, x);
const seed = 1337;
const terrain = makeTerrain(seed, z, x);
const data = new Uint8Array(16 * 16 * 256);
const trees = [];
for (let i = 0; i < 16; i++) {
for (let j = 0; j < 16; j++) {
const height = terrain[i * 16 + j];
// everything above is air
// that block is grass
// everything below is dirt
const offset = i * (16 * 256) + j * 256;
const stoneHeight = Math.max(48, height);
const grassHeight = stoneHeight + 8;
const waterHeight = 67;
let currentHeight = 0;
data.set(Array(stoneHeight).fill(BlockType.STONE), offset);
data.set(Array(grassHeight - 1 - stoneHeight).fill(BlockType.DIRT), offset + stoneHeight);
currentHeight = stoneHeight;
data.set(Array(grassHeight - currentHeight).fill(BlockType.DIRT), offset + currentHeight);
currentHeight = grassHeight;
if (grassHeight < waterHeight) {
data.set(Array(waterHeight - grassHeight + 1).fill(BlockType.WATER), offset + grassHeight - 1);
data.set(Array(waterHeight - currentHeight).fill(BlockType.WATER), offset + currentHeight);
currentHeight = waterHeight;
} else {
data[offset + grassHeight - 1] = BlockType.GRASS;
if (hasATree(seed, z + i, x + j)) {
trees.push([i, j, currentHeight]);
}
}
const surfaceHeight = Math.max(waterHeight, grassHeight);
data.set(Array(256 - surfaceHeight).fill(BlockType.AIR), offset + surfaceHeight);
data.set(Array(256 - currentHeight).fill(BlockType.AIR), offset + currentHeight);
}
}
for (const tree of trees) {
makeATree(data, tree, seed, z, x);
}
return {
position: {z, x},
data,
@@ -140,6 +183,14 @@ function faceTexture(type, dir) {
case BlockType.DIRT: return [2, 15];
case BlockType.STONE: return [3, 15];
case BlockType.WATER: return [4, 15];
case BlockType.TREE:
switch (dir) {
case '+y':
case '-y':
return [5, 15];
default: return [6, 15];
}
case BlockType.LEAVES: return [7, 15];
default: return [0, 0];
}
}
@@ -156,6 +207,16 @@ function faceCenter(blockCenter, dir) {
}
}
function isTransparent(type) {
switch (type) {
case BlockType.WATER:
case BlockType.LEAVES:
case BlockType.AIR:
return true;
default: return false;
}
}
function makeFaceList(data, chunkz, chunkx, blockLookup) {
const lookup = (i, j, k) => {
if (i < 0 || j < 0 || i > 15 || j > 15) {
@@ -176,7 +237,7 @@ function makeFaceList(data, chunkz, chunkx, blockLookup) {
];
const solidFaces = [];
const waterFaces = [];
const transparentFaces = [];
for (let i = 0; i < 16; i++) {
for (let j = 0; j < 16; j++) {
@@ -185,21 +246,28 @@ function makeFaceList(data, chunkz, chunkx, blockLookup) {
if (data[bi] === BlockType.AIR) {
continue;
}
for (const {dir, faceCenter} of neighbors(i, j, k)
.filter(({ block }) => block === BlockType.AIR || (block === BlockType.WATER && data[bi] !== BlockType.WATER))) {
if (data[bi] !== BlockType.WATER) {
solidFaces.push({
blockIndex: bi,
if (isTransparent(data[bi])) {
for (const { dir, faceCenter } of neighbors(i, j, k)
.filter(({ block }) => block === BlockType.AIR)
) {
if (data[bi] === BlockType.WATER) {
faceCenter[1] -= 0.15;
}
transparentFaces.push({
dir,
face: makeFace(dir, faceTexture(data[bi], dir), faceCenter),
blockIndex: bi,
});
} else {
faceCenter[1] -= 0.15;
waterFaces.push({
blockIndex: bi,
}
} else {
for (const { dir, faceCenter } of neighbors(i, j, k)
.filter(({ block }) => isTransparent(block))
) {
solidFaces.push({
dir,
face: makeFace(dir, faceTexture(data[bi], dir), faceCenter),
})
blockIndex: bi,
});
}
}
}
@@ -208,7 +276,7 @@ function makeFaceList(data, chunkz, chunkx, blockLookup) {
return {
solidFaces,
waterFaces,
transparentFaces,
};
}
@@ -292,20 +360,19 @@ function invalidateChunkGeometry(world, i, j) {
}
function addFace(chunk, block, dir) {
if (block.type === BlockType.WATER) {
const face = createChunkFace(block, dir);
if (dir === '+y') {
face[1] -= 0.15;
}
const face = createChunkFace(block, dir);
if (isTransparent(block.type)) {
chunk.transparentFaces.push(face);
} else {
chunk.faces.push(createChunkFace(block, dir));
chunk.faces.push(face);
}
}
export function createChunkFace(block, dir, center = null) {
center = center ?? faceCenter(block.centerPosition, dir);
if (block.type === BlockType.WATER && dir === '+y') {
center[1] -= 0.15;
}
return {
dir,
blockIndex: block.blockIndex,
@@ -337,7 +404,7 @@ export function updateWorldGeometry(gl, world, z, x, timeLimit = 10000) {
const faces = makeFaceList(chunk.data, chunk.position.z, chunk.position.x, lookup);
chunk.faces = faces.solidFaces;
chunk.transparentFaces = faces.waterFaces;
chunk.transparentFaces = faces.transparentFaces;
}
chunk.buffer = makeBufferFromFaces(gl, chunk.faces.map(f => f.face));
@@ -487,6 +554,7 @@ export function markBlock(world, cameraPosition, direction, maxDistance) {
export function destroyBlock(world, block) {
const trimFaces = chunk => {
chunk.faces = chunk.faces.filter(({blockIndex}) => chunk.data[blockIndex] !== BlockType.AIR);
chunk.transparentFaces = chunk.transparentFaces.filter(({blockIndex}) => chunk.data[blockIndex] !== BlockType.AIR);
}
block.chunk.data[block.blockIndex] = BlockType.AIR;
@@ -511,8 +579,6 @@ export function destroyBlock(world, block) {
.filter(({ block }) => block.type !== BlockType.AIR &&
block.type !== BlockType.UNDEFINED)
.forEach(({ block, dir }) => {
// TODO: don't add transparent faces twice
// since we "forget" to trim them
addFace(block.chunk, block, dir);
trimFaces(block.chunk);