Files
skills/fish-pipeline/SKILL.md
T
2026-11-09 18:23:06 +03:00

16 KiB
Raw Blame History

name, description
name description
fish-pipeline Переносит рыбу из 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-версии:

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 вручную.

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#:

unity command script recompile --project-path <project>
unity command script recompile_status --project-path <project>

Запустить Test Hanging и проверить:

  • создание Rigidbody, joints, mesh и материалов;
  • blendShapeCount и имена форм;
  • Animation.isPlaying;
  • все кривые loop указывают на "" и только на blendShape.*;
  • веса форм меняются во времени;
  • Tail Impulse двигает последний сегмент одновременно с morph-loop.

Сравнить BakeMesh до и после morph или импульса. Для текущего baseline при Breath = 10 максимальное смещение вершины должно быть примерно 0.00178 м, а не 0.17772 м.

Проверить консоль:

unity command console status --project-path <project>
unity command console get --level error --project-path <project>

После теста остановить 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 выполнять только по явному запросу пользователя.