Зачем они

vladirr

Привет всем! Вот и решился я снова потревожить ваш покой. Пойдем мы, как говорится — ab ovo, то бишь от постановки яйц… тьфу, задачи. 🙂

Те, кто занимался низкополигональным моделированием, особенно если занимались не только в качестве хобби, знают две очень полезные утилиты — Summary Info (вызывается последовательным нажатием ALT+F+U, дает полную информацию о составе и объектах сцены) и PoligonCounter (по умолчанию — клавиша Q/(в пятой версии — клавиша 7), показывает количество вершин и фейсов для выделенного объекта). В принципе — этого вполне достаточно, чтобы контролировать процесс моделирования… но лично мне неудобно с ними работать — а потому приступим…

Сегодняшний урок достаточно прост, поскольку несет нагрузку не учебную, а абсолютно практическую — мы попытаемся сделать так, чтобы нужная нам при работе информация всегда находилась перед глазами, а не где-то там, куда добираться 15 вест, зимой и в гору 🙂 На кого расчитан данный урок? Я подразумеваю, что вы в состоянии самостоятельно открыть окно MaxScriptListener, не задавая вопросов что это такое (ну в крайнем случае — подсмотрев в справке :)), создать объект, выделить его и снять выделение, обнулить/перезапустить 3dsmax (да простят меня разработчики, но в дальнейшем я буду называть его просто «максом» )… ну и еще умение печатать без ошибок :)… вроде все, поскольку теорией мучить сегодня я не буду, кто хочет — сам найдет, а кто не хочет… думаю все понятно.

Для начала определимся, что же мы хотим видеть?
Нас интересуют в общем-то только два параметра — количество фейсов в сцене вообще, и количесво их же, но у редактируемого в данный момент объекта. Но поскольку при работе с конкретным объектом некоторые особо продвинутые товарищи предпочитают видеть еще и количество вершин в нем, то, что же делать — дадим им такую возможность :). Итак, мы должны получить в конечном итоге три следующие строки:

SelectObj Vertex= ###
SelectObj Face =###
Scene Face = ###

Задача поставлена, соответственно — половина дела сделана.

Переходим к решению.

Во первых, у нас сразу же есть проблема — тот же PolygonCounter имеет небольшой недочет — при выделении сплайнового объекта он начинает показывать количество вершин и полигонов для этого объекта, исходя из преобразования данного сплайна в mesh — объект. Не знаю, какой логикой пользовались создатели данного скрипта, но для нужд низкополигонального моделирования она не подходит — даже если в процессе создания модели и используются сплайны, то только как вспомогательные объекты, ну а при переводе данного объекта в вид редактируемого mesh-а любой нормальный моделлер сократит количество полигонов получаемого объекта до минимума. Эрго — данная информация нам просто не нужна, а в некоторых случаях даже вредна, поскольку дает искаженное представление о состоянии сцены. Ввиду вышесказанного все сплайновые объекты мы с вами должны будем просто проигнорировать в нашем алгоритме, для чего ввести соответствующее условие выбора. А поможет нам в этом метод Category, возвращающий категорию выбранного объекта.

Обнулите макс, создайте в нем бокс как представителя класса геометрических объектов, и окружность, как представителя класса спрайновых объектов. Нажмите клавишу Q/7 (либо другим способом запустите PolygonCounter) и выделите сначала бокс, а потом окружность. Наблюдая за значениями количества фейсов убедитесь в наличии ошибки алгоритма. Для полной уверенности можно так же взглянуть на Summary Info — этот алгоритм не учитывает полигоны для сплайнов — и правильно делает 🙂
Убедились? Будем лечить… Открываем окно MaxScriptListener и в нижнем поле вводим:

$Circle01.category (Circle01 — имя нашей окружности, если у Вас не совпадает, подставьте свое)

Жмем Enter (на цифровой панели!) и получаем в том же окне ответ:

#Splines

Все верно, это у нас сплайн 🙂
Однако сейчас мы указали объект напрямую, через его имя, но делать это постоянно мы не можем, потому воспользуемся тем фактом, что данный объект выделен, следовательно — может быть вызван с помощью метода $selection
выделен у нас при работе один объект, следовательно он будет первым и единственным элементом массива, представляющего выделение. Для проверки введем:

$selection.count — мы запрашивает количество элементов массива

после ввода получаем ответ

