Диета без планшета...

"Хорошая мышь - дохлая мышь..."
(Wacom)

С этим спорным утверждением можно было бы согласиться, особенно ближе к концу процесса скиннинга. Особенно если вместо изящного стила с послушным почти одушевленным вакомовским пером люфтят под пальцами бесчувственные и безинтеллектуальные мышиные кнопки, и временами хочется просто придушить этого гнусного зверька.

Но, как говорил уважаемый тов. Саахов: "Торопиться не надо!". Уничтожите одно устройство ввода, потом другое, потом... А потом глядишь, и вводить разучитесь.

Итак - скиннинг... Муторное занятие, занимающее по шкале отвратности гнусное предпоследнее место перед текстурированием в UV пространстве. И совсем плохо если некому спихнуть это творческое занятие. Ну, некому - и баста. А задачка следующая - отскинить кольчужную рубашонку на уже развешенном (от слова Вес) Body, например. До того ломает грузить в Трах-едитор тестовые анимации и елозить пером по этой кофточке. Тем более, что уже один раз это было уже сделано, на сАмом деле на самОм теле. "Обидно, клянусьчестноеслово!" - если снова вспомнить Саахова. Мозги вяло сопротивляются, но выдают сентенцию такого рода - возьми вес CV-точки от Body и присвой его ближайшей к ней CV-точке одежки. Тут грудью встает природная Лень, это ж после навески Скин-кластера сколько раз нужно одно и то же долбить: выделить исходную, зайти в Компонент-едитор, снова выделить, скопировать вес, снять выделение, выделить целевую точку, опять в Компонент, отключить Весовую Нормализацию, опять выделить, запэйстить, снова включить Нормализацию... И так по каждой паре точек! Это нормально? - спросят мозги с руками хором, и добавят, невоспитанно указывая пальцами: - Нехай MEL занимается этим!

MEL вздохнет - опять крайний! Самая работа для него. Щас наваяем приблуду - и кофе пить. Заодно и занятие для мыша найдется, и WACOM побережется.

Прикинем структуру скрипта и список его необходимых действий.

Для начала нужно построить какое-то окно для ввода и отображения информации. На этом этапе можно пофантазировать об интерфейсе, полях ввода, полях отображения, переключателях, кнопочках и т.д. Забегая вперед, и не навязывая кому-то, возможно, для кого-то жуткий дизайн, предположительное окно могло бы выглядеть следующим образом, подсвечивая синим гордое название "CVs Weught Duplicator":

Вызов процедуры можно оформить иконкой и поместить ее на полку - видна маленькая фигурка Шварценеггера. Опять же ж удобно и очень тешит самолюбие. Вероятно, что понадобятся поля для отображения информации об исходном объекте (Source) и целевом (Destination). Хорошо бы иметь возможность работать не только с парами точек, но и с парами поверхностей, чтобы, так сказать, оптом копировать веса. Для этого можно разделить рабочий поток на два соответствующих направления: WorkFlow можно направить по шоссе обработки массивов точек CV D-Array (по умолчанию) или по куриной тропе парных клевков CVs S/D-Pair (для чего, разумеется, придется кликнуть МЫШОМ в пустой radioButton). Затем, произведя выделение нужной пары, нажимаем кнопочку в разделе Operations. Веса копируются. Вроде, все. Чуть не забыл, при выделении массива точек, перед копированием нужно сначала еще произвести процедуру поиска ближайшей точки.

Итак, есть некое тело одетое в кольчужные доспехи. И на теле, и, подчеркиваю, на кольчуге, уже ДОЛЖЕН быть скин-кластер. Читаем строку подсказки Select source surface or CVs и выбираем исходную поверхность.

Она подсветилась. Слева в полях Source можно прочесть имя выделенной поверхности и имя воздействующего на нее Skin-cluster.

Смотрим на подсказку Select destination surface. Выбираем. Поверхность подсвечивается и сразу входит в режим выделения компонентов. Нажимаем F9 и выделяем нужный массив точек (опять Мышом).

Пусть это будет Юбка кольчуги. Справа появились имена выделенной поверхности и соответствующего Skin-cluster.

Теперь Мышкой можно нажимать кнопочку Search-Duplicate. Она запустит два процесса подряд, сначала поиск ближайших к выделенным точек и формирование пар,

а затем копирование веса в каждой паре от источника к цели.

Окошечки прогресса операций поиска и дупликации скрасят ожидание.

Чтобы убедиться в идентичности весов, можно выделить (как вы полагаете каким устройством?) на глазок пару, в которой присутствовала бы одна из точек обработанного набора из кольчуги и ближайшая к ней точка тела, и открыть Component Editor.

