Привет всем! Вот и решился я снова потревожить ваш покой. Пойдем мы,
как говорится - 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
Источник: http://www.render.ru/books/show_book.php?book_id=45 |