Newer
Older
rpg / v2 / packages / engine / src / models / stamp.ts
/**
 * Штамповка модели в мир: (0,0,0) модели → заданная мировая точка.
 * Клиппинг: воксели вне вертикали мира и вне опционального прямоугольника
 * (half-open [min, max)) пропускаются. Возвращает число поставленных
 * вокселей; dirty-трекинг мира обычный (remeshDirty демки подхватит).
 * С opts.texRemap текстурный слой модели (model.tex, id тайлей локальны)
 * штампуется в мир через переводчик локальный id → мировой id.
 */
import { VoxelWorld } from '../world/world';
import { decodeModel, texBytes } from './format';
import type { VoxelModel } from './format';

export interface StampClip {
    /** half-open границы клиппинга в мировых координатах. */
    minX?: number; minY?: number; minZ?: number;
    maxX?: number; maxY?: number; maxZ?: number;
}

export interface StampOptions {
    /** Переводчик id тайля модели → мировой id слоя тайлей мира. */
    texRemap?: (localId: number) => number;
}

export function stampModel(
    world: VoxelWorld, model: VoxelModel, x: number, y: number, z: number,
    clip: StampClip = {}, opts: StampOptions = {},
): number {
    const grid = decodeModel(model);
    const tex = opts.texRemap ? texBytes(model) : null;
    const inClip = (wx: number, wy: number, wz: number): boolean =>
        (clip.minX === undefined || wx >= clip.minX) && (clip.maxX === undefined || wx < clip.maxX) &&
        (clip.minY === undefined || wy >= clip.minY) && (clip.maxY === undefined || wy < clip.maxY) &&
        (clip.minZ === undefined || wz >= clip.minZ) && (clip.maxZ === undefined || wz < clip.maxZ);
    let placed = 0;
    for (let ly = 0; ly < grid.sy; ly++)
        for (let lz = 0; lz < grid.sz; lz++)
            for (let lx = 0; lx < grid.sx; lx++) {
                const slot = grid.get(lx, ly, lz);
                if (slot === 0) continue;
                const wx = x + lx, wy = y + ly, wz = z + lz;
                if (!inClip(wx, wy, wz)) continue;
                if (world.set(wx, wy, wz, slot)) placed++;
                if (tex) {
                    const id = tex[lx + grid.sx * (ly + grid.sy * lz)];
                    if (id !== 0) world.setTex(wx, wy, wz, opts.texRemap!(id));
                }
            }
    return placed;
}