Уроки: 3ds Max

MAX SCRIPT. Пишем экспортер.

Данным уроком я хочу продолжить тему геймдева, начатую мной ранее. В принципе урок будет интересен не только людям интересующихся этим направлением 3Д, а всем кто не знаком с MAX SCRIPT и хочет научится писать свои скрипты, которые могут отлично сэкономить время при выполнении рутинных операций и не только.
В прошлом уроке я рассказывал вам о использовании графического движка OGRE и утилиты/плагина под 3DS MAX для экспорта сцен - oFusion. Но у данного подхода есть небольшой, но довольно неприятный минус, это платность этого самого oFusion при использовании его в коммерческих целях. Данный факт не помеха пока вы изучаете и экспериментируете с OGRE, но когда вы захотите наконец написать свой супер мега шутер, или простенькую казуальную игрушку, возникнет вопрос о оплате n-ой суммы за использование oFusion, или же написать свой экспортер. Второй вариант я вижу на много предпочтительней, как в плане экономии денег, так и в плане полного контроля и интегрирования нужных вам функций в экспортер, таких как физика, звук и т.д.
Наш экспортер, как я упоминал ранее, мы будем писать на MAX SCRIPT-е. Это даёт нам много преимуществ: совсем не обязательно знание программирования, простота написания и редактирования, универсальность относительно версий 3DS MAX.
Давайте определимся с "планом работ", а точнее что должен уметь наш экспортер, какие мы будем реализовывать опции и т.д. Самое главное, это экспорт самих моделей в огровский .mesh формат и запись их положения в сцене. Экспорт моделей стандартным огровским экспортером разбит на 2 части, это експорт модели из макса в .xml файл, и последующей конвертации его в .mesh отдельной программкой, что является очень не удобным и забирает много времени. Тем более, что экспортировать можно только по одной модели за раз, что является совсем не допустимым при экспорте большой сцены с множеством объектов. Сама часть создания .xml и конвертации нареканий не вызывает, по этому мы не будем их переделывать, а создадим оболочку, которая по очереди будет экспортировать и конвертировать все имеющиеся в сцене модели. Данные о позиции, ориентации и других параметрах каждой модели мы сохраним в файл со структурой xml, что даст возможность без проблем загрузить всё это в вашу игру.
Начинать мы будем с простого, постепенно добавляя нужные нам возможности. Итак, запускаем 3DS MAX, открываем закладку "Utilities" и нажимаем кнопочку "MAXScript", после чего вы увидите появившийся роллаут MAXScript:



Значения кнопок "New Script", "Open Script", "Run Script" я думаю, в пояснении не нуждаются, их названия говорят сами за себя, а вот кнопочка "Open Listener" открывает окошко содержащее 2 поля. Верхнее поле представляет собой что то типа лога, в которое макс выводит информацию после каких либо действий пользователя, а в нижнем можно вводить команды макс скрипта для одноразового немедленного выполнения. При написании скриптов данное окно желательно держать открытым, так как там будут отображаться ваши ошибки и вообще много полезной информации. Нажимаем кнопочку "New Script", перед нами появится чистый, готовый к работе, текстовый редактор. Предлагаю сразу же сохранить его. Я сохранил его под именем LesExpScene.ms. Сейчас нам надо расставить визуальные элементы управления, кнопки, выпадающие списки ит.д. Для чего выберите Edit->Edit Rollaut из меню редактора скриптов. Это откроет окно Visual MAXScript, где мы быстро и наглядно расставим всё, что нам нужно. Серый квадрат по центру, это и есть наш будущий роллаут. Большинство элементов находящихся в низу данного окна мы разберём по ходу работы. Для начала нам необходимо текстовое поле, чтоб мы могли назвать файл нашей сцены. Для этого нажмите кнопочку "Edit Box" на нижней панели, и нарисуйте прямоугольник, он и будет служить нам полем для ввода текста. Также для более удобного размещения элементов можно использовать кнопку "Grid/Snap" находящуюся в верхней части.


