# Re2DSprite v2 — большой PNG персонажа

Экспериментальный формат развёртки всего тела. В одном PNG находятся цвет,
варианты лица и карты поверхности. Готовых ракурсов и кадров ходьбы в нём нет.
Нативный генератор поворачивает отсчёты поверхности и выдаёт обычный 2D-спрайт
128×128 (pixel) или 512×512 со сглаживанием (anime) в существующий батч. UI остаётся RmlUi, игровая оркестрация — `$`.
Старые атласы [v1](RE2DSPRITE_V1) и их растер 64×64 продолжают работать.

## PNG и координаты

RGBA, квадрат 1024/2048/3072/4096; поставляемые ассеты — **4096×4096**.
Альфа только 0/255. Ни JSON-сайдкар, ни меш, ни готовые виды не требуются.
Рабочая сетка текущего прототипа — 1024×1024; ниже координаты этой сетки.
Для 4096 умножить координаты на 4. Размер хранилища не означает четыре
тысячи уникальных пикселей детализации: исходный сгенерированный арт имеет
разрешение 1254×1254 и уменьшен с Lanczos при упаковке материалов. Финальное увеличение PNG
делается с nearest, чтобы сохранить машинные карты. Размер исходной карты
поверхности остаётся 256×192; anime интерполирует её непрерывные участки.

| Область `[x,y,w,h]` | Содержимое |
|---|---|
| `[0,0,640,192]` | непрерывная поверхность головы, фронт в центре, шов затылка на краях |
| `[640,0,192,192]` | два материала ушей |
| `[832,0,96,192]` | пары глаз: open / half / closed / happy, 4 строки по 48 |
| `[928,0,48,192]` | рот: closed / open / smile / talk |
| `[976,0,48,192]` | брови: neutral / angry / sad / surprised |
| `[0,208,832,188]` | восемь отдельных лент волос |
| `[832,208,192,188]` | материал хвоста |
| `[0,408,320,168]` | поверхность торса |
| `[320,408,192,168]` | левая/правая рука |
| `[512,408,192,168]` | левая/правая нога |
| `[704,408,320,168]` | юбка или нижняя часть костюма |
| `[0,576,576,192]` | фартук, банты, аксессуары; пока резерв |
| `[576,640,448,128]` | обувь |

PNG-шаблон: `assets/rotsprite/rotsprite_v2_template.png`.
Маскот: `demos/assets/art/mascot/russi_rotsprite_v2.png`.
Соседние `russi_rotsprite_swim.png`, `russi_rotsprite_police.png` и
`russi_rotsprite_short.png` — доноры заменяемых частей той же раскладки.
Шаблон содержит нейтральные цветные силуэты и действующие карты, без надписей
в активных пикселях. Для нового арта закрашивать цветовые части, сохранять
служебные области; изменение формы требует обновить координаты/покрытие.

## Качество anime и расширение SUB

Режим anime сохраняет детали **внутри** ячейки материала: при декодировании
кэшируется сетка 5×5 texel, а при синтезе цвет и покрытие берутся по UV внутри
участка. Цвет больше не восстанавливается только из четырёх углов.
Рабочий буфер 1024×1024 разрешается в спрайт 512×512 с учётом alpha;
координаты суставов по-прежнему выражены в логическом квадрате 128×128.
Нативные функции `r2d_rotsprite_v2_anime` / `_workspace` сохраняют контракт
256×256 для существующих C-пользователей. `_sized(..., size)` поддерживает
256 и 512. Повторно используемый workspace корректно меняет размер.

Новый компилятор записывает маркер `(83,85,66,255)` (`SUB`) в `(3,960)`.
Только при наличии этого маркера действуют дополнительные каналы:

- X = `(XY.R-128)/4 + (XY.B >> 4)/64`.
- Y = `(XY.G-128)/2 + (XY.B & 15)/32`.
- Z = `(depth.R-128)/4 + (depth.G / 17)/64`, целочисленное деление.
- ID.G хранит исходную группу материала до разделения на кости.
- coverage.R=128 хранит прозрачную опорную точку; 255 — непрозрачную.
  Прозрачные точки нужны для интерполяции края, но сами не рисуют пятна
  и не закрывают другие слои в pixel-режиме.

Соседние точки одной группы материала могут принадлежать разным костям:
каждый угол участка следует своей кости. Это сохраняет соединение в коленях
и локтях. Несвязанные группы не соединяются. Старые PNG без SUB читаются
по прежним правилам; новые SUB-атласы требуют обновлённого движка.

У Руси-тян уточнены пропорции волос и юбки, свободная поза рук,
контур хвоста и обуви. Под прядями есть непрерывный слой волос ID=24
(материал `[0,576,192,192]`); остальные резервные области не менялись.
Статичные портреты в демо используют остановленный idle, чтобы не синтезировать
одинаковую картинку заново каждый кадр. Всё выводится через существующий
2D-батч; отдельная 3D-сцена, модельный формат и 3D-рендерер не добавлены.

