Каждый день - небольшой праздник.. жаль, что по утрам, когда ночь прошла в безумной головомойке.
СБОРКА КАСТОМ СКИНЕД МЕШАВерхний пост - дурацкий костыль.. развиваю тему...
Важно! Это не панацея и очень частный случай. В моей ситуации
все компоненты имеют один шейдер и потому в сшивании мешей отсутствует проверка рендереров на сабмеши. Об проверках и сабмешах написано в примерах документации.
Код рассчитан на работу с единичными весами, веса на 2 и 4 кости требуют некоторой добавки буковок и цыферок.
Синтаксис подсвечивать не буду из-за перегруженности его обертки.
Код перемешан с текстом по причине того что это не готовый скрипт, а методика, которую надо понять, а не заюзать.
Итак....
Представим ситуацию, что нам надо смержить голову-торс-ноги-кепку (платформу-турель-башню-ствол), которые хранятся отдельными скинедмешами к их родному скелету.
Для этого нам надо иметь:
- иерархию скелета - массив костей, причем не список Трансформов из сцены, ибо он не факт совпадает с последовательностью индексов косточек в весах.
- Код: Выделить всё
var bones : Transform[];
- биндингпозу - массив, что описывает струтуру скелета для скининга начального меша
- Код: Выделить всё
var bindPoses : Matrix4x4[];
- набор мешей ( голова-торс-ноги, привязанные к нашему скелету ) доступный для обращения.
В примере я налинковал себе меши прямо в скрипт для гибкости тестирования.
- Код: Выделить всё
var meshBody : Mesh;
var meshHead : Mesh;
var meshLeg : Mesh;
- шаблон скинедмеш, который, во-первых, облегчит работу с персонажем в редакторе (желтый кубик всяко лучше невидимки

) и послужит ресурсом для получения данных по костям и позе. В примере я экспортнул скелет+4скинедмеша(шаблон и три компонента).
Далее, создавая префаб скелета, удалил компоненты, оставив лишь кости и шаблон. Назвал шаблон "character", чтобы найти его по имени, что не суть важно.
Шаблон можно просто прискинить к root-кости. Кстати, позу можно выдернуть из любого меша-компонента далее, а вот чтобы получить кости, требуется скинедмеш. Если составные части скелета будут храниться, как чистые меши - нужен шаблон. Если части сядут в префабы со скинедмешами и материалами, шаблон можно удалить и читать кости и позу из первого попавшегося компонента.
Далее, что мы делаем (собственно, функция-сборщик):
- читаем из шаблона кости
- Код: Выделить всё
var character : Transform = transform.Find("character");
var smr : SkinnedMeshRenderer = character.GetComponent(SkinnedMeshRenderer);
bones = smr.bones;
- читаем из шаблона позу
- Код: Выделить всё
bindPoses = smr.sharedMesh.bindposes;
- убиваем шаблон, ибо дело он свое сделал и больше нам ни на что не нужен.
- Код: Выделить всё
Destroy(character.gameObject);
- создаем девственный скинедмеш для будущего заполнения его смыслом:
- Код: Выделить всё
gameObject.AddComponent(SkinnedMeshRenderer);
var renderer : SkinnedMeshRenderer = GetComponent(SkinnedMeshRenderer);
Подгружаем в CombineInstance все меши и мержим их. В примере все реализовано для быстроты теста, не смотрите на это.
- Код: Выделить всё
var combine : CombineInstance[] = new CombineInstance[num];
combine[0].mesh = meshBody;
combine[1].mesh = meshHead;
combine[2].mesh = meshLeg;
var res : Mesh = new Mesh();
res.CombineMeshes (combine, true, false);
Вот тут закралась одна какашка:
Наши меши имеют биндингпозы идентичные с шаблоном, ибо привязаны и меши и шаблон к одному скелету, но комбайнеру на это глубоко наплевать и он логично полагает, что каждый следующий меш имеет собственные косточки и потому индекс 0 второго меша на 4-х костях станет уже индексом 4, этот же индекс третьего меша станет 8 и так далее.
В результате, имея скелет из четырех костей, мы получим новый меш лишь частично к нему прискиненный. Большая часть меша схлопнется в никуда, не имея информации об своих костях выше индекса последней кости в скелете (для скелета на четырех костях - это индекс 3).
Я со вчерашней ночи все никак не мог сообразить, куда пропадают составные части, пока не решил распечатать веса собранного меша.

Вылечить проблему оказалось просто - достаточно пробежать по индексам меша и пересчитать их, взяв остаток от числа костей скелета.
- Код: Выделить всё
var weights : BoneWeight[] = res.boneWeights;
for( i = 0; i < weights.length; i++ )
weights[i].boneIndex0 = weights[i].boneIndex0 % bones.length;
res.boneWeights = weights;
Далее назначаем новому мешу и рендереру скелет и биндингпозу, что вырезали из шаблона.
- Код: Выделить всё
res.bindposes = bindPoses;
renderer.bones = bones;
И, наконец, вешаем меш в качестве шареда для скинед-меш-рендерера.
- Код: Выделить всё
renderer.sharedMesh = res;
В результате мы имеем уникальный кастомизированный однодравкольный меш, собранный из сколько угодно деталей. Что, собственно я и пытался получить