Теперь с правой стороны вы можете видеть параметры нарисованного нами "Edit Box". Сначала дайте имя данному элементу в поле "name", но помните, что мы будем использовать его в тексте скрипта, поэтому давайте узнаваемые имена, чтоб потом долго не вспоминать какой же элемент вы назвали "xyz" или "abc". Я назвал его "scName". В поле "caption" впишите "Scene Name", это надпись которая будет отображаться возле нашего поля. В "width" и "height" установите 200 и 17 соответственно. Сейчас давайте установим два обьекта "Color Picker" которые будут отвечать за выбор Ambient и Background цветов для нашей сцены. Нажмите на нижней панели кнопку "ColorPicker" и нарисуйте 2 прямоугольника. Для первого установите такие параметры:
name - amColor
caption - Ambient Color
width - 120
height - 32

Для второго:
name - bcColor
caption - Back Color
width - 120
height - 32

Нам надо добавить элементы отвечающие за настройку теней, и для более красивого вида нашего экспортера мы объединим их в одну группу, для чего выберите "Group Box" и нарисуйте квадрат побольше. В "caption" впишите "Shadow Settings", поле "name" в данном случае значения не имеет, так как обращаться к нему из скрипта мы не собираемся. Внутри разместите 2 объекта "Drop Down List" один "ColorPicker" и один "Label". "Drop Down List" - это выпадающий список, а "Label" мы используем для более компактного размещения элементов внутри нашей группы, так как "ColorPicker" со стандартным "caption" будет занимать слишком много места. Чтоб вам стало понятней, посмотрите на скриншот:



И установите следующие параметры для данных объектов:
Первый "Drop Down List":

name - shadowTec
caption - Shadow Technique:
width - 136
height - 40
items - "NONE", "STENCIL_MODULATIVE", "STENCIL_ADDITIVE", "TEXTURE_MODULATIVE", "TEXTURE_ADDITIVE"

В поле "items"мы вписали те пункты, которые будут показаны при развороте списка. Вписывать их надо обязательно в кавычках.

Второй "Drop Down List":

name - shadowSyze
caption - Shadow Texture Syze:
width - 136
height - 40
items - "128", "256", "512", "1024", "2048"

Для элемента "Label" в поле "caption" напишите "Shadow Color". А элементу "ColorPicker" установите "name" - "shadowColor" и поле "caption" сделайте пустым. Разместите всё это так как показано на скриншоте выше. Сейчас самое время сохранить нашу работу. Для этого выбирете File->Save или просто закройте окно Visual MAXScript и на вопрос сохранять ли изменения ответьте Да. Всё нами проделанное выше окажется строками кода макс скрипта в нашем файле. Сохраните его.
Нам осталось разместить всего несколько элементов. Как это делается я думаю больше объяснять не надо, поэтому я приведу только скриншот с их размещением и параметры которые следует установить


Первый Edit Box, для отображения и/или ввода с клавиатуры пути для сохранения продуктов жизнедеятельности нашего скрипта.

name - saveText
caption - save to:
width - 255
height - 17

Кнопка "Browse" для выбора того самого пути.

name - butSave
caption - Browse
width - 80
heiht - 24

Check Box для выбора конвертировать ли модели в .mesh формат после экспорта, или оставить их в XML.

name - convert
caption - Convert XML
width - 96
height - 17
enabled - true
checked - true

Опция "enabled" указывает будет ли активен этот чекбокс или нет. А "checked" выбран ли он.

Второй Edit Box, для отображения пути к программе конвертирования XML в .mesh (Она идёт в комплекте с файлами Огра)

name - XMLText
caption - XML Converter
width - 255
height - 17

Ещё одна кнопка "Browse", для выбора пути к XML конвертеру.

name - butXML
caption - Browse
width - 80
height - 24

Второй чекбокс предназначен для выбора экспортировать ли модели в принципе, или только файл с их размещением и настройками сцены. Экономит время если вы изменили только цвет фона или позицию модели, например.

name - onlyxml
caption - Export XML Scene Only
width - 137
height - 17

