Контроллер transform_script в 3ds Max

Всем привет. Предлагаю вашему вниманию еще один урок по скриптованию на языке MaxScript.Как и в предидущем уроке www.render.ru/books/show_book.php?book_id=2167 реч пойдет о написании скрипт-контроллера, только тему немного разовьем. Там мы использовали определенные параметры для контроля движения объектов а здесь для контроля движения одних объектов мы мы будем использовать другие объекты. Если вы заинтересовались то начнем.

Команды и правиа написания скрипта которые рассматривались в предидущих уроках я повторять не буду. Если что -то не понятно смотрите уроки или выносите на обсуждение.

Для начала создадим бокс и сферу.Открываем редактор скриптов(не забудте открыть и отладчик) и пишем в нем

b = box()
b.length = 10
b.width = 10
b.height = 10
g = sphere()
g.radius = 50
g.pos = [100,0,0]

Бокс это объект, трансформациями (перемещение,вращение,масштаб) которого мы будем управлять используя геометрию сферы. Для этого назначим ему контроллер анимации transform_script docs.autodesk.com/3DSMAX/16/ENU/MAXScript-Help/files и введем в него нашу сферу под именем g

b.transform.controller = transform_script()
b.transform.controller.addNode "g" ($Sphere001)

Осталось ввести текст скрипт-контроллера но для начала объясню что такое матрица трансформации. Команда transform дает/назначает матрицу трансформации на объект. Если вбить в редактор b.transform то увидим ее в отладчике. Должно получиться matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0] это данные формата matrix3, которые определяют положение,вращение и масштаб объекта. Матрица состоит из набора четырех данных формата point3. Последний набор это мировые координаты положения объекта (нулевые в нашем случае) тут все просто, но вот первые три это вектора в виде point3 которые определяют вращение и масштаб объекта. Что такое вектор в виде point3. это вектор исходящий из начала координат в точку с заданными координатами. В нашем случае это вектора идущие по осям координат и с длинной равной 1. Т. е. мы получили единичную матрицу, говорящую о том что трансформаций нет. Выберем наш бокс панель инструментов раздел Motion (см. рис. 1). и откроем наш контроллер дважды щелкнув по нему. В открывшемся окне в списке переменных будет переменная g с назначенной на нее сферой а в поле текста единичная матрица. Можете поиграться подставляя в нее вместо нулей и единиц разные значения и нажимая Evaluate, чтобы посмотреть как это работает. Например если подставить в третьем наборе вместо единицы три то кубик вырастет в три раза.Надеюсь все более-менее понятно, идем дальше.


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

docs.autodesk.com/3DSMAX/16/ENU/MAXScript-Help/files/ которая определяет пересечение луча и тела. Для создания луча служит команда ray которой нужны два аргумента. Первый это точка начала луча в виде point3 , второй это вектор направления луча в виде point3. Результатом команды intersectray тоже является луч с началом в точке пересечения и направленный также как нормаль к поверхности в точке пересечения. По данным этого луча мы создадим новую матрицу и впишем ее в контроллер. Следующей строкой в редакторе будет строка текста контроллера.

b.transform.controller.script = "

дале следует текст скрипта, в нем первым делом определяем transform как единичную матрицу

transform = matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0]

создаем луч с именем r1, начальная позиция луча такая же как у кубика (ее даст команда transform.translation) только вертикальную координату поднимем на 500 что-бы заведомо обеспечить пересечение луча и сферы. Направление луча строго вниз его даст point3 вектор [0,0,-1].

r1 = ray [transform.translation.x,transform.translation.y,transform.translation.z + 500] [0,0,-1]

создаем переменную r2 и назначаем ей луч пересечения сферы g и луча r1
r2 = intersectray g r1

Надеюсь я понятно объясняю и вам пока все понятно. Если это не так то нужно самому(самой) прокрутить все в уме или отработать в редакторе данные команды чтобы придти к пониманию потому как без него дальнейшее прохождение урока бессмыслено. Далее более сложная для понимания часть.
После того как мы создали луч пересечения мы фактически можем определить по нему матрицу трансформации но это только для тех случаев когда пересечение имеет место быть (бокс над/под сферой), если пересечения не будет то команда intersectray выдаст undefined (неназначено) и дальнейшая работа с ним производиться не будет. По этому создаем тест проверки (операторы if then) наличия пересечения (не равно undefined) и добавляем проверку для всех положительных координат z пересечения (ее выдает команда для луча pos.z). Это делается чтобы создать плоскость земли (равную нулевой в нашем случае) т. е. координаты пересечения ниже нулевой плоскости рассматриваться не будут и кубик не провалится под землю.Оператор and в данном условии означает логическое сложение т. е. тест проходит когда выполняются оба условия.


