Список примеров

Примеры программирования контроллеров Float_Expression и Float_Script

Содержание

Постановка задачи

Управляющий элемент (контроллер) Float_Expression может быть ассоциирован с произвольным, принимающим вещественные значения анимируемым параметром объекта 3ds Max.
Заданное в контроллере выражение должно возвращать вещественную величину. Как правило, это величина изменяется во времени.
Возвращаемое контроллером значение используется для построения анимационной кривой управляемого параметра.
Рассматривается пример создания бегущего по цилиндрическим лампочкам света (рис. 1).

Бегущий огонь на основе контроллеров Float_Expression и Float_Script

Рис. 1. Бегущий свет

Каждому цилиндру назначается стандартный материал, диффузионная компонента которого имеет значение Orange. Управляемым параметром является свойство SelfIllumAmount (самоизлучение, Self-Illumination) этого материала.
Для первой лампочки (первого справа цилиндра) это свойство изменяется во времени по приведенному на рис. 2 закону.

Изменение свойства SelfIllumAmount при создании бегущего света

Рис. 2. График изменения свойства SelfIllumAmount первой лампочки

Чтобы увидеть этот график, следует (после выполнения нижеприводимой программы) выбрать цилиндр, например Cylinder01, и выполнить меню – Graph Editors – Track View Curve Editor – в появившемся дереве найти Cylinder01 и позиционироваться на ветви Self-Illumination.
Лампочка горит, когда Self-Illumination = 100.
Графики изменения свойства SelfIllumAmount прочих лампочек получаются в результате фазового сдвига приведенного на рис. 2 графика. Величина сдвига вычисляется по формуле

dT = 100 / ((n - 1) * nL), где

100 – длина анимационного интервала;
nL – число лампочек;
n – число включений каждой лампочки на анимационном интервале.

Реализация на базе контроллера Float_Expression

При работе с контроллером Float_Expression выражение может включать приведенные на рис. 3 функции.

В диалоге программирования Float_Expression нажата кнопка Function List

Рис. 3. Список функций контроллера Float_Expression

Для просмотра этого списка следует дважды ударить мышью по иконке, сопровождающей на рис. 2 ветвь Self-Illumination, и в открывшемся окне (рис. 4) нажать на кнопку Function List.

Диалог программирования контроллера Float_Expression

Рис. 4. Диалог программирования контроллера Float_Expression

Выражение Expression может включать скалярные (Scalars, не векторы) и векторные (Vectors, Point3-векторы) переменные, а также переменные T, F, S и NT, значения которых определяются текущим временем анимации.
В нашем случае можно поступить следующим образом:

  1. Назначить каждой лампочке свой материал.
  2. Параметр SelfIllumAmount каждого материала ассоциировать с отдельным контроллером Float_Expression.
  3. Установить выражение каждого контроллера Float_Expression, обеспечивающее показанный (с учетом фазы) на рис. 2 график включения-выключения лампочки.

Для записи надлежащего выражения введем n векторов p1, p2, …, pn, где n – это число включений лампочки на анимационном интервале. Каждый вектор содержит два элемента: начало и конец отрезка включения лампочки. Так, для первой лампочки (индексация компонентов вектора начинается с нуля) в примере с nL = 5 и n = 5 формируем следующие векторы (dT = 5):

p1 = [0, 5, 0]
p2 = [25, 30, 0]
p3 = [50, 55, 0]
p4 = [75, 80, 0]
p5 = [100, 105, 0]

То есть первая лампочка горит на отрезке от 0 до 5, затем на отрезке от 25 до 30 и так далее. Иными словами, на этих отрезках значение параметра SelfIllumAmount материала, назначенного лампочке (цилиндру), должно быть равно 1. На прочих отрезках лампочка выключена и, следовательно, SelfIllumAmount = 0.
Эту закономерность обеспечит следующее выражение:

if(F >= comp(p1, 0), if(F < comp(p1, 1), 1, if(F >= comp(p2, 0), if(F < comp(p2, 1), 1, if(F >= comp(p3, 0), if(F < comp(p3, 1), 1, if(F >= comp(p4, 0), if(F < comp(p4, 1), 1, if(F >= comp(p5, 0), if(F < comp(p5, 1), 1, 0), 0)), 0)), 0)), 0)), 0)

Функция comp возвращает значение компонента вектора (первого параметра функции). Индекс компонента – это второй параметр функции. Индексация начинается с нуля.
Более наглядно выражение при n = 2 (nL = 5, dT = 20):

if(F >= comp(p1, 0), if(F < comp(p1, 1), 1, if(F >= comp(p2, 0), if(F < comp(p2, 1), 1, 0), 0)), 0)

в котором

p1 = [0, 20, 0]
p2 = [100, 120, 0]

Получив время F (номер текущего кадра анимации), контроллер оценит заданное в нем выражение и вернет либо 1.0, либо 0.0.
В приводимом ниже коде функция mkArrM формирует для каждой лампочки массив arrM с векторами-отрезками включения лампочки.
Функция mkExpr, получив массив arrM, формирует выражение для очередного контроллера Float_Expression.
Функция mkFltX2 формирует контроллер Float_Expression для управления параметром SelfIllumAmount материала одной лампочки (параметр ob).
Эта функция получает массив arrM с векторами-отрезками включения лампочки, выражение expr и ссылку ob на соответствующий цилиндр-лампочку. Затем функция формирует материал, назначает его цилиндру, добавляет контроллеру необходимое число векторных переменных, устанавливает его выражение и назначает контроллер параметру SelfIllumAmount созданного материала.

-- Формирует для каждой лампочки массив arrM с векторами-отрезками включения лампочки
fn mkArrM n nL dT tS = (
 arrM = #()
 t = tS
 for k2 = 1 to n do (
  append arrM [t, t + dT, 0]
  t = t + dT * nL
 )
 return arrM
)
-- Формирует выражение контроллера Float_Expression
fn mkExpr arrM = (
 cnt = arrM.Count
 expr = ""
 addX2 = ""
 for k2 = 1 to cnt do (
  p = "p" + k2 as string
  addX = "if(F >= comp(" + p + ", 0), if(F < comp(" + p + ", 1), 1, "
  addX2 += "), 0)"
  expr += addX
 )
 return expr + " 0" + addX2
)
-- Формирует материал, контроллер Float_Expression и назначает его параметру SelfIllumAmount
fn mkFltX2 arrM expr ob = (
 std = standard diffuse:orange showInViewport:true
 ob.Material = std
 fltX = float_Expression()
 cnt = arrM.Count
 for k2 = 1 to cnt do (
  p = "p" + k2 as string
  fltX.AddVectorConstant p arrM[k2]
 )
 fltX.SetExpression expr
 fltX.Update()
 std.SelfIllumAmount.Controller = fltX
)
-- Подготовка сцены
delete $*
sliderTime = 0f
viewport.ActiveViewport = 4
max vpt persp user
animationRange = interval 0f 100f
timeConfiguration.RealTimePlayback = on
max tool zoomExtents
-- Радиус и высота цилиндра-лампочки
r = 10; h = 40
-- Число лампочек и число включений каждой лампочки
nL = 5; n = 5
-- Время включения лампочки
dT = 100 / ((n - 1) * nL)
-- Координата первой справа лампочки
ps = [120, 0, 0]
-- Создаем контроллеры, материалы и объекты-лампочки
for k = 1 to nL do (
 -- Формируем массив arrM с векторами-отрезками включения лампочки k
 arrM = mkArrM n nL dT (dT * (k - 1))
 -- Формируем выражение очередного контроллера Float_Expression
 expr = mkExpr arrM
 ps -= [h, 0, 0]
 -- Формируем горизонтальный цилиндр в позиции ps
 ob = cylinder radius:r height:h rotation:(quat 0 0.707107 0 0.707107) pos:ps
 -- Формируем материал, контроллер Float_Expression
 -- и назначаем его параметру SelfIllumAmount
 mkFltX2 arrM expr ob
)
-- Воспроизводим анимацию
playAnimation()

Реализация на базе контроллера Float_Script

Выражение (скрипт) контроллером Float_Script может содержать произвольный правильно написанный MAXScript-код, возвращающий вещественную величину.
В примере с лампочками для получения приведенной на рис. 2 анимационной кривой подходит следующий код:

sOn = 0
for k = 1 to n do
 if F > arrM[k][1] and F <= arrM[k][2] do (
  sOn = 1
  exit
 )
sOn

где arrM – по-прежнему массив с векторами-отрезками включения лампочки.
Используемые в коде переменные n и arrM должны иметь глобальную видимость.
Употребим этот код (в подходящем виде) в качестве скрипта контроллера Float_Script.

-- Формирует для каждой лампочки массив arrM с векторами-отрезками включения лампочки
fn mkArrM n nL dT tS = (
 arrM = #()
 t = tS
 for k2 = 1 to n do (
  append arrM [t, t + dT, 0]
  t = t + dT * nL
 )
 return arrM
)
-- Формирует скрипт контроллера Float_Script
fn mkExpr k = (
 return "sOn = 0
 for k = 1 to n do (
  p = arrM" + (k as string) + "[k]
  if F > p[1] and F <= p[2] do (
   sOn = 1
   exit
  )
 )
 sOn"
)
-- Формирует материал, контроллер Float_Script и назначает его параметру SelfIllumAmount
fn mkFltS expr ob = (
 std = standard diffuse:orange showInViewport:true
 ob.Material = std
 fltS = float_Script script:expr
 std.SelfIllumAmount.Controller = fltS
)
-- Подготовка сцены
delete $*
sliderTime = 0f
viewport.ActiveViewport = 4
max vpt persp user
animationRange = interval 0f 100f
timeConfiguration.RealTimePlayback = on
max tool zoomExtents
-- Радиус и высота цилиндра-лампочки
r = 10; h = 40
-- Число лампочек
nL = 5
-- Число включений каждой лампочки
global n = 5
-- Время включения лампочки
dT = 100 / ((n - 1) * nL)
-- Координата первой справа лампочки
ps = [120, 0, 0];
global arrM1 = #(), arrM2 = #(), arrM3 = #(), arrM4 = #(), arrM5 = #()
-- Создаем контроллеры, материалы и объекты-лампочки
for k = 1 to nL do (
 -- Формируем массивы с векторами-отрезками включения лампочки
 case k of (
  1: arrM1 = mkArrM n nL dT (dT * (k - 1))
  2: arrM2 = mkArrM n nL dT (dT * (k - 1))
  3: arrM3 = mkArrM n nL dT (dT * (k - 1))
  4: arrM4 = mkArrM n nL dT (dT * (k - 1))
  5: arrM5 = mkArrM n nL dT (dT * (k - 1))
 )
 -- Формируем скрипт очередного контроллера Float_Script
 expr = mkExpr k
 ps -= [h, 0, 0]
 -- Формируем горизонтальный цилиндр в позиции ps
 ob = cylinder radius:r height:h rotation:(quat 0 0.707107 0 0.707107) pos:ps
 -- Формирует материал, контроллер Float_Script и назначает его параметру SelfIllumAmount
 mkFltS expr ob
)
-- Воспроизводим анимацию
playAnimation()

Заключение

Вариант с контроллером Float_Script, возможно, более нагляден, но не столь удобен в употреблении, поскольку при увеличении числа лампочек придется модифицировать код, добавляя в него определения массивов arrM*. Кроме того, этот вариант требует больших ресурсов, опять-таки из-за необходимости держать массивы arrM* в количестве, не меньшем числа лампочек.

Источники

  1. Autodesk® 3ds Max® 2009 MAXScript Reference.
  2. Бартеньев О. В. Программирование модификаторов 3ds Max. Учебно-справочное пособие. – М.:Физматкнига, 2009. – 341 с.

Список примеров

Рейтинг@Mail.ru