И последняя, самая главная кнопка "Export". Её назначение, я думаю, в пояснении не нуждается.

name - expBut
caption - Export
width - 100
height - 30

Всё, с размещением элементов мы закончили. Давайте дадим имя нашему роллауту. Выделите тот серый квадрат в нутри которого мы размещали все элементы и установить ему: name - ExportRollout и caption - FreeS Export Dialog. Сохраните всё это, и окно визуального редактирования можно закрыть.

Наверное вам не терпится увидеть в действии то, что вы так долго делали. Но пока это всего лишь набор строк. Чтоб "дать им жизнь" мы создадим окно, в котором разместится данный ролаут, и установим кнопку для его открытия. Сперва поместите содержимое архива в папку "3DSMAX\UI\Icons".

Icons

Это картинка которая будет отображаться на кнопке. Если вы закрыли ваш скрипт, откройте его. Сейчас мы имеем:

rollout ExportRollout "FreeS Export Dialog" width:296 height:528
(
--
-- Тут описание всех созданных нами элементов интерфейса.
-- Комментарии в МаксСкрипте начинаются с двойного дефиса.
--

   )

В самом начале, перед данным текстом, мы должны вписать следующее:

macroScript ExportDialog
category:"FreeS Tools"
internalCategory:"Export Dialog"

-- Текст кнопки.
buttonText:"FreeS Ogre Export Dialog"

-- Надпись появляющаяся при наведении мыши на кнопку.
tooltip:"FreeS Ogre Export Dialog"

-- Иконка отображаемая на кнопке.
Icon:#("FreeS",3)
(

-- Имя и размеры создаваемого окна.
FreeSOgreExportFloater = newRolloutFloater "FreeS Export Dialog" 300 600;

И в самом конце:

-- Добавляем наш ролаут в окно.
addRollout ExportRollout FreeSOgreExportFloater ;

)

Сохраните изменения в тексте скрипта, и мы продолжим. Чтобы макс начал использовать данный скрипт нажмите File->Evaluate All. Если вы допустили какую либо ошибку, данные о ней появятся в окне MAXScript Listener. Чтоб не тратить много времени на поиск ошибки в случае её возникновения, сравните свой скрипт с моим:

LesExpScene_1

Нам осталось показать кнопку запуска скрипта, и делается это очень просто. Выберите пункт меню Customize->Customize User Interface... Сейчас мы создадим Toolbar который и будет содержать нашу кнопку, и любые другие, по вашему желанию. Нажмите кнопочку "New" и в появившемся окошке дайте имя создаваемому тулбару, и нажмите "Ок". Появится маленькое окошко, на которое мы можем переместить нужные нам кнопки. В окне "Customize User Interface" в "Category:" найдите "FreeS Tools" (Если вы следовали моим советам, и не назвали категорию в скрипте по другому) и увидите в низу список возможных кнопок в этой категории. В данном случае у вас будет только один пункт, который вы и перетащите мышью в только что созданный тулбар.


Теперь вы можете разместить этот тулбар в любом месте интерфейса 3D Max, а при нажатии на кнопку мы увидим созданный нами роллаут. Правда пока он никак не реагирует на нажатия кнопок, и не может сохранять свои настройки, но сейчас мы это исправим.