1 — мы получили число элементов выделения, как ни удивительно, но оно и правда равно еденице 🙂 Вводим:

$selection[1] — доступ к первому элементу массива, жмем enter, получаем ответ:
——————
$Circle:Circle01 @ [-11.532874,-35.139202,0.000000] — в квадратных скобках — положение опорной точки в пространстве, у Вас, естественно — другие значения…

Убедились, что первый элемент выделения при условии, что выделен один объект — сам этот объект, теперь можем смело требовать доступа к его свойствам, в частности — узнавать его категорию:

$selection[1].category
—————-
#Splines

выделим бокс, и повторим ввод, для чего мышкой установим курсор в окне MaxScriptListener на ту же строку и снова нажмем enter (для выполнения операции, вводимой с клавиатуры нажимать enter нужно именно на цифровой панели).

$selection[1].category
———————————-
#Standard_Primitives

как видно, данный способ позволяет определить категорию выделенного объекта.

теперь определимся с фейсами-вершинами… для получения такой информации воспользуемся методом getPolygoncount, возвращаюшим массив из двух элементов — количество фейсов, и количество вершин:

getPolygoncount $selection[1]
———————————————-
#(12, 8)

Ну это уже практически все 🙂

наша задача теперь выглядит следующим образом — убедиться, что выбранный объект не является сплайном, и если это условие истинно, то показать количество фейсов и вершин. Отмечу только, что в максе сплайновый объект может быть представлен в виде собственно сплайна — $Splines и в виде шейпа — редактируемой плоской формы… поскольку для нас в данном случае это едино, то условие придется делать двойное:

if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
)
else
(
getPolygoncount $selection[1]
)

Мы указали, что если выделенный объект является плоской формой, то ничего делать не надо, в противном случае — вывести значения количества фейсов и вершин. В окно проекции выделите бокс, введите указанный выше текст в окне MaxScriptListener, выделите его целиком мышью и нажмите ввод, если все сделано правильно — должен появиться ответ:

#(12, 8)

теперь выделите окружность, снова выделите весь текст и нажмите ввод — ответ должен быть:

undefined — то есть не определено… что и требовалось.

Осталось только получить доступ к значениям количества вершин и фейсов по отдельности. Поскольку это тоже элементы массива, то введем переменную, через которую будем к ним обращаться… модифицированный алгоритм выглядит следующим образом:

if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
)
else
(
(getPolygoncount $)[1]
)
if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
)
else
(
(getPolygoncount $)[2]
)

повторите операции с проверкой работы алгоритма, поочередно выделяя бокс и окружность, и выполняя написанный выше код

С общими параметрами разобрались, теперь полезем в «святая святых» 🙂 — займемся интерфейсом. Для начала в окне MaxScriptListener выполните File->New Script (нажмите CTRL+N) и создайте пустой файл сценария.
—————
macroScript ScenePolyCounter
category:»MAX Script Tools»
internalcategory:»MAX Script Tools»
buttontext:»ScenePolyCounter»
toolTip:»ScenePolyCounter»
(
)
—————-
Сохраните файл как

\\root_max\UI\Macroscripts\Macro_ScenePolyCounter.ms

где root_max — корневая директория макса. Таким образом мы разместили наш скрипт в разделе пользовательского интерфейса. С помошью проводника найдите этот файл и измените ему разрешение на *.mcr — чтобы макс воспринимал его как макроскрипт. У меня получилось следующим образом:

D:\WORK\3D_PROG\3DSMAX5\UI\Macroscripts\Macro_ScenePolyCounter.mcr

Теперь надо закрыть макс и снова стартовать его… Идем в меню Customize->Customize User Interface, вкладка Toolbars,
группа — main UI, категория, как и указали — MAX Script Tools. Если все сделано правильно, то в окне Action должен находиться наш макрос. Берем его, и ташим на панель закладок, например — на вкладку объекты. В принципе это не важно, поскольку в последствии мы все равно удалим эту кнопку, но поскольку работать мы будем с объектами, то так несколько удобнее. У нас появилась новая кнопка с надписью ScenePolyCounter. Закрываем окно редактирования интерфейса и пытаемся нажать на нашу кнопку. Ничего не происходит, и это правильно, ведь мы не указали — что именно должно происходить… Сейчас мы запишем туда код обработчика, и все начнет работать 🙂

