Излишний 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 и какого-нить хитрого автомата разброса весов по воздействиям.

Вот таким вот образом. Если кому-то интересен сам скриптик, могу выслать пошалить.

Спасибо В.В. Забелину за наставление на путь.

И всем Спасибо за внимание,

Игорь Леводянский

208 0 850 1
0
RENDER.RU