Когда передо мной стал вопрос как сохранять настройки отдельных объектов в сцене, то я решил записывать их в User Defined Properties, что очень удобно в плане использования и редактирования. Найти это можно щёлкнув правой кнопкой мыши на каком либо объекте, и в появившемся меню выбрать "Properties..." где вы и найдёте закладку "User Defined". А вот с сохранением настроек сцены было не всё так гладко, но в конце концов я решил что их тоже можно сохранить в User Properties специально созданного для этого объекта, который будет игнорироваться экспортером в качестве меша, и будет служить только контейнером нужных параметров. Я дал ему имя "SceneBox". Создавать его и записывать все параметры мы конечно же будем при помощи MAXScript.
Все наши дальнейшие действия будут базироваться на событиях, которые происходят при выполнении каких либо действий. Например, при нажатии кнопки открывающей наш тулбар происходит событие "open" в этот момент нам надо проверить есть ли в сцене "SceneBox", и если да, то установить всем элементам роллаута сохранённые в нём параметры, а если его нет, то есть роллаут открывается впервые для данной сцены, то надо его создать.
Для проверки присутствия "SceneBox" мы используем конструкцию "try()catch()". В скобках после "try" мы попробуем выбрать этот бокс, и если его не окажется, начнёт выполнятся код находящийся в "catch()", если же операция выбора пройдёт успешно, то "catch()" пропускается, скрипт продолжит свою работу дальше.
Выбирать объект в MAXScript можно многими способами, но в данном случае самым удобным для нас будет выбор по имени. Чтоб MAXScript понял что мы обращаемся именно к имени, перед ним надо ставить знак "$"
Записывать данные в User Properties мы будем таким образом:
setUserProp /переменная или имя объекта/ /имя параметра/ /значение параметра/

А читать так:
значение параметра = getUserProp /переменная или имя объекта/ /имя параметра/

Каждый из созданных нами ранее элементов интерфейса содержит множество параметров, и обращение к ним происходит через точку, то есть:

Цвет объекта "shadowColor" в формате RGB можно получить так:
shadowColor.color

Чтоб получить только синий параметр цвета:
shadowColor.color.b

При сохранении и загрузке цветов мы будем делать это отдельно для каждого элемента RGB, так как корректно загрузить цвет записанный одной строкой мне не удалось.
Итак, после строк описания интерфейса, перед скобкой закрывающей роллаут мы пишем:

 -- Когда наш роллаут открылся
on ExportRollout open do
(
-- пробуем
try (

-- выбрать объект с именем "SceneBox"
scb = select $SceneBox
)
-- если не получилось
catch (

-- создаём бокс
ScBox = Box()
-- даём ему имя
ScBox.name = "SceneBox"
-- и поместим его подальше, чтоб не мешал
ScBox.pos.z = -5000

-- после чего записываем все необходимые параметры.
setUserProp ScBox "SHADOWT" shadowTec.selection
setUserProp ScBox "SHADOWS" shadowSyze.selection
setUserProp ScBox "SHADOW_R" shadowColor.color.r
setUserProp ScBox "SHADOW_G" shadowColor.color.g
setUserProp ScBox "SHADOW_B" shadowColor.color.b

setUserProp ScBox "AMBIENT_R" amColor.color.r
setUserProp ScBox "AMBIENT_G" amColor.color.g
setUserProp ScBox "AMBIENT_B" amColor.color.b

setUserProp ScBox "BCKGRND_R" bcColor.color.r
setUserProp ScBox "BCKGRND_G" bcColor.color.g
setUserProp ScBox "BCKGRND_B" bcColor.color.b

setUserProp ScBox "SCNAME" "defscene"
setUserProp ScBox "SAVEP" "C:\\"
setUserProp ScBox "XMLPATH" "C:\\"
setUserProp ScBox "CONVERT" true
setUserProp ScBox "ONLYXML" false

)
-- а тут мы читаем все параметры, и устанавливаем их соответствующим элементам.
SC = getUserProp $SceneBox "SCNAME"
scName.text = (SC as String)
amColor.color.r = getUserProp $SceneBox "AMBIENT_R"
amColor.color.g = getUserProp $SceneBox "AMBIENT_G"
amColor.color.b = getUserProp $SceneBox "AMBIENT_B"

bcColor.color.r = getUserProp $SceneBox "BCKGRND_R"
bcColor.color.g = getUserProp $SceneBox "BCKGRND_G"
bcColor.color.b = getUserProp $SceneBox "BCKGRND_B"

shadowTec.selection = getUserProp $SceneBox "SHADOWT"
shadowSyze.selection = getUserProp $SceneBox "SHADOWS"

shadowColor.color.r = getUserProp $SceneBox "SHADOW_R"
shadowColor.color.g = getUserProp $SceneBox "SHADOW_G"
shadowColor.color.b = getUserProp $SceneBox "SHADOW_B"

saveText.text = getUserProp $SceneBox "SAVEP"
XMLText.text = getUserProp $SceneBox "XMLPATH"
convert.checked = getUserProp $SceneBox "CONVERT"
XMLText.enabled = convert.checked
butXML.enabled = convert.checked
onlyxml.checked = getUserProp $SceneBox "ONLYXML"
)
Теперь наш скрипт может читать сохранённые данные, но нам надо чтоб наш роллаут реагировал на нажатия кнопок,
выбор цвета и т.д. , и сохранял их. Реализовывать мы это будем по описанному выше принципу.
-- Когда в "Scene Name" введён текст, то
on scName entered text do
(
-- записываем изменённый параметр.
setUserProp $SceneBox "SCNAME" scName.text
)

