|
|
||
|
||
|
|
||
| Страница 1 из 31 |
С позиции пользователя код, в частности, должен решать две следующие задачи:
Сгенерированное помощником решение включает два cpp-файла с исходным кодом – это DllEntry.cpp и cube.cpp. Первый файл содержит код, обслуживающий dll-библиотеку. Это файл в нашем случае практически не требует изменений: #include "cube.h"extern ClassDesc2 *GetCubeDesc(); HINSTANCE hInstance; // Функция DllMain вызывается Windows при загрузке DLL // Также функция может вызываться во время таких операций, // как воспроизведение изображения (Rendering) BOOL WINAPI DllMain(HINSTANCE hinstDLL, ULONG fdwReason, LPVOID) { if (fdwReason == DLL_PROCESS_ATTACH) { hInstance = hinstDLL; DisableThreadLibraryCalls(hInstance); } return TRUE; } // Возвращает строку с описанием DLL __declspec( dllexport ) const TCHAR* LibDescription() {return GetString(IDS_LIBDESCRIPTION);} // Возвращает число классов плагина // В нашем случае DLL позволяет оперировать одним классом Cube __declspec( dllexport ) int LibNumberClasses() {return 1;} // Возвращает описание i-го класса плагина __declspec( dllexport ) ClassDesc *LibClassDesc(int i) { switch(i) { case 0: return GetCubeDesc(); default: return 0; } } __declspec( dllexport ) ULONG LibVersion() {return VERSION_3DSMAX;} // Вызывается один раз при загрузке плагина в 3ds Max // Если в качестве результата указать FALSE, то система не будет загружать плагин, // а DLL будет интерпретироваться как свободная библиотека __declspec( dllexport ) int LibInitialize(void) {return TRUE;} // Вызывается один раз при выгрузке плагина из 3ds Max // Возвращаемый результат приложением не используется __declspec( dllexport ) int LibShutdown(void) {return TRUE;} // // Возвращает строку таблицы символов ресурса TCHAR *GetString(int id) { static TCHAR buf[256]; if (hInstance) return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL; return NULL; } Главная функция формирует DllMain hInstance – дескриптор экземпляра плагина, передаваемый файлу cube.cpp посредством заголовочного файла cube.h.
На рис. 20 и 21 показаны иерархии классов, лежащих в основе классов cube и cubeClassDesc. Рис. 20. Класс cube: иерархия родительских классов Рис. 21. Класс cubeClassDesc: иерархия родительских классов Класс cube обеспечивает создание и управление примитивом. #define cube_CLASS_ID Class_ID(0xd667c5aa, 0xb65e9ddb) #define PBLOCK_REF 0 class cube : public SimpleObject2 { public: // Ссылка на интерфейс static IObjParam *ip; // Флаг ручного (по кнопке Create) ввода примитива static BOOL kbrdCreate; // Из класса BaseObject CreateMouseCallBack *GetCreateMouseCallBack(); // Из класса Object BOOL HasUVW(); void SetGenUVW(BOOL sw); int CanConvertToType(Class_ID obtype); Object *ConvertToType(TimeValue t, Class_ID obtype); void GetCollapseTypes(Tab &clist,Tab &nlist); // Из класса GeomObject int IntersectRay(TimeValue t, Ray &ray, float &at, Point3 &norm); // Возвращает структуру ObjectState ObjectState Eval(TimeValue t) {return ObjectState(this);}; // Из класса Animatable void BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev); void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next); // Из класса SimpleObject // Строит меш void BuildMesh(TimeValue t); // Проверяет корректность задания параметров объекта BOOL OKtoDisplay(TimeValue t); // Обновляет пользовательский интерфейс void InvalidateUI(); // Загрузка и сохранение данных плагина IOResult Load(ILoad *iload); IOResult Save(ISave *isave); // Из класса Animatable Class_ID ClassID() {return cube_CLASS_ID;} SClass_ID SuperClassID() {return GEOMOBJECT_CLASS_ID;} void GetClassName(TSTR& s) {s = GetString(IDS_CLASS_NAME);} void DeleteThis() {delete this;} // RefTargetHandle Clone(RemapDir &remap); // // Получает из таблицы символов имя класса TCHAR *GetObjectName() {return GetString(IDS_CLASS_NAME);} // Число блоков параметров int NumParamBlocks() {return 1;} // Возвращает блок параметров по его номеру IParamBlock2 *GetParamBlock(int i) {return pblock2;} // Возвращает блок параметров по его идентификатору IParamBlock2 *GetParamBlockByID(BlockID id) {return (pblock2->ID() == id) ? pblock2 : NULL;} // Конструктор / Деструктор cube(); ~cube(); }; class cubeClassDesc : public ClassDesc2 { public: int IsPublic() {return TRUE;} void *Create(BOOL) {return new cube();} const TCHAR *ClassName() {return GetString(IDS_CLASS_NAME);} SClass_ID SuperClassID() {return GEOMOBJECT_CLASS_ID;} Class_ID ClassID() {return cube_CLASS_ID;} const TCHAR *Category() {return GetString(IDS_CATEGORY);} const TCHAR *InternalName() {return _T("cube");} HINSTANCE HInstance() {return hInstance;} }; static cubeClassDesc cubeDesc; ClassDesc2 *GetCubeDesc() {return &cubeDesc;} // Имена диалогов IDD_KBRD и IDD_PARAMS enum {cube_kbrd, cube_params}; // Имена ассоцируемые с упраляющими элементами диалогов // Элементы IDC_KBSZ, IDC_KBSZSPIN enum {cube_kb_size}; // Элементы IDC_SZ, IDC_SZSPIN enum {cube_size}; // Блок параметров диалога IDD_KBRD static ParamBlockDesc2 cube_kbrd_blk (cube_kbrd, _T("cubeKbrd"), 0, &cubeDesc, P_CLASS_PARAMS + P_AUTO_UI, IDD_KBRD, IDS_KBRD, BEGIN_EDIT_CREATE, APPENDROLL_CLOSED, NULL, cube_kb_size, _T("kbSize"), TYPE_FLOAT, 0, IDS_CB_SIZE, p_default, 40.0, p_range, 0.0f, 100.0f, p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE, IDC_KBSZ, IDC_KBSZSPIN, 0.1f, end, end ); // Блок параметров диалога IDD_PARAMS static ParamBlockDesc2 cube_param_blk (cube_params, _T("params"), 0, &cubeDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, PBLOCK_REF, IDD_PARAMS, IDS_PARAMS, 0, 0, NULL, cube_size, _T("size"), TYPE_FLOAT, P_ANIMATABLE, IDS_CB_SIZE, p_default, 0.0f, p_range, 0.0f, 100.0f, p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE, IDC_SZ, IDC_SZSPIN, 0.1f, end, end ); // Инициализация свойств ip и kbrdCreate класса cube IObjParam *cube::ip = NULL; BOOL cube::kbrdCreate = FALSE; // Создаем диалог с P_AUTO_CONSTRUCT-блоком параметров // Диалог IDD_KBRD будет создан при обращении cubeDesc.BeginEditParams cube::cube() {cubeDesc.MakeAutoParamBlocks(this);} cube::~cube() { } IOResult cube::Load(ILoad *iload) {return IO_OK;} IOResult cube::Save(ISave *isave) {return IO_OK;} class cubeKBDlgProc : public ParamMap2UserDlgProc { public: cube *ob; cubeKBDlgProc(cube *cb) {ob = cb;} INT_PTR DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void DeleteThis() {delete this;} }; // Обеспечивает создание куба после нажатия на кнопку Create диалога IDD_KBRD INT_PTR cubeKBDlgProc::DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg != WM_COMMAND) return FALSE; if (LOWORD(wParam) != IDC_CREATE) return FALSE; float cbSize = cube_kbrd_blk.GetFloat(cube_kb_size); if (cbSize == 0.0) return TRUE; // Устанавливаем IDC_SZ = IDC_KBSZ if (ob->TestAFlag(A_OBJ_CREATING)) ob->pblock2->SetValue(cube_size, 0, cbSize); // Флаг ручного ввода ob->kbrdCreate = TRUE; // Формируем единичную матрицу Matrix3 tm(1); // Устанавливаем в матрице порцию Translate (перемещение) аффинных преобразований tm.SetTrans(Point3(0, 0, 0)); ob->suspendSnap = FALSE; // Формируем куб ob->ip->NonMouseCreate(tm); return TRUE; } // Вызывается при создании очередного экземпляра куба (из класса Animatable) void cube::BeginEditParams(IObjParam *ip,ULONG flags, Animatable *prev) { SimpleObject::BeginEditParams(ip, flags, prev); this->ip = ip; if (kbrdCreate) { // Если ранее был выполнен ручной ввод, то устанавливаем IDC_SZ = IDC_KBSZ pblock2->SetValue(cube_size, 0, cube_kbrd_blk.GetFloat(cube_kb_size)); kbrdCreate = FALSE; } cubeDesc.BeginEditParams(ip, this, flags, prev); // Фиксируем пользовательскую процедуру, // ассоциированную с блоком параметров cube_kbrd_blk cube_kbrd_blk.SetUserDlgProc(new cubeKBDlgProc(this)); } void cube::EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) { SimpleObject::EndEditParams(ip, flags, next); cubeDesc.EndEditParams(ip, this, flags, next); // Плагин должен вызывать методы интерфейса ip только // между BeginEditParams и EndEditParams this->ip = NULL; } // Из класса Object // Вернуть флаг наличия у объекта UVW-координат BOOL cube::HasUVW() {return TRUE;} // Можно модифицировать, исходя из целей проекта void cube::SetGenUVW(BOOL sw) {if (sw == HasUVW()) return;} // Класс обработки мышиных событий class cubeCreateCallBack : public CreateMouseCallBack { cube *ob; // Указатель на объект Point3 p0; // Первая точка в мировой системе координат Point3 p1; // Вторая точка в мировой системе координат public: int proc(ViewExp *vpt, int msg, int point, int flags, IPoint2 m, Matrix3 &mat); void SetObj(cube *cb) {ob = cb;} }; int cubeCreateCallBack::proc(ViewExp *vpt, int msg, int point, int flags, IPoint2 m, Matrix3 &mat){ if (msg == MOUSE_POINT || msg == MOUSE_MOVE) { switch(point) { case 0: ob->suspendSnap = TRUE; // m - позиция мыши в оконных координатах // p0 - позиция мыши в мировых координатах p0 = vpt->SnapPoint(m, m, NULL, SNAP_IN_PLANE); // Порция Translate (перемещение) аффинных преобразований позиции mat.SetTrans(p0); // Изменяем значение параметра cube_size диалога IDD_PARAMS ob->pblock2->SetValue(cube_size, ob->ip->GetTime(), 0.0f); break; case 1: { ob->suspendSnap = TRUE; // p1 - новая позиция мыши в мировых координатах p1 = vpt->SnapPoint(m, m, NULL, SNAP_IN_PLANE); // Управляем размером куба в зависимости от положения мыши ob->pblock2->SetValue(cube_size, ob->ip->GetTime(), 0.5f * Length(p1 - p0)); // Создаем и отображаем меш в видовом порте cube_param_blk.InvalidateUI(); break; } case 2: return CREATE_STOP; } } else if (msg == MOUSE_ABORT) return CREATE_ABORT; return TRUE; } static cubeCreateCallBack cubeCreateCB; CreateMouseCallBack *cube::GetCreateMouseCallBack() { cubeCreateCB.SetObj(this); return &cubeCreateCB; } // Получает размер куба, строит его меш и формирует группы сглаживания void cube::BuildMesh(TimeValue t) { float size, h; ivalid = FOREVER; pblock2->GetValue(cube_size, t, size, ivalid); h = 0.5 * size; // Число вершин и граней в меш mesh.setNumVerts(8); mesh.setNumFaces(12); // Координаты вершин mesh.setVert(0, Point3(-h, -h, -h)); mesh.setVert(1, Point3(h, -h, -h)); mesh.setVert(2, Point3(-h, h, -h)); mesh.setVert(3, Point3(h, h, -h)); mesh.setVert(4, Point3(-h, -h, h)); mesh.setVert(5, Point3(h, -h, h)); mesh.setVert(6, Point3(-h, h, h)); mesh.setVert(7, Point3(h, h, h)); // Состав граней mesh.faces[0].setVerts(0, 2, 3); mesh.faces[1].setVerts(3, 1, 0); mesh.faces[2].setVerts(4, 5, 7); mesh.faces[3].setVerts(7, 6, 4); mesh.faces[4].setVerts(0, 1, 5); mesh.faces[5].setVerts(5, 4, 0); mesh.faces[6].setVerts(1, 3, 7); mesh.faces[7].setVerts(7, 5, 1); mesh.faces[8].setVerts(3, 2, 6); mesh.faces[9].setVerts(6, 7, 3); mesh.faces[10].setVerts(2, 0, 4); mesh.faces[11].setVerts(4, 6, 2); // Группы сглаживания mesh.faces[0].setSmGroup(2); mesh.faces[1].setSmGroup(2); mesh.faces[2].setSmGroup(4); mesh.faces[3].setSmGroup(4); mesh.faces[4].setSmGroup(8); mesh.faces[5].setSmGroup(8); mesh.faces[6].setSmGroup(16); mesh.faces[7].setSmGroup(16); mesh.faces[8].setSmGroup(32); mesh.faces[9].setSmGroup(32); mesh.faces[10].setSmGroup(64); mesh.faces[11].setSmGroup(64); // Устанавливаем видимость внешних ребер граней (диагональные ребра не видны) for (int k = 0; k < 12; k++) { mesh.faces[k].setEdgeVisFlags(1, 1, 0); mesh.faces[k].setMatID(1); } // Назначаем UVW-координаты Matrix3 tm(1); tm.Scale(Point3(h, h, h)); tm = Inverse(tm); mesh.ApplyUVWMap(MAP_BOX, 1.0f, 1.0f, 1.0f, 0, 0, 0, 0, tm); mesh.InvalidateTopologyCache(); } // Добавлен код проверки корректности параметра size BOOL cube::OKtoDisplay(TimeValue t) { float size; pblock2->GetValue(cube_size, t, size, FOREVER); return (size } void cube::InvalidateUI() {cube_param_blk.InvalidateUI(pblock2->LastNotifyParamID());} // Находит точку пересечения луча ray с поверхностью (см. класс Ray) // и нормаль к поверхности в этой точке int cube::IntersectRay(TimeValue t, Ray &ray, float &at, Point3 &norm) { return SimpleObject::IntersectRay(t, ray, at, norm); } // Методы, обеспечивающие преобразование и копирование объекта Object* cube::ConvertToType(TimeValue t, Class_ID obtype) { return SimpleObject::ConvertToType(t, obtype); } int cube::CanConvertToType(Class_ID obtype) { if (obtype == defObjectClassID || obtype == triObjectClassID) return 1; else return SimpleObject::CanConvertToType(obtype); } void cube::GetCollapseTypes(Tab &clist, Tab &nlist) { Object::GetCollapseTypes(clist, nlist); } RefTargetHandle cube::Clone(RemapDir &remap) { cube *newob = new cube(); newob->ReplaceReference(0, remap.CloneRef(pblock2)); newob->ivalid.SetEmpty(); BaseClone(this, newob, remap); return newob; } Приведенный код можно скопировать в созданный помощником проект Visual Studio. При этом следует копировать коды обоих файлов – и DllEntry.cpp, и cube.cpp. |