Аналогичным образом копируются веса и в случае работы с парами точек. Только здесь не нужен поиск, достаточно выделить исходную и целевую точки (при этом не нужно использовать Shift).

В разделе Indicate содержатся несколько кнопок (кнопки БОЛЬШИЕ, любым Мышом попасть можно). Они предназначены для удобства работы.

Например, кнопка CE вызывает Component Editor. Кнопка Flash позволяет высветить уже обработанные пары точек. Галочка в CheсkBox под названием Highlight CVs permanently подсвечивает обработанные точки автоматически. Но при этом для последующего выделения пар точек придется лишний раз кликать для снятия выделения. Поэтому по умолчанию для ускорения техпроцесса эта опция не задействована. Подсвечивать можно соответственно либо точки Source, либо Dest, либо те и другие Both. Если в течение какого-то промежутка времени работа по копированию весов в парах не закончена, обработанный массив можно сохранить в файле с помощью вкладки меню File\Save, а затем вернуться и считать этот же массив соответственно File\Open.

При переходе к новым парам поверхностей необходимо нажать Reset для подготовки программы к новой работе. Либо закрыть програму и открыть ее снова.

Рассмотрим узловые моменты скрипта.

//Основная процедура

global proc prWeightDuplicator()

{

global string $winWDup;

//Вызов процедуры полной очистки

butFullReset;

//Вызов процедуры построения окна.

createWinWDup;

/*Отслеживается событие SelectionChanged.

При его возникновении вызывается процедура

обновления данных окна.*/

scriptJob

-parent $winWDup //присоединение scriptJob к рабочему окну-родителю

-event "SelectionChanged" "updateWinWDup";

}

В основной процедуре используется обработка события "SelectionChanged", то есть обрабатывается реакция на изменение в листе выделенных объектов. Это связано с необходимостью обновить ("updateWinWDup")данные в окне СРАЗУ после выбора МЫШОМ сначала исходного, а затем и целевого объектов. Нужно обратить внимание на строку подсказки. Например, там высвечивается предложение "Select source surface or CVs". Смело выделяете исходный объект. Затем появляется предложение "Select destination surface". Выполните и это. Теперь бездушный ящик знает, какие поверхности вы подразумеваете.

Рассмотрим подробнее построение окна.

//Процедура построения окна UI

proc createWinWDup ()

