Да будет скрипт. Методическое пособие по сотворению террейна

Широка страна моя родная…
(Навязчивая мелодия)

Катастрофа со временем! Заказчики жмут, сроки горят, а еще по террейну, как говорится, конь не валялся, даже копыто поставить некуда – земля безвидна и пуста!

- Что-то текст неоригинален. Все это уже было, - вдруг просыпается внутренний голос.

- Где качнуть, ну, хоть почитать? – спрашиваю, разглаживая морщинистое дежа вю на лбу.

- Ветхий Завет, Книга Бытия, - сообщает голос и продолжает: - Эх, ты! Жертва атеизма!

Вздыхаю. На рабочем столе обои с надписью «Моя Родина – СССР». Много в ней лесов, полей и рек… Словно на террейне, однако - огромадный, как Союз Нерушимый, тут тебе и береговая линия, и перепад высот, и реки, и озера, море, острова, болота, горы, город, поселки, заводы, дамбы, электростанции… В каком пакете это Бытие моделилось – непостижимо! Руки опускаются.

- Да-а! Генераторы террейна тут не катят. Давай, начинай с чего-то для вдохновения – бурчит внутренний голос и развивает тему, зануда:

- Террейн большой - 16x16 км2. Для живости ландшафта нужен перепад высот, ну, например, в 1 километр (Это между самым глубоким омутом и самым высоким холмиком). Хорошо бы подробностей побольше, особенно в береговой линии да в местах возможных взрывов (чтоб воронки оставались, а не просто декальки с вывороченной землей). Ограничить бы надо возможности выхода за пределы террейна естественными препятствиями в виде глубокого-глубокого моря и неприступных гордых гор. И добавь пару островов, реку, изрежь пейзаж причудливой дельтой, насади деревьев. Свободное творчество. День первый.

- Уф, нарисовали, вроде. Полюбуйтесь. Деревья, конечно, немасштабные, и вообще, но… Безгрешные могут камней покидать. Может, по пивку?

Но внутренний голос не дает роздыху:

- Представь террейн в виде прямоугольного меша с регулярной сеткой. Тогда достаточно на него спроецировать grayscale bitmap в качестве карты высот и применить displace.

Хорошо. Попробуем определиться с возможностями.

Grayscale bitmap. По картинке можно прорисовать и карту высот. Самые темные места – это дно моря, соответственно чем светлее, тем выше.

Как применить displace. Можно построить шейдер, но проще применить команду меню Edit Poligons/Color/Prelight (Maya Software).

Не забыть бы предварительно установить источник освещения, чтобы команда могла сработать, например ambientLight. Само собой нужна галочка для Displace Geometry. Выставляем 1000 Sample Scale Factor в разделе Storage Options (перепад высот). Зато быстро, раз – и дисплэйс готов.

Шаг регулярной сетки. С одной стороны, хочется, чтобы шаг был помельче. Например, та же воронка от взрыва гранаты на сеточке с шагом в 10 см получится очень живописно. Опять же береговая линия станет непринужденно изрезанной. Как в жизни! Отлично – делаем!

- Разогнался! – усмехается внутренний голос, - Ну-ка, прикинь количество полигонов при размере террейна 16х16 км с шагом в 10 см.

- Упс! – чешу затылок, - 160 000 х 160 000 = 25 600 000 000. Облом, даже без Triangulate. Молчу. И в горле пересохло.

Голос предлагает:

- Зайди со стороны игрового движка.

Вопрос, может быть, спорный, но предположим, что движок будет крутить мешик в 1,5 миллиона полигонов, или 750 000 регулярных фэйсов, то есть получаем сеточку примерно 800х800. Таким образом, шаг сетки 16000 / 800 = 20 метров. Какие уж там воронки! Пойду за пивом.

- А вручную! – никак не угомонится внутреннее я.

Что-ж, можно, засучив рукава, упереться задом в кресло и построить все вручную. Там где надо, полигонов можно и прибавить, а где-то и убрать, чтобы выйти на эти 1,5 миллиона. Но как-то влом. Уж больно нудная работа. Предел. Тупик. Мель.

- А может с мели да на MEL, - бурчит внутренний голос.

- А почему бы и нет! На MEL, так на MEL, - киваю и соглашаюсь.

Опять проблемы. И самая главная, как заставить MEL в нужных местах увеличивать или уменьшать шаг сетки! А что если…

По картинке террейна или по карте высот прорисовываем белым цветом береговую линию и места, где нужны особо подробный ландшафт. Остальное заливаем черным. Получаем «карту подробностей террейна».

