From 6cd9967566827b5bebdaa295f58a0a7eb9bea4dd Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Mon, 9 Nov 2026 18:23:06 +0300 Subject: [PATCH] Add fish-pipeline skill --- README.md | 17 ++ fish-pipeline/SKILL.md | 268 +++++++++++++++++++++++++ fish-pipeline/references/experience.md | 60 ++++++ 3 files changed, 345 insertions(+) create mode 100644 fish-pipeline/SKILL.md create mode 100644 fish-pipeline/references/experience.md diff --git a/README.md b/README.md index f708565..2124ff0 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,20 @@ git clone https://git.shanaty.ru/Emil_Shanaty/skills.git mkdir -p ~/.agents/skills cp -r skills/game-designer ~/.agents/skills/ ``` + +## fish-pipeline + +Перенос рыбы из Blender в Unity с физическим скелетом, skinning и независимыми +morph/shape-key анимациями. Содержит экспорт GLB, настройку Rigidbody/ConfigurableJoint, +нормализацию morph-весов, проверки через BakeMesh и правила адаптации к новым моделям и API. + +- [Инструкции скилла](fish-pipeline/SKILL.md) +- [Проверенный опыт](fish-pipeline/references/experience.md) + +### Установка + +```sh +git clone https://git.shanaty.ru/Emil_Shanaty/skills.git +mkdir -p ~/.agents/skills +cp -r skills/fish-pipeline ~/.agents/skills/ +``` diff --git a/fish-pipeline/SKILL.md b/fish-pipeline/SKILL.md new file mode 100644 index 0000000..ae72ba6 --- /dev/null +++ b/fish-pipeline/SKILL.md @@ -0,0 +1,268 @@ +--- +name: fish-pipeline +description: >- + Переносит рыбу из Blender в Unity с физическим скелетом, skinning и независимыми + morph/shape-key анимациями. Использовать для FishGen, импорта GLB, ретопологии, + настройки Rigidbody/ConfigurableJoint, BlendShape loop, исправления надувания и + чрезмерных morph-деформаций. Адаптируется к модели и версиям API и обновляет + собственные инструкции только после проверенных результатов. +--- + +# Fish Pipeline + +Перенос рыбы из Blender в Unity с двумя независимыми уровнями деформации: + +- физический скелет двигает тело и хвост через `Rigidbody` и `ConfigurableJoint`; +- morph-анимация меняет только `blendShape`-веса в собственном loop и не управляет костями. + +Не считать задачу завершённой только по успешному импорту. Проверять геометрию, +материалы, физику, morph-веса и фактическую деформацию через `BakeMesh`. + +## Самообновление и адаптация + +При каждом вызове: + +1. Сначала читать этот файл и `references/experience.md`. +2. Проверять версии Unity, `com.unity.cloud.gltfast`, `com.unity.pipeline`, Unity CLI и Blender. + Использовать локальную документацию установленных пакетов и introspection API, а не память. +3. Не копировать параметры прошлой рыбы вслепую. Определять по данным модели: + - продольную ось и направление головы; + - размеры и масштаб; + - тип `Renderer`; + - число костей и morph targets; + - `GetBlendShapeFrameWeight` и диапазон значений кривых; + - пути `AnimationClip` и привязки кривых. +4. Если API отличается, сначала найти рабочий вызов через документацию или RNA/командную схему. +5. После успешной проверки обновлять этот skill и `references/experience.md`: + - записывать только подтверждённые симптомы, причины, исправления и способы проверки; + - устаревшие инструкции заменять, а не накапливать противоречия; + - неудачные попытки сохранять только как краткие предупреждения с итогом; + - объединять повторы и поддерживать файл компактным. +6. Приглашение пользователя к исправлению считать ошибкой pipeline. Сначала найти причину, + затем менять код или настройки. Не маскировать ошибку простым уменьшением анимации. +7. После подтверждения пользователем рабочего результата зафиксировать его как текущий baseline. + +Ограничения самообновления: + +- обновления выполняются только во время вызова skill и в рамках запроса пользователя; +- не запускать фоновые процессы, goals или cron для самообновления; +- не делать commit/push без явной просьбы; +- не менять пользовательские модели и анимации без необходимости; +- перед опасными изменениями сохранять исходники или делать отдельную рабочую копию; +- не записывать секреты, токены и личные данные; +- не отключать телеметрию и не менять настройки приватности. + +## Текущий проверенный контур + +- Unity: `6000.6.8f1`. +- `com.unity.cloud.gltfast`: `6.20.0`. +- `com.unity.pipeline`: `0.10.0-exp.1`. +- Blender: `5.2.3 LTS`. +- UPM-пакет: `/home/emil/Projects/Unity-tools/fish-gen`. +- Тестовый Unity-проект: `/home/emil/Projects/FishingGame`. +- Текущий модуль привязки: `fish-gen/Editor/FishModelBinder.cs`. +- Текущая morph-модель: `fish-gen/Models/Mesh0Animated.glb`. + +Это отправная точка, а не обязательные пути для других проектов. В другом проекте сначала +определить его корень и расположение FishGen через `unity status` и Package Manager. + +## 1. Подготовка модели в Blender + +Сохранять исходник отдельно. Для игровой рыбы предпочтительна чистая топология и UV. + +При ручной ретопологии: + +- сохранять петли вдоль позвоночника и у оснований плавников; +- строить низкополигональную поверхность поверх исходника; +- использовать Shrinkwrap и перенос/запекание текстур; +- проверять границы, non-manifold рёбра и тонкие плавники. + +При автоматической ретопологии не считать Decimate достаточным для анимации. Если QuadriFlow +отклоняет mesh, проверять manifold и нормали. Для сложной модели допустим Voxel/Sharp ReMesh, +но результат проверять визуально и по топологии. + +### Shape keys и маски + +- `Basis` не редактировать. +- Vertex Group ограничивает существующую деформацию, но не создаёт её. +- Для движения глаз и рта создавать отдельные shape keys и плавные маски. +- Для независимого morph-loop не использовать костную анимацию. +- Проверять крайние значения ключей на пересечения и растяжения кожи. + +При UV Data Transfer в Blender учитывать направление: для переноса из выделенных исходников +в активный target использовать `use_reverse_transfer=True`. Предварительно проверять RNA: +имена enum и поведение оператора зависят от версии. При ошибке восстанавливать исходник, +а не продолжать с повреждёнными UV. + +## 2. Экспорт GLB с morph-анимацией + +Перед экспортом убедиться, что shape keys имеют кривые. Экспортировать только нужный объект. +Пример для проверенной Blender-версии: + +```python +import bpy + +bpy.ops.object.select_all(action='DESELECT') +obj = bpy.data.objects["Mesh_0"] +obj.select_set(True) +bpy.context.view_layer.objects.active = obj + +bpy.ops.export_scene.gltf( + filepath="/home/emil/Projects/Unity-tools/fish-gen/Models/Mesh0Animated.glb", + export_format='GLB', + use_selection=True, + export_animations=True, + export_animation_mode='ACTIVE_ACTIONS', + export_frame_range=True, + export_force_sampling=True, + export_morph=True, + export_morph_animation=True, + export_apply=False) +``` + +Для другой модели заменить имя, путь и режим экспорта после чтения её action/NLA. +Не запекать shape keys в mesh. + +## 3. Импорт и проверка данных в Unity + +Использовать `unity-cli`, `unity-pipeline` и `unity-package-management`, если они доступны. +Пакеты добавлять через Package Manager, не редактировать `manifest.json` вручную. + +```bash +unity command script eval 'AssetDatabase.ImportAsset( + "Packages/com.shanaty.fish-gen/Models/Mesh0Animated.glb", + ImportAssetOptions.ForceUpdate); return "Imported";' +``` + +Проверить: + +- `SkinnedMeshRenderer` и материал; +- `mesh.blendShapeCount` и имена форм; +- наличие `AnimationClip`; +- `AnimationUtility.GetCurveBindings(clip)`; +- размеры mesh и направление головы. + +glTFast импортирует morph-анимации, но их проигрывание и назначение путей требует +отдельной настройки. + +## 4. Физический скелет + +Текущий рабочий режим Fish Editor: **Tools → Fishing → Fish Editor → Test Hanging**. + +Для каждого сегмента: + +- `Rigidbody`; +- `CapsuleCollider` с фактическими размерами сегмента; +- `ConfigurableJoint` с предыдущим сегментом; +- ограничения углов, пружина и damping. + +Первый сегмент соединять с kinematic `Mouth Anchor`. Не задавать непропорциональный +`localScale` физическому телу: размер задаётся коллайдеру, а визуальный mesh остаётся +отдельным дочерним объектом. Для короткой цепочки отключать self-collision между сегментами. + +`Tail Impulse` должен прикладывать силу к кончику последнего Rigidbody через +`AddForceAtPosition`, поочерёдно меняя направление. + +## 5. Skinning без повреждения morph targets + +Для текущей модели ось `+Y` соответствует голове, а скелет идёт от головы к хвосту по `+X`. +Для неё используется поворот `Quaternion.Euler(0, 0, 90)`. Для другой рыбы сначала определить +ориентацию — не применять этот поворот автоматически. + +Базовые правила: + +- позиции вершин: поворот, равномерный масштаб и перенос начала координат; +- нормали и tangents: только поворот; +- веса вершин: плавная смесь двух ближайших костей по продольной координате; +- `bindposes[i] = bones[i].worldToLocalMatrix * visual.localToWorldMatrix`; +- источник может быть как `MeshRenderer`, так и `SkinnedMeshRenderer`. + +Для каждого morph target копировать все кадры через `GetBlendShapeFrameVertices` и +`AddBlendShapeFrame`: + +- `deltaVertices`: поворот и тот же равномерный масштаб, что у базовых вершин; +- `deltaNormals` и `deltaTangents`: только поворот; +- исходный `AnimationClip` не изменять. + +### Два разных веса — не смешивать + +**Morph-frame weight** и **animation-curve weight** — разные вещи. + +В проверенном glTFast `GetBlendShapeFrameWeight` возвращал `1`, тогда Unity ожидал `100`. +Это вызывало экстраполяцию и надувание. При переносе определять диапазон и нормализовать его +к `0–100`. Если исходный диапазон `0–1`, умножать frame weight на `100`; если уже `0–100`, +не умножать повторно. + +Кривые morph-анимации также могут иметь диапазон `0–1`, тогда как Unity BlendShape использует +`0–100`. Вычислять коэффициент по фактическим ключам. Текущий пользовательский уровень +амплитуды — `20%` от исходной, но сначала исправлять нормализацию и только затем настраивать +силу анимации. + +## 6. Независимый morph-loop + +Использовать `Animation` на том же GameObject, где находится `SkinnedMeshRenderer`. + +Для каждого исходного клипа: + +1. Создать новый `AnimationClip` с `legacy = true` и `wrapMode = Loop`. +2. Копировать только кривые `SkinnedMeshRenderer` с `blendShape.*`. +3. Transform-кривые исключать, чтобы анимация не боролась с физикой. +4. Путь кривой заменить на `""`, поскольку renderer находится на том же объекте. +5. Ключи копировать в новый `AnimationCurve`, не мутируя исходный клип. +6. Установить кривую через `loopClip.SetCurve`. +7. Добавить клип в `Animation`, включить loop и `playAutomatically`. + +Исходный путь вида `Mesh_0` нельзя оставлять без проверки: он относится к иерархии Blender, +а не к созданному `Fish Visual`. + +## 7. Обязательная проверка + +После изменения C#: + +```bash +unity command script recompile --project-path +unity command script recompile_status --project-path +``` + +Запустить `Test Hanging` и проверить: + +- создание Rigidbody, joints, mesh и материалов; +- `blendShapeCount` и имена форм; +- `Animation.isPlaying`; +- все кривые loop указывают на `""` и только на `blendShape.*`; +- веса форм меняются во времени; +- `Tail Impulse` двигает последний сегмент одновременно с morph-loop. + +Сравнить `BakeMesh` до и после morph или импульса. Для текущего baseline при `Breath = 10` +максимальное смещение вершины должно быть примерно **0.00178 м**, а не 0.17772 м. + +Проверить консоль: + +```bash +unity command console status --project-path +unity command console get --level error --project-path +``` + +После теста остановить Play Mode, снять временный Run In Background override и удалить +временные probe-скрипты и тестовые объекты. + +## 8. Диагностика + +| Симптом | Сначала проверить | +|---|---| +| Рыба надувается | `GetBlendShapeFrameWeight`, экстраполяцию и фактический `BakeMesh` | +| Анимация почти незаметна | Диапазон ключей `0–1` против Unity `0–100` | +| Глаза/рот не двигаются | `legacy`, `Animation.isPlaying`, имена форм и пути кривых | +| Анимация ломает физику | Наличие transform-кривых вместо только `blendShape.*` | +| Mesh внизу/боком | Ось модели, bindposes и направление головы | +| Текстуры испорчены | UV, направление Data Transfer, активную UV-развёртку | +| Суставы дрожат | Масштаб Rigidbody, массы, solver iterations и self-collision | + +## 9. Фиксация результата + +После успешной проверки: + +- сохранить `.meta` вместе с ассетами; +- обновить этот skill и `references/experience.md` по правилам выше; +- убедиться, что временные probe-файлы удалены; +- commit/push выполнять только по явному запросу пользователя. diff --git a/fish-pipeline/references/experience.md b/fish-pipeline/references/experience.md new file mode 100644 index 0000000..e1be6b1 --- /dev/null +++ b/fish-pipeline/references/experience.md @@ -0,0 +1,60 @@ +# Проверенный опыт Fish Pipeline + +Обновлять только после подтверждения. Повторяющиеся записи объединять. Устаревшие правила +заменять в `SKILL.md`, а здесь сохранять краткую историю причин и проверок. + +## 2026-11-09 — Базовый физический скелет + +- Создана цепочка из 6 Rigidbody и 6 ConfigurableJoint с kinematic mouth anchor. +- Нестабильность уменьшается, когда физический transform не масштабируется, а размер задаётся + CapsuleCollider. Визуальная капсула находится в дочернем объекте. +- Tail Impulse через `AddForceAtPosition` к кончику последнего сегмента успешно раскачивает цепь. + +## 2026-11-09 — GLB и skinning + +- `com.unity.cloud.gltfast` импортирует mesh, материалы, morph targets и AnimationClips. +- Для первой модели 6 костей и 6194 вершины успешно деформировались после импульса. +- Направление модели нужно определять отдельно; поворот `Z +90` подходит только для проверенной + ориентации `head +Y` к `tail +X`. + +## 2026-11-09 — Независимая morph-анимация + +- Shape keys `Eye_Look_Left`, `Eye_Look_Right`, `Open_mouths`, `Breath` экспортированы из Blender + с `export_morph=True`, `export_morph_animation=True`, `ACTIVE_ACTIONS`. +- В Unity найден один 4-секундный клип с кривыми `blendShape.*`. +- Для независимого loop создан новый legacy `AnimationClip`, путь кривой изменён на пустой, + transform-кривые отброшены. Физика и morph-анимация работают одновременно. + +## 2026-11-09 — Нормализация morph-frame + +Симптом: рыба сильно надувалась, `Breath = 10` давало смещение вершины `0.17772 м`. + +Причина: glTFast вернул `GetBlendShapeFrameWeight = 1`, тогда Unity ожидал диапазон `0–100`. +Копирование значения `1` приводило к экстраполяции в 10 раз. + +Исправление: нормализовать frame weight к `0–100` при создании target mesh. + +Проверка после исправления: при `Breath = 10` максимальное смещение стало `0.00178 м`. +Не уменьшать анимацию до такой проверки — сначала исправить frame weight. + +## 2026-11-09 — Значения кривых + +- Исходные morph-кривые имеют диапазон `0–1`, Unity BlendShape использует `0–100`. +- При переносе ключи копируются в новый `AnimationCurve`; исходный клип не изменяется. +- Текущая пользовательская сила воспроизведения — 20% от исходной амплитуды. Это настройка после + исправления frame weight, а не решение проблемы надувания. + +## 2026-11-09 — UV Data Transfer в Blender + +- В Blender 5.2 направление оператора критично: для selected source → active target требуется + `use_reverse_transfer=True`. +- Неправильное направление повредило UV исходника в текущей сцене; источник восстановлен из + отдельной сохранённой копии. +- Перед вызовом читать RNA: допустимые enum для `layers_select_src/dst` зависят от контекста. + +## 2026-11-09 — Ретопология + +- Простой Decimate не признан подходящим для анимации этой рыбы. +- QuadriFlow отклонил исходную поверхность; Voxel/Sharp ReMesh дал 16166 квадов и сохранил форму. +- UV восстановлены через Data Transfer в правильном направлении. Для скелета нужна отдельная + проверка non-manifold участков.