Fix transparent blocks
This commit is contained in:
+103
-21
@@ -1,14 +1,15 @@
|
||||
import { Component } from "$/common/ecs/mod.ts";
|
||||
import { block_value, chunk_key, default_block_value } from "$/common/utils.ts";
|
||||
import { AIR_ID, BlockChange } from "$/common/protocol.ts";
|
||||
import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.ts";
|
||||
import { BlockRegistry, EverythingRegistry, RENDER_LAYERS, RenderLayer } from "$/common/everything_registry.ts";
|
||||
import { AIR, CHUNK_AREA, CHUNK_HEIGHT, CHUNK_SIZE, Faces, ID_MASK, VOID } from "../../common/constants.ts";
|
||||
import { AssetManager } from "../assets.ts";
|
||||
import { ClientWorld } from "../client_world.ts";
|
||||
import { ChunkWorkerPool } from "../chunk_workers.ts";
|
||||
import { worldgen_mods } from "../mods.ts";
|
||||
import type { FromChunkWorker } from "../workers/chunk_messages.ts";
|
||||
import { create_vertex_buffer, destroy_vertex_buffer, Texture } from "../renderer/mod.ts";
|
||||
import { create_index_buffer, create_vertex_buffer, destroy_buffer, Texture } from "../renderer/mod.ts";
|
||||
import { crosses_planes } from "../workers/translucent_sort.ts";
|
||||
import { Camera } from "./camera.ts";
|
||||
|
||||
export interface Block {
|
||||
@@ -28,12 +29,32 @@ export interface Chunk {
|
||||
dirty: boolean;
|
||||
// bumped on every mesh request so late results from older requests get ignored
|
||||
mesh_version: number;
|
||||
opaque_vertex_buffer?: GPUBuffer;
|
||||
opaque_vertex_count?: number;
|
||||
transparent_vertex_buffer?: GPUBuffer;
|
||||
transparent_vertex_count?: number;
|
||||
meshes: Partial<Record<RenderLayer, ChunkMesh>>;
|
||||
// only for translucent meshes that have to be sorted again as the camera moves
|
||||
translucent_sort?: TranslucentSort;
|
||||
}
|
||||
|
||||
export interface ChunkMesh {
|
||||
vertex_buffer: GPUBuffer;
|
||||
quad_count: number;
|
||||
// the translucent layer's quads sorted back to front, the others are drawn in order
|
||||
index_buffer?: GPUBuffer;
|
||||
}
|
||||
|
||||
interface TranslucentSort {
|
||||
// the mesh_version of the mesh these are for
|
||||
mesh_version: number;
|
||||
centers: Float32Array;
|
||||
planes: [Float32Array, Float32Array, Float32Array];
|
||||
// where the camera was for the last sort that was requested
|
||||
camera: number[];
|
||||
// sorts come back out of order, older ones than what's shown get skipped
|
||||
requested: number;
|
||||
applied: number;
|
||||
}
|
||||
|
||||
const FLOATS_PER_QUAD = 4 * 9;
|
||||
|
||||
export { chunk_key };
|
||||
|
||||
const NEIGHBOR_OFFSETS = [[-1, 0], [1, 0], [0, -1], [0, 1]] as const;
|
||||
@@ -92,6 +113,7 @@ export class Dimension extends Component {
|
||||
dirty: true,
|
||||
generated: false,
|
||||
mesh_version: 0,
|
||||
meshes: {},
|
||||
};
|
||||
this.chunks.set(chunk_key(x, z), chunk);
|
||||
return chunk;
|
||||
@@ -294,7 +316,7 @@ export class Dimension extends Component {
|
||||
}
|
||||
|
||||
// sends every dirty chunk that can be meshed to the workers
|
||||
request_meshes() {
|
||||
request_meshes(camera: Camera) {
|
||||
for (const chunk of this.chunks.values()) {
|
||||
if (!chunk.dirty || !this.can_mesh(chunk)) {
|
||||
continue;
|
||||
@@ -308,15 +330,41 @@ export class Dimension extends Component {
|
||||
chunk_z: chunk.z,
|
||||
version: chunk.mesh_version,
|
||||
padded_chunk,
|
||||
camera: [camera.x, camera.y, camera.z],
|
||||
}, [padded_chunk.buffer]);
|
||||
}
|
||||
}
|
||||
|
||||
// like sodium, a chunk's translucent quads only change order when the camera crosses one of the
|
||||
// planes they lie on, so that's the only time they get sorted again
|
||||
update_translucent_sorting(camera: Camera) {
|
||||
const position = [camera.x, camera.y, camera.z];
|
||||
for (const chunk of this.chunks.values()) {
|
||||
const sort = chunk.translucent_sort;
|
||||
if (!sort || !crosses_planes(sort.planes, sort.camera, position)) {
|
||||
continue;
|
||||
}
|
||||
sort.camera = position;
|
||||
sort.requested += 1;
|
||||
this.workers.post({
|
||||
type: "sort",
|
||||
chunk_x: chunk.x,
|
||||
chunk_z: chunk.z,
|
||||
version: sort.mesh_version,
|
||||
sort_version: sort.requested,
|
||||
centers: sort.centers,
|
||||
camera: position,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#on_worker_message(message: FromChunkWorker) {
|
||||
if (message.type === "generated") {
|
||||
this.#on_generated(message.chunk_x, message.chunk_z, message.blocks, message.spills);
|
||||
} else {
|
||||
} else if (message.type === "meshed") {
|
||||
this.#on_meshed(message);
|
||||
} else {
|
||||
this.#on_sorted(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,13 +418,47 @@ export class Dimension extends Component {
|
||||
|
||||
this.delete_chunk_mesh(chunk);
|
||||
|
||||
chunk.opaque_vertex_buffer = create_vertex_buffer(message.opaque_vertices.subarray(0, message.opaque_count));
|
||||
chunk.opaque_vertex_count = message.opaque_count / 9;
|
||||
for (const layer of RENDER_LAYERS) {
|
||||
const { vertices, quad_count } = message[layer];
|
||||
if (quad_count === 0) {
|
||||
continue;
|
||||
}
|
||||
chunk.meshes[layer] = {
|
||||
vertex_buffer: create_vertex_buffer(vertices.subarray(0, quad_count * FLOATS_PER_QUAD)),
|
||||
quad_count,
|
||||
};
|
||||
}
|
||||
|
||||
chunk.transparent_vertex_buffer = create_vertex_buffer(
|
||||
message.transparent_vertices.subarray(0, message.transparent_count),
|
||||
);
|
||||
chunk.transparent_vertex_count = message.transparent_count / 9;
|
||||
const translucent = message.translucent;
|
||||
if (translucent.quad_count === 0) {
|
||||
return;
|
||||
}
|
||||
chunk.meshes.translucent!.index_buffer = create_index_buffer(translucent.indices);
|
||||
if (translucent.sort_type === "dynamic") {
|
||||
chunk.translucent_sort = {
|
||||
mesh_version: message.version,
|
||||
centers: translucent.centers,
|
||||
planes: translucent.planes,
|
||||
camera: message.camera,
|
||||
requested: 0,
|
||||
applied: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#on_sorted(message: Extract<FromChunkWorker, { type: "sorted" }>) {
|
||||
const chunk = this.get_chunk(message.chunk_x, message.chunk_z);
|
||||
const sort = chunk?.translucent_sort;
|
||||
const mesh = chunk?.meshes.translucent;
|
||||
// remeshed since, or a newer sort already came back
|
||||
if (!sort || !mesh || sort.mesh_version !== message.version || message.sort_version <= sort.applied) {
|
||||
return;
|
||||
}
|
||||
sort.applied = message.sort_version;
|
||||
if (mesh.index_buffer) {
|
||||
destroy_buffer(mesh.index_buffer);
|
||||
}
|
||||
mesh.index_buffer = create_index_buffer(message.indices);
|
||||
}
|
||||
|
||||
// a neighbor's leaves, only fill air so the result doesn't depend on which chunk loaded first.
|
||||
@@ -398,14 +480,14 @@ export class Dimension extends Component {
|
||||
}
|
||||
|
||||
delete_chunk_mesh(chunk: Chunk) {
|
||||
if (chunk.opaque_vertex_buffer) {
|
||||
destroy_vertex_buffer(chunk.opaque_vertex_buffer);
|
||||
chunk.opaque_vertex_buffer = undefined;
|
||||
}
|
||||
if (chunk.transparent_vertex_buffer) {
|
||||
destroy_vertex_buffer(chunk.transparent_vertex_buffer);
|
||||
chunk.transparent_vertex_buffer = undefined;
|
||||
for (const mesh of Object.values(chunk.meshes)) {
|
||||
destroy_buffer(mesh.vertex_buffer);
|
||||
if (mesh.index_buffer) {
|
||||
destroy_buffer(mesh.index_buffer);
|
||||
}
|
||||
}
|
||||
chunk.meshes = {};
|
||||
chunk.translucent_sort = undefined;
|
||||
}
|
||||
|
||||
get_looked_block(
|
||||
|
||||
Reference in New Issue
Block a user