Предисловие.
Это пособие было написано, когда я был ещё в десятом классе, так что не обессудьте за нестрогость некоторых суждений, нерациональность некоторых примеров, наличие ошибок, если таковы имеют место быть. Спасибо.
Методическое пособие содержит материал для самостоятельного изучения языка сценариев MAXScript. Цель пособия – научить пользователя самостоятельно расширять инструментарий 3D Studio Max.
Это пособие было написано, когда я был ещё в десятом классе, так что не обессудьте за нестрогость некоторых суждений, нерациональность некоторых примеров, наличие ошибок, если таковы имеют место быть. Спасибо.
Методическое пособие содержит материал для самостоятельного изучения языка сценариев 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. Инструменты для мыши
Содержание:
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 – это инструмент, предназначенный для визуального проектирования (Вы можете самостоятельно расположить тот или иной объект в форме перетаскиванием мыши).
Во вкладке 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 (управление событиями) можно активировать (дезактивировать) событие для конкретного элемента и записать команды, которые будут выполняться при данном событии.
Что бы понять, как всё работает на практике, реализуем инструмент 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 приведён окончательный текст сценария.
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. Успехов!

Комментариев нет:
Отправить комментарий