69 lines
3.4 KiB
JavaScript
69 lines
3.4 KiB
JavaScript
// ==================== ОККЛЮЗИЯ 3D — плотная сетка ====================
|
||
// Границы мира фиксированы генератором: WORLD_DEPTH = 48 по X и Z (то есть
|
||
// gx,gz из -48..48), а по Y генерация идёт от BEDROCK_GY-1 = 44 вниз до 0.
|
||
// 97 * 45 * 97 = 423 855 клеток — 414 КБ в Uint8Array.
|
||
//
|
||
// Зачем: isHiddenAt проверяла 6 соседей через getBlock, а тот строил строку
|
||
// "gx,gy,gz" и искал в Map. На 68k кандидатов это ~409k строковых
|
||
// конкатенаций — профиль показал 129 мс из 153 мс всего перестроения мешей.
|
||
// Здесь 6 чтений массива вместо 6 строковых lookup'ов.
|
||
//
|
||
// ВАЖНО: сетку обязаны обновлять все, кто меняет мир — setBlock/removeBlock
|
||
// в world-storage3d.js. Иначе окклюзия разойдётся с реальными блоками и
|
||
// внутренние грани начнут просвечивать (или исчезнут видимые блоки).
|
||
import { BLOCKS } from '../data/blocks.js';
|
||
|
||
export const OCC_X = 2 * 48 + 1; // 97
|
||
export const OCC_Z = 2 * 48 + 1;
|
||
export const OCC_Y = 45; // gy 0..44
|
||
export const OCC_MIN = -48, OCC_MAX = 48, OCC_MAX_GY = 44;
|
||
|
||
// Значения ячеек
|
||
export const OCC_EMPTY = 0; // блока нет
|
||
export const OCC_PRESENT = 1; // блок есть, но он прозрачный/жидкий/декор
|
||
export const OCC_OPAQUE = 2; // непрозрачный блок — закрывает соседей
|
||
|
||
const occGrid = new Uint8Array(OCC_X * OCC_Y * OCC_Z);
|
||
|
||
// Раскладка: (gy*OCC_Z + (gz-OCC_MIN))*OCC_X + (gx-OCC_MIN)
|
||
// => шаг по gx = 1, по gz = OCC_X, по gy = OCC_X*OCC_Z
|
||
export const OCC_STEP_X = 1;
|
||
export const OCC_STEP_Z = OCC_X;
|
||
export const OCC_STEP_Y = OCC_X * OCC_Z;
|
||
|
||
export function occIndex(gx, gy, gz) {
|
||
if (gx < OCC_MIN || gx > OCC_MAX || gz < OCC_MIN || gz > OCC_MAX ||
|
||
gy < 0 || gy > OCC_MAX_GY) return -1;
|
||
return (gy * OCC_Z + (gz - OCC_MIN)) * OCC_X + (gx - OCC_MIN);
|
||
}
|
||
|
||
// Плотность блока для оклюзии
|
||
export function occDensity(t) {
|
||
const def = BLOCKS[t];
|
||
if (!def) return OCC_EMPTY;
|
||
const opaque = !!def.solid && !def.fluid && !def.decor && !def.climbable &&
|
||
def.alpha === undefined;
|
||
return opaque ? OCC_OPAQUE : OCC_PRESENT;
|
||
}
|
||
|
||
export function occSet(gx, gy, gz, density) {
|
||
const i = occIndex(gx, gy, gz);
|
||
if (i >= 0) occGrid[i] = density;
|
||
}
|
||
|
||
// Пересобрать сетку целиком из массива блоков. Нужна при загрузке сейва и
|
||
// после полной генерации — там setBlock вызывается с bumpWorld=false, но
|
||
// запись в сетку всё равно идёт, так что это страховка на случай, когда
|
||
// массив блоков был подменён снаружи.
|
||
export function occRebuildFrom(blocksArr) {
|
||
occGrid.fill(OCC_EMPTY);
|
||
for (let i = 0; i < blocksArr.length; i++) {
|
||
const b = blocksArr[i];
|
||
if (b.dead) continue;
|
||
const idx = occIndex(b.gx, b.gy, b.gz || 0);
|
||
if (idx >= 0) occGrid[idx] = occDensity(b.t);
|
||
}
|
||
}
|
||
|
||
export function occClear() { occGrid.fill(OCC_EMPTY); }
|