{

...

if (`window -exists winWDupUI`)

{

deleteUI winWDupUI;

}

$winWDup = `window -t "CVs Weight Duplicator " winWDupUI`;

Это процедура прощупывания на существование одноименного элемента интерфейса и при необходимости удаления лишней копии...

А вот и разделы меню.

// Разделы меню

menuBarLayout -p winWDupUI winWDupMenuBar ;

menu -label "File" winWDupFileOperations;

menuItem -label "Open" -c ("fileSourceDestPairsOpen");

menuItem -label "Save" -c ("fileSourceDestPairsSave");

menu -label "Reset" winWDupReset;

menuItem -label "Reset..." -c ("butFullReset");

menuItem -label "Close Progress" -c ("progressWindow -endProgress;");

menuItem -label "Clear Arrays" -c ("arraysReset");

menuItem -label "Clear Names" -c ("namesReset");

menuItem -label "Default Variables" -c ("initSet");

menu -label "Help";

menuItem -l "Help..." -c ( "winWDupHelp" );

menuItem -l "About..." -c ( "winWDupAbout" );

Раскладка окна...

columnLayout winWDupWinWhole;

rowColumnLayout -numberOfColumns 2 -columnWidth 1 125 -columnWidth 2 125 winWDupFrSelBl02;

textField -text $sourceSkin -ed false sourceSkinTF;

textField -text $destSkin -ed false destSkinTF;

textField -text $sourceSkinCl -ed false sourceSkinClTF;

textField -text $destSkinCl -ed false destSkinClTF;

textField -text $sourceCV -ed false sourceCVTF;

textField -text $destCV -ed false destCVTF;

setParent..;

В этих полях отражена необходимая контрольная информация о выбранных парах, а именно имя поверхности, имя влияющего скин-кластера, и при работе с парами точек - номер CV точки. Это может быть полезно, например, если перепутан поорядок выбора, то есть на запрос об источнике вы указали на цель, и наоборот. Чтобы поменять из местами существует кнопка Swap:

rowColumnLayout -numberOfColumns 1 -columnWidth 1 250 winWDupFrSelBl01;

button -w 200 -label " Source <=== Swap ===> Destination" -command "butSwap";

setParent..;

А вот и вышеописанное переключение между путями рабочего потока.

frameLayout -w 250 -mw 5 -mh 5 -label " WorkFlow" -labelAlign "center" -borderStyle "etchedOut" winWDupFrSel;

rowColumnLayout -numberOfColumns 1 -columnWidth 1 235 winWDupFrSelBl01;

$radSel = `radioButtonGrp -numberOfRadioButtons 2

-labelArray2 "CVs D-Array" "CVs S/D-Pair"`;

setParent..;

setParent..;

Далее описание назначения кнопок, в подразделах раскладки окна. Хоть и нудно, но нужно просто быть внимательным, чтобы точно оследить и вовремя закрыть каждый подраздел раскладки окна, связанный с соответствующим Parent.

frameLayout -w 250 -mw 5 -mh 5 -label " Operations" -labelAlign "center" -borderStyle "etchedOut" winWDupFrDup;

rowColumnLayout -numberOfColumns 2

-columnWidth 1 117 -columnWidth 2 116;

button -label "Search - Duplicate" - en true -command "butAutoSearchDupl" butASD;

button -label "Duplicate" -en false -command "butDuplWeight" butD;

setParent..;

setParent..;

frameLayout -w 250 -mw 5 -mh 5 -label " Indicate" -labelAlign "center" -borderStyle "etchedOut" winWDupFrInd;

rowColumnLayout -numberOfColumns 3

-columnWidth 1 145 -columnWidth 2 45 -columnWidth 3 45;

$chBoxHiL = `checkBox -label "Highlight CVs permanently"`;

button -w 45 -label "Flash" -command "butDone";

button -w 45 -label "CE" -command "ComponentEditor";

$radHil = `radioButtonGrp -numberOfRadioButtons 3

-cw3 55 45 45

-labelArray3 "Source" "Dest" "Both"`;

text " ";

text " ";

setParent..;

setParent..;

//Строка Статуса

textField -w 250 -text $sSt -ed false winStatusLine;

//Значения по умолчанию

checkBox -edit -value off $chBoxHiL;

radioButtonGrp -edit -en false -select 3 $radHil;

radioButtonGrp -edit -en true -select 1 $radSel;

//Функционирование

checkBox -edit

-onCommand "radioButtonGrp -edit -en true $radHil;"

-offCommand "radioButtonGrp -edit -en false $radHil;"

$chBoxHiL;

radioButtonGrp -edit

-onCommand1

"button -e -en true butASD; button -e -en false butD;"

-onCommand2

"button -e -en false butASD; button -e -en true butD;"

$radSel;

showWindow $winWDup;

}

Остановимся поподробнее на самой процедуре поиска пар ближайших точек.

//Процедура поиска пар и заполнения временных массивов ими

proc butAutoSearch ()

{

...

Если помните, мы выделяли массив точек.

//Запись выделенных точек destSkin во временный массив

$selDestCur = `ls -sl -fl`;

флаг -fl (-flatten) позволяет занести в элементы массива поименный список, а не оптовый через тире.

//Цикл перебора точек временного массива $selDestCur поверхности destSkin для поиска ближайшей точки на поверхности sourceSkin

$cvSB = `polyEvaluate -v $sourceSkin`;

for ($i =0 ; $i < size($selDestCur); $i++)

{ //Загоняем дистанцию в предельный максимум

float $distSD = 1000000000.000;

//получаем массив координат текущей dest точки

float $destCoord[] = `pointPosition -w $selDestCur[$i]`;

//переводим координаты в вектор

vector $dC = <<$destCoord[0], $destCoord[1], $destCoord[2]>>;

$cvSp = 1;

while ($cvSp < $cvSB[0]) //цикл по всем точкам sourceSkin

{

//получаем массив координат текущей source точки

float $sourceCoord[] = `pointPosition -w ($sourceSkin + ".vtx[" + (string) $cvSp + "]")`; //переводим координаты в вектор

vector $sC = <<$sourceCoord[0], $sourceCoord[1], $sourceCoord[2]>>;

Собственно вычисление удаления между точками - результирующая магнитуда двух векторов. Найдена минимальная - имеем рабочий индекс пары точек.

//Поиск индекса по минимальной текушей дистанции между точками

float $distSDCur = mag (<<($dC.x - $sC.x), ($dC.y - $sC.y), ($dC.z - $sC.z)>>); //вычисление магнитуды результирующего вектора (расстояние между точками)

if ($distSDCur < $distSD)

{

$indS = $cvSp;

$distSD = $distSDCur;

}

// $distSD = `min $distSD $distSDCur`;

$cvSp++;

}

//Запись найденной точки на sourceSkin во временный массив $selSourceCur

$selSourceCur[$i] = ($sourceSkin + ".vtx[" + (string) $indS + "]");

}

...

}

