Экспортёр для XSI

В свете последних тенденций борьбы с пиратством да и просто исходя из экономии денег (или их отсутствия), стало очень актуально переходить с мегадорогих пакетов разработки на что-то более дешёвое. Поэтому было решено написать плагин не для 3DMax или Maya, а для менее популярного XSI, но зато более финансово доступного.

Оказалось что всё очень даже неплохо. С версии 6 (судя по документации), данная среда поддерживает плагины, написанные на C#, а писать на нём подобные вещи – неописуемое удовольствие. Замечу, что написание и отладка базовой функциональности у меня заняла один день, правда пришлось выключить ICQ, IRC и браузер.


Итак, отправной точкой будет установка XSI – проблем с этим возникнуть не должно.
Чем данное приложение хорошо, так это тем, что оно умеет сгенерировать заготовку проекта на том языке, который мы выберем. Такой себе визард, но не в студию встроенный, а в XSI. Поэтому, чтобы облегчить себе жизнь – сгенерим заготовку для проекта на C#

Создание проекта

Запускаем среду, и в ней запускаем plugin manager (File->Plugin Manager). Выбираем закладку plugins, и жмём кнопку new, всплывет предложение выбрать тип плагина, я выбрал command, так как нам нужна кнопка, на которой будет слово “Export to gameformat”. Нам предложат кастомизировать проект – лучшее место для того, чтобы правильно назвать кнопочку, которая будет отвечать за экспорт и выбрать её местоположение в иерархии кнопок среды. Для того, чтобы она лежала в разделе File->Export нам нужно указать значение параметра Add to menu равным siMenuMainFileExport
Дальше вносим параметры по усмотрению и жмём кнопку Generate Code.
После этого запускаем студию, открываем полученный проект и начинаем программировать.

Программирование

XSI API не вызывает дикого отторжения, в отличие от некоторых других, и по началу вообще выглядит очень здорово. Но как и везде – находятся свои подводные камни, которые усложняют нашу жизнь.

Нам предоставляют три основных объекта

CXSIApplicationClass m_xsi;
CXSIFactoryClass m_fact;
CXSIUtilsClass m_utils;

При помощи которых можно получать доступ ко всем внутренностям. Есть, правда, ещё один класс, который нам заботливо забыли подсунуть при генерации проекта – это CXSIUIToolkitClass, штука, позволяющая получить доступ к пользовательскому интерфейсу. В нашем случае – это вызов диалогового окна, которое спросит у пользователя имя файла, в который он хочет экспортировать свою мега модель.
Использование выглядит приблизительно так:


CXSIUIToolkitClass toolkit = new CXSIUIToolkitClass();

FileBrowser fb = toolkit.FileBrowser;

fb.DialogTitle = “Select file to save model”;
fb.InitialDirectory = @”c:\”;

fb.ShowSave();

UberModelSerializer serializer = new UberModelSerializer(fb.FilePathName);

Дальше нам понадобится сцена, которую мы хотим экспортировать, и тут нам пригодится объект класса CXSIApplicationClass, у которого есть в наличии аксессор ActiveSceneRoot


CXSIApplicationClass xsi = new CXSIApplicationClass();
serializer.Serialize(xsi.ActiveSceneRoot);

Объекты, которые нарисовал художник являются детьми сцены, при чём в прямом смысле, поэтому доступ к ним мы получаем через scene.Children, замечу, что среди них ещё будет замешан источник освещения и камера.
Доступ к основным свойствам объекта получить вроде бы просто, но именно здесь нас ожидает первый камешек.
Т.к. физические свойства лежат в “разделе” Kinematics, то и мы пытаемся их извлечь оттуда.


obj.Kinematics.Global.Parameters[“posx”]

Вот так вот у нас должны извлечься глобальные координаты, но не тут-то было, мы получим некий com_object. Чтобы получить числовое значение, нам надо сделать вот так:


float.Parse( obj.Kinematics.Global.Parameters[“posx”].get_Value(0).ToString() )

Вот теперь в наших руках есть нормальное значение.
Ещё у объектов бывают custom параметры – параметры, которые пользователь сам задал, они извлекаются следующим образом:


obj.Properties[“CustomPSet”].Parameters[“speed”].get_Value(0).ToString()

Замечу, что при извлечении параметров, да и вообще во всех случаях нужно учитывать возможность того, что параметра или свойства может не существовать, в таком случае мы напоремся на ArgumentNullException и плагин бесшумно прекратит работу, поэтому нужно уделить внимание валидации значений. XSI при получении необработанного исключения просто тушит выполнение плагина.

Отладка

Отлаживать наш плагин очень легко и удобно. Нам нужно загрузить плагин в XSI, и студией подключиться к процессу, при этом порядок выполняемых действий не играет роли.
Загрузка плагина осуществляется следующим образом: В XSI открываем plugin manager, у нас будет открыта первая же закладка – Tree, жмём кнопку Load и выбираем DLL, которая получилась после сборки нашего проекта в студии.
Подключаемся к процессу XSI вот так: в студии жмём debug->Attach to process, в списке ищем XSI.exe и подключаемся к нему. Студия перейдёт в режим отладки – время расставлять breakpoints и готовиться читать debug log.
После того, как мы отладимся или поймём, что надо вносить изменения – отсоединяемся от процесса и выгружаем плагин из XSI. К счастью для того, чтобы сделать последнее нам не надо перегружать всю среду. Достаточно всё в том же менеджере плагинов в разделе tree выбрать наш и в контекстном меню, которое появляется по нажатию правой кнопки мыши нажать unload.
Сделать это нужно для того, чтобы среда отдала доступ к файлу, т.к. без этого компиляция закончится неудачей – студия не сможет записать новую DLL поверх старой.