# Re2DSprite v3 — плотная «геометрическая картинка»

v3 снимает главный потолок v2: геометрия больше не хранится редкой сеткой 256×192 с кэшем цвета 5×5.
Один PNG хранит **плотную поверхность**: каждый тексель — цвет, позиция, нормаль, блеск и до четырёх костей с весами.
Рантайм сам строит из соседних текселей четырёхугольники, переносит их костями, рисует программным растеризатором
(SS×2, попиксельный свет) и отдаёт обычный 2D-спрайт + карту глубины для `$.re2dWorld`.
Меша, сцены и 3D-API в игре нет: это внутренняя растеризация одного спрайта.

## Контейнер

PNG шириной `3·W` и высотой `1 + 2·H`. Строка 0 — заголовок: `R2D`/`ROT`/`(3,0,0)`, затем W, H (16 бит), число колонок и слоёв, `extent`.
Шесть слоёв W×H (3 колонки × 2 ряда): цвет · позиция hi · позиция lo (16 бит на ось, единицы Re2D −128…128) ·
нормаль (+блеск в A) · кости (id−1) · веса (сумма 255). Альфа слоя позиции 255 — тексель существует.
Единицы и оси совпадают с v2 (X вправо, Y вниз, Z к зрителю), поэтому матрицы частей `R2DRotModelPose.parts[id]` применяются без изменений;
`extent` — размер холста в единицах Re2D (v2: 128).

## Рантайм

- Соседние тексели образуют ячейку, если рёбра короче 5× медианного шага; пирамида уровней 2×2; уровень выбирается по размеру растра (`projection.detail`).
- Кожный скининг (LBS) матрицами частей; клипы `skin` (`animations.json`) — матрицы костей по кадрам, линейная интерполяция.
- Свет синтеза: ключевой, заполняющий, полусфера неба/земли, контровой, блик Блинна; множитель `tint` — освещение мира (`.color(...)`).
- Растеризация делится на горизонтальные полосы и идёт на потоках SDL (`R2D_ROT_THREADS`), блоки 16×16 ячеек отсекаются по экранному габариту.
- При растре ≤ 1024 — два отсчёта на пиксель по каждой оси; выше — один отсчёт и сглаживание силуэта по окрестности 3×3.
- Сворачивается и загружается в текстуру только изменённая область результата.
- Для World отдаётся `sample_depth`; цвет/глубина — как у v2 anime.

JS: `$.re2dSprite.from('x.character.json')` определяет v3 по заголовку PNG. `info()` добавляет `grid`, `extent`, `level`, `levels`, `synthMs`.

## Проекция (`projection` в character.json)

| Поле | Значение |
|---|---|
| `bodyScale` | увеличение проекции: 1 — весь `extent` помещается в спрайт |
| `raster` | размер синтеза, 128..2048 (по умолчанию 1024). Для спрайтов мира достаточно 512: кадр мира 1024×576 |
| `detail` | 0.25..8 — допустимый размер текселя выбранного уровня в пикселях (по умолчанию 1.25) |
| `light` | 24 числа или объект освещения синтеза |
| `eye` | `[x,y,z]`, z>0 — глаз перспективы в пространстве вида (единицы Re2D, после поворота позы). Нет поля — ортографическая проекция |
| `window` | `[x0,y0,x1,y1]` в долях спрайта — видимое окно; вне его ничего не рисуется (спрайт больше экрана) |
| `cull` | `true` — пропускать ячейки, отвернувшиеся от зрителя (только замкнутые модели) |
| `motionLod` | 0..4 — на сколько уровней грубее рисовать кадры движения; когда поза устоялась, кадр дорисовывается в полном качестве |

**Перспектива.** Луч из глаза через точку пересекает плоскость `z=0` модели; ось взгляда попадает в центр спрайта.
Фокус в пикселях экрана: `f = S·bodyScale·eye.z/extent`, где `S` — размер спрайта на экране. Вершины ближе `0.2·eye.z` к глазу отбрасываются.
Так ставится оружие от первого лица: глаз — там, где он задуман в исходной модели, спрайт центрируется на прицеле,
`window` обрезает то, что за экраном (пример — `demos/re2d_dust2/aks3/bake_aks3.sh` и `AKS` в `demos/re2d_dust2/main.js`).

## Получение v3

- `r2d-sdk bake-re2d3 модель.fbx|glb|obj` — FBX/GLB/OBJ → v3 с костями и клипами скелета (см. [SDK.md](SDK)). Поля за краем UV-острова ложатся на ближайшую точку ребра: поверхность доходит до ребра и не торчит за него.
- `r2d-sdk convert-re2d3 russi.png --character russi.character.json --name russi3` — апгрейд v2: геометрия ячеек делится на d×d текселей,
  цвет берётся прямо из атласа 4096 (без кэша 5×5), нормали из градиента; `--raster N` записывает `projection.raster`. Сменные `variants` (hair/costume) у v3 не переносятся.

## Ограничения

Скининг линейный (в v2 — dual quaternion для суставов); мимика v2 работает через видимость частей. Время синтеза зависит от размера
растра и уровня: автомат от первого лица на 2048 — ≈20 мс в покое и ≈10 мс в движении (`motionLod: 1`), зомби и Руся на 512 — ≈2,5 мс.