Стоит упомянуть, что ввод данных в окно происходит только при возникновении события - хоть что-то выбрано. Этот дополнительный анализ позволяет правильно избрать путь по Workflow, а затем по флаговому приоритету первичного выбора не перепутать Source и Destination.

//Прием данных в окно производится только при возникновении события SomethingSelected

if(`isTrue SomethingSelected`)

{

//Ветвление по выборке CVs D-Array / CVs S/D-Pair

$slRadSel = `radioButtonGrp -q -sl $radSel`;

switch ($slRadSel)

{

case 1: //CVs D-Array

$swSel++;

//Определение приоритета первичного выбора

switch ($swSel)

{

case 1:

string $sourceA[] = `ls -sl`;

$sourceSkin = $sourceA[0];

$sourceSkinCl = `findRelatedSkinCluster $sourceSkin`;

$sSt = "Select destination surface";

break;

case 2:

string $destA[] = `ls -sl`;

$destSkin = $destA[0];

$destSkinCl = `findRelatedSkinCluster $destSkin`;

$sSt = "Select CVs for Auto Search Pairs Duplication";

select -r $destSkin;

hilite $destSkin;

break;

default:

$selDestCur = `ls -sl -fl`;

break;

}

break;

case 2: //CVs S/D-Pair

$swSel++;

//Определение приоритета первичного выбора

switch ($swSel)

{

case 1:

$selSourceCur = `ls -sl -fl`;

$sourceCV = $selSourceCur[0];

string $delme[] = `listRelatives -p $sourceCV`;

$delme = `listRelatives -p $delme[0]`;

$sourceSkin = $delme[0]; //для передачи в proc butDuplWeight

$sourceSkinCl = `findRelatedSkinCluster $sourceSkin`; //для передачи в proc butDuplWeight

$sSt = "Select destination CV";

break;

case 2:

$selDestCur = `ls -sl -fl`;

$destCV = $selDestCur[0];

string $delme[] = `listRelatives -p $destCV`;

$delme = `listRelatives -p $delme[0]`;

$destSkin = $delme[0]; //для передачи в proc butDuplWeight

$destSkinCl = `findRelatedSkinCluster $destSkin`; //для передачи в proc butDuplWeight

select -tgl $sourceCV;

$sSt = "Press Swap, Duplicate or clear selection";

break;

default: //Попробовать автоматически запускать дупликацию

// $swSel = 0;

break;

}

break;

}

}

else

{

namesReset;

}

При копировании весов используется отключение и включение нормализации весов.

//Процедура копирования весов точки-источника в целевую точку

proc butDuplWeight()

{

...

//Отключение нормализации весов в целевом кластере

setAttr ($destSkinCl + ".normalizeWeights") false;

//Рабочий цикл копирования весов.

for ($i = 0; $i< (size($sourceInfl)); $i++)

{

//Получение веса текущего влияния на точку-источник

float $sourceInflWeight = `skinPercent -t $sourceInfl[$i] -q $sourceSkinCl $selSourceCur[$idx]`;

// print $i; print ", ";print $sourceInfl[$i]; print " = "; print $sourceInflWeight; print " \n";

//Принудительный ввод веса текущего влияния на целевую точку

skinPercent -tv $sourceInfl[$i] $sourceInflWeight $destSkinCl $selDestCur[$idx];

}

//Включение нормализации весов в целевом кластере

setAttr ($destSkinCl + ".normalizeWeights") true;

//Обновление Строки Статуса окна

$sSt = "Duplicate is done. Clear Selection for continuing.";

textField -e -tx $sSt winStatusLine;

}

...

}

Все фрагменты скрипта реальны, но приведены только в качестве иллюстрации.

И что проку от урока? Этим уроком решительно утверждается, что работать с весами точек можно и нужно не только имея под рукой "обязательный" планшет. Иногда достаточно и подручных средств вроде верного, хоть и потрепанного мыша. Так же как и не всякая дорогая диета даст результат при сбросе собственного веса. Иногда достаточно работы с отягощениями вроде своего потрепанного, но все-таки верного тела... не исключая мозгов.

Спасибо форуму http://www.realtime.ru/forum/ за нескупую информацию, Андрею Тарновскому за ясную постановку задачи, потраченное время на нудоту тестирования и моральную поддержку.

Приложение

457 0 850 2
1
2006-06-10
Полезная статейка. Только вот у меня Wacom Volito 2 и я тащусь!!!!
RENDER.RU