Войти
Sergei ShaykinСтатьи

Основы плагиностроения к 3D Studio MAX. (3 стр)

Автор:

Max должен как-то распознать ваш plug-in, чтобы подгрузить его к себе. Первое правило мы рассмотрели в первой части урока, вы должны определить пять функций определённого формата и с определенными именами. Кроме того, эти функции должны быть видны извне, то есть быть экспортируемыми. Это можно осуществить, например, создав .def-файл следующего вида:

LIBRARY MyUtility
EXPORTS
  LibDescription    @1
  LibNumberClasses  @2
  LibClassDesc      @3
  LibVersion        @4
SECTIONS
  .data READ WRITE

Почти все эти функции не нуждаются в описании, кроме одной, а именно LibClassDesc(). В вашем файле, кроме класса самого plug-in'а должен быть ещё один класс, который объясняет Max'у, с каким типом plug-in'а тот имеет дело. Вы предоставляете реализацию этого класса, который в свою очередь должен быть порожден от класса ClassDesc2, и возвращаете указатель на этот класс для Max'а функцией LibClassDesc(). (Класс ClassDesc2 является расширением класса ClassDesc, введенным в третьей версии SDK, точнее ClassDesc2 порожден от класса ClassDesc. Это к тому, что вас не должно смущать, что вы порождаете свой класс от ClassDesc2, а функция LibClassDesc() возвращает указатель на класс ClassDesc.)

Max получает указатель на класс и, пользуясь методами этого класса, управляет вашим plug-in'ом.

Каждый тип plug-in'ов различается идентификаторами. Например, в нашем случае мы создаем утилиту. Утилитные классы имеют свой общий уникальный идентификатор UTILITY_CLASS_ID. Этот идентификатор вы возвращаете методом SuperClassID() вашего описывающего класса. Теперь Max будет знать, что он имеет дело с утилитным plug-in'ом. Кроме того, ваш plug-in должен иметь свой уникальный идентификатор. Вы просто задаете его с помощью двух случайно выбранных чисел, главное, чтобы этот идентификатор не совпал с идентификатором другого plug-in'а. Например, если у вас есть заготовка проекта для plug-in'ов, не забывайте каждый раз при создании нового plug-in'a менять этот идентификатор. Иначе, Max, просто не загрузит plug-in с повторяющимся id.

Этот id вы возвращаете методом ClassID().

Метод Create() обычно создает собственно объект вашего plug-in'а и возвращает указатель на него, но поскольку, как мы говорили в предыдущей части, утилитные plug-in'ы могут иметь всего один экземпляр класса, как мы и сделали - один глобальный объект, то поэтому в методе мы ничего не создаем, а просто возвращаем указатель на него:

return &theMyUtility;

Пользователь вашего plug-in'а сможет его различить только по названию. Чтобы задать вам это название, его необходимо вернуть методом ClassName(), например:

return _T("MyUtility");

или же, если вы предпочитаете строки хранить в ресурсах:

return GetString(IDS_CLASS_NAME);
где функция GetString(), описанная в первой части урока, возвращает строку из ресурсов по идентификатору IDS_CLASS_NAME.

Plug-in'ы одного типа могут также делиться ещё на категории. Всё различие - это название категории:

const TCHAR* MyUtilityClassDesc::Category(){
  return GetString(IDS_CATEGORY);
}

Ниже приведена достаточная работающая реализация описательного класса. Методы, которые не описаны выше, прокомментированы. Этот код может быть просто добавлен в файл MyUtility.cpp, описанный в предыдущей части урока.

// определяем уникальный id для Plug-in'а
#define MYUTILITY_CLASS_ID Class_ID(0x70459cb4, 0x3a8a2d69)

// класс-описатель для вашего plug-in'а
class MyUtilityClassDesc : public ClassDesc2 
{
public:
  SClass_ID SuperClassID()
  {
    return UTILITY_CLASS_ID;
  }
  
  Class_ID ClassID()
  {
    return MYUTILITY_CLASS_ID;
  }
  
  void * Create(BOOL loading = FALSE)
  {
    return &theMyUtility;
  }
  
  const TCHAR *  ClassName()
  {
    return GetString(IDS_CLASS_NAME);
  }
  
  const TCHAR * Category()
  {
    return GetString(IDS_CATEGORY);
  }