-- когда изменился амбиент цвет
on amColor changed col do
(
-- тоже записываем изменения
setUserProp $SceneBox "AMBIENT_R" amColor.color.r
setUserProp $SceneBox "AMBIENT_G" amColor.color.g
setUserProp $SceneBox "AMBIENT_B" amColor.color.b
)

-- аналогично
on bcColor changed col do
(
setUserProp $SceneBox "BCKGRND_R" bcColor.color.r
setUserProp $SceneBox "BCKGRND_G" bcColor.color.g

setUserProp $SceneBox "BCKGRND_B" bcColor.color.b
)
-- если произошел выбор техники теней
on shadowTec selected sel do
(
setUserProp $SceneBox "SHADOWT" shadowTec.selection
)

-- если мы изменили размер теней
on shadowSyze selected sel do
(
setUserProp $SceneBox "SHADOWS" shadowSyze.selection
)

-- а если поменяли цвет теней
on shadowColor changed col do
(
setUserProp $SceneBox "SHADOW_R" shadowColor.color.r
setUserProp $SceneBox "SHADOW_G" shadowColor.color.g
setUserProp $SceneBox "SHADOW_B" shadowColor.color.b
)

-- если мы вписали путь сохранения сцены вручную то
on saveText entered text do
(
setUserProp $SceneBox "SAVEP" saveText.text
)

-- а если нажали кнопку "Browse"
on butSave pressed do
(
-- получаем выбраный путь сохранения
pa=getSavePath()
-- проверяем его на корректность
if (pa!=undefined) then (
-- дописываем два обратных слеша в конец пути
pa=pa+"\\"
-- выводим полученный путь в поле "save to:"
saveText.text=pa
-- и записываем изменения
setUserProp $SceneBox "SAVEP" pa
)
)
-- если мы поставили или сняли галочку
on convert changed state do
(
-- записываем изменения
setUserProp $SceneBox "CONVERT" convert.checked

-- и устанавливаем зависимость активности "XMLText" и "butXML" от нашего выбора
XMLText.enabled = convert.checked
butXML.enabled = convert.checked
)

--если путь к XML конвертеру введён вручную
on XMLText entered text do
(
-- записываем изменения
setUserProp $SceneBox "XMLPATH" XMLText.text
)

-- если нажата кнопка "Browse" действуем аналогично выбора пути сохранения
on butXML pressed do
(
pa=getSavePath()
if (pa!=undefined) then (
pa=pa+"\\"
XMLText.text=pa
setUserProp $SceneBox "XMLPATH" pa
)
)
-- если изменился статус "Export XML Scene Only"
on onlyxml changed state do
(
-- тоже записываем изменения
setUserProp $SceneBox "ONLYXML" onlyxml.checked
)

-- А тут мы будем вызывать саму функцию экспорта, если была нажата кнопка "Export"
on expBut pressed do
(

)

Сохраните все изменения, сделайте File->Evaluate All и попробуйте поизменять параметры в созданном нами окне. Всё должно сохраняться и загружаться. Если у вас что то не работает, или вы получили сообщение о ошибке, посмотрите на мой файл скрипта, возможно вы упустили какую либо деталь.

