Управляющий элемент (контроллер) Float_Expression может быть ассоциирован с произвольным, принимающим вещественные значения анимируемым параметром объекта 3ds Max.
Заданное в контроллере выражение должно возвращать вещественную величину. Как правило, это величина изменяется во времени.
Возвращаемое контроллером значение используется для построения анимационной кривой управляемого параметра.
Рассматривается пример создания бегущего по цилиндрическим лампочкам света (рис. 1).
Рис. 1. Бегущий свет
Каждому цилиндру назначается стандартный материал, диффузионная компонента которого имеет значение Orange. Управляемым параметром является свойство SelfIllumAmount (самоизлучение, Self-Illumination) этого материала.
Для первой лампочки (первого справа цилиндра) это свойство изменяется во времени по приведенному на рис. 2 закону.
Рис. 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 выражение может включать приведенные на рис. 3 функции.
Рис. 3. Список функций контроллера Float_Expression
Для просмотра этого списка следует дважды ударить мышью по иконке, сопровождающей на рис. 2 ветвь Self-Illumination, и в открывшемся окне (рис. 4) нажать на кнопку Function List.
Рис. 4. Диалог программирования контроллера Float_Expression
Выражение Expression может включать скалярные (Scalars, не векторы) и векторные (Vectors, Point3-векторы) переменные, а также переменные T, F, S и NT, значения которых определяются текущим временем анимации.
В нашем случае можно поступить следующим образом:
Для записи надлежащего выражения введем 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 может содержать произвольный правильно написанный 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* в количестве, не меньшем числа лампочек.