## Служебные карты действительно участвуют в проекции

Четыре карты `[0/256/512/768,768,256,192]` описывают верхние 1024×768.
Отсчёт карты `(mx,my)` относится к цвету `(4mx,4my)`. На больших PNG обе
координаты умножаются на масштаб. Один отсчёт считывается на блок 4×4
рабочей сетки. Пространство `y=960..1023` — заголовок и резерв метаданных.

| Карта | Каналы |
|---|---|
| ID | R = часть; с SUB G = группа материала; с BLD B = вторая часть |
| глубина | R = целая компонента Z; с SUB G = дробная; с BLD B = вес |
| покрытие | R = 255 непрозрачный, 128 прозрачная опора SUB, 0 отсутствующий |
| координаты | R = X, G = Y; с SUB B = дробные компоненты X/Y |

`X=(R-128)/4`, `Y=(G-128)/2`, `Z=(R_depth-128)/4`.
Оси: X вправо, Y вниз, Z к зрителю. ID/глубина/координаты активной точки обязаны иметь alpha=255.
SUB также допускает alpha=0 у материала прозрачной опорной точки. Активный ID вне диапазона 1..254 отклоняется.
ID: 1 голова, 2 уши, 3 волосы, 4 хвост, 5 головной убор, 6 торс, 7/11 руки, 8/12 ноги,
9 юбка/низ, 13/14 обувь, 16..19 глаза, 32..35 рот, 48..51 брови.
Полностью пустые карты не допускаются; формат требует хотя бы один
поддерживаемый отсчёт. Донор может содержать только заменяемую часть. Неактивные резервные материалы не рисуются.

В `(0,960)`, `(1,960)`, `(2,960)` находятся RGBA
`(82,50,68,255)`, `(82,79,84,255)`, `(2,4,4,255)` — R2D / ROT / v2.
Для PNG 4096 это блоки 4×4. Чисто рисовальная картинка без заголовка
не станет v2 автоматически.

При загрузке C декодирует карты в собственный кэш отсчётов. Поворот yaw/pitch
и локальные суставы меняют координаты; лицевые детали лежат по кривизне головы, чёлка может перекрывать брови;
локальный буфер глубины решает
перекрытия, включая волосы и лицо. В режиме pixel цвет берётся только из исходных texel, без интерполяции. При загрузке
для материальных частей выбирается существующий texel, ближайший к среднему
цвету непрозрачных пикселей блока 4×4: одиночный контур не становится
широкой полосой. Глаза, рот и брови используют ближайший непрозрачный texel к центру блока. След отсчёта — 2×2 пикселя, при равной глубине выбирается
ближайший отсчёт; при полном совпадении выигрывает более светлый исходный
texel, затем применяется устойчивое сравнение цвета. Это дискретная 2.5D-проекция: край может менять силуэт
на один пиксель, а тонкие прозрачные ленты под скользящим углом могут иметь
разрывы. Генератор не восстанавливает отсутствующие поверхности из рисунка.

## Мимика, костюмы и движение

```js
const russi = $.re2dSprite.create('art/russi.png',{id:'russi'})
    .at(400,300).size(384,384).re2dRig({body:true}).re2dHotReload();
russi.re2dPose(30,0).re2dEmotion('happy');
russi.re2dPart('costume','art/police.png').re2dPart('hair','art/short.png');
russi.re2dMotion('walk'); // idle / walk / run; второй аргумент — скорость цикла
russi.re2dRig({armLeft:70,headYaw:-25});
```

Мимика: независимые глаза/рот/брови через `.re2dExpression({...})`;
`.re2dEmotion()` даёт neutral/happy/angry/sad/surprised/sleepy.
Моргание и речь в демо используют те же состояния, без кадров поворота.

Подменяются группы head/hair/tail/hat/torso/arms/legs/skirt/shoes/costume. Донор —
PNG v2. В текущем прототипе группа `head` означает поверхность, уши и головной убор;
мимика сохраняется. Замена копирует только выбранные отсчёты и их цвет,
сохраняет позу, выражение и другую одежду. Временный GPU-ресурс донора
освобождается; неизменяемый исходник на диске не переписывается.
Перечитывание всех доноров атомарно: невалидный файл сохраняет персонажа.

