суббота, 1 сентября 2007 г.

Методическое пособие "Введение в MAXScript". Часть 1.


  Предисловие.

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

  Методическое пособие содержит материал для самостоятельного изучения языка сценариев MAXScript. Цель пособия – научить пользователя самостоятельно расширять инструментарий 3D Studio Max.

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

Содержание:

Вступление
1. Понятие MAXScript
2. Первые шаги
3. Инструменты MAXScript
  3.1. Меню MAXScript
  3.2. Окно MAXScript Listener
  3.3. Окно редактора MAXScript
4. Синтаксис сценариев MAXScript
5. Основы программирования сценариев MAXScript
  5.1. Переменные
  5.2. Операторы
    5.2.1. Операторы присваивания
    5.2.2. Операторный приоритет
    5.2.3. Оператор If
    5.2.4. Оператор Case Of
    5.2.5. Оператор цикла For
  5.3. Функции
  5.4. Коллекции и массивы
  5.5. Создание функций
  5.6. Работа со строками
6. Инструменты для мыши

  Введение.


  В программе 3D Studio Max для художника предоставляется огромное количество различных инструментов, однако некоторые возможности, наверняка, не предусмотрены разработчиками. К счастью, данная проблема решена с помощью средства MAXScript, позволяющего добавить программе новые функции и возможности под конкретные запросы пользователя. Многие, кто работают в 3D Studio Max, даже не знают о существовании инструмента MAXScript. Это и объяснимо, инструмент специфичный и литературы для него не так много. Это и послужило поводом написания статьи о MAXScript.

  При изучении MAXScript проще будет тем, кто занимается программированием или хотя бы имеет какое-нибудь представление о нём. Но, не смотря на это, я постараюсь ознакомить Вас как с фундаментальными сведениями данного средства, так и с более сложными, но, на мой взгляд, интересными и полезными. Для этого необходимо иметь опыт работы с программой 3D Studio Max, знать его основы. Вам не обязательно читать абсолютно все разделы. Изучайте то, что считаете нужным и интересным для вас. Но листинги с примерами рекомендую не пропускать.

  Хотелось бы отметить, что в моих целях является не только помочь освоить MAXScript, но и показать возможности программирования в приложениях, рассчитанных на работу с трёхмерной графикой, в частности 3D Studio Max. Помимо теории я постарался разместить как можно больше листингов с аргументированными примерами сценариев для более тщательного изучения материала, по возможности старался, чтобы исходный текст был практичен и имел функциональную нагрузку. На практике материал будет усваиваться лучше, быстрее, а, главное, интересней. Итак, начнём.

  1. Понятие MAXScript

  Первый и вполне нормальный вопрос, возникающий у новичка – “Что же такое MAXScript?”. MAXScript – это встроенный в 3D Studio Max язык сценариев. Впервые он появился в 3-й версии 3ds max. Сложным языком его назвать трудно, но недооценивать его тоже нельзя. Впрочем, всё зависит от того, насколько сложная задача перед Вами стоит. Какие возможности открывает MAXScript, перечислить трудно ввиду своей обширности, часть из них Вы найдёте в разделах статьи. Всё сводится к автоматизации работы с объектами, анимацией, материалами, текстурами, эффектами и другими единицами 3ds max.

  Что касается поставленной Вами задачи перед инструментом MAXScript, стоит спросить себя, нужно ли к нему вообще обращаться. Иногда лучше лишний раз поискать нужный Вам модификатор или инструмент, чем писать плагин, выполняющий функции данного модификатора (инструмента). Думаю, вы со мной согласитесь: хорошее знание программы - это не только знание большинства её возможностей, но и рациональное использование этих возможностей.

  Имеет смысл затронуть понятие плагин (подключаемые приложения, отвечающие за конкретные задачи). Написать плагин для 3ds max можно с помощью MAXScript или С++. Если необходимо писать крупные сценарии с множеством различных операций, то C++ более предпочтительный, так как написанные на нем приложения работают быстрее. Явным преимуществом MAXScript является практически неограниченный и в свою очередь простой доступ к работе с 3ds max. В данной статье мы научимся писать плагины на MAXScript.

  Если говорить о принципах работы средства MAXScript, то тут аналогичная ситуация, как, к примеру, у макросов офисного пакета Microsoft (в частности, Microsoft Word, Microsoft Excel и т.д.): можно “записывать” и “воспроизводить” те или иные последовательности действий. Соответственно, для создания сценариев MAXScript также предусмотрено несколько инструментов, отвечающих за “запись” и “воспроизведение”, речь о которых пойдёт далее.


2. Первые шаги


  Давайте начнём не с нагрузки теорией, а с практики. Попробуем создать систему из нескольких примитивных объектов, имитирующую часы при помощи MAXScript. Сейчас наша задача – не просто создать часы, но и “оживить” механизм при помощи макроязыка. Предложенный пример может показать, насколько нестандартные задачи может решать инструмент MAXScript. Я не буду комментировать какие-либо фрагменты кода, заострять на чём-либо внимание, так как сейчас наша задача – просто ознакомиться с инструментом, понять принцип работы.

  Найдите в меню раздел MAXScript и щёлкните на New Script (рис. 1), появится текстовый редактор, в который необходимо ввести текст сценария (программный код), указанный в листинге 1.


Рис. 1. Щёлкните на New Script

Листинг 1. Создание механизма часов и их работы

rollout showtime "time" width:224 height:88
(
timer tmrSecond "Timer" pos:[63,79] width:24 height:24 interval:1000 active:false
button btnStart "Start" pos:[128,48] width:88 height:24
label lblTime "" pos:[40,8] width:160 height:24
label lbl5 "Time:" pos:[8,8] width:24 height:24
button btnStop "Stop" pos:[32,48] width:88 height:24

on tmrSecond tick do

select objects
max delete 

s = stringstream localtime 
seek s 11; table = readchars s 8
text size:50 text:table pos: [0,60,0]

seek s 11; hour = readchars s 2
seek s 14; minute = readchars s 2
seek s 17; second = readchars s 2

torus name: "watch" pos: [0,0,0] radius1: 250 segs: 12

--Создание секундных стрелок
ChamferCyl radius: 10 height: 10 sides: 15 fillet: 1 pos: [0, 0, 0] name: "cSecond"
ChamferBox length: 200 pos: [0, 100, 0] Width: 5 Height: 3 name: "bSecond"

$bSecond.parent = $cSecond
rotate $cSecond (angleaxis -(6 * second as integer) [0,0,1])

--Создание минутных стрелок
ChamferCyl radius: 10 height: 10 sides: 15 fillet: 1 pos: [0, 0, 10] name: "cMinute"
ChamferBox length: 150 pos: [0, 75, 10] Width: 5 Height: 3 name: "bMinute"
$bMinute.parent = $cMinute
rotate $cMinute (angleaxis -(6 * minute as integer) [0,0,1])

--Создание часовых стрелок
ChamferCyl radius: 10 height: 10 sides: 15 fillet: 1 pos: [0, 0, 20] name: "cHour"
ChamferBox length: 100 pos: [0, 50, 20] Width: 5 Height: 3 name: "bHour"
$bHour.parent = $cHour
rotate $cHour (angleaxis -(30 * hour as integer) [0,0,1])

select objects
$.material = meditMaterials[1]

lblTime.caption = localtime

)
on btnStart pressed do
(
meditMaterials[1].diffuse = color 288 0 0
select objects
max delete 

tmrSecond.active = true
)
on btnStop pressed do tmrSecond.active = false

)createdialog showtime width:244 height:88


В результате выполнения (<CTR+E> в окне редактора) введённого сценария появится форма time с двумя кнопками Start и Stop (рис. 2). Далее щелкните на Start, и вот Вам часы, показывающие реальное время (рис. 3). Можете теперь гордиться своим первым опытом работы в MAXScript. Поздравляю!

Рис. 2. Форма time

Рис. 3. Часы, созданные в MAXScript

3. Инструменты MAXScript

  Для работы с MAXScript нужно знать его инструментарий и уметь с ним обращаться. В данной главе будет изложен необходимый для этого материал.

3.1. Меню MAXScript

  Для работы с MAXScript часто потребуется вызывать его меню (рис. 4). Меню MAXScript содержит следующие команды: 

1. New Script (создать новый сценарий): открывает текстовый редактор для написания и редактирования сценария;
2. Open Script (открыть сценарий): открывает диалоговое окно Choose Editor File (Выбор файла), выбранный пользователем файл (расширения *.ms или *.mcr) открывается в окне редактора MAXScript;
3. Run Script (выполнить сценарий): открывает и сразу выполняет сценарий (горячие клавиши выполнения сценария в окне редактора – Ctrl+E);
4. MAXScript Listener (слушатель MAXScript): открывает окно для записи и выполнения команд, для вывода отчётов;
5. Macro Recorder (запись макроса): активирует или дезактивирует запись макроса;
6. Visual MAXScript Editor (редактор интерфейса): открывает инструмент для создания и расположения элементов управления;


Рис. 4. Меню MAXScript

  Итак, зная меню MAXScript, можно выделить три основные рабочие области: текстовый редактор, окно MAXScript Listener, окно Visual MAXScript Editor. В данной главе мы ознакомимся пока с первыми двумя рабочими областями, последняя будет разобрана чуть позже.

3.2. Окно MAXScript Listener

  Основная функция данного окна – это “общение” с программой 3D Studio Max при помощи команд MAXScript. В данном окне есть два текстовых поля: розовое и белое. Рассмотрим подробно, для чего они нужны. Использование большинства операций 3D Studio Max (перетаскивание объекта в сцене, изменение параметров, открытие окон и т.д.) сопровождается построчным выводом команд в розовом поле. Таким образом, можно узнать, как выглядит команда MAXScript для той или иной операции. Откройте окно MAXScript Listener и попробуйте создать в сцене сферу, в результате чего в розовом поле появится команда MAXScript для создания сферы. Теперь разберём нижнее текстовое поле. Оно позволяет получить отчёт выполненного сценария (вывод численных или текстовых значений, вывод текста об ошибках и т.д.). В обоих окнах можно самому вводить команды, выполнение которых происходит после нажатия <Enter>. Текстовые поля MAXScript Listener также расположены в нижним левом углу программы (рис. 5). Щелкнув в любом из полей правой кнопкой мыши, вы можете вызвать окно MAXScript Listener.


