# CLAUDE.md — движок v2 (воксельный)

**Всегда общайся с пользователем на русском.**

Направление и решения — `docs/plan.md` (в корне репо), разделы «Уроки
черновика → видение движка v2» и «Направление v2». Открытые вопросы v2 —
дописывать туда, не решать молча.

## Что v2 есть (решено)

- **Воксельный движок** как продолжение пиксель-арта: рендер сцены в низкое
  разрешение (nearest-апскейл), палитра вокселей = арт-библии v1
  (`docs/art-style.md`), shadow map + воксельный AO.
- **three.js — только низкоуровневый рендер-бэкенд** (роль Pixi в v1); ядро
  движка о бэкенде не знает — рендер за узким интерфейсом.
- **Формат моделей**: внутренний JSON (валидация агентом) + импорт/экспорт
  `.vox` (MagicaVoxel).
- **Генерация моделей**: процедурные генераторы кодом (основной путь),
  image-to-3D + вокселизация с апрув-гейтом, ручной MagicaVoxel для героев;
  визуальный гейт — turntable-листы.
- **Анимация = данные**: рига + кривые ключевых кадров в JSON, процедурная
  локомоция; валидация числовая (непрерывность цикла, контакт стопы) +
  снапшоты поз в агентный мост.
- **Агентный мост и CLI-проверки — часть ядра v2**, не надстройка. Демка —
  изометрия с фиксированной камерой; фокус работы — движок и инструментарий.

## Уроки v1, обязательные к соблюдению (docs/plan.md)

1. Все позиции/направления — в одном мировом пространстве; проекция на
   границе рендера. Не смешивать пространства (главный источник багов v1).
2. Визуальная подсистема считается готовой только со скриншот-гейтом, не
   только снапшот-ассертами.
3. Чистая математика — отдельно от вьюх, тестируется без рендера.
4. Не оставлять подключённый, но неиспользуемый путь; удалять или связывать.

## Команды (из `v2/`)

```bash
npm install          # установка зависимостей (workspaces)
npm test             # юнит-тесты (Vitest)
npm run typecheck    # tsc --noEmit (движок + демка)
npm run check:fast   # typecheck + тесты
node apps/demo/tools/checks/render.mjs   # скриншот-гейт рендера (Chromium, /tmp/v2_render_*.png)
npx vite --port 5299                     # демка (в apps/demo), http://localhost:5299
```

## Структура

- `packages/engine` (`@rpg/engine`) — ядро:
  - `src/voxel/grid.ts` — воксельная сетка (Uint8, 0 = пусто, иначе индекс палитры);
  - `src/voxel/mesher.ts` — culled-меш + классический AO 0..3 (схема 0fps,
    флип диагонали); `meshVoxels` для сетки, `meshRegion` — область по
    функции доступа; чистая математика, тестируется без рендера;
  - `src/world/chunk.ts` — чанк 16×H×16; `src/world/world.ts` — мир-словарь
    чанков (get/set через границы, dirty-трекинг: правка боковой кромки
    помечает соседа); `src/world/worldMesher.ts` — `meshChunk` через
    `world.get`, шов чанков бесшовный (culled и AO через границы);
  - `src/render/voxelRenderer.ts` — three.js-бэкенд: ортокамера диметрии 2:1,
    dirLight + shadow map + hemisphere, низкое разрешение (480×270);
  - `src/math/rng.ts` — детерминированный Rng (mulberry32): next/int/range/chance/pick;
  - `src/models/format.ts` — формат модели (JSON: size + плотный base64 +
    слоты-палитра), encode/decode/validate (числовая валидация агентом);
  - `src/models/generators.ts` — процедурные генераторы: `treeModel` (варианты
    0 круглая / 1 колонна / 2 ель), `boulderModel` (union сфер, свет на
    верхушках столбцов); (вид, сид) → VoxelModel, детерминировано;
  - `src/models/stamp.ts` — `stampModel`: штамповка модели в мир с клиппингом
    (half-open прямоугольник + вертикаль мира), возвращает число вокселей;
  - `tools/agent-lib.mjs` — браузерный каркас проверок (launchBrowser с
    опциональным viewport/
    startDevServer/openGame/AgentClient/Checks), swiftshader в headless;
  - `tools/png.mjs` — PNG-кодек (colorType 6 и 2 — скриншоты Chromium RGB).
- `apps/demo` — демо-полигон: `src/main.ts` (мир 6×6 чанков 96×96 «пепельный
  луг», деревья/валуны — из генераторов через `stampModel`, дом — ручной путь,
  ремешинг по dirty-чанкам), `tools/checks/render.mjs` — скриншот-гейт:
  нет pageerror, меш собран, тени ползут (утро/вечер), апскейл кратен 2×2,
  многокрасочность, 36 чанков, постройка+мешинг < 2 с (замер ~0.5 с),
  `setVoxel` на шве меняет кадр, 12+ деревьев всех 3 вариантов, генератор
  детерминирован и варианты различаются (12 проверок).
  Мост демки: `window.__agent.setSun(k)`/`stats()`/`setVoxel(x,y,z,c)`/`chunkStats()`/`modelStats()`/`stampTree(v,x,z,seed)`/`perf()`.
- `apps/demo/turntable.html` + `src/turntable.ts` — turntable-лист: все варианты
  генераторов (деревья/валуны) × 4 ракурса на подставках 8×8×2; мост
  `sheetInfo()` (геометрия листа для расчёта ячеек) и `setModels(on)` (скрыть
  модели для референсного кадра). Канвас в левом верхнем углу — пиксели
  скриншота считаются от (0,0). Гейт `tools/checks/turntable.mjs`: проекция
  ячеек той же матрицей, что камера, эллипс 48×42 в ромбе подставки,
  5 проверок (ячейки непустые, варианты попарно различаются).

## Известные грабли (уже собранные)

- **Знак Y в изо-направлении камеры**: вектор «target → камера» должен иметь
  y = +1. С минусом камера встаёт под землю — сцена рендерится «из-под»,
  верхние грани без света и всё почти чёрное (снапшоты при этом зелёные —
  ловится только скриншот-гейтом).
- **Скриншоты headless Chromium — RGB (colorType 2), без альфы** — декодер
  `tools/png.mjs` v2 это умеет (у v1 умел только RGBA).
- **three без типов**: нужен `@types/three` в devDependencies движка.
- **`VoxelGrid.count()` — метод, а не геттер**: в тестах легко написать
  `g.count` без скобок и получить функцию (дважды ловили).
- **Дробные координаты в `VoxelGrid.set` молча игнорируются** (индекс
  Uint8Array 4.5 — no-op): центры ячеек считать целыми (`floor(pitch/2)`).
- **Скриншот = вьюпорт браузера**, не страница: канвас выше дефолтного
  вьюпорта 960×540 обрезается снизу; `launchBrowser({viewport})` и канвас
  без центрирования — иначе все координаты гейта плывут.

## Рабочие привычки

- Для задач из 3+ шагов веди список задач и иди автономно.
- Комментарии и докстринги — по-русски; хелперы до ~70 строк.
- Изменения API — с доками в том же коммите.