Хитрости MaxScript
…или как я гифку повторял
Предупреждение: в этом посте будет много матана математики. Это для тех, кто хочет разобраться. Кому интересен сам процесс это будет не так страшно.
Предыстория
Я увидел эту гифку ВКонтакте, в тематическом сообществе с подписью:
«Что видит обычный человек? Вращающуюся гифку. Что видит 3D-моделлер? Как работает сглаживание»
Комментировать подпись не буду, а вот попробовать сделать подобные катающиеся фигурки захотелось. Для куба я уже выводил необходимые формулы и катал его по плоскости, теперь дело за правильным многоугольником.
Ученье — свет
Первое, что я решил сделать, это вывести универсальную формулу для многогранника с N сторонами, начиная от 3.
Извините за почерк, какой есть. Собственно, в чем вывод? Необходимо было узнать, с какой до какой высоты меняется положение центра многогранника, а так же зависимость поворота от позиции центра по оси X. Вроде бы, все верно вывел.
Проблемы с 3D Max и MaxScript
Вывести формулу — еще полдела, как ее грамотно перевести в MaxScript да еще и связать параметры? А как правильно установить начальную точку отсчета? Со всем этим я разбирался дольше, чем выводил необходимые зависимости. Думаю, мои изыскания окажутся полезными.
Первая проблема: как грамотно создать многогранники? Писать отдельный скрипт? Какой-то маразм. Все проще. Мы создаем несколько цилиндров в ряд с разным количеством граней и выключенным сглаживанием (поля sides и галка smooth):
Эта операция так же позволяет нам найти опорную точку (все цилиндры имеют ребро на оси X), что будет весьма полезно.
Затем надо задать всем цилиндрам одинаковое количество сегментов по высоте и саму высоту (которая станет шириной). Это можно сделать, выполнив в строке MaxScript Listener команды (предварительно выделив все цилиндры):
$.heightsegments = 1 и $.height = 20 соответственно
Далее необходимо поставить эти цилиндры на то самое ребро. Для этого надо переставить режим работы с объектами в «Use Pivot Center», т.е. вращать каждый объект относительно своего якоря. И привязку надо не забыть включить.
В итоге должно получится что-то подобное:
Все цилиндры многогранники стоят на своих ребрах, имеют одинаковую ширину и всего один сегмент по ширине.
Что дальше? А дальше нужно обязательно сделать следующее: зайти в Иерархию, нажать Reset Pivot и потом Center to Object (при нажатом Affect Pivot Only). Это необходимо для адекватного связывания параметров и установки якоря объекта в его центр. Если не выполнить этих операций, то при связывании параметров наши многогранники будут вести себя не так, как ожидается.
Программное связывание параметров и другие проблемы
В этом разделе я перехожу непосредственно к MaxScript и тем «откровениям», которыми хотел поделиться.
Размножить многогранники в один ряд и задать каждому свое количество граней я решил руками, это было быстро. А вот связать параметры оказалось не так просто. Я нашел в Help’е прекрасную команду paramWire.connect и подумал, что вот оно, счастье-то. Но не все так просто. Привыкнув к ActionScript, я перенес свой опыт на MaxScript и попробовал следующий код:
Поясняю: я думал, что в MaxScript можно обратиться к позиции объекта так же, как и во Flash. Это не так. Чтобы выяснить, как же правильно связать параметры, пришлось использовать Macro Recorder (очень полезная утилита, когда не знаете, как правильно должен выглядеть код. Гораздо полезнее Help’а, на мой взгляд). В итоге строка связывания параметров выглядит так:
Код по кусочкам: символ $ — обращение к выделенному объекту, первая переменная после команды connect — параметр, от которого будет зависеть вторая переменная, stringExp — выражение, являющееся строковой переменной, содержащее переменную, указанную в квадратных скобках у первого параметра (в данном случае Y_Position).
Я не зря выделил слово «строковой», на этом я тоже споткнулся. Об этом чуть позже.
Следующая задача — сделать многогранники с одинаковыми гранями по длине. Для этого из моего вывода выразим радиус и применим этот кусок кода к каждому цилиндру:
Я сделал это просто через кнопку. То есть создал rollout, в нем одна кнопка, и при нажатии на нее исполняется этот код. Попробуйте сами, не стесняйтесь.
Кто попробовал — молодцы, должны были заметить косяк. Не буду томить, косяк в строке alp=, а именно в делении 360 на удвоенное количество граней. Это деление возвращает целочисленное (integer) значение, а нам нужно значение с плавающей запятой. Следующий код правильный:
Не пробовал сам, но, возможно проще было бы написать 360.0 вместо 360.
Вот, что получилось, после применения этих команд к нескольким многогранникам:
Теперь у них каждая грань — квадрат со стороной 20 (так как я ставил высоту так же 20).
Дальше нам нужно объединить связывание параметров и этот кусок кода.
Остановимся подробнее на переменных stringExp и stringExpRot: вы можете заметить, что все переменные указаны с оператором as string, переводящем их в строчки. Это необходимо, чтобы из кучи переменных сделать одну строчку, содержащую определяющий параметр. Не знаю, возможно ли в такую строку запихивать несколько параметров и связывать, но подозреваю, что нельзя.
Выглядят эти строки страшно, но это необходимо (в них скрыта вся математика, в том числе и та, которую я не учел на бумаге). Если писать переменные без оператора as string, то он попробует сложить числа и строки и выдаст вам ошибку. Так же я определяю эти выражения отдельно, потому функция connect воспринимает строго 3 параметра на вход и складывать строчку там не получится (лично у меня не получилось, и именно на этом я споткнулся).
Такие вот откровения и нюансы работы MaxScript открываются при попытке написать простенький скрипт.
Выводы и послесловие
А результат этого всего: используйте Macro Recorder, когда документации не хватает. Функция mod имеет не такой привычный вид (надо писать mod x y, а не x mod y). Полезная функция для отладки (нашел методом тыка): print (неожиданно, да?).
Если присматриваться в моей сцене к нижним ребрам, то они все-таки двигаются, а не стоят на местах, как должны. Значит, мой вывод формул неверный. Тем не менее, при визуализации это выглядит так, как хотелось бы.