Рис. 5. Текстовые поля MAXScript Listener


  Каким же образом осуществляется “диалог” при помощи окна MAXScript Listener? Рассмотрим несколько примеров. Для начала попробуйте ввести какое-нибудь математическое выражение, после чего нажмите <Enter>. В результате MAXScript Listener выдаст Вам отчёт в нижнем поле (рис. 6). Кстати, после того как вы нажали на <Enter>, MAXScript сохранил результат в отведённую специально ячейку, в которой хранится только последний результат. К данной ячейке можно обратиться при помощи символа <?> (после ввода данного символа и нажатия <Enter> MAXScript Listener выведет последней сохранённый результат).


Рис. 6. Пример вычисления математических операций


  Давайте теперь поработаем с объектами с помощью окна MAXScript Listener. Создайте примитивный объект (например, сферу) и выберите его, затем введите в окне команду Showproperties $ (знак $ хранит в себе имя выбранного Вами объекта в сцене), выберите созданный Вами объект и нажмите на <Enter>. Если Вы всё сделали верно, ниже обнаружите список свойств объекта (рис.7).


Рис. 7. Свойства объекта Sphere 

  Теперь я Вам предлагаю создать сферу при помощи MAXScript. Что бы сделать это, необходимо знать команды для создания сферы. Поэтому создадим в сцене сферу вручную, скопируем получившуюся команду в розовом окне и откинув лишнее (ненужные свойства), выполним её, задав значения для радиуса, количества сегментов и положения (рис. 8). Чтобы создать объект со всеми заданными по умолчанию параметрами, достаточно вместо всех свойств написать <()>, например: sphere(), box(), plane() и т.д.


Рис. 8. Создание объекта Sphere при помощи макроязыка


  Как видите, я присвоил созданному мной объекту имя Earth, теперь я могу “обращаться” (обращение к любому объекту в сцене начинается со знака <gt;, затем следует имя объекта) к данному объекту, а именно, я могу в любой момент получить любое свойство или поменять свойство самому. Обращение к свойству объекта в сцене имеет следующий вид:


lt;имя объекта>.<название свойства>


  Чтобы уменьшить количество сегментов у сферы, к примеру, на 5, введите нижеуказанную строчку: 

$Earth.segs = $Earth.segs - 5 

  До этого были рассмотрены выражения из команд, входящих в состав MAXScript. Если же ввести, например, выражение hello there, он выдаст сообщение с ошибкой Type error (Ошибка ввода) (рис. 9). Это говорит о том, что MAXScript не знает подобных команд или выражений. Любое выражение в сценарии, которое не может быть выполнено в MAXScript влечет за собой оповещение об ошибки.


Рис. 9. Ошибка ввода

3.3. Окно редактора MAXScript

  Мы не будем ограничиваться “однострочным программированием”, ведь мы учимся писать сценарии. В окне MAXScript Listener писать сценарии мы не целесообразно, поскольку для этого существует специально отведенное окно редактора, речь о котором пойдет в этом пункте. А окно MAXScript Listener будем использовать, в основном, в трех случаях: для выполнения простых однострочных команд, для анализа отчёта и отладки сценариев, для получения команд некоторых операций 3D Studio Max.

  Окно редактора MAXScript – это текстовый редактор для редактирования файлов сценария. В нём можно открывать любой текстовый файл. В редакторе MAXScript, выберете команду MAXScript->New Script, в результате чего откроется область для редактирования сценариев (рис. 10).

Риc. 10. Редактор MAXScript

  Если есть необходимость проверить фрагмент кода в редакторе, выделите его, после чего нажмите <Shift+Enter> (или на <Enter> на цифровой клавиатуре). Выбранный фрагмент сценария будет автоматически скопирован в окно MAXScript Listener и, непосредственно, выполнен.

  Как Вы уже заметили редактор MAXScript и окно MAXScript Listener схожи по функциональности, не удивительно, что и большинство команд в меню совпадают. Так, например, команды меню File, Search и Help совпадают, за исключением команды Evaluate All (выполнить всё). Данная команда предназначена для выполнения всего сценария, а не одной строчки, как это делается в окне MAXScript Listener. Команды меню Edit предназначены для текстового редактирования, так что думаю, на этом останавливаться не буду, за исключением команд New Rollout и Edit Rollout (о них речь пойдёт в следующих разделах).

4. Синтаксис сценариев MAXScrip

  В процессе написания сценариев необходимо учитывать особенности синтаксиса сценариев. Рассмотрим некоторые из них. Во-первых, выполнение написанного Вами сценария осуществляется построчно, строка за строкой, поэтому необходимо учитывать последовательность вашего кода. Чтобы расположить несколько операций в одной строчке, между ними ставится символ точки с запятой. Например:

R = 7; S = R + 5

  Во-вторых, многие команды имеют “рамки”, хранящие содержимое (тело), над которым работают данные команды. Эти “рамки” можно установить символами: <(> - для открытия, <)> - для закрытия. Чтобы понять значимость обособления, рассмотрим следующую задачу: необходимо вывести в окне MAXScript Listener квадраты чисел от одного до десяти. Что бы прогнать все необходимые нам значения, потребуется оператор цикла. На каждом этапе нужно вычислить квадрат i-го значения и вывести его в окне MAXScript Listener, т.е. данные две операции должны быть внутри цикла, что делается обособлением:

for i=1 to 10 do (k=i^2; print k as string)

  После выполнения вышеуказанной строчки, получили то, что и требовалось от поставленной нами задачи. Теперь рассмотрим тот же сценарий, но без обособления:

for i=1 to 10 do k=i^2; print k as string

  В результате в окне MAXScript Listener выведется только одно значение 100. Дело в том, что цикл работал только с математической операцией, а операция вывода произошла после окончания работы цикла и вывела последнее значение, заложенное в переменной k. 

  При написании того или иного сценария, необходимо учитывать то, что вы можете ни раз обратиться к нему снова, вспомнив о некоторых допустимых просчётах. Если код громоздкий, то помимо исправления сценария, необходимо сначала потратить время на поиск ошибки. Во избежание этой проблемы полезно аргументировать некоторые фрагменты сценария, и сделать это можно примечаниями. Их использование значительно облегчит ориентирование в больших сценариях. Более того с их помощью, можно временно закомментировать какой-либо фрагмент кода. Обозначение, с которого должно начинаться примечание – <-->. Например:

render frame:15 –- визуализация 15-го кадра 

  Данное обозначение рассчитано на выделения примечанием целой строки. Если необходимо обособить, допустим, только часть строки, для этого существуют обозначения: /* - для открытия, */ - для закрытия. Текст примечаний идентифицируется зелёным цветом. 

  В синтаксисе сценариев также важно учитывать следующее: прописные и строчные символы воспринимаются одинаково (слова SO, so, So для MAXScript означает одно и то же). 

  В языке сценариев применяется цветовое выделение программного кода. В состав входят 4 цвета: зелёным выделяется примечания, синим – операторы языка сценариев, коричневым – текст, идущий в кавычках и, соответственно, чёрным – всё остальное.


5. Основы программирования сценариев MAXScript

  В данном разделе будет изложен основной материал по программированию сценариев MAXScript: процедуры, выражения, переменные и т.д. В дебри лезть мы не будем, поскольку это уже является задачей целой книги, а не статьи. Изучив изложенный по основам программирования материал, у вас будет некий “фундамент” знаний MAXScript. Без подобного “фундамента”, поверьте, писать сценарии не имеет никакого смысла. Если Вы занимаетесь программированием, Вы, наверняка, встретите знакомые операторы, функции и т.д., что не удивительно, ибо MAXScript – это тот же язык программирования, только рассчитанный на решение несколько иного спектра задач.

5.1. Переменные и типы данных

  В процессе программирования Вы неоднократно встречаетесь с таким понятием, как переменная. Переменная (variable) – это ячейка памяти, предоставляемая тем или иным языком программирования (в нашем случае языком сценариев MAXScript) для хранения информации.

  В MAXScript существует несколько типов переменных: Integer (хранит целые числа), Float (хранит числа с плавающей точкой), String (хранит текстовую информацию). Чтобы ваш программный код был приятно читабельным и разборчивым, рекомендую, чтобы имя, объявляемое Вами переменной, начиналось с префикса, состоящего из 1-4 букв, который будет определять принадлежность сохраняемых данных к определённому типу. Вот некоторые из вариантов префиксов: bln отвечает за тип Boolean (булевский), int отвечает за тип Integer (целое число), str – за тип String (строка), obj – за тип Object (объект) и т.д. Использование префиксов целесообразно в случае больших сценариев, когда используется более 10 переменных – будет легче ориентироваться в типах переменных.

  В работе с переменными существуют следующие ограничения:
1. Имя переменной не должно быть ключевым словом (ключевым или служебным словами называются слова, используемые для работы с какими-либо операторами, относящиеся к синтаксису MAXScript);
2. В имени не должно быть точек, запятых и других символов;
3. В состав имени не должны входить русские буквы;
4. Имя переменной не должно совпадать с именами констант (это может быть число π (3.14159), синтаксис которого pi или основание натурального логарифма e (2.71828) и т.д.);
5. Имя переменно не должно начинаться с цифр.

  Но перед тем как работать с переменными, их необходимо объявить (создать ячейку и дать ей имя). Объявляется переменная следующим образом: сначала идёт присвоенное вами имя переменной, затем знаком <=> присваивается некоторое значение, после чего идут служебное слово As и тип переменной. Нижеуказанный листинг кода говорит о том, что мы объявляем переменную strCount типа String и присваиваем ему значение 15 (сли мы не укажем тип переменной, то переменная будет хранить число, а не текст):