В legacy `.create(PNG)` псевдоскелет фиксирован: торс → голова,
плечи → руки, бёдра → ноги → колени → стопы. Проекция тела включается
через `rotRig({body:true})`. В режиме тела голова масштабируется .8 и
сдвигается на Y=-22. Параметры: phase в радианах, stride в градусах (ограничен ±75),
armLeft/armRight — подъём руки в плоскости, headYaw — отдельный поворот
головы. Idle/walk/run — процедурные циклы **на месте**; перемещение узла
по миру остаётся обычной `.at()`/игровой логикой R2D. Общего импорта
скелетов, IK, физики одежды и контакта стоп с произвольной землёй пока нет.
`.info().joints` сообщает положения суставов в пикселях растера 128×128;
демо переводит кисти на экран и позволяет тянуть их мышью.

## Обновление файлов и ресурсы

`.re2dHotReload(true)` опрашивает исходный PNG и доноров дважды в секунду.
Проверяются время изменения и размер локального файла. PNG из упакованного
VFS неизменяемы. `.re2dReload()` перечитывает вручную. Позы, мимика,
подмены и движение сохраняются; `.info()` сообщает reloads/reloadError.
При повреждённом PNG остаётся последний действующий ресурс, ошибка
доступна структурированно. Левый обзор PNG в демо — обычная текстура;
его кеш обновляется обычным перезапуском сцены, независимо от live-проекции.

У узла собственные CPU-кэш, текстура и handle; remove, смена сцены,
замена и hot reload освобождают старые ресурсы. Несколько персонажей не
делят позу. Nearest и экранный pixel snapping сохраняются. Общая
архитектура renderer и поведение обычных спрайтов не меняются.

## Проверка и авторинг

`python3 tools/make_rotsprite_v2.py` повторно компилирует пять поставляемых
PNG из сохранённых исходных изображений в `demos/rotsprite/source/`.
Исходный арт создан встроенным imagegen, запись запросов —
`demos/rotsprite/source/PROMPTS.md`; повторная компиляция генерации не требует.

Проверки: `tests/js/rotsprite_test.mjs`, `build/tests/r2d_rotsprite_test`
(ASan/UBSan), `tests/agent/highlevel_rotsprite_test.py`, демо и общие тесты.
Запуск: `./build/russiano2d --game demos --scene re2dsprite`.

## Аниме-проекция

`.re2dStyle('anime')` включает отдельный сглаженный режим для v2. Соседние
ячейки одной группы материала и разницей координат не более 3 образуют локальные
непрерывные участки. Интерполяция координат и цвета применяется внутри
участка, без соединения разных частей или швов. Для изолированных отсчётов
остаётся круглый footprint; изолированные края ушей/волос отбрасываются,
а тонкие соседние брови/рот соединяются непрерывной полосой. Штатная проекция выполняется в 1024×1024 с разрешением
покрытия и alpha-weighted уменьшением до 512×512; старые C-обёртки сохраняют 256×256. RGB краёв продолжается
на один прозрачный texel, чтобы linear не добавлял чёрную кайму. Это временные локальные
участки при синтезе RGBA: игрового mesh-ассета, 3D-сцены или нового renderer нет.

В anime нет палитровой квантизации, привязки позиции/масштаба к пикселям
и принудительного nearest. Выходной спрайт использует linear; привычные
pixel-правила сохранены в режиме `pixel` и v1. Координаты joints остаются
в каноническом поле 128×128 в обоих режимах. Стиль сохраняется вместе с
подменами, позой и мимикой при `.re2dReload()` и hot reload.

Демо использует anime. Авторские материалы головы, волос, лица, горничной,
купальника и полиции перерисованы с широкими чистыми тенями и цветными
контурами; прежние исходники сохранены как `*_pixel.png`. Запросы и происхождение:
`demos/rotsprite/source/ANIME_PROMPTS.md`. Сглаживание увеличивает CPU-работу;
это экспериментальный CPU-прототип, не обещание качества финального персонажа.

## Профиль и объёмы персонажа

Авторский компилятор `tools/make_rotsprite_v2.py` записывает в карты головы
симметричный профиль: переносицу, выступ носа, верхнюю/нижнюю губу,
подбородок и сужение челюсти. Это одна поверхность из того же материала,
не готовые виды в PNG. Мимика использует те же авторские координаты глубины лица;
рот расположен почти на коже, чтобы в профиль не висеть отдельным пятном.

Детали лица односторонние: в anime нормаль вычисляется по соседним точкам карты,
а в pixel используется приближение. Видимость проверяется с учётом
общего yaw/pitch и независимого headYaw. При повороте дальний глаз и бровь
скрываются, ближний глаз сжимается геометрической проекцией поверхности.
Это общий механизм для ID лица, а форма носа/челюсти остаётся данными PNG.

