Mesh chunks

This commit is contained in:
2026-03-13 09:30:41 -03:00
parent e890c561cd
commit 9db229dde4
6 changed files with 141 additions and 54 deletions
+4 -1
View File
@@ -21,6 +21,9 @@ export interface Chunk {
x: number; x: number;
z: number; z: number;
blocks: Int32Array; blocks: Int32Array;
vertexBuffer?: WebGLBuffer;
vertexCount?: number;
dirty: boolean;
} }
export class Dimension extends Component { export class Dimension extends Component {
@@ -30,7 +33,7 @@ export class Dimension extends Component {
tick_timer = 0; tick_timer = 0;
add_chunk(x: number, z: number) { add_chunk(x: number, z: number) {
const chunk = { x, z, blocks: new Int32Array(CHUNK_AREA * CHUNK_HEIGHT) }; const chunk = { x, z, blocks: new Int32Array(CHUNK_AREA * CHUNK_HEIGHT), dirty: true };
this.chunks.push(chunk); this.chunks.push(chunk);
return chunk; return chunk;
} }
+1 -1
View File
@@ -16,7 +16,7 @@ export function start_game(world: ClientWorld) {
world.dimension = new Dimension(); world.dimension = new Dimension();
dimension.add(world.dimension); dimension.add(world.dimension);
world.add_entity(dimension); world.add_entity(dimension);
generate(world, dimension.get(Dimension)!, 32, 32); generate(world, dimension.get(Dimension)!, 16 * 8, 16 * 8);
create_player(world); create_player(world);
+39 -5
View File
@@ -9,13 +9,14 @@ const VERTS_PER_SPRITE = 6;
const FLOATS_PER_VERT = 9; const FLOATS_PER_VERT = 9;
let program: WebGLProgram; let program: WebGLProgram;
let buffer: WebGLBuffer; let main_buffer: WebGLBuffer;
const vertex_data = new Float32Array(MAX_SPRITES * VERTS_PER_SPRITE * FLOATS_PER_VERT); const vertex_data = new Float32Array(MAX_SPRITES * VERTS_PER_SPRITE * FLOATS_PER_VERT);
let vert_index = 0; let vert_index = 0;
let pos_loc: GLint; let pos_loc: GLint;
let uv_loc: GLint; let uv_loc: GLint;
let color_loc: GLint;
let texture_loc: WebGLUniformLocation | null; let texture_loc: WebGLUniformLocation | null;
let mvp_loc: WebGLUniformLocation | null; let mvp_loc: WebGLUniformLocation | null;
let col_diffuse_loc: WebGLUniformLocation | null; let col_diffuse_loc: WebGLUniformLocation | null;
@@ -75,8 +76,8 @@ export function init_window(canvas_element: HTMLCanvasElement) {
program = create_program(vertex_src, fragment_src); program = create_program(vertex_src, fragment_src);
buffer = gl.createBuffer()!; main_buffer = gl.createBuffer()!;
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bindBuffer(gl.ARRAY_BUFFER, main_buffer);
const stride = FLOATS_PER_VERT * 4; const stride = FLOATS_PER_VERT * 4;
@@ -88,7 +89,7 @@ export function init_window(canvas_element: HTMLCanvasElement) {
gl.enableVertexAttribArray(uv_loc); gl.enableVertexAttribArray(uv_loc);
gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, stride, 12); gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, stride, 12);
const color_loc = gl.getAttribLocation(program, "vertexColor"); color_loc = gl.getAttribLocation(program, "vertexColor");
gl.enableVertexAttribArray(color_loc); gl.enableVertexAttribArray(color_loc);
gl.vertexAttribPointer(color_loc, 4, gl.FLOAT, false, stride, 20); gl.vertexAttribPointer(color_loc, 4, gl.FLOAT, false, stride, 20);
@@ -153,6 +154,9 @@ export function begin_mode_3d(new_camera: Camera) {
gl.enable(gl.DEPTH_TEST); gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE); gl.enable(gl.CULL_FACE);
gl.depthMask(true);
gl.enable(gl.POLYGON_OFFSET_FILL);
gl.polygonOffset(1, 1);
update_camera(); update_camera();
} }
@@ -167,6 +171,8 @@ export function end_mode_3d() {
mode3d = false; mode3d = false;
gl.disable(gl.DEPTH_TEST); gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE); gl.disable(gl.CULL_FACE);
gl.depthMask(false);
gl.disable(gl.POLYGON_OFFSET_FILL);
} }
export function clear_background(r: number, g: number, b: number, a = 1) { export function clear_background(r: number, g: number, b: number, a = 1) {
@@ -179,7 +185,7 @@ export function flush_batch() {
return; return;
} }
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bindBuffer(gl.ARRAY_BUFFER, main_buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertex_data.subarray(0, vert_index), gl.STREAM_DRAW); gl.bufferData(gl.ARRAY_BUFFER, vertex_data.subarray(0, vert_index), gl.STREAM_DRAW);
if (mode3d) { if (mode3d) {
@@ -188,6 +194,11 @@ export function flush_batch() {
gl.uniformMatrix4fv(mvp_loc, false, ortho); gl.uniformMatrix4fv(mvp_loc, false, ortho);
} }
const stride = FLOATS_PER_VERT * 4;
gl.vertexAttribPointer(pos_loc, 3, gl.FLOAT, false, stride, 0);
gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, stride, 12);
gl.vertexAttribPointer(color_loc, 4, gl.FLOAT, false, stride, 20);
gl.uniform4f(col_diffuse_loc, 1, 1, 1, 1); gl.uniform4f(col_diffuse_loc, 1, 1, 1, 1);
gl.activeTexture(gl.TEXTURE0); gl.activeTexture(gl.TEXTURE0);
@@ -199,6 +210,29 @@ export function flush_batch() {
vert_index = 0; vert_index = 0;
} }
export function flush_buffer(buffer: WebGLBuffer, draw_count: number) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
if (mode3d) {
gl.uniformMatrix4fv(mvp_loc, false, mvp);
} else {
gl.uniformMatrix4fv(mvp_loc, false, ortho);
}
const stride = FLOATS_PER_VERT * 4;
gl.vertexAttribPointer(pos_loc, 3, gl.FLOAT, false, stride, 0);
gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, stride, 12);
gl.vertexAttribPointer(color_loc, 4, gl.FLOAT, false, stride, 20);
gl.uniform4f(col_diffuse_loc, 1, 1, 1, 1);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, current_texture);
gl.uniform1i(texture_loc, 0);
gl.drawArrays(gl.TRIANGLES, 0, draw_count);
}
export function resize_canvas() { export function resize_canvas() {
const width = self.innerWidth; const width = self.innerWidth;
const height = self.innerHeight; const height = self.innerHeight;
+61 -43
View File
@@ -1,7 +1,8 @@
import { flush_batch, get_current_texture, push_vertex, set_current_texture } from "./core.ts";
import { Texture } from "./types.ts"; import { Texture } from "./types.ts";
export function push_cube( export function push_cube_to_mesh(
vertices: number[],
vertex_count: number,
texture: Texture, texture: Texture,
x: number, x: number,
y: number, y: number,
@@ -24,11 +25,6 @@ export function push_cube(
top = true, top = true,
bottom = true, bottom = true,
) { ) {
if (get_current_texture() !== texture.tex) {
flush_batch();
set_current_texture(texture.tex);
}
const x2 = x + w; const x2 = x + w;
const y2 = y + h; const y2 = y + h;
const z2 = z + d; const z2 = z + d;
@@ -38,63 +34,85 @@ export function push_cube(
const u1 = (sx + sw) / texture.width; const u1 = (sx + sw) / texture.width;
const v1 = (sy + sh) / texture.height; const v1 = (sy + sh) / texture.height;
let i = vertex_count;
const push = (px: number, py: number, pz: number, u: number, vv: number) => {
vertices[i++] = px;
vertices[i++] = py;
vertices[i++] = pz;
vertices[i++] = u;
vertices[i++] = vv;
vertices[i++] = r;
vertices[i++] = g;
vertices[i++] = b;
vertices[i++] = a;
};
// front (+z) // front (+z)
if (front) { if (front) {
push_vertex(x, y, z2, u0, v0, r, g, b, a); push(x, y, z2, u0, v1);
push_vertex(x2, y, z2, u1, v0, r, g, b, a); push(x2, y, z2, u1, v1);
push_vertex(x, y2, z2, u0, v1, r, g, b, a); push(x2, y2, z2, u1, v0);
push_vertex(x2, y, z2, u1, v0, r, g, b, a);
push_vertex(x2, y2, z2, u1, v1, r, g, b, a); push(x, y, z2, u0, v1);
push_vertex(x, y2, z2, u0, v1, r, g, b, a); push(x2, y2, z2, u1, v0);
push(x, y2, z2, u0, v0);
} }
// back (-z) // back (-z)
if (back) { if (back) {
push_vertex(x2, y, z, u0, v0, r, g, b, a); push(x2, y, z, u0, v1);
push_vertex(x, y, z, u1, v0, r, g, b, a); push(x, y, z, u1, v1);
push_vertex(x2, y2, z, u0, v1, r, g, b, a); push(x, y2, z, u1, v0);
push_vertex(x, y, z, u1, v0, r, g, b, a);
push_vertex(x, y2, z, u1, v1, r, g, b, a); push(x2, y, z, u0, v1);
push_vertex(x2, y2, z, u0, v1, r, g, b, a); push(x, y2, z, u1, v0);
push(x2, y2, z, u0, v0);
} }
// left (-x) // left (-x)
if (left) { if (left) {
push_vertex(x, y, z, u0, v0, r, g, b, a); push(x, y, z, u0, v1);
push_vertex(x, y, z2, u1, v0, r, g, b, a); push(x, y, z2, u1, v1);
push_vertex(x, y2, z, u0, v1, r, g, b, a); push(x, y2, z2, u1, v0);
push_vertex(x, y, z2, u1, v0, r, g, b, a);
push_vertex(x, y2, z2, u1, v1, r, g, b, a); push(x, y, z, u0, v1);
push_vertex(x, y2, z, u0, v1, r, g, b, a); push(x, y2, z2, u1, v0);
push(x, y2, z, u0, v0);
} }
// right (+x) // right (+x)
if (right) { if (right) {
push_vertex(x2, y, z2, u0, v0, r, g, b, a); push(x2, y, z2, u0, v1);
push_vertex(x2, y, z, u1, v0, r, g, b, a); push(x2, y, z, u1, v1);
push_vertex(x2, y2, z2, u0, v1, r, g, b, a); push(x2, y2, z, u1, v0);
push_vertex(x2, y, z, u1, v0, r, g, b, a);
push_vertex(x2, y2, z, u1, v1, r, g, b, a); push(x2, y, z2, u0, v1);
push_vertex(x2, y2, z2, u0, v1, r, g, b, a); push(x2, y2, z, u1, v0);
push(x2, y2, z2, u0, v0);
} }
// top (+y) // top (+y)
if (top) { if (top) {
push_vertex(x, y2, z2, u0, v0, r, g, b, a); push(x, y2, z2, u0, v1);
push_vertex(x2, y2, z2, u1, v0, r, g, b, a); push(x2, y2, z2, u1, v1);
push_vertex(x, y2, z, u0, v1, r, g, b, a); push(x2, y2, z, u1, v0);
push_vertex(x2, y2, z2, u1, v0, r, g, b, a);
push_vertex(x2, y2, z, u1, v1, r, g, b, a); push(x, y2, z2, u0, v1);
push_vertex(x, y2, z, u0, v1, r, g, b, a); push(x2, y2, z, u1, v0);
push(x, y2, z, u0, v0);
} }
// bottom (-y) // bottom (-y)
if (bottom) { if (bottom) {
push_vertex(x, y, z, u0, v0, r, g, b, a); push(x, y, z, u0, v1);
push_vertex(x2, y, z, u1, v0, r, g, b, a); push(x2, y, z, u1, v1);
push_vertex(x, y, z2, u0, v1, r, g, b, a); push(x2, y, z2, u1, v0);
push_vertex(x2, y, z, u1, v0, r, g, b, a);
push_vertex(x2, y, z2, u1, v1, r, g, b, a); push(x, y, z, u0, v1);
push_vertex(x, y, z2, u0, v1, r, g, b, a); push(x2, y, z2, u1, v0);
push(x, y, z2, u0, v0);
} }
return i;
} }
+2
View File
@@ -21,6 +21,8 @@ export function load_texture(image: HTMLImageElement): Texture {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
return { return {
tex: texture, tex: texture,
+34 -4
View File
@@ -2,15 +2,33 @@ import { get_sprite_region } from "$/common/utils.ts";
import { Chunk, CHUNK_SIDE_SIZE, Dimension } from "$/client/components/dimension.ts"; import { Chunk, CHUNK_SIDE_SIZE, Dimension } from "$/client/components/dimension.ts";
import { TEXTURE_SIZE } from "$/common/constants.ts"; import { TEXTURE_SIZE } from "$/common/constants.ts";
import { EverythingRegistry, TileRegistry } from "$/common/everything_registry.ts"; import { EverythingRegistry, TileRegistry } from "$/common/everything_registry.ts";
import { push_cube } from "$/client/renderer/mod.ts"; import { flush_buffer, gl, push_cube_to_mesh, set_current_texture } from "$/client/renderer/mod.ts";
export function render_dimension(dimension: Dimension /*, camera: Camera*/) { export function render_dimension(dimension: Dimension /*, camera: Camera*/) {
set_current_texture(dimension.image.tex);
for (const chunk of dimension.chunks) { for (const chunk of dimension.chunks) {
render_chunk(chunk, dimension); if (chunk.dirty) {
if (chunk.vertexBuffer) {
gl.deleteBuffer(chunk.vertexBuffer);
}
chunk.dirty = false;
make_chunk_mesh(chunk, dimension);
}
render_chunk(chunk);
} }
} }
function render_chunk(chunk: Chunk, dimension: Dimension) { function render_chunk(chunk: Chunk) {
if (!chunk.vertexBuffer || !chunk.vertexCount) {
return;
}
flush_buffer(chunk.vertexBuffer, chunk.vertexCount);
}
function make_chunk_mesh(chunk: Chunk, dimension: Dimension) {
const vertices: number[] = [];
let count = 0;
const blocks_registry = EverythingRegistry.get_registry<TileRegistry>("blocks"); const blocks_registry = EverythingRegistry.get_registry<TileRegistry>("blocks");
for (let i = 0; i < chunk.blocks.length; i += 1) { for (let i = 0; i < chunk.blocks.length; i += 1) {
const block_nid = chunk.blocks[i]; const block_nid = chunk.blocks[i];
@@ -40,7 +58,9 @@ function render_chunk(chunk: Chunk, dimension: Dimension) {
const region = get_sprite_region(texture_id); const region = get_sprite_region(texture_id);
push_cube( count = push_cube_to_mesh(
vertices,
count,
dimension.image, dimension.image,
wx, wx,
y, y,
@@ -64,4 +84,14 @@ function render_chunk(chunk: Chunk, dimension: Dimension) {
bottom, bottom,
); );
} }
chunk.vertexBuffer = gl.createBuffer();
chunk.vertexCount = count / 9;
gl.bindBuffer(gl.ARRAY_BUFFER, chunk.vertexBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array(vertices),
gl.STATIC_DRAW,
);
} }