Fix transparent blocks
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
// translucent quad sorting, the way sodium does it (its "translucency sorting"), simplified for quads
|
||||
// that all face along an axis.
|
||||
// every chunk's translucent mesh gets a sort type when it's meshed:
|
||||
// - none: the order can't matter, one quad or all of them in a single plane
|
||||
// - static: all quads face along one axis, so sorting them by distance along their normal once is right
|
||||
// from anywhere the camera can see them
|
||||
// - dynamic: sorted by distance to the camera, and sorted again only when the camera crosses one of the
|
||||
// planes the chunk's quads lie on, the only time the order between them can change (sodium's GFNI)
|
||||
|
||||
export type SortType = "none" | "static" | "dynamic";
|
||||
|
||||
// same order as the mesher's faces
|
||||
export const FACE_NORMALS = [
|
||||
[0, 1, 0], // top
|
||||
[0, -1, 0], // bottom
|
||||
[0, 0, 1], // front
|
||||
[0, 0, -1], // back
|
||||
[-1, 0, 0], // left
|
||||
[1, 0, 0], // right
|
||||
] as const;
|
||||
|
||||
export const FACE_AXIS = [1, 1, 2, 2, 0, 0] as const;
|
||||
|
||||
// for each quad: its center, and which face it is
|
||||
export interface TranslucentQuads {
|
||||
centers: Float32Array;
|
||||
faces: Uint8Array;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export function choose_sort_type(quads: TranslucentQuads): SortType {
|
||||
if (quads.count <= 1) {
|
||||
return "none";
|
||||
}
|
||||
|
||||
let axes = 0;
|
||||
for (let q = 0; q < quads.count; q++) {
|
||||
axes |= 1 << FACE_AXIS[quads.faces[q]];
|
||||
}
|
||||
// more than one axis, the order depends on where the camera is
|
||||
if (axes & (axes - 1)) {
|
||||
return "dynamic";
|
||||
}
|
||||
|
||||
// quads facing opposite ways on the same axis are never both visible over each other, so only
|
||||
// the distance along the axis matters. if they all share a plane, nothing can overlap at all
|
||||
const axis = FACE_AXIS[quads.faces[0]];
|
||||
const plane = quads.centers[axis];
|
||||
for (let q = 1; q < quads.count; q++) {
|
||||
if (quads.centers[q * 3 + axis] !== plane) {
|
||||
return "static";
|
||||
}
|
||||
}
|
||||
return "none";
|
||||
}
|
||||
|
||||
// the unique coordinates of the planes the quads lie on, per axis, sorted. the camera crossing one of
|
||||
// these is what triggers a dynamic sort
|
||||
export function quad_planes(quads: TranslucentQuads): [Float32Array, Float32Array, Float32Array] {
|
||||
const sets = [new Set<number>(), new Set<number>(), new Set<number>()];
|
||||
for (let q = 0; q < quads.count; q++) {
|
||||
const axis = FACE_AXIS[quads.faces[q]];
|
||||
sets[axis].add(quads.centers[q * 3 + axis]);
|
||||
}
|
||||
return sets.map((set) => Float32Array.from(set).sort()) as [Float32Array, Float32Array, Float32Array];
|
||||
}
|
||||
|
||||
// whether moving the camera from a to b crosses any of the planes
|
||||
export function crosses_planes(planes: [Float32Array, Float32Array, Float32Array], a: number[], b: number[]) {
|
||||
for (let axis = 0; axis < 3; axis++) {
|
||||
if (a[axis] === b[axis] || planes[axis].length === 0) {
|
||||
continue;
|
||||
}
|
||||
if (count_below(planes[axis], a[axis]) !== count_below(planes[axis], b[axis])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function count_below(sorted: Float32Array, value: number) {
|
||||
let lo = 0;
|
||||
let hi = sorted.length;
|
||||
while (lo < hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (sorted[mid] < value) {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
// back to front, for the camera at camera_x/y/z when the sort type is dynamic
|
||||
export function sort_quads(
|
||||
quads: TranslucentQuads,
|
||||
sort_type: SortType,
|
||||
camera_x: number,
|
||||
camera_y: number,
|
||||
camera_z: number,
|
||||
): Uint32Array {
|
||||
const { centers, faces, count } = quads;
|
||||
if (sort_type === "dynamic") {
|
||||
return quad_indices(sort_by_distance(centers, count, camera_x, camera_y, camera_z));
|
||||
}
|
||||
|
||||
const order = new Uint32Array(count);
|
||||
for (let q = 0; q < count; q++) {
|
||||
order[q] = q;
|
||||
}
|
||||
if (sort_type === "static") {
|
||||
// for quads facing the camera, the ones further along their normal are closer to it
|
||||
const keys = new Float32Array(count);
|
||||
for (let q = 0; q < count; q++) {
|
||||
const [nx, ny, nz] = FACE_NORMALS[faces[q]];
|
||||
keys[q] = centers[q * 3] * nx + centers[q * 3 + 1] * ny + centers[q * 3 + 2] * nz;
|
||||
}
|
||||
order.sort((a, b) => keys[a] - keys[b]);
|
||||
}
|
||||
return quad_indices(order);
|
||||
}
|
||||
|
||||
// dynamic sorting only needs the centers, so resorting doesn't need the whole mesh
|
||||
export function sort_by_distance(
|
||||
centers: Float32Array,
|
||||
count: number,
|
||||
camera_x: number,
|
||||
camera_y: number,
|
||||
camera_z: number,
|
||||
): Uint32Array {
|
||||
const order = new Uint32Array(count);
|
||||
const distances = new Float32Array(count);
|
||||
for (let q = 0; q < count; q++) {
|
||||
order[q] = q;
|
||||
const dx = centers[q * 3] - camera_x;
|
||||
const dy = centers[q * 3 + 1] - camera_y;
|
||||
const dz = centers[q * 3 + 2] - camera_z;
|
||||
distances[q] = dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
return order.sort((a, b) => distances[b] - distances[a]);
|
||||
}
|
||||
|
||||
// two triangles per quad, drawn in the given order
|
||||
export function quad_indices(order: Uint32Array): Uint32Array {
|
||||
const indices = new Uint32Array(order.length * 6);
|
||||
for (let i = 0; i < order.length; i++) {
|
||||
const v = order[i] * 4;
|
||||
const o = i * 6;
|
||||
indices[o] = v;
|
||||
indices[o + 1] = v + 1;
|
||||
indices[o + 2] = v + 2;
|
||||
indices[o + 3] = v;
|
||||
indices[o + 4] = v + 2;
|
||||
indices[o + 5] = v + 3;
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
Reference in New Issue
Block a user