Add a few trees
This commit is contained in:
128
world.js
128
world.js
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user