strCount = 15 as String

  Говоря о типах данных, мы затронули текстовую и числовую информацию, однако этим перечень типов не ограничивается. Следующий тип данных характеризуется как множество 2-3 численных значений Point2 (2D-точка), Point3 (3D-точка). Явным примером данного типа информации является положение объекта в сцене (координаты X, Y, Z). На рисунке 11 представлен пример работы с данными типа Point3, проследите результаты выполнения в окне MAXScript Listener.


Рис. 11. Пример использования типа данных Point3

  Последний тип данных должен быть вам, наверняка, знаком. Любой созданный вами объект в сцене также относится к определённому типу данных, только более сложному в плане своей структуры, так как содержит множество других типов данных (свойства объекта). Объект, например, Sphere – это один из сложных типов данных, содержащий множество свойств: Name типа String, Radius типа Float, Segments типа Integer, Position типа Point3 и т.д. Изучая MAXScript, полезно воспринимать объект в сцене, как отдельную структуру данных, а не изображение в сцене.

5.2. Операторы

  Операторы (operator) – это выражения, заставляющие провести ту или иную работу с переменной, объектом и т.д. Многие операторы Вам уже известны: математические (сложение, вычитание, отрицание, умножение, деление, возведение в степень), логические («равно», «больше», «меньше», «больше или равно», «меньше или равно» «или», «и»). Напомню, что логические операторы при сравнении двух значений возвращают или истина (true) или ложь (false). Но это далеко не все операторы, другие мы разберём в следующих пунктах.

5.2.1. Операторы присваивания

  Пожалуй, один из самых простых операторов – оператор присваивания. Что касается, назначения, так название говорит само за себя: присваивать значения. Знаком присваивания, как Вы уже догадались, является <=>. В нижеуказанном примере переменной r присваивается значение 15, после чего радиусу объекта sphere01 устанавливается значение переменной r, т.е. 15.

r = 15; $sphere01.radius = r

  А здесь случай аналогичен, только мы работаем уже с текстовой информацией:

n = "My sphere"; $sphere01.name = n

  В этих примерах свойствам объекта sphere01 присваиваются значения переменных r и n, можно сделать наоборот: присвоить переменным значения свойств объекта. Например:

s = $sphere01.segs -- количество сегментов

  Ранее было сказано, что любой объект в сцене относится к конкретному типу данных. Поэтому любой объект в сцене можно заложить в ячейку, например:

obj = $ -- сохранение в ячейке obj выбранного в сцене объекта

5.2.2. Операторный приоритет

  Вам знакома шутка “Сколько будет два плюс два, и умножить на два”? Не знаю как Вы, а я и многие мои знакомые ловились на ней, отвечая 8. Ясно, что первой выполняется операция умножения, затем только сложение, и правильный ответ тогда 6. Я не зря вспомнил эту шутку, так как она яркий тому пример, когда программисты совершают ошибку в записи математического выражения. Операторный приоритет (operator precedence) является одной из основных причин многочисленных ошибок в программном коде. Рассмотрим два варианта вычисления среднего значения (листинг 2.1 и листинг 2.2):

Листинг 2.1. Пример с ошибкой
num1 = 2; num2 = 3
average = num1 + num2/2 as float

Листинг 2.2. Правильный вариант
num1 = 2; num2 = 3
average = (num1 + num2)/2 as float

  В первом варианте пользователь думает, что MAXScript будет следовать его логике и тем самым выдаст результат 2.5, но на самом деле MAXScript в первую очередь вычислит частное значения num2 от 2 и только потом к нему прибавит значение переменной num1 (результат 3.5). Решим эту проблему, обособив операцию сложения скобками, тем самым, дав ей первый приоритет выполнения (листинг 2.2). Ответ тогда будет верным 2.5. И мой Вам совет: если не помните приоритет операции, смело обособляйте её скобками, не ошибётесь.

5.2.3. Оператор If

  Иногда необходимо, что бы некоторые операции проводились только при определённых условиях. За данную “необходимость” отвечает оператор условия If, полная запись которого выглядит следующим образом:
If <условие> Then <команда> Else <выражение, выполняемое в противном случае>

  Если не требуется указывать действия, выполняемые в противном случае, можно использовать следующую запись оператора if:
If <условие> Do <команда>

  На рисунке 12 показан пример использования данного оператора и на рисунке 13 – результат. 


Рис. 12. Использование оператора If


Рис. 13. Результат кода

  В данном разделе хотелось бы заострить ваше внимание на одном важном нюансе. Не путайте оператор присваивания <=> с логическим оператором сравнения <==>. Соответственно, если в разделе <условие> оператора if вы сравниваете два значения, то используется <==>, а не <=>.

5.2.4. Оператор Case Of

  Что, если условий несколько? Использование оператора условия If допустимо, но не рационально. На этот случай предусмотрен оператор Case Of. Ниже приведена структура конструкции Case Of:

Case <переменная> Of 
(
<значение 1>: <операция 1>
<значение 2>: <операция 2>
)

5.2.5. Оператор цикла For

  Следующий немаловажный оператор – оператор цикла (loop) For. Создание циклов используется для выполнения повторяющихся действий. Его иногда называют цикл со счётчиком. Синтаксис оператора For (указания шага не обязательно, по умолчанию шаг равен единице) имеет следующий вид:

For <переменная> = <начальное значение> To <конечное значение> By <шаг> do <команда> 

  На рисунке 14 изображён листинг с кодом, результатом которого является синусоида из сфер радиусом 5 в плоскости Top (рис. 15).

Рис. 14. Использование оператора For

Рис. 15. Результат кода

  Рассмотрим другой пример. На рисунке 16 показан фрагмент кода, вычисляющего объём пространства, занимаемого только сферами по формуле 4/3 π∑_(i=1)^N▒R^3 .Перед тем как его запускать, создайте несколько сфер. При выполнении данного кода появится диалоговое окно с результатом (рис. 16). Заметьте, запись оператора несколько отличается от предыдущего варианта. Его синтаксис выглядит следующим образом:

For <переменная> In <массив/коллекция> By <шаг> do <команда> 

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


Рис. 16. Код вычисления объёма пространства, занимаемого сферами.

  Объём вычисляется с учётом того, что сферы не пересекаются, иначе занимаемый ими объём не будет суммой объёмов каждой сферы (может быть такой вариант, что сферы совпадают или пересекаются). В общем, задача усложняется математически, придётся поднапрячь мозги и вспомнить стереометрию. Для нас это не важно, главное, что Вы увидели оператор For на деле и имеете какое-то представление о нём. Теперь, думаю, нетрудно понять, как устроен инструмент 3ds max Array (рис. 17).


Рис. 17. Окно инструмента Array.

  В данном примере были использованы диалоговое окно и работа со строками. О строках будет сказано далее. Что касается диалоговых окон, для их описания я не стал отдельно создавать раздел. Хочу только сказать, что любая программа, написанная программистом должна информировать или предупреждать о действиях пользователя. Для этого существуют следующие типы диалоговых окон:

Простое сообщение:
messageBox <текст сообщения> title: <название сообщения>

Сообщение с выбором да/нет:
queryBox <текст сообщения> title: <название сообщения>

Сообщение с выбором да/нет/отмена:
yesNoCancelBox <текст сообщения> title: <название сообщения>

5.3. Функции

  Функция (function) – это выражение, возвращающее некоторое значение по заданному аргументу (аргументов может быть несколько, и типы данных могут быть разными). Имейте ввиду, в MAXScript понятия функции и метода являются синонимами. В инвентарь MAXScript входит немало различных функций. Это могут быть тригонометрические функции, функции преобразований и др. Некоторые функции могут заменить операторы. Например, выражение sqrt(x) можно записать иным образом, используя при этом математический оператор, и выражение запишется x^0.5. Более того, программисту предоставляется возможность самому создавать функции. В таблице 1 приведены некоторые функции для работы с числами.

Таблица 1. Перечень функций
Запись в MAXScript Функция
sin(x), cos(x), tan(x) Вычисление синуса, косинуса, тангенса угла
asin(x), acos(x), atan(x) Вычисление арксинуса, арккосинуса, арктангенса угла 
radToDeg(x) Преобразование радианов в градусы
degToRad(x) Преобразование градусов в радианы
ceil(x) Округление сверху
floor(x) Округление снизу 
sqrt(x) Вычисление квадратного корня числа
mod x y Вычисление остатка от деления x на y
pow x y Возведение числа x в степень y


5.4. Коллекции и массивы
  Массив (array) - это группа переменных. Обращение к тому или иному элементу массива осуществляется за счёт индекса (порядкового номера). Объявление массива может выгладить, например, так:

Day = #(“Sunday”, “Monday”, “Tuesday”, “Thursday”, “Wednesday”, “Friday”, “Saturday”) 

  Тогда элемент массива Day[3], будет иметь значение “Tuesday”. Отличительная особенность MAXScript от остальных языков программирования – это возможность хранения, различных типов переменных в одном массиве. Ниже объявлен массив, в состав которого входят элементы, хранящие текстовую информацию и числовую:

A = #(“Text”, 25, 46, “String”)

  Для добавления нового элемента в массив используется ключевое слово Append. Синтаксис добавления имеет следующий вид:

