Излишний Weight
Излишний Вес, он словно бес,
Он цепко держит наши органы в осаде,
А также виден он и спереди, и сзади,
Чтоб он исчез, излишний вес.
(Юрий Визбор)
Кто бы мог подумать, что проблема снижения веса может быть актуальна не только "для двух дам, разговор которых подслушал правдивый автор известной песни в ресторане аэропорта города Челябинска, в то время, когда туда по случаю непогоды совершали посадки самолеты различных направлений". Работники Мыша и Дисплея не менее трепетно относится к излишнему весу, правда не своему, а своих персонажей. Всем аниматорам уже наверное осточертел процесс выскинивания мешей, особенно когда это касается игровых движков. Колбасишься и день, и другой, и третий, пайнтишь веса скина, чуть не вылизываешь его. И на тебе! Когда все закончено и хочется потянуться и похвастать результатом (это уже на уровне рефлекса или народной приметы) приходит программист и объявляет, что движок будет поддерживать только четыре кости на точку меша. Б...! И что, все заново? Да ничуть! - скажут бывалые Майщики, - Устанавливай максимум влияний (Set Max Influences) в нужное число, жми батон и экспорть в ихний продвинутый движок! А дотошный молодняк возражает, однако: - Так порушим с таким трудом выскиненные веса! И тут начинаются проблемы. Веса-то, конечно, порушатся, но не мешает для начала проверить, как это повлияет на анимацию, вдруг прокатит нормально. Не прокатило? Тогда придется потрясти мозгом. А вот неплохо было бы , ежели бы... например, можно было выделять наборы точек, количество влияний на которые превышает оговоренное программистом число. А потом чего-нибудь с этим набором сотворить, чтобы количество влияний как-то уменьшить. Попробуем поупражняться в MEL. Говорят, панацея от всего. И в первую очередь - от свободного времени. Затягивает, сволочь, и уже не отпускает, зверюга. Значит так! Есть некая полигональная поверхность, которая прискинена к костям и не только отпайнтена по весам (это при лучшем раскладе), но еще и проанимирована (просто кранты). Раз есть skin, то должно быть и его Имя и влияющий skinCluster, что и подтверждает ChannelBox после выделения меша в разделе INPUTS. Достать эту инфу можно используя команды ls с флагом -sl, а также listHistory -il 2, где флаг с аргументом 1 или 2 поставит в заполняемом массиве имя skinCluster на второе место. Подсчитать количество управляющих точек поверхности несложно с помощью команды polyEvaluate с флагом -v и упоминанием об имени поверхности $nameCurSurf. Вот что получается:
//Определяем имя выбранной поверхности
string $nSS[] = `ls -sl`;
string $nameCurSurf = $nSS[0];
// print "SurfaceName is: "; print $nameCurSurf;
//Определяем имя Skin Claster
//Важно! обратить внимание на флаг - уровень важности узла.
string $skCl[] = `listHistory -il 2`;
string $skinCl = $skCl[1];
// print "influence: "; print $skinCl;
//Получаем количество точек поверхности
int $iBd[] = `polyEvaluate -v $nameCurSurf`;
int $iBound = $iBd[0];
// print "CV Surface Count is: "; print $iBound;
Продолжаем.
Зная количество точек, нетрудно организовать цикл, в котором можно определить поименно ненулевые воздействия на каждую точку меша. Кроме тех мизерных которые в любом случае попадут под обрезание командой Prune. Для этого заполним некий строковый массив string $skPc[] командой skinPercent с флагом -ib и ограничивающей тот самый мизер неким float $cvWghA, а также идентификатором текущей рассматриваемой точки $skinCl ($nameCurSurf + ".vtx[" + (string) $iStep + "]"). Нужно подчеркнуть, что команда переведена в режим запроса флагом -q, ПОСЛЕ которого следует флаг -t, отвечающий за возврат имен воздействий на точку. Останется только подсчитать количество этих воздействий, и, если оно превышает заданное число $nJointCur, выделить текущую точку. Кроме этого есть возможность узнать минимальное воздействие в числовом виде. Заполним другой массив float $skPcW[] командой skinPercent со знакомым флагом -q и флагом -v, возвращающим цифровые величины воздействий на точку. Опять же в цикле до последнего воздействия определяем минимальное строкой $skW = min ($skPcW[$mStep] , $skW); где используется функция min, сравнивающая два числа. Эту цифру можно использовать как аргумент для Prune.
//Загоняем предел веса в максимум
float $skW = 1.0;
//цикл перебора точек поверхности
int $iStep = 0;
do
{
//Массив имен ненулевых воздействий Skin Cluster на рассматриваемую точку поверхности
string $skPc[] = `skinPercent -ib $cvWghA -q -t $skinCl ($nameCurSurf + ".vtx[" + (string) $iStep + "]")`;
//Определяем длину массива, то есть количество ненулевых воздействий в текущем Skin Cluster
int $inflCount = (size($skPc));
//Сравниваем количество элементов массива с порогом, устанавливающим минимальное количество ненулевых влияний костей на точку
if ($inflCount > ($nJointCur - 1))
{
select -tgl ($nameCurSurf + ".vtx[" + (string) $iStep + "]");
//Массив весов ненулевых воздействий Skin Cluster на выбранную точку поверхности.Цикл выбора минимального
float $skPcW[] = `skinPercent -ib $cvWghA -q -v $skinCl ($nameCurSurf + ".vtx[" + (string) $iStep + "]")`;
int $mStep = 0;
do
{
$skW = min ($skPcW[$mStep] , $skW);
$mStep++;
}
while ($mStep < $inflCount);
}
$iStep++;
}
while ($iStep < $iBound);
//Расчет нового уровня отсечки веса
$cutWgh = $skW + $cvWghA;
Фактически этими строками описана процедура поиска набора искомых точек. Вроде все! Но хочется построить и какой-то интерфейсик для общения. Сразу же пресечем попытки Maya нас запутать и утопить в болоте UI.
//Строим окно информации и запросов
string $winInCVFind = "winICVF";
if (`window -exists $winInCVFind`)
{
deleteUI $winInCVFind;
}
То есть, если окно с таким именем уже присутствует в UI, то безжалостно его убиваем.
Начинаем постройку окна командой window с флагом -t, назначающим окну последующее за ним (флагом) Имя, укажем строковый идентификатор окна.
window -t "CVs Non-Zero Weight Finding Process" $winInCVFind;
columnLayout winInCL;
Наведем красоту этакой рамочкой:
frameLayout -labelVisible false -marginWidth 5 - marginHeight 5;
Раскладка окна следующая:
columnLayout;
Первый горизонтальный блок сотоит из трех колонок. В них внесены поля с информацией о поверхности и цифра точности.
rowColumnLayout -numberOfColumns 3
-columnWidth 1 110 -columnWidth 2 75 -columnWidth 3 160;
text "DFS";
text "Surface Name";
textField -w 100 -text $nameCurSurf -ed false;
text " ";
text "CV Amount";
textField -w 100 -text ($iBound - 1) -ed false;
text " ";
text "Influence By ";
textField -w 100 -text $skinCl -ed false;
text " ";
text "Accuracy";
floatField
-ed false
-minValue 0.0001 -maxValue 0.005
-v $cvWghA
-s 0.0005
fWA;
setParent..;
Второй горизонтальный блок сотоит из двух колонок. Здесь выделяется кнопка обрезки излишних весов, вызывающая процедуру "butPruneW" и слайдер с полем пороговой цифры этого обрезания.
rowColumnLayout -numberOfColumns 2
-columnWidth 1 105 -columnWidth 2 240;
button -w 70 -label "PruneWeigth" -command "butPruneW";
floatSliderGrp
-field true
-minValue 0.001 -maxValue 0.5
-fieldMinValue 0.001 -fieldMaxValue 0.5
-value $cutWgh
-s 0.001
fWC;
setParent..;
Третий горизонтальный блок сотоит из трех колонок. В нем присутствуют поле, задающее желаемый максимум воздействий на точку, и слайдер с полем, отражающим текущее количество воздействий.
rowColumnLayout -numberOfColumns 3
-columnWidth 1 75 -columnWidth 2 30 -columnWidth 3 240;
text "Joints Max/Cur";
intField
-ed true
-minValue 1 -maxValue 20
-v $nJoint
-s 1
iJM;
intSliderGrp
-field true
-minValue 1 -maxValue 20
-fieldMinValue 1 -fieldMaxValue 20
-value $nJointCur
iJC;
setParent..;
Четвертый горизонтальный блок сотоит из пяти колонок. Здесь используются только кнопки с назначенными им процедурами. Например, кнопка "Find" отвечает за описанную выше процедуру поиска набора точек, а кнопка "Component Editor" - за вызов соответствующего окна. Назначение остальных кнопок ясно.
rowColumnLayout -numberOfColumns 5
-columnWidth 1 53 -columnWidth 2 52 -columnWidth 3 80 -columnWidth 4 100 -columnWidth 5 60;
button -label "Find" -command "butFind";
button -label "Reset" -command "butRes";
button -label "Automate" -command "autoProc";
button -label "Component Editor" -command "openCE";
button -label "Exit" -command "closeWinCVFind";
setParent ($winInCVFind + "|winInCL");
Внизу окна не мешает организовать строку статуса для отображения текущей информации.
//Формирование строки статуса
textField -text $sSt - ed false -w 359 winStatusLine;
showWindow $winInCVFind;
Алгоритм работы прослеживается таким. Выделяется поверхность и запускается скрипт. Возникает окно (во, чудеса!)
Неплохо быубедиться в соответствии информации с данными Channel Box. Вроде бы нормально.
В появляющемсяокне выставляются желаемые параметры, например:
Joints Max 4, Joints Cur 9 и нажимается кнопка Find. Это означает, что желаемый предел воздействийравен 4, но хочется проверить, есть ли точки, на которые давят аж 9воздействий. Разумеется найдутся! Хоть одна!
И если вызвать Component Editor, то видно, что это не иллюзия.
В строке статуса построенного окна отображается минимальный вес, воздействующий на найденную точку.
Дальше есть два варианта: можно нажать на батон с садистской надписью PruneWeigth и зарубить все веса ниже отображаемого в поле рядом (а там цифра выше чем в строке статуса примерно на величину, обозначенную в поле точности вычисления). На точку сразу станут воздействовать уже не 9 костей, а только 6, поскольку видно, что три воздействия в полях Component Editora равны 0.004, что подпадает под, пардон, обрезание. Взгляните в Editor. Нетути больше 9 воздействий. Ну и, как говорится, XYZ с ними.
А можно пойти другим путем и вручную разбросать веса по полям Component Editor.
Закончив разборки с 9 воздействиями, переходим к 8, затем (кто бы мог подумать) к 7, и так последовательно до 4. УРА! Программисты останутся довольны, движок не <перегреется>, а геймеры валом повалят покупать новую гейму, и потекут денежки, сладко звеня.
Есть еще батона с невнятным лэйблом Automate. Это всего лишь автоматическая реализация первого варианта, то есть с PruneWeigth, но автоматически. Это приводит к таким же примерно результатам, как и известный уже Set Max Influences, только гораздо дольше. Но в принципе, можно, наверное, придумать какой-то алгоритмик и здесь, этакое сочетание Prune и какого-нить хитрого автомата разброса весов по воздействиям.
Вот таким вот образом. Если кому-то интересен сам скриптик, могу выслать пошалить.
Спасибо В.В. Забелину за наставление на путь.
И всем Спасибо за внимание,