Вполне вероятно, что можно заставить MEL «унюхать» различие между светлыми и темными участками. Если спроецировать текстуру «карты подробностей» на меш, то будем иметь четкую взаимосвязь между физическими координатами Vertex Faces меша и соответствующими UV координатами. Останется определить, содержит ли UV пространство текстуры в координатах ограниченных каждым Vertex Face, светлые тона. Если да, то этот фэйс разбивается, если нет – остается неизменным.

- Ну, давай, давай, - подгоняет внутренний голос.

Не гони! Сейчас надо внимательно осмотреться в хелпе. Что может понадобиться?

Команда colorAtPoint возвратит вес цвета в указанной точке UV пространства от 0 (черный.) до 1 (белый).

Команда polyInfo может возвратить список вертексов у выделенного фэйса, если использовать флаг -<strong>faceToVertex(-fv)</strong>.

Команда polyEditUV с флагом-q возвращает U и V значения выделенного вертекса отселектированного фэйса.

Команда ls с флагом–selection (-sl) выдаст на гора весь список имен отселектированных объектов. Кстати флаг <strong>-flatten (-fl)</strong>организует поименный список элементов, а не «оптовый через тире от и до». Некстати, если есть желание загнать в переменную нечто, возвращаемое любой командой, пользуйтесь символом обратного апострофа (не путать с прямым), например: string $aw[] = `ls -sl`;

Команда select позволяет варьировать наборы из активного листа выделенных элементов в зависимости от используемого флага, например флаг –clear (-cl) снимет выделение со всех объектов, а флаг –replace (-r) позволяет переопределить заново элементы активного листа. Краткое напоминание о любой команде можно получить, набрав в командной строке help, аргументом которой является запрашиваемая команда. Вот что возвращает Script Editor, например в случае:

help select;<br />// Result: <br />Synopsis: select [flags] [String...]<br />Flags:<br />-add - <br />-adn -allDependencyNodes <br />-ado -allDagObjects <br />-af -addFirst <br />-all - <br />-cl -clear <br />-d -deselect <br />-hi -hierarchy <br />-ne -noExpand <br />-r -replace <br /> -tgl -toggle <br /> -vis -visible<br />//

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

Команда polySubdivideFacet разбивает выделенный фэйс на части.

Команда sort сортирует элементы массива по возрастанию, например.

Команда print с любой переменной или константой в виде аргумента помогает организовать тесты выполнения кода. Строковый аргумент "\n" означает перевод строки при отображении ряда данных.

Цикл while дает возможность закольцевать кусок кода до тех пор, пока не выполнится какое-то условие.

Условный оператор if-else инструктирует Maya на выполнение блока кода, если проверочное условие соблюдается, а в противном случае – на выполнение альтернативного блока кода.

Выстраивается следующая технологическая цепочка:

1. Выделяем фэйс.

2. Определяем имя фэйса с помощью команды ls с флагом <strong>–sl</strong>.

3. Получаем список вертексов из 4 элементов, подставив имя фэйса в команду polyInfo с флагом -fv.

4. Определяем uv-координаты каждого элемента из полученного выше списка вертексов с помощью команды polyEditUV с флагом -q.

5. Сканируем все UV-пространство внутри полученных 4-х пар uv-координат на наличие нечерного цвета, используя команду colorAtPoint с указанием текущей пары uv-координат.

6. Если сканирование определяет наличие нечерного цвета – разбиваем выделенный фэйс.

- Это все – ля-ля, - бесцеремонно заявляет внутренний голос, - ближе к телу.

Вот же наглая сущность. Но обойдемся без словесной перепалки.

Вот кое-что и поконкретнее.

Тыкаем мышом в меш, переходим в режим выделения компонентов Face и селектируем нужный фэйс. После этого запускаем скрипт, часть кода которого приведена ниже. Код проверяет фэйс на «нечерность», то есть содержит ли фэйс светлые тона текстуры «карты подробностей».