Торс компилятора имеет шею, расширение плеч, грудную клетку, талию и
разную глубину спереди/сзади; конечности сужаются к локтям/запястьям и
коленям/лодыжкам. Таз купальника имеет более выраженный задний объём, полицейская юбка —
узкий силуэт, юбка горничной — расширение ткани к низу. Якоря плеч и рук v2 находятся на X=±9.5, Y=-10.
Псевдоскелет и координаты joints согласованы с этими якорями.
Одежда и волосы по-прежнему экспериментальны; точного соответствия
анатомической turnaround-схеме эта заготовка не обещает.

Проверки: `python3 tests/rotsprite/profile_test.py` проверяет профиль носа,
губ и подбородка в математике и четырёх PNG; `r2d_rotsprite_test` проверяет
скрытие дальнего глаза в обеих проекциях и независимый поворот головы.

## Референс маскота

OBJ Meshy использован только вне движка для сравнения фронта, профиля и
пропорций. R2D не загружает OBJ или его треугольники. Карты PNG содержат
округлый череп, короткую челюсть и небольшой нос; белые боковые банты
относятся к группе ушей. Исходник головы: `head_identity.png`.
Происхождение: `demos/rotsprite/source/IDENTITY_REFERENCE.md`.

## Непрерывные материалы одежды

Материал горничной (`maid_unwrap.png`) содержит четыре непрерывные полосы:
торс, рукав с манжетой/кожей, юбка, чулок. Компилятор размещает фронт фартука
в центре UV-полосы, спину у шва; отдельные силуэты одежды не растягиваются
на цилиндр. Подмены купальника и полиции сохраняют прежние материалы.
`hair_soft.png` нормализуется по каждой из восьми прядей отдельно; длинные
пряди доходят до талии. Хвост использует изогнутую ленту той же палитры.
Банты — отдельные вырезанные материалы `bow_clean.png` в группе ушей.
Уши и банты считают UV от начала каждого блока 96 px; разрыв
абсолютного modulo устраняется в компиляторе.
Запросы встроенного image_gen: `demos/rotsprite/source/POLISH_PROMPTS.md`.

`python3 tests/rotsprite/material_test.py` проверяет отсутствие дырок в картах
торса/рукавов/ног, непрерывность координат ушей/бантов, фронт/спину фартука и переход ткани в манжету/кожу в PNG.

### Подбородок

Нижняя часть головы в авторском компиляторе растягивается после Y=3,
сходясь к мягкой вершине Y=11.5. Рот находится на Y=7.2; профиль носа и
положение глаз сохранены. Тест профиля проверяет в готовых PNG, что ниже
рта осталось минимум три единицы поверхности кожи, а не срез на уровне губ.

### Профиль носа

Переносица, небольшой выступ кончика и углубление под ним задаются общей
авторскими координатами глубины лица. Положение кончика Y=4.4; верхняя губа — Y=6.6.
Тест профиля проверяет выступ относительно переносицы и переход под носом
в готовых координатных картах PNG. Материал `nose_detail.png` добавляет
тонкий тёплый штрих на ту же поверхность для фронта; это не отдельный ракурс.
Запрос: `demos/rotsprite/source/NOSE_PROMPT.md`.

## Общие модели без фиксированного rig

[JSON v1](RE2DSPRITE_JSON) выносит авторскую поверхность, части, скелет,
анимации, группы доноров и сокеты в данные. `.from(JSON)` использует явные
матрицы частей, ID 1..254; старый `.create(PNG)` сохраняет legacy rig.
В демо используются russi.character.json, russi.animations.json и отдельные
PNG/JSON АК-47, пистолета, дробовика. Поверхность маскота находится в
demos/rotsprite/source/*.surface.json; анатомические формулы больше не
зашиты в авторский Python-компилятор.

Полное руководство: [RE2DSPRITE_GUIDE.md](RE2DSPRITE_GUIDE).

## Плавные суставы и непрерывная деформация

Участки anime растеризуются обратным билинейным отображением: UV и глубина
вычисляются на одной поверхности, без диагонального перелома между треугольниками.

Компилятор поддерживает `blendWidth` в правилах `segments`: ширину перехода
в логических единицах вокруг `greaterThan`. Нулевое значение сохраняет жёсткое
разделение. Внутри перехода вес второй кости задаётся smoothstep.
Маркер `BLD` `(66,76,68,255)` в `(4,960)` включает ID.B (вторая кость)
и depth.B (её вес 0..255). Без маркера эти каналы не участвуют в сгибании.
Жёсткие преобразования смешиваются двойными кватернионами, сохраняя толщину
сустава; для произвольного масштаба используется линейное смешивание координат.
Положение сокетов определяется костями, а не весами материала.

У maid плавные локти и колени, укороченный торс, округлённый силуэт юбки
и сужающийся контур кистей. Эти изменения правят только координатные карты:
цветовые материалы сохранены без изменений.

Полный справочник формул и функций по частям тела: [RE2DSPRITE_MATH.md](RE2DSPRITE_MATH).