Правый клик мыши по нашей кнопке, выбираем пункт Edit Macro Script. Вводим обработчик нажатия, конечный текст выглядит следующим образом:

————————
macroScript ScenePolyCounter
category:»MAX Script Tools»
internalcategory:»MAX Script Tools»
buttontext:»ScenePolyCounter»
toolTip:»ScenePolyCounter»
(
local ScenePolyCounterOn = false

on ischecked return ScenePolyCounterOn

on execute do
(
ScenePolyCounterOn = not ScenePolyCounterOn
completeredraw ()
updateToolbarButtons()
)

)
————————

мы добавили:
-обработчик нажатия кнопки ischecked, возвращающий значение переменной ScenePolyCounterOn , в которой хранится состояние нашего макроса — активирована или не активирована. По умолчанию, естественно, неактивна (false).
— обработчик события execute — исполнение, то есть собственно последовательность выпоняемых действий. Первым делом мы в нем меняем значение активности макроса на противоположное, поскольку с каждым вызавом макроса он последовательно активируется и дезактивируется. Для описания этого мы воспользовались оператором NOT, меняющим значение булевской переменной (тип ДА НЕТ) на противоположное. Оставшиеся две строки — перерисовка рабочего экрана и кнопки вызова скрипта.
Нажимаем комбинацию клавиш CTRL+E — так называемое событие Evaluate — аналог компиляции програмного кода. не закрывая окна с телом скрипта пробуем нажать на нашу кнопку. Как мы видим, она изменяет свое состояние, но больше ничего не происходит. Ну что ж, «продолжаем разговор» (С)… Введем следующий код:
——————————
macroScript ScenePolyCounter
category:»MAX Script Tools»
internalcategory:»MAX Script Tools»
buttontext:»ScenePolyCounter»
toolTip:»ScenePolyCounter»
(
local ScenePolyCounterOn = false
local TextFaceObj, TextVertexObj, TextFaceAll
local lastViewport
fn printtext =
(
TextFaceObj = «SelObjFace : «
TextVertexObj = «SelObjVertex :»
TextFaceAll = «SceneFace : «
gw.wtext [5, 40, 0] TextFaceObj color:(color 255 234 0)
gw.wtext [5, 60, 0] TextVertexObj color:(color 255 234 0)
gw.wtext [5, 80, 0] TextFaceAll color:(color 255 234 0)
gw.enlargeUpdateRect #whole
gw.updateScreen()
if viewport.activeViewport != lastViewport then
(
completeredraw()
lastViewport = viewport.activeViewport
)
)

on ischecked return ScenePolyCounterOn

on execute do
(
if ScenePolyCounterOn then
unregisterRedrawViewsCallback printtext
else
(
registerRedrawViewsCallback printtext
)
ScenePolyCounterOn = not ScenePolyCounterOn
completeredraw ()
updateToolbarButtons()
)
)
—————————
нажимаем комбинацию CTRL+E (компилируем код) и нажимаем на кнопку нашего макроса. В активном видовом окне появились три надписи, соответствующие установленным нами. Убеждаемся, что при переключении окон проекции надписи следуют за переключением, а по выключении макроса — исчезают, и идем дальше. Давайте разберемся, что же мы написали.

Во-первых, мы добавили переменную lastViewport, в которой хранится значение текущего окна проекции, и добавили действие — при смене окна проекции перерисовать все видовые окна — completeredraw().

if viewport.activeViewport != lastViewport then
(
completeredraw()
lastViewport = viewport.activeViewport
)

В качестве условия мы поставили выражение viewport.activeViewport != lastViewport . Таким образом, в случае если хранящееся в переменной lastViewport значение текущего окна проекции не совпадает с реальным, получаемым с помощью вызова viewport.activeViewport, мы перерисовываем все окна проекций и меняем значение переменной lastViewport на актуальное. Закомментируйте или удалите строки с данным условием, и попробуйте попереключать окна проекций при активном макросе, чтобы стала понятна необходимость данного условия…
Так же мы добавили вывод наших надписей на экран:

