Yakushev Alexsandr
Здравствуйте уважаемые читатели! Вашему вниманию предлагается урок, в котором будет рассмотрено множество интересных возможностей maxscript’а. Они, на мой взгляд, будут полезны не только 3д-художникам, желающим разнообразить свои познания, но и инженерам — программистам.
Основу урока составляет описание практического применения maxscript’а для расчета аэродинамических характеристик летательных аппаратов на гиперзвуковом участке полета. Реализованная программа была многократно испытана и ее результаты были подвергнуты тщательному анализу. Как выяснилось, относительная погрешность, при сравнении с экспериментальными данными, составляет всего 5-6%!
Заранее приношу свои извинения за возможные ошибки и упущения. Сам я не являюсь профессиональным программистом, но все же постараюсь описать элементы программы в максимально доступной форме.
Итак, начнем:
Как и во всех современных языках программирования в макроязыке применяется цветовая идентификация:
Зеленым — обозначаются, сопровождающие программу, пояснения
Синим — обозначаются различные функции Maxscript’a, коих бесчисленное множество!:)
Коричневым — обозначаются различные тексты, которые будут отображаться в интерфейсе программы
Черным — все остальное.
Так как наша программа будет заниматься не только расчетами, но и построением графиков, то нам пригодиться стандартная графическая функция, которую я выдернул из прилагающихся туториалов по maxscript’у:
— создание графической функции для построения прямых линий
function BLine bmp x1 y1 x2 y2 c =
(
local ary = #(c)
— assign originals
local Xb = x1 as integer
local Yb = y1 as integer
— build the line deltas
local dX = x2-x1 as float
local dY = y2-y1 as float
— straight horiz
if dy == 0.0f do
(
local xsign = 1
if xb > x2 then xsign = 1 as integer
if x2 < xb then xsign = -1 as integer
setPixels bmp [xb,yb] ary
while xb != x2 do
(
xb += xsign
setPixels bmp [xb,yb] ary
)
return true
)
— straight vertical
if dx == 0.0f do
(
local ysign = 1
if yb > y2 then ysign = 1 as integer
if y2 < yb then ysign = -1 as integer
setPixels bmp [xb,yb] ary
while yb != y2 do
(
yb += ysign
setPixels bmp [xb,yb] ary
)
return true
)
— no straights, go for bresenham line slide
— set up the movements
local xsign = 1
if xb > x2 then xsign = 1 as integer
if x2 < xb then xsign = -1 as integer
local ysign = 1
if yb > y2 then ysign = 1 as integer
if y2 < yb then ysign = -1 as integer
dx = abs(dx)
dy = abs(dy)
setPixels bmp [xb,yb] ary
— line more vertical than horizontal
if dx < dy then
(
p = 2 * dx — dy
const1 = 2 * dx
const2 = 2 * (dx — dy)
while yb != y2 do
(
yb += ysign
if p < 0 then ( p = p + const1 )
else
(
p += const2
xb += xsign
)
setPixels bmp [xb,yb] ary
)
)
— line more horizontal than vertical
else
(
p = 2 * dy — dx
const2 = 2 * (dy — dx)
const1 = 2 * dy
while xb != x2 do
(
xb = xb + xsign
if p < 0 then ( p = p + const1 )
else
(
p = p + const2
yb = yb + ysign
)
setPixels bmp [xb,yb] ary
)
)
)
При желании можно обойтись и без нее, но все-таки с этой функцией интерфейс программы легче воспринимается человеческим глазом.
Теперь приступим к созданию плавающего окна и его интерфейса:
Присвоим переменным параметры ширины и высоты плавающего окна и встроенной в него пиксельной карты, которая затем пригодится нам для построения графиков функций:
— объявление переменных
M_width = 860 — ширина плавающего окна
M_hight = 490 — высота плавающего окна
R_width = 800 — ширина пиксельной карты
R_hight = 400 — высота пиксельной карты
Создадим плавающее окно, которое я назвал :
— создание нового плавающего окна
Main = newRolloutFloater «Построитель аэродинамических функций» M_width M_hight
Далее:
— объявление внутри плавающего окна локального свитка
rollout Aero «Окно просмотра»
Любой локальный свиток должен иметь начало и конец, которые обозначаются соответственно , :
(— начало свитка
Теперь, когда мы объявили начало локального свитка, можно начинать вставлять различные элементы интерфейса:
— создание пиксельной карты
bitmap gr width:R_width height:R_hight color: white
— создание цифровых меток для маркировки оси ординат (левый край)
label lbl21 «-2.0» pos:[0,395] width:20 height:18
label lbl22 «-1.5» pos:[0,350] width:20 height:18
label lbl23 «-1.0» pos:[0,300] width:20 height:18
label lbl24 «-0.5» pos:[0,250] width:20 height:18
label lbl25 «0.0» pos:[0,200] width:20 height:18
label lbl26 «0.5» pos:[0,150] width:20 height:18
label lbl27 «1.0» pos:[0,100] width:20 height:18
label lbl28 «1.5» pos:[0,50] width:20 height:18
label lbl29 «2.0» pos:[0,0] width:20 height:18
— создание цифровых меток для маркировки оси ординат (правый край)
label lbl51 «-2.0» pos:[825,395] width:20 height:18
label lbl52 «-1.5» pos:[825,350] width:20 height:18
label lbl53 «-1.0» pos:[825,300] width:20 height:18
label lbl54 «-0.5» pos:[825,250] width:20 height:18
label lbl55 «0.0» pos:[825,200] width:20 height:18
label lbl56 «0.5» pos:[825,150] width:20 height:18
label lbl57 «1.0» pos:[825,100] width:20 height:18
label lbl58 «1.5» pos:[825,50] width:20 height:18
label lbl59 «2.0» pos:[825,0] width:20 height:18
— создание цифровых меток для маркировки оси абсцисс (горизонтальная ось)
label lbl30 «0» pos:[22,407] width:20 height:18
label lbl31 «10» pos:[60,407] width:20 height:18
label lbl32 «20» pos:[104,407] width:20 height:18
label lbl33 «30» pos:[148,407] width:20 height:18
label lbl34 «40» pos:[192,407] width:20 height:18
label lbl35 «50» pos:[240,407] width:20 height:18
label lbl36 «60» pos:[284,407] width:20 height:18
label lbl37 «70» pos:[329,407] width:20 height:18
label lbl38 «80» pos:[373,407] width:20 height:18
label lbl39 «90» pos:[418,407] width:20 height:18
label lbl40 «100» pos:[458,407] width:20 height:18
label lbl41 «110» pos:[500,407] width:20 height:18
label lbl42 «120» pos:[545,407] width:20 height:18
label lbl43 «130» pos:[590,407] width:20 height:18
label lbl44 «140» pos:[635,407] width:20 height:18
label lbl45 «150» pos:[679,407] width:20 height:18
label lbl46 «160» pos:[724,407] width:20 height:18
label lbl47 «170» pos:[768,407] width:20 height:18
label lbl48 «180» pos:[812,407] width:20 height:18
Цифровые метки, в данном случае, необходимы только для повышения удобства чтения полученных данных. Надеюсь, что пока понятно, что обозначают , :, если, все таки возникли вопросы, то просто поэкпериментируйте с параметрами и, думаю, все встанет на свои места.
Далее:
— создание внутри локального свитка 4-х групп:
— первая
groupBox grp1 «Построение аэродинамических коэффициентов в связанной и поточной системе координат» pos:[5,420] width:835 height:47
—(
radiobuttons copy_type
labels:#(«Cx «, «Cy «, «Cz «, «Cxa «, «Cya «, «K = (Cya/Cxa) «) pos: [260,440]
default:1
pickbutton bn_Master «Выделить объект» pos:[10,435] width:130 height:27
button knopka «Построить» pos:[705,435] width:130 height:27
—)
— вторая
group «Переменные величины»
(
spinner x1 «Аэродинамический коэффициент K :» range:[0,3,2] type:#float pos:[170,490] width:100
spinner x2 «Шаг:» range:[0.1,90,5] type:#float pos:[640,490] width:50
—spinner x3 «Number of points» range:[36,800,36] type:#integer pos:[580,490] width:80
label lbl1 «Площадь миделя:» pos:[340,490] width:100 height:18
editText edt1 text:»3.14159″ pos:[430,490] width:100 height:18
)
— третья
group «Быстрое построение простых объектов (площадь миделя: 3.14159; позиция центра: [0,0,0])»
(
button Constr_sphere «Создать сферу» pos:[10,535] width:100 height:27
spinner CS_rad «радиус:» range:[0,100,1] type:#float pos:[150,545] width:55
spinner CS_s «число сегментов:» range:[0,100,18] type:#integer pos:[285,545] width:75
button Constr_cone «Создать конус (высота h=1)» pos:[685,535] width:150 height:27
spinner C_rad «радиус:» range:[0,100,1] type:#float pos:[480,545] width:55
spinner C_s «число сторон:» range:[0,100,24] type:#integer pos:[600,545] width:65
)
— четвертая
group «Вывод данных»
(
button Otp «Записать данные в файл» pos:[10,590] width:140 height:27
label lbl8 «по адресу:» pos:[155,597] width:70 height:18
editText edt2 text:»D:\\out1.txt» pos:[210,597] width:70 height:18
button disp «Отобразить и сохранить» pos:[685,590] width:150 height:27
button Vert «Записать в файл координаты вершин (по фэйсам)» pos:[290,590] width:260 height:27
label lbl9 «по адресу:» pos:[555,597] width:70 height:18
editText edt3 text:»D:\\out2.txt» pos:[610,597] width:70 height:18
)
Группы созданы двумя разными способами, но дают почти один и тот же результат. Какая же между ними разница? создает группу размеры и положение которой задаются параметрами , а создает группу, которая автоматически подстраивается под размеры плавающего окна и размеры встроенных в нее элементов интерфейса.
С помощью переключателей в первой группе будет производиться выбор характеристик, которые необходимо построить на пиксельной карте. Значение означает, что по умолчанию будет выбрана первая характеристика . Хотелось бы еще пояснить, значение команд . A — задает нижний диапазон, B — верхний, C — значение, отображаемое при запуске программы, — дословно переводится, как , а вообще означает дробный тип отображаемых чисел, — соответственно, целый тип. Значение всех остальных параметров легко понять, если их поварьировать и посмотреть на результат, нажав , что означает выполнить написанный скрипт.
Но здесь может возникнуть небольшая трудность с отображением заданного интерфейса, чтобы ее избежать, просто добавьте ниже следующие строки:
)— конец свитка
Addrollout Aero main — добавление свитка к плавающему окну
Если все сделано правильно, то Вы должны получить следующий результат:

Изучив все параметры удалите строки:
)— конец свитка
Addrollout Aero main — добавление свитка к плавающему окну
Потому что они понадобятся нам позже.
Итак, создав необходимый интерфейс, можно приступить к описанию событий (т.е. что у нас произойдет при нажатии на ту или иную кнопку):
— при нажатии на кнопку производится запись координат вершин каждого треугольника выделенного объекта в текстовый файл
on Vert pressed do
(
local o=convertToMesh $ — локальной переменной присваивается, преобразованный в Mesh(сетку), выделенный объект
local s=edt3.text as string — локальной переменной присваивается путь, по которому будет сохранен текстовый файл
local d=createFile s — по указанному адресу создается файл (пока пустой)
for f in o.faces do — объявление цикла по фэйсам (треугольникам, из которых состоит объект)
(
Vx=(meshop.getVertsUsingFace o f.index) as array — получение массива фэйсов
for i=1 to 3 do — цикл по трем вершинам рассматриваемого фэйса
(
local b=getVert o Vx[i] — переменной «b» присваивается значение i-ой вершины фэйса
format «% % %» b.x b.y b.z to:d — запись координат [x,y,z] i-ой вершины
format «\n» to:d — запись перехода на следующую строку
)
)
close d — закрывает созданный файл
Запись координат вершин(по фэйсам) необходима для того, чтобы появилась возможность обрабатывать данные не только в 3Dstudio Max’е, но и в других программах, таких как Маткад или Матлаб.
— при нажатии на кнопку будет создана сфера с указанными в обработчике события параметрами
on Constr_sphere pressed do
(
local s = sphere radius: CS_rad.value segs: CS_s.value smooth:off
max tool zoomextents all — показать сферу в области просмотра
)
Значения и считываются с показаний соответствующих счетчиков(spinner) и .
— при нажатии на кнопку будет создан конус с указанными в обработчике события параметрами
on Constr_cone pressed do
(
local c = cone radius1: C_rad.value sides: C_s.value height: 1 heightsegs: 1 capsegs: 1 smooth:off
rotate c 90 [0,1,0] — повернуть конус на 90 градусов относительно оси Y
move c [-0.3, 0, 0] — передвинуть конус по оси X на -0.3 единицы
max tool zoomextents all — показать конус в области просмотра
)
— при нажатии на кнопку и после выделения объекта в сцене произойдет следующее событие
on bn_Master picked obj do
(
obj.name = «Выделен» — кнопку переименовать в
bn_Master.text=obj.name — присвоить объекту имя
select $Выделен — выделить объект в сцене с именем
)
— при открытии плавающего окна произойдет следующее событие
on Aero open do
(
— оформление дизайна пиксельной карты
local bm = bitmap R_width R_hight color: white — создание пиксельной карты
local col = color 220 220 220 — создание локальной переменной со значением цвета в виде массива
for x=0 to R_width by 44.444444 do( — создание вертикальных линий
BLine bm x 0 x R_hight col )
for y=0 to R_hight by 50 do( — создание горизонтальных линий
BLine bm 0 y R_width y col )
BLine bm 0 ((R_hight)/2) R_width ((R_hight)/2) red — создание оси абсцисс красного цвета
gr.bitmap = bm — записать полученное изображение в пиксельную карту
)
Проще говоря, данное событие описывает то, что Вы увидите на месте пиксельной карты при первом запуске скрипта (Ctrl+e). Как же все таки создаются вертикальные и горизонтальные линии?
Эти строки
for x=0 to R_width by 44.444444 do( — создание вертикальных линий
BLine bm x 0 x R_hight col )
означают примерно следующее:
Для x от 0 до «R_width» (значение было задано в начале) с интервалом 44.4444 выполнить функцию , при этом первые два параметра — это начало линии, а последних два параметра «x R_hight» — это конец линии, «col» — переменная со значением цвета.
Далее:
— при нажатии на кнопку в группе произойдет следующее событие
on disp pressed do
(
display gr.bitmap — отображение полученной пиксельной карты
)
Осталось описать события к двум кнопкам — это кнопки и . Так как описания событий к этим кнопкам довольно похожи и, при этом, довольно объемны, то ограничусь описанием события только для кнопки , а описание для кнопки можно, при желании, посмотреть в оригинале программы.
Событие для кнопки интересно тем, что в нем производится расчет площади, как отдельных фэйсов, так и всего объекта, так же вычисляются нормали и вектора из центра глобальной системы координат к центрам фэйсов. Все эти операции можно выполнять с помощью стандартных функций макроязыка, а можно вычислять самим с помощью методов аналитической геометрии. В событии описываются, как тот, так и другой способ. Способы, которые дают быстрейший результат — активированы, а те, что замедляют расчеты приведены в виде подсказок:
— при нажатии на кнопку произойдет следующее событие
on knopka pressed do
( — начало события
clearlistener() — отчистка «Слушателя» (вызывается клавишей F11 при активном главном окне 3dstudio MAX!)
— оформление дизайна пиксельной карты
local bm = bitmap R_width R_hight color: white — создание пиксельной карты
local col = color 220 220 220 — создание локальной переменной со значением цвета линий в виде массива
for x=0 to R_width by 44.444444 do( — создание вертикальных линий
BLine bm x 0 x R_hight col )
for y=0 to R_hight by 50 do( — создание горизонтальных линий
BLine bm 0 y R_width y col )
BLine bm 0 ((R_hight)/2) R_width ((R_hight)/2) red — создание оси абсцисс красного цвета
ary = #(blue) — создание переменной со значением цвета построительных точек (цвет графика функции)
— объявление переменных необходимых для расчета
o = converttoMesh $ — преобразование выделенного объекта в доступную для редактирования сетку
Ka = x1.value — переменной присваивается значение счетчика
Smid = edt1.text as float — переменной присваивается значение в текстовом поле
— начало циклического изменения значений угла от 0 до 180 градусов с шагом, указанным счетчиком
for i = 0 to 180 by x2.value do
( — начало цикла по
P1 = [0.0, 0.0, 0.0] — объявление переменной для обнуления суммы в цикле по фэйсам при первой итерации
potok = [cos i, 0, sin i] — объявление переменной потока (обеспечивается вращение единичного вектора против часовой стрелки на 180 градусов)
— начало цикла обращения к фэйсам объекта
for f in o.faces do
(— начало цикла по
— получение массива фэйсов
index = (meshop.getVertsUsingFace o f.index) as array
— получение координат каждой вершины рассматриваемого фэйса в виде векторов
a = getVert o index[1] — координаты первой вершины
b = getVert o index[2] — координаты второй вершины
c = getVert o index[3] — координаты третьей вершины
— расчет площади рассматриваемого фэйса (где функция — вычисление длины вектора, а функция — векторное произведение двух векторов)
— вычисление площади фэйса с помощью стандартной функции макроязыка
—local Square = meshop.getFaceArea o f.index
— вычисление площади фэйса методом аналитической геометрии
vec1 = b — a
vec2 = c — a
Square = (length(cross vec1 vec2))/2
— получение вектора нормали к данному фэйсу
local N = getFaceNormal o f.index
—an=(b.y-a.y)*(c.z-a.z)-(b.z-a.z)*(c.y-a.y)
—bn=(b.z-a.z)*(c.x-a.x)-(b.x-a.x)*(c.z-a.z)
—cn=(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x)
—N = [an, bn, cn]
Nn = normalize N — нормализация(преобразование к величине от 0 до 1) нормали фэйса
— вычисление вектора, направленного из центра глобальной системы координат к центру данного фэйса
local Cf = meshop.getFaceCenter o f.index
—amc = (a.x + b.x + c.x)/3
—bmc = (a.y + b.y + c.y)/3
—cmc = (a.z + b.z + c.z)/3
—Cf = [amc, bmc, cmc]
Cf_n = normalize Cf — его нормализация
— направляем все нормали в одну сторону — наружу объекта (где функция — это функция скалярного перемножения двух векторов)
N_edit = if (dot Nn Cf_n)>0 then Nn else -Nn
local Uv = (dot N_edit potok)^2
— расчет значения вектора потока в данном фэйсе
P = if (dot N_edit potok)
— суммирование значений векторов потока по всем фэйсам
P1 = P1+P
)— конец цикла по
— привидение к безразмерному виду суммарного значения вектора потока делением на площадь миделя (напомню, что Pd.x=Cx, Pd.y=Cy, Pd.z=Cz), знак минуса здесь необходим для зеркального отображения графика относительно оси X
Pd = P1/Smid
— если убрать перед словами знаки тире, то в «Слушатель» будут выведены значения и , это было удобно при отработке программы
—print «Угол»
—print i
—print «Значение потока»
— вычисление значений аэродинамических коэффициентов и в поточной системе координат
Cxa = (Pd.x)*cos(i)+(Pd.z)*sin(i)
Cya = (Pd.z)*cos(i)-(Pd.x)*sin(i)
Kachestvo = (-Cya)/(Cxa) — вычисление аэродинамического качества
— в зависимости от выбранного положения переменной присваивается нужное значение
P_end = (if copy_type.state == 1 then Pd.x else
if copy_type.state == 2 then Pd.y else
if copy_type.state == 3 then Pd.z else
if copy_type.state == 4 then Cxa else
if copy_type.state == 5 then Cya else Kachestvo)
—print P_end
setpixels bm [4.4444*i, 100*P_end + 200] ary — запись значений в переменную
gr.bitmap = bm — запись значений переменной в пиксельную карту
) — конец цикла по
) — конец события
Хотелось бы обратить Ваше внимание на то, каким образом производится выбор с помощью и на значения параметров в сроке , коэффициенты 4.4444, 100, 200 необходимы лишь для масштабирования выводимого графика под пиксельную карту, а параметр — задан выше и обозначает цвет графика.
Ну вот, почти и все, осталось только закрыть свиток , в котором мы описывали интерфейс и производили расчеты и создать, как во всех нормальных программах :), свитки и :
)— конец свитка
Addrollout Aero main — добавление свитка к плавающему окну
— объявление свитка
rollout Aero2 «Помощь»
(
label lbl1a «Данная программа производит расчет и построение, по уточненной теории Ньютона, сверхзвуковых стационарных аэродинамических характеристик элементов
летательных аппаратов. К таковым относятся: аэродинамические коэффициенты в связанной системе координат Cx, Cy, Cz; аэродинамические коэффициенты в
скоростной (поточной) системе координат Cxa, Cya; аэродинамическое качество K = Cya/Cxa.
Плюсы и недостатки программы:
Плюсами данной программы, несомненно, являются скорость получения результата с возможностью его последующего сохранения или в виде
изображения, в любом из распространенных сейчас форматов, или записи результатов расчета в текстовый файл в виде матрицы столбца.
К недостаткам относятся — а) направление осей связанной системы координат в 3DstudioMAX’е немного отличается от общепринятой в аэро-
динамике (ось Z в 3DMAX’е соответствует оси Y в аэродинамике), б)чтобы скопировать в буфер графические построения с цифровыми метками по осям —
— необходимо воспользоваться сочетанием клавиш , при использовании же кнопки будет сохранено
только изображение построенной функции без цифровых меток!
Порядок проведения расчетов и пояснения к интерфейсу программы:
1. Необходимо создать объект и правильно расположить его в области просмотра. Для создания объектов сложной формы необходимо воспо-
льзоваться стандартными средствами построения объектов в 3DstudioMAX’е, а для демонстрационных расчетов можно воспользоваться кнопками
и в группе . Эти кнопки
создают, соответственно, сферу и конус в центре ГСК. Все параметры, в данном случае, заданные по умолчанию(радиус, площадь миделя)-
— заданы правильно.
2. Необходимо указать(выделить) объект кнопкой . После указания объекта кнопка изменит свое название на .
3. В группе задаются такие параметры, как аэродинамический коэффициент, площадь миделя и шаг, с которым будут про-
изводиться расчеты.
4. В группе , для построения графика, необходимо
выбрать нужную функцию и нажать на кнопку .
5. После проведенных исследований может понадобится сохранение результатов расчета или в виде текстового файла, или в виде изображения.
Для этого в группе есть, соответственно, две кнопки и , а если же понадобится
сохранить координаты вершин (по фэйсам) для последующих расчетах в других программах, то необходимо воспользоваться кнопкой
» pos:[10,10] width:860 height:390
)
Addrollout Aero2 main rolledUp:true
— объявление свитка
rollout Aero3 «О программе»
(
label lbl1b «Автор программы: Александр Якушев
Принимали участие в создании программы: Дмитрий Агафонов
Дмитрий (DG) http://dgsd.nm.ru/
Фёдор Мосалов
Руководитель: Миненко Виктор Елисеевич» pos:[10,10] width:790 height:70
)
Addrollout Aero3 main rolledUp:true
Наиболее важным, в плане познания макроязыка, является строка в которой команда означает, что при запуске скрипта свитки будут в свернутом состоянии.
Написать окончательно программу по этому уроку невозможно, так как здесь нет описания события к кнопке , но думаю, что это не проблема, потому что основной целью данного урока была попытка описать те или иные возможности макроязыка для последующего применения в других областях.
Оригинал же скрипта можно скачать здесь.
Хочу выразить огромную благодарность за неоценимую помощь и поддержку Дмитрию (DJ), моим, уже бывшим :), одногрупникам Дмитрию Агафонову и Фёдору Мосалову, а также руководителю дипломного проекта в МГТУ им. Н.Э. Баумана Миненко Виктору Елисеевичу.
Если у Вас возникли вопросы, то пишите, постараюсь помочь.
Удачи!