Append <массив> <элемент>

  Продолжая говорить о массивах, вспомним немного математики. Массиву присуще такое свойство, как размерность. Так до сей момента речь шла об одномерных массивах, так как за индексацию отвечал всего один параметр. В общем же случае массив может быть n-мерным, т.е. возможно n независимых параметров. Двумерный массив, у которого параметры прогоняют значения от 1-го до N и M соответственно – это матрица NxM, и количество элементов равно N*M. Важно понимать эту связь, так как далее мы будем ею неоднократно оперировать.

  Понятие коллекция (collection) аналогична понятию массива, только коллекции предназначены для хранения объектов. В 3D Studio Max существует большое количество различных типов объектов (источники света (lights), камеры (cameras), помощники (helpers) и т.д.). Все эти типы, своего рода, - тоже коллекции. Их можно обозначать за переменные. При вводе данного кода, будут выбраны все возможные типы камер в сцене:

A = cameras; select A 

  Для работы с коллекциями объектов существует полезный символ <*>. Данный символ может хранить в себе отдельную часть имени. Например, чтобы создать коллекцию A из всех объектов в сцене, необходимо ввести следующую строку:

A = $*

  В вышеуказанном примере символом * заменяются все имена объектов в сцене. Теперь создайте несколько объектов sphere и box, при этом, не называя их. Напомню, их стандартные имена будут начинаться с “sphere” – для объектов sphere и “box” – для объектов box, а заканчиваться номерами (01, 02 и т.д.). Рассмотрим различные варианты создания коллекций:

1. Создать коллекцию sph только из объектов sphere:
sph = $sphere*

2. Создать коллекцию bx только их объектов box:
bx = $box*

3. Создать коллекцию только из объектов, чьи имена заканчиваются на “01”:
u = $*01

5.5. Создание функций
  Рано или поздно в процессе написания сценариев у вас появится необходимость, чтобы определенный фрагмент кода, отвечающий за конкретную задачу, использовался в нескольких местах сценария. В таких ситуациях рационально один раз написать сценарий для выполнения задачи, после чего обращаться к нему через определенную конструкцию. Что бы сделать это, MAXScript позволяет пользователю создать собственную функцию. Синтаксис создания функции выглядит следующим образом: 

function <имя функции> <обрабатываемые переменные> = <операции, выполняемые функцией>

  Для обращения к созданной функции используется следующий синтаксис:

<значение> = <имя функции> <аргументы функции> 

  В качестве примера напишем несложную функцию одного аргумента для вычисления сигнатуры, знака математического выражения (листинг 3).

Листинг 3. Функция сигнатуры.
function Sgn number =
(
local t = 0

if number > 0 do t = 1
if number < 0 do t = -1
if number == 0 do t = 0

t -- выводимое функцией значение
)

  Аргументами функции не обязательно должны быть только численная или текстовая информация, аргументами могут быть и объекты. Создадим функцию для вычисления угла, вершинами которого являются любые 3 объекта в сцене (переменные функции будут хранить имена объектов). Этапы работы функции: считываются координаты трёх тел, находятся расстояния, затем при помощи теоремы косинусов вычисляется угол. 

  В листинге 2.1 приведён сценарий функции вычисления угла ABC и на рисунке 18 приведён пример вычисления угла, вершинами которого являются три сферы. Расстояния мы будем находить функцией distance (в листинге 4.1 показана структура работы с данной функцией), данная функция значительно сокращает и упрощает программный код. Чтобы ощутить разницу, в листинге 4.2 показан сценарий, выполняющий те же самые операции, но без использования оператора distance. Согласитесь, первый вариант выглядит куда рациональней. Говорю я об этом потому, что бы вы не торопились писать функцию, прежде проверьте, имеется ли она в наличии MAXScript.

Листинг 4.1. Вычисление угла. Вариант 1
function corner A B C =
(
AC = distance A.pos C.pos
BC = distance B.pos C.pos
AB = distance A.pos B.pos 

acos((BC^2+AB^2-AC^2)/(2*AB*BC))-- угол ABC
)

Листинг 4.2. Вычисление угла. Вариант 2
function corner A B C =
local x1, y1, z1, x2, y2, z2, x3, y3, z3 
x1 = A.pos.x; y1 = A.pos.y; z1 = A.pos.z
x2 = B.pos.x; y2 = B.pos.y; z2 = B.pos.z
x3 = C.pos.x; y3 = C.pos.y; z3 = C.pos.z

AC = sqrt((x3-x1)^2+(y3-y1)^2+(z3-z1)^2)
BC = sqrt((x3-x2)^2+(y3-y2)^2+(z3-z2)^2)
AB = sqrt((x2-x1)^2+(y2-y1)^2+(z2-z1)^2)

acos((BC^2+AB^2-AC^2)/(2*AB*BC))-- угол ABC
)


Рис. 18. Результат вычисления угла

  После того, как мы создали функцию, можно к ней обращаться. Убедитесь в этом сами. Введите в розовом текстовом поле название функции corner и имена любых трёх объектов, находящихся в сцене. Нажав на <Enter>, программа выдаст результат.
Создавая собственные функции, вы имеете ряд преимуществ. Во-первых, так как написанная вами функция есть отдельный фрагмент кода, его можно проверить отдельно, при этом не изменяя текста сценария, где к данной функции есть обращение. Во-вторых, использование функций не загромождает текст сценария лишними объемами текста. В-третьих, существует вероятность, что написанная вами функция может понадобиться в других сценариях. Исходя из этих соображению, рекомендую вам для крупных задач писать функции.

5.6. Работа со строками
  Разработчики MAXScript практически ни в чем не ограничили пользователя в работе с переменными, в том числе и строковыми. Особенно часто приходится обращаться к строкам в работе с файлами (сохранение, открытие, редактирование). Для работы со строками в MAXScript существует немало различных операций, мы разберём лишь основные. У любой строки существует одно единственное свойство - count (количество символов с учетом пробелов). С помощью этого свойства можно, например, узнать количество символов в имени выбранного объекта в сцене:

s=$.name; n=s.count

  Что бы преобразовать переменную, например, с численным значением, в строковую, потребуется следующая конструкция (было использовано в п.5.2.5 рис. 14):

<имя перемееной> as string

  Не воспринимайте строку, как единое целое. Любая строка – это некоторое упорядоченное множество символов, проще говоря, строка – это массив, элементами которого являются символы, и многие операции над строками в MAXScript те же, что и у массивов.

Методическое пособие "Введение в MAXScript". Часть 2.



  Предисловие.

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

  Методическое пособие содержит материал для самостоятельного изучения языка сценариев MAXScript. Цель пособия – научить пользователя самостоятельно расширять инструментарий 3D Studio Max.

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

Содержание:

6. Инструменты для мыши
7. Средство Visual MAXScript
  7.1. Понятие Visual MAXScript
  7.2. Интерфейс Visual MAXScript
  7.3. Компоненты управления
  7.4. Создание инструмента QuasiPlayer
8. Работа с анимацией
9. Работа с полигонами
  9.1. Понятие полигона
  9.2. Построение примитивных объектов
  9.3. Создание “слепка” по заданной картинке
10. Проект MSurface
  10.1. Немного о параметрическом задании поверхности
  10.2. Сцепление вершин параметрической поверхности
  10.3. Создание функции ReplaceLetter
  10.4. Создание плагина MSurface
11. Заключение

6. Инструменты для мыши 
 
  Сценарные инструменты для мыши предназначены для операций, выполняемых мышей в сцене, в частности, для создания новых объектов. Сценарий для мыши обычно пишется для двух типов событий: mousePoint (щелчок) и mouseMove (движение курсора). Событие mouseMove значительно упрощает процесс создания объектов, давая возможность пользователю устанавливать их геометрию движением мышки. Рассмотрим, например, каким образом создаётся объект Cylinder (цилиндр): сначала Вы щёлкаете в том месте, где хотите расположить объект, затем перемещаете курсор тем самым, устанавливая радиус цилиндра, и последний этап – Вы перемещаете курсор, устанавливая высоту. Как видите, в процессе создания объекта мы два раза воспользовались событием mouseMove (установка радиуса и высоты). Любой сценарий для создания объектов начинается с ключевого выражения tool <имя инструмента>, затем для каждого события прописываются необходимые действия. Что бы активировать инструмент, нужно выполнить команду startTool <имя инструмента>. Ниже показана структура сценария:

tool <имя инструмента>
(
on mousePoint click do < выполняемые действия >
on mouseMove click do <выполняемые действия>
...
)

  Помимо этого в событиях учитываются этапы выполнения тех или иных операций, и переменная click отвечает за количество выполненных мышкой событий. Теперь напишем сценарий для всех событий (листинг 5), выполнения которых будут сопровождаться выводом значения click в окне MAXScript Listener. После выполнения сценария попробуйте поработать мышью в рабочей области, как видите, сценарий реагирует на любые изменения. Данный пример будет особенно полезным, когда потребуется проследить очерёдность событий для создания инструмента.