LesExpScene_2

Последнее что нам осталось сделать, это установить реакцию на нажатие кнопки "Export", но в силу обширности темы, этим мы займёмся в следующем уроке.

Если у вас возникнут какие либо вопросы, задавайте их на мой e-mail:
frees_les@mail.ru
А также читайте справку по макс скрипту, там много полезной информации, и всё довольно хорошо и подробно описано.

Спасибо всем кто дочитал до конца :) , удачи вам в ваших начинаниях.

49533 Автор:
Актуальность: 595
Качество: 582
Суммарный балл: 1177
Выбор Публики
Голосов: 60 оценки

Отзывы посетителей:

аватар
 
Devyatyi9 1 0

А не знает кто, можно ли сделать скрипт на авто импорт и автоэкспорт?
Чтобы smd был входящим форматом, а fbx выходящим

аватар
 
Sviat 1 0
Тема очень полезная, жду продолжения.
аватар
 
Freestyler 2 0
Отличный урок, спасибо, гдеже продолжение? :)
аватар
 
Wisp 2 0
OK-5!
Пока я недашол до этого, но когда пригодиться, есть куда посмотреть.
аватар
 
Serega2008 2 0
Урок мне понравился.
Правда хотель бы увидеть работу с MAX SDK.
И еще, если не сложно напиши урок по взаимодействию плагинов.
Например задаешь какие-то параметры для сцены, потом экспортируешь их или визуализируешь сцену использовав эти параметры.
И еще очень хочется увидеть урок по созданию в MAX'e своего собственного объекта, со своими параметрами, функциями и т.д.
Вобщем я как следует намекнул, осталось реализовать:)
Вобщем спасибо автору.
аватар
 
>I<ESTb 1 0
Урок довольно хорош, так что 5/5. А автор молодец!
аватар
 
Андрей Пидвирный 152 0
То что я здесь описал может быть применено к любому движку. Все пункты раздельны между собой. Экспорт сцены в XML формат производится совсем независимо, и каким движком его потом грузить, зависит только от вас.
Экспорт же самих моделей будет в .mesh формат Огра. Но, в силу раздельности компонентов экспортера сам процесс конвертации модели может быть заменён на то что вам нужно.
В принципе цель данного урока не в написании экспортера для огра, а практическом применении, и изучении MAX Script на основе написания экспортера.
аватар
 
Mr.Cherry 2 0
поторопился и 5/5 поставил
это тока на OGRE? если нет - то всё норм, если да - фтопку.
аватар
 
BorisK 22 0
А что тут комментирровать? Конкретная задача, ясное решение. Отличные листинги, подробно прокомментированные. 5/5
аватар
 
Андрей Пидвирный 152 0
Да, отзывов маловато (
Я ждал больше.
2 Pavel Zabelin
Не замечал такого глюка, но посмотрю в эту сторону повнимательней.
аватар
 
KJS 7 0
Frees"у респект - голосую по максимому !!!!!!!!!!!!!!!!!!!!))))))))))))))))
Народ - что-то совсем не шевелимся 4 комента на урок!
аватар
 
Pavel Zabelin 1 0
В свое время писал экспортер меша модели в XML - столкнулся с неприятным моментом макс выделял очень бльшое колво памяти (разростался до 800М) а потом ее освобождал но не всю где то 100М не вычищал. Твой скрипт таким не страдает?
аватар
 
pavel nikolaev 6 0
урок ещё не прочитал, только вступление, но тема одна из наиболее привлекательных. считаю, что автор один из самых полезных пользователей рендера. огромное спасибо!
аватар
 
Андрей Пидвирный 152 0
Большое спасибо.
В следующем уроке мы разберём сам процесс экспорта, науимся устанавливать параметры объектам, и создадим свой материал.
аватар
 
Antim 3D 16 0
сейчас эта тема для меня становится актуальной
Посему выставил 5-ки.
Продолжай в том же духе
Зарегистрируйтесь, чтобы добавить комментарий.
Эту страницу просмотрели: 767 уникальных посетителей