From 61011959f229cf3a520a6b0de12bde1d4355138a Mon Sep 17 00:00:00 2001 From: Paula Date: Sun, 15 Mar 2026 02:48:25 -0300 Subject: [PATCH] Janky (but working) worker for chunk_meshes --- build.ts | 2 +- client/components/dimension.ts | 33 ++++ client/renderer/models.ts | 2 +- client/systems/rendering/chunk_mesh_worker.ts | 160 ++++++++++++++++++ client/systems/rendering/dimension.ts | 64 ++++++- deno.json | 2 +- 6 files changed, 257 insertions(+), 6 deletions(-) create mode 100644 client/systems/rendering/chunk_mesh_worker.ts diff --git a/build.ts b/build.ts index 9ddfeb7..9c4c81c 100644 --- a/build.ts +++ b/build.ts @@ -118,7 +118,7 @@ async function build_assets() { async function build_client() { const _result = await Deno.bundle({ - entrypoints: ["./client/index.html"], + entrypoints: ["./client/index.html", "./client/systems/rendering/chunk_mesh_worker.ts"], outputDir: `${BUILD_FOLDER}/client`, platform: "browser", minify: false, diff --git a/client/components/dimension.ts b/client/components/dimension.ts index 2277212..2bf598f 100644 --- a/client/components/dimension.ts +++ b/client/components/dimension.ts @@ -250,4 +250,37 @@ export class Dimension extends Component { return undefined; } + + create_padded_chunk(chunk: Chunk) { + const cx = chunk.x; + const cz = chunk.z; + + const size = CHUNK_SIZE + 2; + const padded = new Int16Array(size * size * CHUNK_HEIGHT); + + for (let y = 0; y < CHUNK_HEIGHT; y++) { + for (let z = -1; z <= CHUNK_SIZE; z++) { + for (let x = -1; x <= CHUNK_SIZE; x++) { + let block: number; + + if (x >= 0 && x < CHUNK_SIZE && z >= 0 && z < CHUNK_SIZE) { + const index = y * CHUNK_SIZE * CHUNK_SIZE + z * CHUNK_SIZE + x; + block = chunk.blocks[index]; + } else { + const wx = cx * CHUNK_SIZE + x; + const wz = cz * CHUNK_SIZE + z; + block = this.get_block(wx, y, wz) ?? 0; + } + + const px = x + 1; + const pz = z + 1; + const pindex = y * size * size + pz * size + px; + + padded[pindex] = block; + } + } + } + + return padded; + } } diff --git a/client/renderer/models.ts b/client/renderer/models.ts index 69930b9..b118863 100644 --- a/client/renderer/models.ts +++ b/client/renderer/models.ts @@ -1,4 +1,4 @@ -import { Texture } from "./types.ts"; +import type { Texture } from "./types.ts"; const pad = 0.5; diff --git a/client/systems/rendering/chunk_mesh_worker.ts b/client/systems/rendering/chunk_mesh_worker.ts new file mode 100644 index 0000000..48cb873 --- /dev/null +++ b/client/systems/rendering/chunk_mesh_worker.ts @@ -0,0 +1,160 @@ +import type { BlockRegistry } from "$/common/everything_registry.ts"; +import type { SpriteRegion } from "$/common/constants.ts"; +import { + push_back_face, + push_bottom_face, + push_front_face, + push_left_face, + push_right_face, + push_top_face, +} from "../../renderer/models.ts"; +import type { Texture } from "../../renderer/types.ts"; + +type TexturesInfo = Record; + +const CHUNK_SIZE = 16; +const CHUNK_HEIGHT = 128; +const TEXTURE_SIZE = 16; + +const FACE_PUSHING_FUNCTIONS = { + top: push_top_face, + bottom: push_bottom_face, + front: push_front_face, + back: push_back_face, + left: push_left_face, + right: push_right_face, +} as const; + +self.onmessage = (event) => { + const { chunk_x, chunk_z, padded_chunk, blocks_registry, textures_info, image } = event.data; + const [vertices, count] = make_chunk_mesh(chunk_x, chunk_z, padded_chunk, blocks_registry, textures_info, image); + self.postMessage({ vertices, count, chunk_x, chunk_z }); +}; + +function make_chunk_mesh( + chunk_x: number, + chunk_z: number, + padded_chunk: Int16Array, + blocks_registry: BlockRegistry[], + textures_info: TexturesInfo, + image: Texture, +) { + let vertices = new Float32Array(2048); + let count = 0; + + const size = CHUNK_SIZE + 2; + const layer = size * size; + + const padded_index = (x: number, y: number, z: number) => { + return y * layer + z * size + x; + }; + + const show_face = (block: number) => { + if (block === 0) return true; + const info = blocks_registry[block]; + return info?.transparent ?? false; + }; + + for (let y = 0; y < CHUNK_HEIGHT; y++) { + for (let z = 0; z < CHUNK_SIZE; z++) { + for (let x = 0; x < CHUNK_SIZE; x++) { + const px = x + 1; + const pz = z + 1; + + const block_nid = padded_chunk[padded_index(px, y, pz)]; + if (block_nid === 0) continue; + + const block_info = blocks_registry[block_nid]; + + const texture_ids = { + top: "bworld:missing", + bottom: "bworld:missing", + front: "bworld:missing", + back: "bworld:missing", + left: "bworld:missing", + right: "bworld:missing", + }; + + const textures = block_info.textures; + if (!textures) throw new Error(`no textures for ${block_nid}`); + + if (typeof textures === "string") { + texture_ids.top = textures; + texture_ids.bottom = textures; + texture_ids.front = textures; + texture_ids.back = textures; + texture_ids.left = textures; + texture_ids.right = textures; + } else if ("top" in textures && "bottom" in textures && "side" in textures) { + texture_ids.top = textures.top; + texture_ids.bottom = textures.bottom; + texture_ids.front = textures.side; + texture_ids.back = textures.side; + texture_ids.left = textures.side; + texture_ids.right = textures.side; + } else if ("front" in textures && "side" in textures) { + texture_ids.top = textures.side; + texture_ids.bottom = textures.side; + texture_ids.front = textures.front; + texture_ids.back = textures.side; + texture_ids.left = textures.side; + texture_ids.right = textures.side; + } + + const wx = chunk_x * CHUNK_SIZE + x; + const wz = chunk_z * CHUNK_SIZE + z; + + const faces = { + front: show_face(padded_chunk[padded_index(px, y, pz + 1)]), + back: show_face(padded_chunk[padded_index(px, y, pz - 1)]), + left: show_face(padded_chunk[padded_index(px - 1, y, pz)]), + right: show_face(padded_chunk[padded_index(px + 1, y, pz)]), + top: show_face(padded_chunk[padded_index(px, y + 1, pz)]), + bottom: show_face(padded_chunk[padded_index(px, y - 1, pz)]), + } as const; + + for (const side of ["front", "back", "left", "right", "top", "bottom"] as const) { + if (faces[side]) { + const region = textures_info[texture_ids[side]]; + + vertices = ensure_capacity(vertices, count + (6 * 6 * 9)); + count = FACE_PUSHING_FUNCTIONS[side]( + vertices, + count, + image, + wx, + y, + wz, + region.x * TEXTURE_SIZE, + region.y * TEXTURE_SIZE, + TEXTURE_SIZE, + TEXTURE_SIZE, + 1, + 1, + 1, + 1, + ); + } + } + } + } + } + + return [vertices, count]; +} + +function ensure_capacity( + buffer: Float32Array, + required: number, +) { + if (required <= buffer.length) return buffer; + + let new_length = buffer.length; + while (new_length < required) { + new_length *= 2; + } + + const new_buffer = new Float32Array(new_length); + new_buffer.set(buffer); + return new_buffer; +} diff --git a/client/systems/rendering/dimension.ts b/client/systems/rendering/dimension.ts index 4a584f2..a9cdc7f 100644 --- a/client/systems/rendering/dimension.ts +++ b/client/systems/rendering/dimension.ts @@ -14,16 +14,74 @@ import { set_current_texture, } from "$/client/renderer/mod.ts"; import { Camera } from "$/client/components/camera.ts"; +import { AssetManager } from "../../assets.ts"; + +let gdimension: Dimension; + +const worker = new Worker(new URL("./systems/rendering/chunk_mesh_worker-AYRYRGPW.js", import.meta.url), { + type: "module", +}); + +worker.onmessage = (event) => { + const { vertices, count, chunk_x, chunk_z } = event.data; + const chunk = gdimension.get_chunk(chunk_x, chunk_z); + if (!chunk) { + console.error("bad"); + return; + } + gdimension.delete_chunk_mesh(chunk); + chunk.dirty = false; + chunk.vertex_buffer = gl.createBuffer(); + chunk.vertex_count = count / 9; + + gl.bindBuffer(gl.ARRAY_BUFFER, chunk.vertex_buffer); + gl.bufferData( + gl.ARRAY_BUFFER, + vertices.subarray(0, count), + gl.STATIC_DRAW, + ); + //make_chunk_mesh(chunk, dimension, camera); +}; + +function strip_functions>(obj: T) { + const out: any = {}; + + for (const k in obj) { + const v = obj[k]; + + if (typeof v === "function") continue; + + if (v && typeof v === "object") { + out[k] = strip_functions(v); + } else { + out[k] = v; + } + } + + return out; +} export function render_dimension(dimension: Dimension, camera: Camera) { + gdimension = dimension; + set_current_texture(dimension.image.tex); + for (const chunk of dimension.chunks) { if (chunk.dirty) { - dimension.delete_chunk_mesh(chunk); + worker.postMessage({ + chunk_x: chunk.x, + chunk_z: chunk.z, + padded_chunk: dimension.create_padded_chunk(chunk), + blocks_registry: strip_functions(EverythingRegistry.get_registry("blocks")), + textures_info: AssetManager.instance.get("bworld:textures_info"), + image: { width: dimension.image.width, height: dimension.image.height }, + }); chunk.dirty = false; - make_chunk_mesh(chunk, dimension, camera); + // dimension.delete_chunk_mesh(chunk); + + // make_chunk_mesh(chunk, dimension, camera); // only generate one mesh per frame - break; + // break; } } for (const chunk of dimension.chunks) { diff --git a/deno.json b/deno.json index 897a831..08ca3c2 100644 --- a/deno.json +++ b/deno.json @@ -6,7 +6,7 @@ "compilerOptions": { "lib": ["dom", "dom.asynciterable", "dom.iterable", "deno.ns", "deno.unstable"] }, - "unstable": ["bundle"], + "unstable": ["bundle", "raw-imports"], "fmt": { "useTabs": true, "indentWidth": 4,