if r2 != undefined and r2.pos.z > 0 then

Далее в скобках то что нужно сделать при успешном прохождении теста, а именно создать переменную m и назначить ей матрицу из направления (команда matrixFromNormal). За направление берем направление луча r2 (команда dir), приравненное по длине к единице (normalize).Четвертое Point3 матрицы m т. е. позицию устанавливаем как позицию луча пересечения r2. И последним действием назначаем на матрицу трансформации кубика нашу получившуюся матрицу m.

(
m = matrixFromNormal (normalize r2.dir)
m.row4 = r2.pos
transform = m
)

Следующей строкой прописываем действия если тест не проходит (оператор else). В них мы устанавливаем трансформации кубика по исходной единичной матрице т. е. кубик остается на исходном месте когда нет его пересечения со сферой или когда оно ниже нулевой плоскости. И последней строкой закрываем ковычки текста скрипт-контроллера.

else transform = matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0]
"

Вот скрипт целиком, можно скопировать в редактор.

b = box()
b.length = 10
b.width = 10
b.height = 10
g = sphere()
g.radius = 50
g.pos = [100,0,0]
b.transform.controller = transform_script()
b.transform.controller.addNode "g" ($Sphere001)
b.transform.controller.script = "
transform = matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0]
r1 = ray [transform.translation.x,transform.translation.y,transform.translation.z + 500] [0,0,-1]
r2 = intersectray g r1
if r2 != undefined and r2.pos.z > 0 then
(
m = matrixFromNormal (normalize r2.dir)
m.row4 = r2.pos
transform = m
)
else transform = matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0]
"

Результатом действия скрипта будет создание сферы и бокса с наложенным на него transform_script контроллером. Если скрипт сработал верно то при перемещении сферы к кубику тот будет двигаться как на первой gif.


Если этого не получилось смотрите отладчик и исправляйте ошибки. Если все верно то идем дальше. Теперь усложним задачу. Создадим множество кубиков лежащих в нулевой плоскости. Для этого будем использовать двойной цикл (цикл в цикле). Внешний будет задавать приращение координаты y начального положения кубиков а внутренний приращение x. Как оно работает - да очень просто. На каждом шаге внешнего цикла проходит полностью внутренний цикл. Каждый цикл у нас идет от 1 до 10 по этому в итоге подучим 100 боксов. Перед каждым циклом задаем начальное значение его координаты - это нули. Эти координаты вводим в контроллер как константы. Их же прописываем в тексте скрипта в четветом позиционном наборе Point3 начальной матрицы, чтобы контроллер задал нужное нам начальное положение кубиков. В конце каждого цикла задаем приращение его координаты собственно для чего цикл был и нужен. Я сделал 15 (эту цифру можете рассматривать как шаги как милиметры или пиксели да как угодно, смотря в каких координатах вы мыслите и какие системные единицы). Вот скрипт целиком. Так же можете скопировать и выполнить.


g = sphere()
g.radius = 50
g.pos = [100,0,0]
y = 0
for i = 1 to 10 do
(
x = 0
for i = 1 to 10 do
(
b = box()
b.length = 10
b.width = 10
b.height = 10
b.pos = [x,y,0]
b.transform.controller = transform_script()
b.transform.controller.addNode "g" ($Sphere001)
b.transform.controller.addConstant "x" (x)
b.transform.controller.addConstant "y" (y)
b.transform.controller.script = "transform = matrix3 [1,0,0] [0,1,0] [0,0,1] [x,y,0]
r1 = ray [transform.translation.x,transform.translation.y,transform.translation.z + 500] [0,0,-1]
r2 = intersectray g r1
if r2 != undefined and r2.pos.z > 0 then
(
m = matrixFromNormal (normalize r2.dir)
m.row4 = r2.pos
transform = m
)
else transform = matrix3 [1,0,0] [0,1,0] [0,0,1] [x,y,0]
"
x = x +15
)
y = y + 15
)

Плюс метода в том что кубики реагируют на любое изменение геометрии сферы включая модификаторы и работу с подобъектами сетки. Изучив и поняв как все работает вы сможете создавать собственную анимацию используя свою фантазию и возможности програмирования на языке MaxScript. В титульном видео я добавил скалирование боксов по z и изгиб управляющего тела. Но это уже сами, сами.

Больших творческих узбеков.


671 0 850 10
3
2015-07-10
Впечатляет! Вот бы разобраться ещё во всём этом) Большое спасибо за урок :)
2015-07-10
И ещё. Можно получить проект с рабочим скриптом? Что бы было с чем сравнить при изучении.
2015-08-14
Так в конце урока рабочий скрипт и есть.
RENDER.RU