Листинг 5. События
tool example
(
on freeMove do print "Free Moving"
on mousePoint click do print ("Click: " + click as string)
on mouseAbort click do print ("Abort: " + click as string)
on mouseMove click do print ("Moving: " + click as string)
on start do print "Starting"
on end do print "Ending"
)
startTool example

  Для работы с мышью существуют полезные константы, вот некоторые из них: worldPoint – координаты курсора в активной области сценария, worldAngle – значения углов между осями X, Y, Z, worldDist – расстояния в X, Y, Z от предыдущей точки нажатия до активной точки.

  Рассмотрим, как использовать инструменты для мыши на примере создания объекта в сцене. Пусть нашим конечным объектом будет молекула ДНК. Получим мы эту молекулу из простого примитива Plane, обращаясь последовательно к двум модификаторам Twist (скручивание) и Lattice (решётка). Что бы применить модификаторы в сценарии и установить параметры, нужно знать команды, поэтому сделаем сначала всё вручную, после чего скопируем нужные нам команды в окне MAXScript Listener. Напомню, что оба модификатора находятся во вкладке Modifiers (модификаторы)->Parametric Deformers (параметрическое деформирование). Далее, зная нужные команды, напишем сценарий для создания молекулы ДНК, размеры которой будут устанавливаться движением курсора в рабочей области. Этапы событий будут следующие: при первом щелчке создаётся плоскость, применяются модификаторы, задаются параметры, далее, не отпуская кнопки мыши и меняя положение курсора, мы задаём длину молекулы, отпустив кнопку, инструмент закрываем (для закрытия используется ключевое слово #stop). В листинге 6 представлен сценарий для выполнения нашей задачи.

Листинг 6. Создание молекулы ДНК
tool DNA
(
local s
local a = 0

on mousePoint click do
(
case click of
(
1:
(
--создание плоскости
s = Plane length:100 width:70 lengthsegs:4
s.widthsegs = 1

--обращение к парамеретрическим модификаторам Tiwst и Lattice
select s
macros.run "Modifiers" "Twist"
macros.run "Modifiers" "Lattice"

--задание свойств модификатора Lattice
s.modifiers[#Lattice].Joint_Radius = 10
s.modifiers[#Lattice].Strut_Segments = 25
s.modifiers[#Lattice].Strut_Sides = 20
s.modifiers[#Lattice].Joint_Segs = 10

--задание свойств модификатора Twist
s.modifiers[#Twist].angle = 180
s.modifiers[#Twist].axis = 1
)--end 1:

2: #stop
)--end case
)--end mousePoint


on mouseMove click do
(
if click == 2 do
(
a = abs(length worldpoint)
s.length = a
s.lengthsegs = a/25
s.modifiers[#Twist].angle = 2*a
s.pos.y = - s.length/2
)
)--end mouseMove

)--end tool

startTool DNA

  После выполнении сценария наведите курсор в любое место рабочей области. Как видите, курсор выделен в виде крестика, который информирует о том что, созданный нами инструмент DNA запущен. Теперь попробуйте создать молекулу ДНК. Должно получиться примерно, как на рисунке 19. Данный сценарий является хорошим примером, когда MAXScript позволяет автоматизировать часть действий, в нашем случае: создание объекта, установление модификаторов и задание параметров.

Рис. 19. Модель молекулы ДНК


7.1. Понятие Visual MAXScript
  Средство Visual MAXScript – это, фактически, визуальная среда программирования, даже само название говорит само за себя Visual (визуальный) MAXScript. Так Basic – язык, Microsoft Visual Basic – визуальная среда, C++ – язык, Borland C++ Builder – среда, Pascal – язык, Borland Delphi – среда, аналогичная ситуация и здесь: MAXScript – язык, Visual MAXScript – её визуальная среда. В состав “арсенала” данного средства входят различные элементы (кнопки, текстовые поля, списки и т.д.), предназначенные для разработки пользовательского интерфейса. Иначе говоря, средство Visual MAXScript – это инструмент, предназначенный для визуального проектирования (Вы можете самостоятельно расположить тот или иной объект в форме перетаскиванием мыши).

7.2. Интерфейс Visual MAXScript

  Во вкладке Utilities панели Command щёлкните на кнопке More. В появившемся списке выберите элемент Visual MAXScript и нажмите на OK. В результате откроется окно Visual MAXScript (рис. 20). Открыть данное окно можно иным способом: в окне редактора MAXScript в меню Edit выберете команду Edit Rollout (<F2>).

  Как видно, окно разбито на две основные части. Слева расположена рабочая область, форма. Форма – это контейнер, где располагаются компоненты управления, собственно говоря, здесь и осуществляется процесс проектирования пользовательского интерфейса. Справа же во вкладке Value (значение) находится список всех возможных свойств редактируемого Вами компонента управления (на рис. 20 выбрана форма, соответственно в списке Value показаны все её возможные свойства: class, name, caption и т.д.), во вкладке Event Handlers (управление событиями) – события редактируемого компонента (о событиях речь пойдёт в следующих разделах). Ниже расположена панель компонентов (элементов) формы, рабочий “арсенал” визуального проектирования. Выше расположены основная панель инструментов и строка меню.

Рис. 20 . Окно Visual MAXScript. 1 – форма (рабочая область); 2 – список свойств компонентов управления; 3 – список событий компонентов управления; 4 – панель компонентов; 5 – главная панель инструментов; 6 – строка меню
7.3. Компоненты управления
  Все компоненты (расположены внизу), входящие в состав средства Visual MAXScript разработаны для выполнения специфичных задач. Каждый компонент отвечает за определённую операцию, так, например, текстовые поля предназначены для ввода и вывода текстовой информации, кнопки – для активации тех или иных действий и т.д. После того как Вы расположили на форме тот или иной элемент управления, справа отобразятся значения для положения и размеров элемента, остальные параметры будут установлены по умолчанию. Для обращения к элементу в сценарии необходимо присвоить индивидуальное имя (name). После того как все элементы расположены на форме, во вкладке Event Handlers (управление событиями) можно активировать (дезактивировать) событие для конкретного элемента и записать команды, которые будут выполняться при данном событии.

7.4. Создание инструмента QuasiPlayer
  Что бы понять, как всё работает на практике, реализуем инструмент QuasiPlayer, способный оптимизировать просмотр анимации после визуализации (rendering). Предположим, Вы работаете над анимацией, причём количество кадров, количество объектов в сцене, наличие спецэффектов и других факторов существенно тормозит процесс визуализации. Работая с анимацией, часто возникает необходимость просмотреть предварительный вариант, для чего требуется визуализация каждого кадра и возникает необходимость выходить из рабочей области 3ds max для загрузки видео-файла. Задача будучи созданного проигрывателя QuasiPlayer – устранить вышеуказанные препятствия. Наличие одной только области для просмотра анимации свидетельствует о том, что без визуального интерфейса не обойтись.

  Рассмотрим теперь всё поэтапно. Перед тем, как создавать интерфейс инструмента, нужно чётко понимать, какие возможности мы хотим дать пользователю инструмента. В инструменте QuasiPlayer пользователю потребуются следующие элементы:

1. область для просмотра анимации (элемент Bitmap);
2. бегунок для проигрывания (элемент Slider);
3. счётчики для задания начального, конечного кадров и шага анимации (элемент Spinner);
4. кнопка для создания массива кадров после визуализации (элемент Button);
5. элемент для активации проигрывания (элемент CheckButton);
6. таймер для проигрывания (элемент Timer);
7. рамка для визуального ограничения параметров (элемент GroupBox).

  Создаём новое окно редактора MAXScript, открываем средство Visual MAXScript. Пока только разместим на форме элементы управления, после чего закрываем Visual MAXScript, сохранив изменения. Содержимое инструмента должно располагаться, примерно как на рисунке 21.

Рис. 21. Размещение элементов управления на форме

  После закрытия Visual MAXScript в окне редактора MAXScript появится сценарий для задания формы с именем unnamedRollout и расположения элементов управления в данной форме (рис. 22). Любой элемент задаётся конкретной последовательностью: команда создания элемента, имя для обращения к элементу, название, порядок остальных элементов не важен. В сценарии можно задать несколько рабочих форм. 

  При повторном обращении к Visual MAXScript в некоторых версиях 3ds max существует неисправность: текст сценария местами покрывается серым фоном, а весь текст становится синим. Что бы привести сценарий к нормальному виду, выделите весь текст (<CTRL+A>), после чего выполните < CTRL+D>.

Рис. 22. Сценарий создания формы unnamedRollout и расположения элементов на форме

  Элементы управления мы разместили, остаётся теперь присвоить форме и элементам управления индивидуальные имена, а также добавить параметры некоторым элементам (рис. 23). Для этого нет необходимости снова открывать Visual MAXScript, проще будет сделать всё вручную, в окне редактора. Для предварительного просмотра инструмента воспользуйтесь командой:

createdialog <имя формы (в нашем случае player)>

Рис. 23. Окончательный вариант сценария интерфейса

  Что бы оживить интерфейс необходимо прописать команды для следующих событий: нажатие кнопки btnCreate, активация (дезактивация) кнопки ckbStart, активация (дезактивация) галочки chkAnimation, изменение ползунка sldAnimation. 

  Рассмотрим, как прописывать команды для события на примере элемента btnCreate. Заходим в редактор Visual MAXScript, выбираем элемент btnCreate, открываем вкладку Event handlers, активируем нужное нам событие pressed (событий может быть несколько). После активации появится окно Edit Event Handler, где прописываются команды для конкретного события (рис. 24). Общая конструкция для прописки событий имеет следующий вид:

On <имя элемента> <событие> [<состояние элемента>] do <команды>

Рис. 24. Написание сценария для события pressed элемента btnCreate

Аналогичным способом прописываются события для остальных элементов. В листинге 7 приведён окончательный текст сценария.

Листинг 7. Сценарий QuasiPlayer
global k=0
global num=0 -- количество разбиений
global len=0 -- количество кадров
global start=0 -- количество кадров

global bitmapMassiv = #()--массив кадров

rollout player "Quasiplayer" width:232 height:336
(
--элементы управления
bitmap bmpVideo "Bitmap" pos:[8,8] width:216 height:176
button btnCreate "Create frames" pos:[144,304] width:80 height:24
GroupBox grpPro "Properties:" pos:[8,216] width:216 height:80
spinner spnFrom "From:" pos:[32,240] width:64 height:16 range:[0,10000,0] type:#integer scale:10
spinner spnTo "To:" pos:[112,240] width:56 height:16 range:[0,10000,100] type:#integer scale:10
spinner spnStep "Step:" pos:[32,264] width:64 height:16 range:[1,100,20] type:#integer scale:1
slider sldAnimation "" pos:[8,184] width:216 height:25 range:[1,1000,0] type:#integer ticks:10
Timer tmrAnimation "Timer" pos:[112,304] width:24 height:24 interval:100 active:false
checkbutton ckbStart "Start" pos:[8,304] width:72 height:24

on btnCreate pressed do
(
bitmapMassiv = #()

--параметры анимации
start = spnFrom.value -- начальный кадр
end = spnTo.value -- конечный кадр
step = spnStep.value -- шаг
num = ceil((end-start)/step) -- количество разбиений
len = end/num -- количество кадров в одном разбиении
head = start/num -- количество разбиений до стартового кадра

progressstart "Creating massiv..."

--создание массива кадров
for i=start to end by step do
(
progressupdate (100.0 * i / num)
append bitmapMassiv (render outputwidth:216 outputheight:176 vfb:false frame:i)
)

sldAnimation.range = [0, num, 0] -- обьновление свойств

progressend()
)--end on btnCreate

on sldAnimation changed val do
(
try bmpVideo.bitmap = bitmapMassiv[val+1] catch print "Massiv is not defined"
)--end on sldAnimation

on tmrAnimation tick do
(
k = sldAnimation.value
k+=1
if k>=(num+1) do k=0

try bmpVideo.bitmap = bitmapMassiv[k+1] catch print "Massiv is not defined"

sldAnimation.value=k
)--end on tmrAnimation

on ckbStart changed state do
(
if state == true then
(
tmrAnimation.active=true
ckbStart.caption = "Stop"
)
else
(
tmrAnimation.active=false
ckbStart.caption = "Start"
)
)--end on ckbStart
)
createdialog player

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

try <операции> catch <операции, выполняемые в случае ошибки>

  В случае когда сценарий выполняет длительные по времени операции, имеет смысл информировать пользователя об оставшемся времени. В нашем случае единственным длительным процессом является цикл для создания массива кадров. На время работы значительно повлияет процесс визуализации, поэтому имеет смысл в данном участке сценария использовать индикатор выполнения (progress bar). Используются следующие выражения: progressStart <текст> - для обращения к индикатору, progressUpdate <пройденный процент прогресса> - для установления выполненного процента , progresend – для закрытия индикатора. 
  После того как сценарий написан и выполнен, появится инструмент Quasiplayer. Создайте какую-нибудь анимацию в сцене, выберите нужный вам вид сцены, установите параметры в инструменте, после чего нажмите Create frames. Сделав всё правильно, можно проигрывать видео (рис. 25). Надобность написанного нами инструмента будет особенно ощутима в работе со сложными и длительными анимациями.
Рис. 25. Использование инструмента Quasiplayer


8. Работа анимацией

  Думаю, говорить о том, что такое анимация нет смысла и заострять внимание на различных модификаторах анимации мы тоже не будем. Для данного раздела достаточно иметь представление о параметрических ключах (parametric keys) и контроллерах (controllers). Если Вы работали с анимацией в 3D Studio Max, то эти два понятия должны Вам быть знакомы. Тем не менее, напомню, параметрические ключи расположены на панели треков, именно они определяют свойства объекта (положение, поворот и т.д.) в конкретный момент времени, что касается контроллеров, с их помощью устанавливается ключи анимационной последовательности. Вполне вероятно, что в процессе работы с анимацией вы не обращались к ни каким контроллерам (любому анимируемому объекту автоматически присваивается контроллер). Типов контролеров немало, и мы попытаемся разобраться с типом, отвечающим за положение объекта в пространстве. Для начала установим его:

PosControll = $.pos.controller
Теперь каждый раз при необходимости работы с анимацией при помощи MAXScript мы будем обращаться к <PosController>.

  Уверен, вам известны следующие понятия: траектория, путь, перемещение. 3D Studio Max может показать реальный путь следования объекта (рис. 26), но он не указывает длину пройденного пути и перемещение. Решим эту проблему при помощи MAXScript.

Рис. 26. Траектория объекта

  Программный код, изложенный ниже (листинг 8), решает нашу проблему. Перед тем, как начать разбираться с ним, поговорим о методах вычисления интересующих нас велечин. С нахождением перемещения не должно возникнуть проблем: нужно найти расстояние между положениями объекта в начальный и конечный моменты времени. Чтобы найти длину всей траектории, будим вычислять расстояния, проходимые объектом за один кадр (в 3D Studio Max 1 секунде соответствует 30 кадров), после чего суммировать их. Методы, как Вы сами убедились, простые. С помощью глобальной переменной animationRange, можно узнать кадры начала и конца анимации, записав, как указано в листинге 8. То есть в массиве мы рассматриваем кадры, когда объект участвует в движении. Указанный ниже контекст позволяет управлять свойствами объекта на конкретном кадре при этом, не переходя на этот кадр.

at time <индекс кадра> <операция> 

  В нашем же случае мы воспользовались им, чтобы получить координаты объекта на данном и последующем шагах, переменным pos1 и pos2 на каждом шаге присваивается значение контроллера, т.е. положение объекта. Далее, с помощью уже известного нам метода distance, находим расстояние между двумя положениями. Следующие строки программного кода уже не должны вызвать у вас затруднения, в них вам всё известно. Существует немало интересных задач, реализуемых с помощью контролера. Нетрудными методами можно рассчитать мгновенную и среднюю скорости объекта, участвующего в анимации, соответственно можно привязать объекту кинетическую энергию (попробуйте сами реализовать это). Я не раз убеждался в том, что 3D Studio Max и MAXScript вместе выступают в роле мощного инструмента для решения задач физического характера.

Листинг 8. Вычисление длины траектории и перемещения.
PosControll = $.pos.controller
Len = 0 
for i = animationRange.Start to animationRange.End do
(
if not i==animationRange.End do
(
at time i (pos1 = PosControll.value)
at time (i+1) (pos2 = PosControll.value)

Len += distance pos1 pos2
)
)
at time (animationRange.Start) pos1 = PosControll.value
at time (animationRange.End) pos2 = PosControll.value

"Length of way= " + (len as string)
"Transference = " + (distance pos1 pos2) as string

  Разобранный выше листинг позволяет лишь обращаться к параметрам контроллеров анимации, но раз речь идёт о работе с анимацией, надо научиться управлять параметрами контроллеров при помощи MAXScript. Синтаксис работы с анимацией выглядит следующим образом:

animate on 
(
at time <номер кадра> (<операция >)
)

  В арсенал объектов 3D Studio Max входят различные источники освещения. Создадим эффект мерцания для источников освещения. Для этого напишем несложную функцию (листинг 9), способную менять коэффициент освещенности (multiplier) с автоматическим установлением анимационных ключей на панели треков. Говоря проще, функция создаёт анимацию мерцания для источника света. После выполнения сценария, функция будет доступна к использованию, ниже приведён синтаксис функции:

twinkle <источник освещения> <начало анимации> <конец анимации> <шаг ключа>

Листинг 9. Создание эффекта мерцания на источнике света.
function twinkle obj start end step= 
(
animate on
(
for i = start to end by step do
(
if (mod i 2)==0 then (at time i (obj.multiplier = 0))--end if
else (at time i (obj.multiplier = 1))--end else
)--end for
)--end aniamte on
)--end function

  Посмотрим, что получилось. Создайте в сцене какой-нибудь объект, наведите на него направленный источник света (рис. 27) Выполните вышеуказанный листинг. Готово, запустите анимацию.

Рис. 27. Траектория объекта

9. Работа с полигонами

9.1. Понятие полигона
  Правильное представление о любых трёхмерных компьютерных объектах включает в себя понятие полигона (polygon). Как любая живая система состоит из простейших единиц – клеток, так и многие объекты в сцене 3D Studio Max состоят из полигонов. Любой полигон имеет видимую и невидимую сторону. Чтобы понять, о чём идёт речь, перейдите в режим каркасного изображения объекта (wireframe), щелкнув правой кнопкой мышки в левом верхнем углу в одном из четырёх окон проекций и выбрав вид wireframe (рис. 28).

Рис. 28. Режим каркасного изображения

  Обратите внимание, все объекты сцены покрыты примитивными геометрическими объектами: треугольниками или четырёхугольниками, полигонами. Итак, попробуем сформулировать определение для чёткого понимания: полигон – это часть плоской поверхности, заданная, как правило, в трёх-четырёх вершинах (vertex). Думаю, понятие вершины хорошо знакомо вам из школьного курса геометрии. Единственное, что нужно иметь ввиду: помимо своих координат, вершина любого полигона имеет свой конкретный индекс. Спрашивается: “В чём надобность индексации вершин в полигонах?”. Ответ на этот вопрос Вы узнаете, когда сами попробуете создать объект, что мы сейчас и попробуем сделать.

9.2. Построение примитивных объектов
  За построение объектов через координаты точек поверхности отвечает проектировщик mesh. Принцип работы данного проектировщика заключается в последовательном “сцеплении” вершин, будучи образованных полигонов. За порядок сцепления отвечают как раз индексы вершин (индексация всегда начинается с единицы). Попробуем сделать поверхность, ограниченную квадратом. Перед тем как обращаться к проектировщику, создадим для начала нужные нам вершины, а точнее массив координат этих вершин:

vertex_array = #([-10,-10,0], [-10, 10, 0], [10, 10, 0], [10, -10, 0])

  Так как речь идёт о квадрате, то мы и рассматриваем лишь четыре вершины. Далее мы создаём массив, отвечающий за порядок сцепления вершин:

index_array = #([1,2,3], [1,4,3])

  Интерпретация массива index_array следующая: сначала создаётся треугольная поверхность с вершинами, индексы которых 1, 2, 3 соответственно, затем соединяются вершины 1, 4, 3 (надеюсь, вы понимаете, что, говоря об индексах, я имею ввиду индексы массива vertex_array). Точки 1 и 3, как вы догадались, выступают в роле узлов, соединяющихся более, чем с двумя другими вершинами. Последнее, что нужно сделать, обратиться к проектировщику mesh:

mesh vertices:vertex_array faces:index_array

Рис. 29. Квадрат, созданный проектировщиком mesh.

  В результате, при последовательном выполнении трёх разобранных строчек программного кода, в сцене 3D Studio Max появится объект (квадрат), состоящий из двух полигонов – равных треугольников. Убедитесь в этом сами (рис. 29). Задавая индексацию в массиве сцепления, помните, что номер используемого индекса не должен превышать количества точек, иначе MAXScript выдаст ошибку. Далее важно отметить, что порядок индексации определяет сторону полигона: индексация по часовой стрелки задаёт невидимую сторону, против часовой – видимую.

  Теперь попробуем создать другую плоскую фигуру – правильный многоугольник. Принцип работы остаётся тем же самым, только вот параметры всех абсолютно вершин задавать вручную мы не будем. Предоставим рутинную работу математике. Теории, изложенной до этого раздела, вполне достаточно для понимания сценария в листинге 10. Поэтому разберём лишь методику построения. Казалось бы, отличий квадрата от правильного многоугольника не так много, не говоря уже о том, что квадрат и есть правильный многоугольник. Тем не менее, явным отличием является порядок сцепления вершин. Я предлагаю вам следующий вариант сцепления. Главным узлом в нашей фигуре будет вершина-центр с индексом 1, далее ниже устанавливается ещё одна вершина на расстоянии Radius. При помощи цикла, будет создаваться новая вершина и сцепляться с главной (с индексом 1) и вершиной, созданной на предыдущем шаге. После того как, очередь дойдёт до последней, сценарий сцепит её с первой и второй вершинами. Кстати говоря, при помощи сценария в листинге 10 можно создать и квадрат, установив значение StepCorner (шаг обхода) 90 градусов. Так что, сейчас мы разобрали наиболее функциональный сценарий.

Рис. 30. Правильный многоугольник, созданный проектировщиком mesh.

Листинг 10. Создание правильного многоугольника
vertex_array = #()
face_array = #()
n=1
StepCorner = 6
Radius = 100 
append vertex_array [0,0,0]
append vertex_array [0,-Radius,0]

for i = -180 to (180-StepCorner) by StepCorner do
(

if not n==360/StepCorner then
(
append vertex_array [Radius*sin(i+StepCorner),Radius*cos(i+StepCorner),0]
append face_array [n+2,n+1,1]
)
else
(
append face_array [n+1,1,2]
)
n+=1
)

mesh vertices:vertex_array faces:face_array

9.3. Создание “слепка” по заданной картинке
  На предыдущем этапе мы научились создавать плоские объекты сцеплением полигонов. Теперь, наверняка, хочется сделать что-то более интересное. Давайте попробуем сделать в сцене «слепок» по заданной картинке. 

  Исходными данными будут графические файлы, но если быть точнее – матрицы MxN (M-длина, N-широта), каждый элемент матрицы – это точка с конкретным цветом. Итак, мы знаем положение точки и её цвет, этого вполне достаточно, чтобы сделать слепок по заданной картинке. Наша задача крайне проста, нам нужно все точки графического файла перевести в сцену 3ds max. Фактически мы создадим точки по координатам x, y. Что бы получился слепок, зададим каждой точке высоту. Высоту будет определять численное значение цвета. На последнем этапе мы сцепляем точки полигонами и получаем «слепок». Алгоритм, как видите, небольшой. Осталось его реализовать. 

  Перед тем как разбирать сценарий, напомню, что начало отсчета координат для пикселей лежит в левом верхнем углу. Сначала мы загружаем исходные данные (графический файл расширения bmp), воспользовавшись функцией OpenBitmap. Аргументом функции будет директория. Далее “собираем урожай”, - создаём множество вершин будучи созданного слепка. На этом этапе мы применим функцию getPixels, которая возращает ряд пикселей по трем параметрам: файл bmp, координаты начала, длина ряда. Остальные фрагменты сценария не должны вызвать у Вас затруднений.

Листинг 11. Создание слепка по заданной картинке
function CreateDispObject file step depth=
(
fileBMP = OpenBitmap file -- загрузка графического файла

VertexArray = #() 
FaceArray = #()

StepX=floor(fileBMP.width/step)
StepY=floor(fileBMP.height/step)

--создание множества вершин "слепка"
for y = 1 to fileBMP.height by step do
(
LinePixel = getPixels fileBMP [0,y] fileBMP.width

for x = 1 to fileBMP.width by step do
(
append VertexArray [x, y, -LinePixel[x].value/depth] -- добавление нового элемента
)--end for x
)--end for y

progressstart "Creating Surface..."
BeginPoint = 0

for i = 1 to (StepY-1) do
(
progressupdate (100.0 * i / (StepY-1))

for j = 1 to (StepX-1) do
(
--добавление индексов сцепления
append FaceArray [j+BeginPoint,j+BeginPoint+1,StepX+j+BeginPoint]
append FaceArray [j+BeginPoint+1,StepX+j+BeginPoint+1,StepX+j+BeginPoint]
)--end for j
BeginPoint+=StepX
)--end for i

obj = mesh vertices:VertexArray faces:FaceArray
progressend()

)--end function

  Теперь, применяя нашу функцию и материалы, можно добиться хорошей реалистичности “сургучной печати”. На рисунке 31 показан пример использования функции CreateDispObject. Ранее мы научились создавать пользовательский интерфейс, соответственно можете довести функцию до инструмента, добавить пользователю возможность самому выбирать графический файл и т.д.

Рис. 31. Применение функции CreateDispObject для создания сургучного слепка

10. Проект MSurface

  Реализуем инструмент (плагин), дающий возможность пользователю создавать в сцене параметризованные поверхности заданием уравнений. Не смотря на краткость постановки, задача непростая, имеет немало подводных камней. Разобравшись в ней, Вы поймёте принцип построения полноценного плагина, а также вспомните курс аналитической геометрии. Будут затронуты уже разобранные нами темы: работа со строками, работа с полигонами, создание плагинов, инструменты для мыши, использование компонентов управления. Ввиду своей обширности я выделил данный раздел как отдельный проект. К конечной цели мы будем подходить “со стороны”, по принципу снежного кома.

10.1. Немного о параметрическом задании поверхности
  Имеет смысл для начала поговорить о параметрическом задании поверхности. Напомню, что поверхность в этом случае задаётся вектором, зависящем от двух параметров U и V (криволинейные координаты поверхности) R(u,v)={x(u,v),y(u,v),z(u,v)}. В этом случае говорят, что поверхность задаётся как множество, являющееся образом при отображении из плоского множества (координаты U и V) в объемное (координаты X, Y, Z). Напомню, что данное утверждение есть грубая формулировка поверхности. Если один из параметров – константа, то уравнение задаст кривую в пространстве (координатная линия).

  Важно понимать, что уравнение задаёт лишь множество точек в пространстве. Что бы получить саму поверхность как конечный объект в сцене 3ds max, нужно грамотно задать массив сцепления точек (см. п.9.2.). Сцеплением мы займемся позже, а пока давайте попробуем создать в сцене множество «точек», координаты которых удовлетворяют уравнению сферы R(θ,φ,r)={rcosθsinφ,r sinθ sinφ,r cosφ}, где θ∈[-π;π],φ∈[0;π] – криволинейные координаты. В листинге 12 представлен сценарий, а на рисунке 32 -соответственно результат.

Листинг 12. Создание сферы точками
R = 100
for i=-180 to 180 by 15 do
(
for j=-90 to 90 by 15 do
(
--параметрическое задание сферы
x=R*cos(i)*cos(j)
y=R*sin(i)*cos(j)
z=R*sin(j)

sphere pos:[x,y,z] radius:2 wirecolor:(color 255 0 0)
)--end for j
)--end for i

  В качестве небольшого упражнения получите координатную линию, задав один из параметров за константу. Убедитесь, что это будет окружность или полуокружность. Думаю, о параметрическом задании поверхности сказано достаточно, что бы идти дальше.

Рис. 32 Создание сферы точками

10.2. Сцепление вершин параметрической поверхности

  Теперь создадим сферу в сцене, но уже как целостный объект, состоящий из множества полигонов. Для данной задачи требуются три стандартных этапа (см. п.9.2.): создать массив координат, создать массив сцепления точек, применить проектировщик mesh. В листинге 13 содержится сценарий для создания параметризованной поверхности на примере сферы, разберём его поэтапно.

  Зададим параметры будучи созданного объекта: интервалы криволинейных координат, шаг и размер. Далее нужно создать массив координат заданной нами поверхности (сфера), при этом прогоняя значения для двух криволинейных координат U и V. Надеюсь, Вы помните, что проектировщик mesh может работать только с одномерными массивами, однако ранее было сказано, что любая поверхность есть отображение из плоского множества в объемное, т.е. требуется двумерный массив. Так как мы знаем все параметры криволинейных координат, то не трудно получить поверхность из одномерного массива, что и сделано в сценарии (листинг 13). В алгоритме сцепления не должно возникнуть трудностей. Теперь мы научились создавать поверхность как образ при отображении из одномерного множества в трёхмерное. Можете поэкспериментировать и задать другие параметризации поверхности (эллипс, цилиндр, геликоид и т.д.).

Листинг 13. Создание сферы полигонами
--параметры объекта
beginU = -180; endU = 180; StepU=10;
beginV = -90; endV = 90; StepV=5;
size = 6;

VertexArray =#() -- массив вершин
FaceArray = #() -- массив сцепления

-- создание массива координат поверхности
for u=beginU to endU by StepU do
(
for v=beginV to endV by StepV do
(
--параметризованное задание поверхности
x=cos(u)*cos(v)
y=sin(u)*cos(v)
z=sin(v)

append VertexArray [size*x, size*y, size*z]
)-- end for v
)-- end for u

--размеры матрицы
n=(endU-beginU)/stepU +1
m=(endV-beginV)/stepV +1

--создание массива сцепления
for i=1 to (n*m-m-1) do
if (not (mod i m ==0) ) do
(
append FaceArray [i, i+1, m+i+1]
append FaceArray [m+i+1, i+m, i]
)--end if
)--end for i

mesh vertices:VertexArray faces:FaceArray

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

10.3. Создание функции ReplaceLetter

  Нетрудно догадаться, что для ввода уравнений в интерфейсе плагина потребуются текстовые поля. Для начала давайте рассмотрим, как должен выглядеть в таком случае этап по созданию массива координат (листинг 14). Мы выберем любые две криволинейные координаты U и V и рассмотрим координату X, пусть переменная S есть введенная пользователем строка, объединяем строку “x=” и переменную S, получаем строку “x=sin(u)*sin(v)”. После выполнения получившейся строки переменной X присваивается численное значение (для выполнения строки используется функция execute). В итоге, заменив переменную S на содержимое текстового поля, мы можем применить сценарий в листинге 14 для дальнейшей реализации плагина.

Листинг 14. Алгоритм для ввода уравнения (без циклов)
u=5; v=7
s="sin(u)*sin(v)"
execute ("x=" + s)
print x as string

Казалось бы, проблема с вводом уравнений решена, однако не стоит забывать, что рассмотренный выше алгоритм имел допущения, связанные с криволинейными координатами. Мы рассмотрели случай, когда U и V принимают по одному значению, однако нам необходимо, что бы каждый из параметров прогонялся массивом, что мы сейчас и сделаем (листинг 15).
Листинг 15. Алгоритм для ввода уравнения (с циклами)
for u=-2 to 2 do
(
for v=-2 to 2 do
(
s="sin(u)*sin(v)"
execute ("x=" + s)
print x as string
)-- end for v
)-- end for u

  Откройте окно MAXScript Listener и посмотрите, какие значения принимает переменная X на каждом этапе. Как видите, значение остается неизменным при любых U и V, что не возможно, поскольку функция sin(u)*sin(v)≠const. К счастью данная проблема решаема с помощью различных операций над строками. Наша задача – написать функцию, которая сможет заменить в строке символ на число. Для работы со строками уже существует функция для замены символов, однако, уверяю Вас, использование этой функции не рационально в случае нескольких замен, можете самостоятельно в этом убедиться. Поэтому будем осуществлять замену вручную, написав функцию (листинг 16). Алгоритм прост: в строке находим положение нужного нам символа, срезаем хвост (строка до символа) и голову (строка после символа) строки, образуем новую строку путём последовательного сцепления головы, значения численной переменной и головы.

Листинг 16. Функция ReplaceLetter
function ReplaceLetter Str Letter Replacement =
(
local head = "" -- начало строки
local tail = "" -- конец строки
local k=0
local Len=(Replacement as string).count

for i=1 to (Str.count) do
(
i+=k
if Str[i]==Letter do
tail = ""; head = ""
for j=1 to (i-1) do head+=Str[j]
for j=(i+1) to (Str.count) do tail+=Str[j]
str = head + "(" + (Replacement as string) + ")" + tail
k+=Len+1
)
)
Str
)
Теперь попробуем выполнить сценарий в листинге 17, дважды применив функцию ReplaceLetter (для U и V). Проблема решена, в этом можете убедиться, просмотрев значения в окне MAXScript Listener. 
Листинг 17. Алгоритм для ввода уравнения с использованием функции ReplaceLetter 
for u=-2 to 2 do
(
for v=-2 to 2 do
(
s="sin(u)*sin(v)"
s1 = ReplaceLetter ("x="+ s ) "u" u
s2 = ReplaceLetter s1 "v" v
execute s2

print x as string
)-- end for v
)-- end for u

  В качестве упражнения предлагаю Вам модифицировать сценарий в листинге 10, а именно: с учётом изложенного материала в пункте 10.3 написать функцию для любых уравнений поверхности.

10.4. Создание плагина MSurface
Итак, все предварительные этапы разобраны, осталось только грамотно всё собрать воедино и написать наконец плагин (листинг 18), сценарий которого состоит из четырёх основных этапов: 

1) определение параметров для объекта (уравнения, интервалы криволинейных координат, шаг, размер);
2) создание элементов управления для изменения параметров (текстовые поля, счетчики), а также возможности выбрать пользователю параметры предложенных в списке поверхностей (сфера, цилиндр, геликоид);
3) блок, где выполняется процесс построения поверхности (для этого блока всё было описано выше в этой главе);
4) создание операций для управления с мышью, дав возможность устанавливать размеры объекта движением мыши.

Листинг 18. Плагин MSurface
plugin simpleObject MSurface 

name: "MSurface"
category: "Maths"
classID:#(0xe405661c,0xacd53b2b)

(
parameters main rollout:params
strX type:#string ui:strX default:"cos(u)*cos(v)"
strY type:#string ui:strY default:"sin(u)*cos(v)"
strZ type:#string ui:strZ default:"sin(v)"

beginU type:#integer ui:beginU default:-180 
endU type:#integer ui:endU default:180
stepU type:#integer ui:stepU default:30 

beginV type:#integer ui:beginV default:-90 
endV type:#integer ui:endV default:90 
stepV type:#integer ui:stepV default:30 

size type:#float ui:size default:6

)--end parameters 

rollout params "Surface"
(
dropDownList ddlEquation "Equations:" items:#("Sphere", "Cylinder", "Cone")

editText strX "X=" default:"cos(u)*cos(v)" 
editText strY "Y=" default:"sin(u)*cos(v)"
editText strZ "Z=" default:"sin(v)"

spinner beginU "beginU:" range:[-10000,10000,-180] type:#integer
spinner endU "endU:" range:[-10000,10000,180] type:#integer
spinner stepU "stepU:" range:[1,180,10] type:#integer

spinner beginV "beginV:" range:[-10000,10000,-180] type:#integer
spinner endV "endV:" range:[-10000,10000,180] type:#integer
spinner stepV "stepV:" range:[1,180,5] type:#integer

spinner size "size:" range:[1,180,6]

on ddlEquation selected sel do
(
case ddlEquation.selection of
(
--уравнения поверхностей
1: (strX.text="cos(u)*cos(v)"; strY.text="sin(u)*cos(v)"; strZ.text="sin(v)") -- сфера
2: (strX.text="cos(u)"; strY.text="sin(u)"; strZ.text="v") -- цилиндр
3: (strX.text="u*sin(v)"; strY.text="u*cos(v)"; strZ.text="u") -- конус
)--end case of 
)--end on ddlEquation 

)--end rollout

on buildMesh do 
(
function ReplaceLetter Str Letter Replacement =
(
local head = "" -- начало строки
local tail = "" -- конец строки
local k=0
local Len=(Replacement as string).count

for i=1 to (Str.count) do
(
i+=k
if Str[i]== Letter do
tail = ""
head = ""
for j=1 to (i-1) do head+=Str[j]
for j=(i+1) to (Str.count) do tail+=Str[j]
str = head + "(" + (Replacement as string) + ")" + tail
k+=Len+1
)--end if
)--end for i
Str
)--end function ReplaceLetter 
try
(
VertexArray =#() -- массив вершин
FaceArray = #() -- массив сцепления

-- создание массива координат поверхности
for u=beginU to endU by StepU do
(
for v=beginV to endV by StepV do
(
--параметризованное задание поверхности
execute (ReplaceLetter (ReplaceLetter ("x="+ strX ) "u" u) "v" v)
execute (ReplaceLetter (ReplaceLetter ("y="+ strY ) "u" u) "v" v)
execute (ReplaceLetter (ReplaceLetter ("z="+ strZ) "u" u) "v" v)

append VertexArray [size*x, size*y, size*z]
)-- end for v
)-- end for u

--размеры матрицы
n=(endU-beginU)/stepU +1
m=(endV-beginV)/stepV +1

--создание массива сцепления
for i=1 to (n*m-m-1) do
if (not (mod i m ==0) ) do
(
append FaceArray [m+i+1, i+1, i]
append FaceArray [i, i+m, m+i+1]
)--end if
)--end for i

setMesh mesh vertices:VertexArray faces:FaceArray

)-- end try

catch (print "Error")

)--end buildMesh

tool create
(
on mousePoint cilck do 
(
if click==1 do (coordsys grid (nodeTM.translation = gridPoint))
)--end on mousePoint

on mouseMove click do
(
case click of
(
2: (size = abs(gridDist.y))
3: (#stop)
)
)--end on mousePoint
)--end tool create
)--end plugin

  Итак, мы реализовали инструмент, позволяющий строить в сцене поверхности через задание параметрических уравнений. Чем полезен данный инструмент? Предположим, в наличии имеются параметрические уравнения поверхности, но представить геометрию поверхности затруднительно. А с помощью объекта MSurface можно создать поверхность, заданную любой параметризацией (с учётом интервалов криволинейных координат), так что решить выше поставленную задачу не составит труда. На рисунке 33 показаны некоторые примеры параметрических поверхностей. Однако этим разнообразие параметрических поверхностей не ограничивается, можете сами поэкспериментировать, комбинируя различные математические операции с параметрами U и V.

Рис. 33. Примеры параметрических поверхностей

  У каждого проекта имеется поле изысканий. Помимо параметрического задания, существуют многие другие способы (явное, неявное и др.). Так что в качестве упражнения можете расширить математическую функциональность инструмента, добавив новые способы задания. Что касается технической части, можете связать инструмент с файлами, где будут храниться уравнения и параметры стандартных поверхностей.

11. Заключение

  Итак, мы прошли немалый путь по изучению основ MAXScript. Мы разобрали следующие разделы: инструменты MAXScript, особенности синтаксиса, основы программирования, инструменты для мыши, а также рассмотрели, как писать сценарии для работы с полигонами и анимацией. Обладая уже полученными знаниями, вы можете самостоятельно приступить к написанию сценариев. Более того, данная статья послужит хорошим толчком для дальнейшего изучения MAXScript как на примерах других сценариев, так и используя различные справочные материалы. Все свои вопросы и пожелания можете присылать на ящик BudennyySemen@gmail.com. Успехов!