Уроки: 3ds Max

Пишем макросы

Привет всем! Вот и решился я снова потревожить ваш покой. Пойдем мы, как говорится - 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

16687 Автор:
Актуальность: 62
Качество: 62
Суммарный балл: 124
Голосов: 4 оценки
Зарегистрируйтесь, чтобы добавить комментарий.
Эту страницу просмотрели: 727 уникальных посетителей