gw.wtext [5, 40, 0] TextFaceObj color:(color 255 234 0)
gw.wtext [5, 60, 0] TextVertexObj color:(color 255 234 0)
gw.wtext [5, 80, 0] TextFaceAll color:(color 255 234 0)
gw.enlargeUpdateRect #whole
gw.updateScreen()
первые три строки — собственно вывод, то есть команда gw.wtext — написать текст, далее координаты вывода в текущем окне, далее — что, собственно писать, ну и в последнем разделе — цвет надписей.
последние две строки —
gw.enlargeUpdateRect #whole — задаем прямоугольник, впределах которого необходимо произвести перерисовку экрана (в данном случае — весь экран)
gw.updateScreen() — собственно перерисовка, то есть вывод на экран заданных значений.

Начинаем собирать кусочки мозаики, то есть создавать конечный код. Дописываем следующее:
——————————-
macroScript ScenePolyCounter
category:»MAX Script Tools»
internalcategory:»MAX Script Tools»
buttontext:»ScenePolyCounter»
toolTip:»ScenePolyCounter»
(
local ScenePolyCounterOn = false
local TextFaceObj, TextVertexObj, TextFaceAll
local lastViewport
fn printtext =
(
TextFaceObj = «SelObjFace : «
TextVertexObj = «SelObjVertex :»
TextFaceAll = «SceneFace : «
if selection.count == 1 do
(
if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
TextFaceObj= «SelObjFace :0»
)
else
(
TextFaceObj= «SelObjFace : » + (getPolygoncount $)[1] as string
)
if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
TextVertexObj = «SelObjVertex :0»
)
else
(
TextVertexObj = «SelObjVertex :» + (getPolygoncount $)[2] as string
)
)
gw.wtext [5, 40, 0] TextFaceObj color:(color 255 234 0)
gw.wtext [5, 60, 0] TextVertexObj color:(color 255 234 0)
gw.wtext [5, 80, 0] TextFaceAll color:(color 255 234 0)
gw.enlargeUpdateRect #whole
gw.updateScreen()
if viewport.activeViewport != lastViewport then
(
completeredraw()
lastViewport = viewport.activeViewport
)
)
on ischecked return ScenePolyCounterOn
on execute do
(
if ScenePolyCounterOn then
unregisterRedrawViewsCallback printtext
else
(
registerRedrawViewsCallback printtext
)
ScenePolyCounterOn = not ScenePolyCounterOn
completeredraw ()
updateToolbarButtons()
)
)
————————-

Что мы добавили? Во-первых, мы определили выделение:
if selection.count == 1 do ,
то есть в случае, если у нас выделен один объект, то мы в соответствии с алгоритмом, выработанным в начале урока, проверяем — является ли он плоской формой, и если не является, то фиксируем количество принадлежащих ему фейсов и вершин.
Так же несколько изменилась строка

TextVertexObj = «SelObjVertex :» + (getPolygoncount $)[2] as string,

здесь мы добавили к нашей записи SetObj… значение, получаемое с помощью метода getPolygonCounter, приписанное в конец записи как строковая переменная: as string для совпадения форматов.
Сохраните макрос и перезапустите макс. Создайте в сцене несколько объектов, включая камеры, вспомогательные объекты, сплайны и, собственно, геометрические объекты. Активируйте наш макрос и попереключайте выделение с одного объекта на другой.

Убедитесь, что все работает как надо…
Небольшое примечание — мы вводили условия только для сплайнов, поскольку во-первых, все остальные объекты, не являющиеся геометрическими — итак не имеют ни вершин, ни фейсов, следовательно — нам жить не мешают 🙂 а во-вторых — как сказано в самом начале — мы делаем инструмент для низкополигонального моделирования, соответственно — остальные объекты нас вообще в данной ситуации не интересуют.
Итак, нам осталось немного — получить информацию о всей сцене. Для этого нам придется воспользоваться во-первых, методом rootnode, а заодно вспомнить, что все объекты сцены являются потомками корневого узла сцены. Следовательно, задав обращение rootnode.children мы должны получить список всех объектов сцены.
Если вы успели обнулить макс — создайте в сцене небольшой хаос из объектов различных категорий 🙂 и открывайте окно MaxScriptListener. Вводим:
rootnode.children
——————————-
#children($Box01, $Cylinder01, $Pyramid01, $L-Ext01, $Plane01, $Arc01, $NGon01, $Fspot01, $Camera01, $BoxGizmo01)

у меня получился вот такой списочек, ваш должен соответствовать тем объектам, которые вы успели наворотить в сцене. Как мы видим, это опять массив, следовательно — доступ к элементу — через индекс. Давайте, к примеру, доберемся до категории и информации о вершинах и фейсах бокса:

getPolygonCount rootnode.children[1]
rootnode.children[1].category
——————————
#(12, 8)
#Standard_Primitives

как мы видим, все правильно и аналогично тем действиям, что мы делали с выделением одного объекта. Разница лишь в том, что в данном случае мы вынуждены будем организовать цикл и последовательно суммировать фейсы всех объектов сцены. Для этого нам понадобится оператор while … do и значение количества объектов в сцене, которое мы получим с помощью метода count.

В общем виде алгоритм будет выглядеть следующим образом:

facecount = 0 — — задаем стартовое значение количества фейсов
iter=1 — — задаем начальное количество итераций цикла
countObjScene = rootnode.children.count — — получаем информацию о количестве объектов в сцене
while iter < (countObjScene+1) do - - задаем цикл
(
if (rootnode.children[iter].category == #shape) or (rootnode.children[iter].category == #Splines) then
(
TextFaceAll= «SceneFace :0»
)
else
(
facecount = facecount + (getPolygoncount rootnode.children[iter])[1]
)
TextFaceAll= «SceneFace : » + facecount as string
iter = iter+1
)

дописываем это выражение в наш макрос и проверяем:
————————————
macroScript ScenePolyCounter
category:»MAX Script Tools»
internalcategory:»MAX Script Tools»
buttontext:»ScenePolyCounter»
toolTip:»ScenePolyCounter»
(
local ScenePolyCounterOn = false
local TextFaceObj, TextVertexObj, TextFaceAll
local lastViewport
fn printtext =
(
TextFaceObj = «SelObjFace : «
TextVertexObj = «SelObjVertex :»
TextFaceAll = «SceneFace : «
if selection.count == 1 do
(
if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
TextFaceObj= «SelObjFace : 0»
)
else
(
TextFaceObj= «SelObjFace : » + (getPolygoncount $)[1] as string
)
if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
TextVertexObj = «SelObjVertex : 0»
)
else
(
TextVertexObj = «SelObjVertex :» + (getPolygoncount $)[2] as string
)
)
facecount=0
countObjScene = rootnode.children.count
iter=1
while iter < (countObjScene+1) do
(
if (rootnode.children[iter].category == #shape) or (rootnode.children[iter].category == #Splines) then
(
TextFaceAll= «SceneFace :0»
)
else
(
facecount = facecount + (getPolygoncount rootnode.children[iter])[1]
)
TextFaceAll= «SceneFace : » + facecount as string
iter = iter+1
)
gw.wtext [5, 40, 0] TextFaceObj color:(color 255 234 0)
gw.wtext [5, 60, 0] TextVertexObj color:(color 255 234 0)
gw.wtext [5, 80, 0] TextFaceAll color:(color 255 234 0)
gw.enlargeUpdateRect #whole
gw.updateScreen()
if viewport.activeViewport != lastViewport then
(
completeredraw()
lastViewport = viewport.activeViewport
)
)
on ischecked return ScenePolyCounterOn
on execute do
(
if ScenePolyCounterOn then
unregisterRedrawViewsCallback printtext
else
(
registerRedrawViewsCallback printtext
)
ScenePolyCounterOn = not ScenePolyCounterOn
completeredraw ()
updateToolbarButtons()
)
)
—————————————————

Должно все работать. Мы неплохо потрудились, напоследок вкусности 🙂

О чем хотелось бы сказать в заключение?
Во первых, о том, что не доделано… можно было задать условие, при котором перерисовывался бы не весь экран, а только та часть его, в которой мы пишем… если кому интересен этот вариант — отправляю разбираться с алгоритмом, реализованным в макросе PoligonCounter, по крайней мере в пятой версии макса этот способ реализован.
Можно было обойтись и без цикла для определения количества полигонов в сцене, однако исходя из того, что макрос писался под задачи низкополигонального моделирования и, соответственно, небольшого количества объектов в сцене и небольшого числа полигонов — данный способ скорее предпочтителен. Причина проста — быстрота написания, легкость доступа к коду и возможность редактирования «на лету», а главное — не требуется дополнительных компиляторов кода, фактически работа ведется в текстовом редакторе.
Ну и главное — как всегда — учите скрипты, они строить и жить помогают 🙂 Два способа изучения мы рассмотрели в данном уроке. Первый — с использованием MaxScriptListener — это хороший инструмент для отладки черновых кусков кода, наблюдения за результатами своих действий. Второй — загрузка своего сценария как макроса и интерактивная его отладка. Лично мне второй способ наиболее симпатичен хотя бы потому, что позволяет сохранить в текстовом файле результаты своих попыток, даже если эти искания приводят к зависанию компьютера (ну бывает, бывает :)) — главное чаще использовать волшебную комбинациюCTRL+S… В качестве источников информации можно использовать справку (она достаточно тупа, не говоря уже про то, что на буржуйском) и волшебную кнопку — правую на мышке. Кликаешь на любую (ну или почти любую) кнопку на панели закладок, выбираешь Edit Macro Script и получаешь неограниченный доступ к информации о строении, методах и процедурах макса. Главное при этом — научиться разбираться в коде и не бояться экспериментировать…