//Заносим отселектированный фэйс в строковый массив <code>$aw[]. Предварительно, разумеется, необходимо выделить этот фэйс.
string $aw[] = `ls -sl`;
//Получаем строку из имени, номера фэйса и списка номеров образующих вертексов, разделенных пробелами.
string $aw[] = `polyInfo -fv $aw`;
//Разбиваем полученную строку на список и заносим его в этот же массив. Номера всех 4-х вертексов теперь содержатся в массиве под индексами от 2-го до 5-го соответственно.
tokenize $aw[0] $aw;
//Для проверки распечатаем элементы массива
print $aw;
//Получаем uv-координаты 1-го вертекса по индексом 2 в массиве $aw[2]. Заносим в соответствующие массивы.
float $pNum[] = `polyEditUV -q ("pPlane1.map[" + $aw[2] + "]")` ;<br /> $Ucoord[0] = $pNum[0]; $Vcoord[0] = $pNum[1];
//Получаем uv-координаты 2-го вертекса по индексом 3 в массиве $aw[3]. Заносим в соответствующие массивы.
float $pNum[] = `polyEditUV -q ("pPlane1.map[" + $aw[3] + "]")` ;<br /> $Ucoord[1] = $pNum[0]; $Vcoord[1] = $pNum[1];
//Получаем uv-координаты 3-го вертекса по индексом 4 в массиве $aw[4]. Заносим в соответствующие массивы.
float $pNum[] = `polyEditUV -q ("pPlane1.map[" + $aw[4] + "]")` ;<br /> $Ucoord[2] = $pNum[0]; $Vcoord[2] = $pNum[1];
//Получаем uv-координаты 4-го вертекса по индексом 5 в массиве $aw[5]. Заносим в соответствующие массивы.
float $pNum[] = `polyEditUV -q ("pPlane1.map[" + $aw[5] + "]")` ;<br /> $Ucoord[3] = $pNum[0]; $Vcoord[3] = $pNum[1];
//Сортируем массивы с парами u-координат и v-координат по возрастанию. Минимальная имеет индекс 0 в массиве, Максимальная имеет индекс 1 в массиве.
$Ucoord = sort ($Ucoord); $Vcoord = sort ($Vcoord);
//Можно распечатать для контроля.
//print $Ucoord; print $Vcoord; print (" " + "\n");
//Зная информацию об этих парах можно организовать цикл сканирования участка в пределах UV-пространства, ограниченного значениями этих пар.
//Цикл сканирования UV-участка на наличие нечерного цвета
//Некий флаг-индикатор «нечерности». Если содержит 0, то фэйс «черный», если 1 – «нечерный».
$wFl = 0;<br /> clear $cFl;
//Присваиваем цикловому шагу минимальное значение по u-координате.
float $uStep = $Ucoord[0];
//Присваиваем цикловому шагу минимальное значение по v-координате.
float $vStep = $Vcoord[0];
//Цикл по u-координате. Условие прекращения цикла $uStep <= $Ucoord[3].
while ($uStep <= $Ucoord[3])<br /> {
//Цикл по u-координате. Условие прекращения цикла $vStep <= $Vcoord[3].
while ($vStep <= $Vcoord[3])<br /> {<br /> //print ("U= " + $uStep + " V= " + $vStep + "\n");
//Определяем цвет текстуры в указанных uv-координатах
$cFl = `colorAtPoint -u $uStep -v $vStep file1`;<br /> if ($cFl[0] > 0)<br /> {
//Если цвет нечерный, то есть >0, то в содержимое флага вносим 1, а цикл прерываем.
$wFl = 1;<br /> break;<br /> }<br /> $vStep = $vStep + (($Vcoord[3] - $Vcoord[0])/$stepFl);
}
$vStep = $Vcoord[0];
if ($cFl[0] > 0)
{<br /> break;<br /> }<br /> $uStep = $uStep + (($Ucoord[3] - $Ucoord[0])/$stepFl);<br /> }<br /> $uStep = $Ucoord[0];

//Печатаем содержимое флага-индикатора.
print $wFl;
//Примечание: переменная $stepFl – это дробность сканирования UV-участка текстуры. Чем выше дробность, тем мельче шаг сканирования, тем точнее и дольше процедура.

Вот, собственно, и решение. Теперь можно оформить этот кусок кода в виде процедуры, возвращающей содержимое флага-индикатора. Затем эту процедуру включить в цикл перебора фэйсов и отселектировать их по признаку «нечерности». А уж тогда с чувством выполненного долга и гордостью можно разбить все фэйсы селекции. Затем цикл повторять (по необходимости), осуществляя все более тщательный поиск и разбиение фэйсов до получения необходимого результата.

Вот вид террейна во время первого цикла работы скрипта:

Это результат первого цикла:

В течение 10 минут пройдены 5 циклов сканирования и разбиения. В результате получен меш следующего вида:

Структура меша нерегулярна. Отмеченные на «карте подробностей» места автоматически разбиты на более мелкие фэйсы с минимальным размером 31.25х31.25 м2. А вот вид в полутонах после применения displace:

- Ну, да! Еще пару циклов и число полигонов возрастет так, что потребуется очень приличный ящик, чтобы этот меш ворочать, - бурчит внутренний голос.

Что тут ответить! Можно схитрить и разбить меш на несколько кусков и обрабатывать их отдельно каждый. Предположим, что этих кусков 16. Их размер 4х4 км2. Запускаем 7 проходов цикла сканирования-разбиения и получаем минимальную ячейку сетки около 7 метров. При этом число треугольников на каждом куске достигает в среднем 80 000, а на весь террейн соответственно 80 000 х 16 = 1 280 000.

- И в чем прикол? – не унимается внутренний голос.

Что ж, смею надеяться, что это эффективнее, чем моделить все руками. Судите сами!

- Да будет скрипт! – раздается наконец слаженное двухголосие. Неплохая кода для дуэта.

Спасибо Славе Хамурарь за художественное воплощение карты.

С Уважением, Игорь Леводянский, ilee@adv.md.

PS. Если кому интересен и актуален финальный вариант скрипта – мыльте.

466 0 850 25
24
2006-05-24
Суперурок! Наконец-то что-то оригинальное и интересно! Так держать %)
2006-05-24
урок отличный... вот только иногда не понятно куда какую мапу кидать... и со скриптом пока не разобрался... чё то не работает он...
2006-05-24
забыл сказать, баллы высшие.
2006-05-24
Я хоть не чего не понимаю майе, но я на 100 процентов уверен что уроки по написанию сриптов - это самые ценные уроки. Тем более такой скрипт просто дастоен аплодисментов таварищи. Молодец, поздравляю .......высший урок...пиши еще.... и естественно баллы высшие!!!
2006-05-25
А теперь бы тоже самое... только полностью.
2006-05-25
Даю две пятерки за "финальный вариант скрипта"! Сторгуемся?
2006-05-25
2 skif "вот только иногда не понятно куда какую мапу кидать... и со скриптом пока не разобрался... чё то не работает он..." две оценки Актуальность и Качество и обе высшие, интересный у тебя подход к голосованию =) а тема урока была неплохая на самом деле...
2006-05-25
Хорошие урок.
2006-05-25
d|g 25.05.2006 09:37 2 skif две оценки Актуальность и Качество и обе высшие, интересный у тебя подход к голосованию =) а тема урока была неплохая на самом деле... А всё просто... идея ОТЛИЧНАЯ, и Актуальная. а если я не могу чего то повторить это значит что у меня руки кривые, а автор всё равно молодец. отсюда и оценки :)
2006-05-26
В максе в 1000 раз быстрее можно сделать =_= и без скриптов
2006-05-27
Bellord (3D Work) 26.05.2006 22:27 В максе в 1000 раз быстрее можно сделать =_= и без скриптов быстрее это как? каким способом? тут как раз урок о скриптописании.... показаны возможности...
2006-05-29
класс
2006-05-29
класс
2006-05-30
Хороший урок. Сам таким, занимался. Как сказал ЕТОТ внутренний голос. " А вручную! – никак не угомонится внутреннее я. Что ж, можно, засучив рукава, упереться задом в кресло и построить все вручную. Там где надо, полигонов можно и прибавить, а где-то и убрать, чтобы выйти на эти 1,5 миллиона. Но как-то влом. Уж больно нудная работа. Предел. Тупик. Мель." Робота стоящая. И покрутив мозгами, этот мел можно не только к ТЕррайну присобачить. Я думаю кто то уже это понял, а кто то попозже поймет. 5
2006-06-01
Ребяты, вот линк на финальный вариант: http://ilee.my1.ru/images/Mels/Terrein/Terrain.mel Там же, но повыше - пару слов о том, как с ним управляться. Всем respect.
2006-06-08
Изложение пацановатое. Идея великолепная. Жаль, не могу голосовать. А в максе такое делает Displace Mesh WSM. Урок - на 10 строк...
2006-06-20
И для чего существует Bryce? Может для Maya эта программа Bryce и в новинку, но там такой модуль, что не только строить разные земли может, но даже в динамике позволяет старить и размывать территории без особых напрягов, и, все это хорошо экспортируется хоть куда (и м Maya тоже)
2006-06-23
Sergey 20.06.2006 00:00 И для чего существует Bryce? Может для Maya эта программа Bryce и в новинку, но там такой модуль, что не только строить разные земли может, но даже в динамике позволяет старить и размывать территории без особых напрягов, и, все это хорошо экспортируется хоть куда (и м Maya тоже) брюс это в принципе гадость... хоты безспорно есть в нём отдельные хорошие моменты. а вообще само то для создания ландшавта это сочетание World mashin + мака(с эитм скриптом :)) вобщем автор ещё раз респект!!!
2006-09-11
Хороший урок!
2006-09-12
Полезно!
2006-09-12
Автору - благодарность.
2008-03-14
Люди ссылка на скрипт "_http://ilee.my1.ru/images/Mels/Terrein/Terrain.mel" неработает выложите пожалуйста кто-то этот скрипт или кинте в ПМ
2010-02-11
Урок шикарный! И с юморком и весьма понятный. И очень важная тема по моему. И вдохновило на кучу идей для своих работ в MEL. Правда я его тока начинаю осваивать и пока без бумажки вообще ничего не могу напрограммить. =)
2014-09-18
Ничего не понятно, "куда-чего-как", наверно урок для избранных... Какая версия майки для него сгодится?
RENDER.RU