Хитрости MaxScript

…или как я гифку повторял

Предупреждение: в этом посте будет много матана математики. Это для тех, кто хочет разобраться. Кому интересен сам процесс это будет не так страшно.

Предыстория

AnimatedGeometry

Я увидел эту гифку ВКонтакте, в тематическом сообществе с подписью:

«Что видит обычный человек? Вращающуюся гифку. Что видит 3D-моделлер? Как работает сглаживание»

Комментировать подпись не буду, а вот попробовать сделать подобные катающиеся фигурки захотелось. Для куба я уже выводил необходимые формулы и катал его по плоскости, теперь дело за правильным многоугольником.

Ученье — свет

Первое, что я решил сделать, это вывести универсальную формулу для многогранника с N сторонами, начиная от 3.

000151875

 

Извините за почерк, какой есть. Собственно, в чем вывод? Необходимо было узнать, с какой до какой высоты меняется положение центра многогранника, а так же зависимость поворота от позиции центра по оси X. Вроде бы, все верно вывел.

Проблемы с 3D Max и MaxScript

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

Первая проблема: как грамотно создать многогранники? Писать отдельный скрипт? Какой-то маразм. Все проще. Мы создаем несколько цилиндров в ряд с разным количеством граней и выключенным сглаживанием (поля sides и галка smooth):

Cylinders

Эта операция так же позволяет нам найти опорную точку (все цилиндры имеют ребро на оси X), что будет весьма полезно.

Затем надо задать всем цилиндрам одинаковое количество сегментов по высоте и саму высоту (которая станет шириной). Это можно сделать, выполнив в строке MaxScript Listener команды (предварительно выделив все цилиндры):

$.heightsegments = 1 и $.height = 20 соответственно

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

UsePivotCenter

В итоге должно получится что-то подобное:

Result

 

Все цилиндры многогранники стоят на своих ребрах, имеют одинаковую ширину и всего один сегмент по ширине.

Что дальше? А дальше нужно обязательно сделать следующее: зайти в Иерархию, нажать Reset Pivot и потом Center to Object (при нажатом Affect Pivot Only). Это необходимо для адекватного связывания параметров и установки якоря объекта в его центр. Если не выполнить этих операций, то при связывании параметров наши многогранники будут вести себя не так, как ожидается.

Программное связывание параметров и другие проблемы

В этом разделе я перехожу непосредственно к MaxScript и тем «откровениям», которыми хотел поделиться.

Размножить многогранники в один ряд и задать каждому свое количество граней я решил руками, это было быстро. А вот связать параметры оказалось не так просто. Я нашел в Help’е прекрасную команду paramWire.connect и подумал, что вот оно, счастье-то. Но не все так просто. Привыкнув к ActionScript, я перенес свой опыт на MaxScript и попробовал следующий код:

Код
paramWire.connect $.pos.y $pos.z y

Поясняю: я думал, что в MaxScript можно обратиться к позиции объекта так же, как и во Flash. Это не так. Чтобы выяснить, как же правильно связать параметры, пришлось использовать Macro Recorder (очень полезная утилита, когда не знаете, как правильно должен выглядеть код. Гораздо полезнее Help’а, на мой взгляд). В итоге строка связывания параметров выглядит так:

Код
paramWire.connect $.pos.controller[#Y_Position] $.pos.controller[#Z_Position] stringExp

Код по кусочкам: символ $ — обращение к выделенному объекту, первая переменная после команды connect — параметр, от которого будет зависеть вторая переменная, stringExp — выражение, являющееся строковой переменной, содержащее переменную, указанную в квадратных скобках у первого параметра (в данном случае Y_Position).

Я не зря выделил слово «строковой», на этом я тоже споткнулся. Об этом чуть позже.

Следующая задача — сделать многогранники с одинаковыми гранями по длине. Для этого из моего вывода выразим радиус и применим этот кусок кода к каждому цилиндру:

Код
 sides = $.sides
 alp = (90 - (360/(2*sides)) as float
 sl = 20
 radius = sl/(2*cos(alp)) as float
 $.radius = radius

Я сделал это просто через кнопку. То есть создал rollout, в нем одна кнопка, и при нажатии на нее исполняется этот код. Попробуйте сами, не стесняйтесь.

Кто попробовал — молодцы, должны были заметить косяк. Не буду томить, косяк в строке alp=, а именно в делении 360 на удвоенное количество граней. Это деление возвращает целочисленное (integer) значение, а нам нужно значение с плавающей запятой. Следующий код правильный:

Код
 sides = $.sides
 alp = (90 - (360/(2*sides) as float) as float
 sl = 20
 radius = sl/(2*cos(alp)) as float
 $.radius = radius

Не пробовал сам, но, возможно проще было бы написать 360.0 вместо 360.

Вот, что получилось, после применения этих команд к нескольким многогранникам:

ScriptResult

 

Теперь у них каждая грань — квадрат со стороной 20 (так как я ставил высоту так же 20).

Дальше нам нужно объединить связывание параметров и этот кусок кода.

Код
rollout MultiAngleAnim "MultiAngleAnim" width:162 height:165
(
 button btn1 "Wire Parametrs" pos:[10,99] width:141 height:53
 on btn1 pressed do
(
 sides = $.sides
 alp = (90 - (360/(2*sides) as float)) as float
 print alp
 sl = 20
 radius = sl/(2*cos(alp)) as float
 $.radius = radius
 stringExp ="(" + sl as string + "/2)*(tan(" + alp as string + ")+(1/cos(" + alp as string + ")-tan(" + alp as string + "))*abs(sin(360*(mod (Y_Position-" + (sl/2) as string + ") " + (2*sl) as string + ")/" + (2*sl) as string + ")))"
 stringExpRot = "-2*pi*(mod Y_Position " + (sides*sl) as string + ")/" + (sides*sl) as string
 paramWire.connect $.pos.controller[#Y_Position] $.pos.controller[#Z_Position] stringExp
 paramWire.connect $.pos.controller[#Y_Position] $.rotation.controller[#X_Rotation] stringExpRot 
)
)
createDialog MultiAngleAnim

Остановимся подробнее на переменных stringExp и stringExpRot: вы можете заметить, что все переменные указаны с оператором as string, переводящем их в строчки. Это необходимо, чтобы из кучи переменных сделать одну строчку, содержащую определяющий параметр. Не знаю, возможно ли в такую строку запихивать несколько параметров и связывать, но подозреваю, что нельзя.

Выглядят эти строки страшно, но это необходимо (в них скрыта вся математика, в том числе и та, которую я не учел на бумаге). Если писать переменные без оператора as string, то он попробует сложить числа и строки и выдаст вам ошибку. Так же я определяю эти выражения отдельно, потому функция connect воспринимает строго 3 параметра на вход и складывать строчку там не получится (лично у меня не получилось, и именно на этом я споткнулся).

Такие вот откровения и нюансы работы MaxScript открываются при попытке написать простенький скрипт.

Выводы и послесловие

А результат этого всего: используйте Macro Recorder, когда документации не хватает. Функция mod имеет не такой привычный вид (надо писать mod x y, а не x mod y). Полезная функция для отладки (нашел методом тыка): print (неожиданно, да?).

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

Использованные и полезные ссылки
Теория MaxScript на русском — ссылка

Официальная справка по MaxScript — ссылка

Добавить комментарий

© 2025 CAsperovskii BLOG.RU // Дизайн и поддержка: GoodwinPress.ru