Ну что ж — вот и конечный текст нашего макроса с небольшим добавлением как в заголовке, так и в самом теле макроса. Заодно и задачка — найти 10 отличий и понять, зачем они нужны 🙂

—————start macros———————————
— MacroScript File

— Created: Sept 12 2003
— Author: VladiRR
— MacroScript for Turning On a Polygon counter in the viewpot.
—***********************************************************************************************
— MODIFY THIS AT YOUR OWN RISK

macroScript ScenePolyCounter
category:»MAX Script Tools»
internalcategory:»MAX Script Tools»
buttontext:»ScenePolyCounter»
toolTip:»ScenePolyCounter»
(
local ScenePolyCounterOn = false
local TextFaceObj, TextVertexObj, TextFaceAll
local lastViewport
fn printtext =
(
try
(
TextFaceObj = «SelObjFace : «
TextVertexObj = «SelObjVertex :»
TextFaceAll = «SceneFace : «
if selection.count == 1 do
(
if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
TextFaceObj= «SelObjFace : 0»
)
else
(
TextFaceObj= «SelObjFace : » + (getPolygoncount $)[1] as string
)
if ($selection[1].category == #shape) or ($selection[1].category == #Splines) then
(
TextVertexObj = «SelObjVertex : 0»
)
else
(
TextVertexObj = «SelObjVertex :» + (getPolygoncount $)[2] as string
)
)
facecount=0
countObjScene = rootnode.children.count
iter=1
while iter < (countObjScene+1) do
(
if (rootnode.children[iter].category == #shape) or (rootnode.children[iter].category == #Splines) then
(
TextFaceAll= «SceneFace :0»
)
else
(
facecount = facecount + (getPolygoncount rootnode.children[iter])[1]
)
TextFaceAll= «SceneFace : » + facecount as string
iter = iter+1
)
gw.wtext [5, 40, 0] TextFaceObj color:(color 255 234 0)
gw.wtext [5, 60, 0] TextVertexObj color:(color 255 234 0)
gw.wtext [5, 80, 0] TextFaceAll color:(color 255 234 0)
gw.enlargeUpdateRect #whole
gw.updateScreen()
if viewport.activeViewport != lastViewport then
(
completeredraw()
lastViewport = viewport.activeViewport
)
)
catch ()

)
on ischecked return ScenePolyCounterOn
on execute do
(
if ScenePolyCounterOn then
unregisterRedrawViewsCallback printtext
else
(
registerRedrawViewsCallback printtext
)
ScenePolyCounterOn = not ScenePolyCounterOn
completeredraw ()
updateToolbarButtons()
)

)
—————end macros———————-

Нам осталось только удалить созданную вначале урока кнопку (с помощью правого клика мышки и выбора меню Delete Button), и назначить нашему макросу горячую клавишу для вызова. Лично у меня сейчас настроен на клавишу Q вызов макроса PoligonCounter, а на комбинацию CTRL+Q — свежесозданный. Используются по очереди и по ситуации оба.

Да, и самое главное — основная задача все-таки не макросы писать (по крайней мере у большинства, я надеюсь) — а потому всем творить, моделировать, анимировать etc. Ну и если не хватает инструментов — создавать их самим 🙂

Успехов всем! Если есть вопросы — пишите vladirr@render.ru, по возможности постараюсь ответить.

Титры:
Создание макроса — 2 часа
Написание урока — 3.5 часа
выпито: кофе — 2 литра
пива — не помню
при написании урока ни один компьютер не пострадал.

Скачать макрос урока

Всегда Ваш — VladiRR

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *