В параметрической модели имеется взаимосвязь параметров ее различных компонентов. В такой модели изменение значения одного параметра влечет изменения значений связанных с ним параметров модели. Подобные модели широко применяются в САПР. Примером такой модели в 3ds Max может служить объект Biped (двуногий).
3ds Max позволяет создавать параметрические модели посредством связывания параметров, употребления соответствующих контроллеров и модификаторов (преимущественно параметрических), а также за счет создания соответствующего MAXSript-кода, обеспечивающего пересчет модели при изменении значения управляющего параметра.
Рассмотрим параметрическую модель (рис. 1, а), в которой реализуются следующие зависимости:
R = abs(0.25 * H);
spherePosition = conePosition + [0, 0, 1.25 * H],
где
R – радиус сферы;
H – высота конуса;
spherePosition – позиция сферы (координаты ее центра);
conePosition – позиция конуса (координаты центра его основания).
а | б |
Рис. 1. Параметризованная модель: а – H = 50; б – H = -50 |
Рассматриваемую модель можно реализовать в 3ds Max несколькими способами. Приведем два из них. В первом употребим контроллеры Float Expression и Position Expression для формирования соответственно первой и второй зависимостей, а во втором – контроллеры Float Script и Position Script.
Создание модели и ее параметризацию средствами контроллеров Float Expression и Position Expression обеспечит следующий код:
delete $*
h = 50
cn = cone radius1:20 radius2:0 height:70 heightSegs:20 sides:24 wireColor:[6, 135, 113]
sph = sphere radius:(0.25 * cn.Height) segs:32 wireColor: [135, 110, 8]
sph.Pos = cn.Pos + [0, 0, 1.25 * cn.Height]
cn.Height.Controller = bezier_float()
fltX = float_expression()
fltX.AddScalarTarget "h" cn.Height.Controller
fltX.SetExpression "abs(0.25 * h)"
sph.Radius.Controller = fltX
--
pstnX = position_expression()
pstnX.AddScalarTarget "h" cn.Height.Controller
pstnX.AddVectorTarget "ps" cn.Position.Controller
pstnX.SetExpression "ps + [0, 0, 1.25 * h]"
sph.Position.Controller = pstnX
animate on (
at time 0f (cn.Height = h; cn.Pos = [0, 0, 0])
at time 50f (cn.Height = -h; cn.Pos = [0, 0, h])
at time 100f (cn.Height = h; cn.Pos = [0, 0, 0])
)
playAnimation()
Порядок действий следующий:
Теперь изменение высоты конуса, например на вкладке Modify после выбора конуса, приведет к изменению радиуса сферы.
Для управления позицией сферы используется контроллер Position Expression, программируемый в следующем порядке:
Наличие такого контроллера обеспечит надлежащее перемещение сферы при изменении позиции конуса.
Для демонстрации результата в точках 0f, 50f и 100f временной шкалы создаются ключи анимации высоты и позиции конуса.
После запуска программы можно открыть диалог настройки контроллера, например Float Expression (рис. 2), выполнив следующие действия: выбрать примитив Sphere – меню Graph Editors – Track View – Curve Editor – ветвь Objects – Sphere01 – Sphere (Object) – Radius – двойной удар мышью.
Рис. 2. Диалог настройки контроллера Float Expression
Порядок запуска MAXScript-кода в 3ds Max описан ниже.
Вторая реализация рассматриваемой модели, основанная на контроллерах Float Script и Position Script, поддерживается следующим кодом:
delete $*
h = 50
cn = cone radius1:20 radius2:0 height:70 heightSegs:20 sides:24 wireColor:clr
sph = sphere radius:(0.25 * cn.Height) segs:32 wireColor:clr2
sph.Pos = cn.Pos + [0, 0, 1.25 * cn.Height]
fltS = float_script()
fltS.AddNode "cn2" cn
fltS.Script = "abs(0.25 * cn2.Height)"
sph.Radius.Controller = fltS
--
psS = position_script()
psS.AddNode "cn2" cn
psS.Script = "cn2.Pos + [0, 0, 1.25 * cn2.Height]"
sph.Position.Controller = psS
animate on (
at time 0f (cn.Height = h; cn.Pos = [0, 0, 0])
at time 50f (cn.Height = -h; cn.Pos = [0, 0, h])
at time 100f (cn.Height = h; cn.Pos = [0, 0, 0])
)
playAnimation()
В обоих контроллерах определяется переменная cn2 (метод AddNode), хранящая ссылку на объект конус. Это обеспечивает доступ к свойствам конуса, что и используется соответствующим образом в Script-выражениях контроллеров (свойство Script).
С другими возможностями программного управления моделями 3ds Max можно познакомиться, обратившись, например, к источникам [1] и [2].
Рассмотренную модель можно, разумеется, реализовать интерактивно, без применения MAXScript. Следующая модель такой альтернативы не имеет.
В рассматриваемом примере регулируемым параметром является радиус скруглений R между отдельными компонентами модели стула. Изменение радиуса влечет изменение высоты ножек стула, размеров его сиденья и спинки (рис. 3).
а | б |
Рис. 3. Параметризованный стул: а - R = 5; б - R = 15 |
Модель имеет следующие характеристики:
Параметризация модели выполняется средствами MAXScript. Код, обеспечивающий построение и управление моделью, а также создание и вывод управляющего диалога, имеет следующий вид:
global rlFlt
global clr = [6, 135, 113], clr2 = [135, 110, 8]
global wX = 60.0, wY = 45.0, wZ = 120.0
global arrC = #(), arrRc = #()
--
fn Prps = (
delete $*
units.DisplayType = #Generic
units.SystemType = #Inches
viewport.SetLayout #layout_4
viewport.ActiveViewport = 4
max vpt persp user
viewport.SetGridVisibility 4 false
max tool zoomExtents
backgroundColor = color 200 200 200
)
--
fn fndPs R wX wY wZ &arrPs = (
x = 0.5 * wX
y = 0.5 * wY
vY = [0, wY, 0]
wZ2 = 0.5 * wZ
p1 = [-x, -y, 0]; p2 = p1 + vY
p3 = [x, -y, 0]; p4 = p3 + vY
p5 = [0, -y, wZ2]; p6 = p5 + vY
p7 = [x, -y, 0.75 * wZ]; p8 = p7 + vY
p9 = [x + 2 * R, 0, wZ]
p10 = [-x, -y, wZ2 - R]
p11 = [x, -y, wZ2 + R]
arrPs = #(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
for k = 1 to 2 do arrC[k].Height = wZ2 - R
for k = 5 to 6 do arrC[k].Height = wX - 2 * R
for k = 7 to 8 do arrC[k].Height = wZ2 - 2 * R
arrC[9].Height = wY - 2 * R
)
--
fn fndPs2 R wX wY wZ &arrPs2 = (
x = 0.5 * wX
y = 0.5 * wY
vY = [0, wY, 0]
wZ2 = wZ / 2
pc1 = [-x + R, -y, wZ2 - R]; pc2 = pc1 + vY
pc3 = [x - R, -y, wZ2 + R]; pc4 = pc3 + vY
pc5 = [x + R, -y, wZ - R]; pc6 = pc5 + vY
pc7 = [x + R, -y + R, wZ]; pc8 = pc7 + [0, wY - 2 * R, 0]
arrPs2 = #(pc1, pc2, pc3, pc4, pc5, pc6, pc7, pc8)
)
--
fn mkChr R wX wY wZ = (
Prps()
arrC = for k = 1 to 11 collect cylinder wireColor:clr2 radius:2
arrPs = #()
arrPs2 = #()
fndPs R wX wY wZ &arrPs
fndPs2 R wX wY wZ &arrPs2
arrC[3].Height = arrC[4].Height = sqrt ((0.5 * wX)^2 + (0.5 * wZ)^2)
for k = 10 to 11 do arrC[k].Height = wY
for k = 5 to 9 do arrC[k].Pivot = [0, 0, 0.5 * arrC[k].Height]
for k = 1 to 11 do arrC[k].Pos = arrPs[k]
for k = 3 to 4 do rotate arrC[k] -(atan (1.0 * wX / wZ)) [0, 1, 0]
for k = 5 to 6 do rotate arrC[k] 90 [0, 1, 0]
for k = 9 to 11 do rotate arrC[k] -90 [1, 0, 0]
rc = arc radius:R from:0 to:90 pie:off reverse:off pos:[0, 0, 0] \
rotation:(quat -0.5 0.5 -0.5 -0.5) wireColor:clr2 \
render_renderable:on render_displayRenderMesh:on render_useViewportSettings:off \
render_mapcoords:on render_displayRenderSettings:off render_viewport_thickness:4.0 \
render_thickness:4.0
arrRc = for k = 1 to 7 collect copy rc
arrRc.WireColor = clr2
arrRc[6].Rotation = quat 0 0 -0.707107 0.707107
arrRc[7].Rotation = quat 0 0 0 1
insertItem rc arrRc 1
arrNg = #(0, 0, 180, 180, 0, 0, 0, 0)
for k = 1 to 8 do (
rotate arrRc[k] arrNg[k] [0, 1, 0]
arrRc[k].Pos = arrPs2[k]
)
max tool zoomExtents
global pln = plane length:wY width:(wX - R) lengthSegs:1 widthSegs:1 \
pos:[0, 0, wZ / 2] wireColor:clr
p = arrPs[7]; p[2] = 0
global pln2 = plane length:wY width:(wZ / 2 - 2 * R) lengthSegs:1 widthSegs:1 \
pos:p wireColor:clr
rotate pln2 -90 [0, 1, 0]
)
--
fn chgChr R wX wY wZ = (
for k = 1 to 2 do arrC[k].Height = 0.5 * wZ - R
for k = 5 to 6 do arrC[k].Scale = [1, 1, (wX - 2 * R) / arrC[5].Height]
for k = 7 to 8 do arrC[k].Scale = [1, 1, (0.5 * wZ - 2 * R) / arrC[7].Height]
arrC[9].Scale = [1, 1, (wY - 2 * R) / arrC[9].Height]
arrC[9].Pos = [0.5 * wX + 2 * R, 0, wZ]
arrC[10].Pos = [-0.5 * wX, -0.5 * wY, 0.5 * wZ - R]
arrC[11].Pos = [0.5 * wX, -0.5 * wY, 0.5 * wZ + R]
arrPs2 = #()
fndPs2 R wX wY wZ &arrPs2
for k = 1 to 8 do arrRc[k].Pos = arrPs2[k]
arrRc.Radius = R
pln.Width = wX - 2 * R
pln2.Width = 0.5 * wZ - 2 * R
)
--
rollout rChr "Chair" width:120 height:100 (
spinner spnR "R " pos:[15,5] width:100 height:20 range:[5,15,1] type:#integer
colorPicker theClr "Color " pos:[15,30] width:100 height:20 color:clr modal:true
button btnNmt "Animation" pos:[15,55] width:100 height:20 toolTip:"Play animation"
button btnCls "Close" pos:[15,80] width:100 height:20 toolTip:"Close Dialog"
on spnR changed val do chgChr val wX wY wZ
on theClr changed newClr do (
clr = newClr
pln.WireColor = newClr
pln2.WireColor = newClr
)
on btnNmt pressed do (
animationRange = interval 0f 100f
timeConfiguration.PlaybackLoop = false
with redraw off (
animate on (
at time 0f chgChr spnR.Range[1] wX wY wZ
at time 50f chgChr spnR.Range[2] wX wY wZ
at time 100f chgChr 10 wX wY wZ
)
)
max tool zoomExtents
playAnimation()
with redraw off (
sliderTime = 0f
chgChr 10 wX wY wZ
)
spnR.Value = 10
)
on btnCls pressed do closeRolloutFloater rlFlt
)
--
function opnRlFlt = (
if rlFlt != undefined then closeRolloutFloater rlFlt
rlFlt = newRolloutFloater "Chair" (rChr.Width + 35) (rChr.Height + 35) 50 50
addRollout rChr rlFlt rolledUp:false
rChr.spnR.Value = 10
)
--
mkChr 10 wX wY wZ
opnRlFlt()
Запуск программы выполняется в 3ds Max в следующем порядке:
Рис. 4. Управляющий диалог Chair
Построение стула обеспечивает функция mkChr, которая принимает в качестве параметров значения радиуса скруглений, длину, ширину и высоту стула – это соответственно параметры R, wX, wY и wZ. Стул состоит из 11 цилиндров (примитив Cylinder), 8 дуг (примитив Arc) и двух плоскостей (примитив Plane). Последние употребляются для отображения сиденья и спинки стула.
Переменные wX, wY и wZ объявлены как глобальные, что позволяет применять их в обработчике on btnNmt pressed управляющего диалога Chair (идентификатор rChr).
Цилиндры хранит массив arrC; номера цилиндров, то есть их индексы в массиве arrC, показаны на рис. 5, а.
а | б |
Рис. 5. Нумерация компонентов модели стула: а – нумерация цилиндров; б – нумерация дуг |
Дуги хранятся в массиве arrRc; их индексы в этом массиве показаны на рис. 5, б.
Плоскости для сиденья и спинки имеют в программе соответственно идентификаторы pln и pln2. Эти имена являются глобальными и поэтому доступны в обработчиках диалога Chair.
Перед построением модели функцией Prps удаляются все элементы сцены и устанавливаются требуемые параметры сеанса – это тип единиц измерения, число видовых портов (4), активный видовой порт (4) и его тип (Perspective). Кроме того, в этом видовом порте устраняется изображение сетки и выполняется команда max tool zoomExtents.
Координаты базовых точек цилиндров и дуг вычисляются соответственно функциями fndPs и fndPs2, принимающими вдобавок к параметрам R, wX, wY и wZ соответственно массивы arrPs и arrPs2. В эти массивы названные функции записывают необходимые для построения примитивов координаты их базовых точек. Нумерация базовых точек отвечает показанным на рис. 5 нумерациям цилиндров и дуг.
Параметризация модели обеспечивается функцией chgChr, которая вызывается обработчиком on spnR changed при изменении счетчика R (spinner spnR) диалога Chair. Также эта функция применяется при создании ключей анимации (кнопка Animation диалога Chair).
Функция принимает параметры R, wX, wY и wZ. Параметр R является изменяемым, и его значение берется равным показанию счетчика R диалога. Прочие параметры после запуска программы не изменяются.
Получив новое значение радиуса, функция chgChr:
В рассматриваемой модели масштабирование цилиндра будет вызывать надлежащий эффект, если базовая точка цилиндра (свойство Pivot) расположена в его центре, а не в центре основания, как это предусмотрено по умолчанию. Эта проблема решается при создании модели функцией mkChr за счет употребления следующего кода:
for k = 5 to 9 do arrC[k].Pivot = [0, 0, 0.5 * arrC[k].Height]
Таким образом, функция chgChr обеспечивает получение экземпляра модели, отвечающего принятому функцией значению радиуса скругления R. Все преобразования выполняются исходя из неизменности габаритов стула и положения его сиденья. Единственными неизменяемыми компонентами модели являются цилиндры 3 и 4, отображающие задние ножки стула.
Кроме обработчика on spnR changed, вызываемого при изменении показания счетчика R, диалог Chair (см. рис. 4) содержит обработчики on theClr changed, on btnNmt pressed и on btnCls pressed do, выполняемые соответственно при нажатии на полосу с цветом (элемент colorPicker), на кнопку Animation (элемент button btnNmt) и Close (элемент button btnCls).
Ключи анимации рассчитываются в точках 0f, 50f и 100f временной шкалы после нажатия на кнопку Animation диалога Chair: выполняется обработчик on btnCls pressed do, в котором трижды вызывается функция chgChr. После воспроизведения анимации (метод playAnimation) временная шкала устанавливается в точку 0f и вновь вызывается функция chgChr со значением параметра R, равным 10. Это обеспечивает появление начального образа стула. Это же значение радиуса скругления устанавливается и в счетчике R диалога (spnR.Value = 10).
Параметрические модели требуют дополнительных издержек при их создании. Поэтому вопрос о целесообразности параметризации модели решается с учетом предполагаемых выгод.
Издержки связаны с необходимостью:
Основной эффект параметризации – это возможность получения модифицированного экземпляра модели, за счет изменения небольшого числа параметров. Так, параметрическая модель кузова автомобиля может обеспечить пересчет всех компонентов кузова при изменении геометрии двери или иной части кузова.
Также параметрические модели несложно снабдить надлежащим, более удобным пользовательским интерфейсом, обеспечивающим управление отдельными параметрами модели и, значит, моделью в целом.
Кроме того, параметризация предоставляет дополнительные возможности для анимации модели, что, в частности, существенно при создании мультимедийных приложений.