  // TRUE - plug-in используется для общего доступа пользователями
  // FALSE - используется только другими plug-in'ами
  int IsPublic()
  {
    return 1;
  }

  // возвращает имя для MaxScript
  const TCHAR * InternalName()
  {
    return _T("MyUtility");
  }

  // возвращает hInstance модуля
  HINSTANCE HInstance()
  {
    return hInstance;
  }
};

// объект класса описателя
MyUtilityClassDesc MyUtilityDesc;

// используется в LibClassDesc().
ClassDesc2* GetMyUtilityDesc()
{
  return &MyUtilityDesc;
}

Для успешной линковки, нужно вставить в проект соответствующий файлы библиотеки из Max SDK. В данном примере нам необходимы библиотеки bmm.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib gup.lib paramblk2.lib. Просто внесите их в свойства проекта и не забудьте указать путь к библиотекам. Можно это всё не делать, если вы пользуетесь Wizard'ом для plug-in'ов Max SDK.

В Max'е принята некая договорённость по расширениям для plug-in'ов. То есть, как уже говорилось, это обычные .dll- файлы, только сами файлы имеют не .dll расширения. Например, утилитный plug-in, по договоренности, имеет расширение .dlu. В нашем случае конечный файл должен называться MyUtility.dlu.

Несколько советов.

Пользуйтесь Wizard'ом для создания каркаса приложения.

Правда Wizard немного глючный, поддерживает не все типы plug-in'ов и не всегда вставляет весь код, минимальный для компиляции или линковки. Всё же полезно для настройки проекта.

С SDK идёт очень много примеров plug-in'ов различных типов, которые полезно просматривать, большинство из них являются исходниками частей самого Max'а.

Иногда бывает полезным разбивать plug-in на две части. Это связано с тем, что не всегда удобно при частом тестировании всё время перезагружать Max, так как Max не позволяет подгружать и выгружать plug-in'ы во время работы. Однако вы можете это сделать сами. То есть первая часть вашего plug-in'а является простой и максимально универсальной. Она загружается Max'ом вместе с загрузкой Max'а и вместе с ним выгружается, как и происходит со всеми plug-in'ами. Эта часть в своё рабочее время подгружает вторую часть, в которой вы и делаете всю работу и которую модифицируете. Например, что можно сделать в утилитном plug-in'е. У вас есть кнопочка. Один раз вы нажимаете, подгружается dll методом LoadLibrary() и нужные функции из вашей dll, которая является второй частью, второй раз нажимаете, dll выгружается.

Теперь вы можете модифицировать ваш plug-in, перекомпилировать его и заново собрать, после чего просто его подгружаете нажатием своей кнопки. То есть не нужно перегружать весь Max.

С чего всё началось. Или слово о версиях.

Понадобился мне как-то плагин к Максу. Было это давно. Тогда ещё версия Макса была 1.2. Почитал я хелп немного и, не долго думая, написал простенький плагинчик-пустышку.

К моему разочарованию Макс не увидел его. Причина крылась в том, что я использовал тогда Visual C++ пятой версии, и те dll-ки, которые он собирал, не подходили к этому Максу.

Я нашёл четвёртую версию Visual C++ и возрадовался, когда у Макса появился новый плагин, написанный мной. Ко второй версии Макса и версии 2.5 я собирал плагины уже пятой версией Visual C++, а к третьей - соответственно шестой.

Шестая версия Visual C++ подходит и для четвёртого Макса.

Для каждой версии Макса требуется свой SDK. Поддержки совместимости плагинов от предыдущей версии Макса к следующей - нет. То есть, чтобы перенести плагин от 2-й версии Макса на 3-ю версию, нужно его перекомпилировать с SDK для 3-й версии Макса. Возможно, также, что придётся поправить код.

Данный пример, работающий для 3-й версии, легко переносится под 4-ю версию. Достаточно прописать новые пути в Visual Studio для этого проекта, то есть пути на директории Include и Lib в 4-й версии SDK.

Заключение



Вот и все, что я хотел про это рассказать. Если что-то непонятно или вы с чем-то не согласны, или же есть у вас какие-либо другие вопросы, пишите письма. Может быть, на их основе выйдет ещё несколько статей, посвящённых созданию plug-in'ов под Max.
Страницы: 1 2 3

#3D Studio MAX, #plug-in, #плагины, #экспорт

13 июля 2001 (Обновление: 14 мая 2013)

Комментарии [14]