/**
* Шиммер воды (полировка прудов): редкие светлые блики на поверхности.
* Отдельное voxel-облако НАД водой (не world.set — без ремешинга мира):
* блики вспыхивают и гаснут фазами от времени цикла, детерминированно
* (гейты кадр-равенств выключают слой вместе с пеплом мостом setAsh).
*/
import type { VoxelCloud, VoxelRenderer } from '@rpg/engine';
/** Воксель поверхности воды: центр куба слоя (блик выступает на 0.05). */
export interface WaterCell {
x: number; // центр куба по x (воксель + 0.5)
z: number; // центр куба по z
}
const STEP = 19; // каждый N-й воксель поверхности — потенциальный блик (редкие)
const GLOW = 0.84; // порог фазы: выше — блик виден (вспышка короткая)
/** Сдвиг блика из плоскости куба воды: куб блика иначе объёмно перекрывается
* с кубом воды (копланарные вертикальные грани) — на кромках пруда z-fighting,
* порядок отрисовки облаков меняется от пересоздания → мерцающие пиксели. */
const EPS = 0.03;
/** Блик: приглушённая рябь и яркая вспышка (поверх W2 #568590). */
const COLORS = ['#8fb8bf', '#cfe4e6'] as const;
export class WaterShimmer {
private readonly cloud: VoxelCloud;
/** Снапшот для моста: бликов в последней позе. */
private lastCount = 0;
constructor(renderer: VoxelRenderer, private readonly cells: readonly WaterCell[]) {
this.cloud = renderer.addVoxelCloud([]);
this.cloud.mesh.castShadow = false; // блик — свет, не объект: без тени
}
/** Вокселей воды в области и бликов сейчас (мост shimmerInfo). */
get info(): { cells: number; visible: number } {
return { cells: this.cells.length, visible: this.lastCount };
}
/** Поза бликов: время цикла (детерминизм гейтов). */
update(t: number): void {
const voxels: { pos: [number, number, number]; color: string }[] = [];
for (let i = 0; i < this.cells.length; i += STEP) {
const c = this.cells[i]!;
const phase = Math.sin(t * 0.9 + i * 2.399); // золотое сечение фаз
if (phase <= GLOW) continue;
voxels.push({
pos: [c.x + EPS, 2.55, c.z + EPS],
color: phase > 0.93 ? COLORS[1] : COLORS[0], // яркая вспышка редка
});
}
this.lastCount = voxels.length;
this.cloud.update(voxels);
this.cloud.mesh.castShadow = false; // пересоздание облака возвращает тень
}
/** Скрыть блики (не водная область / мост setAsh). */
hide(): void {
this.cloud.update([]);
}
dispose(): void {
this.cloud.dispose();
}
}