0. 미리 알아야 할 용어들
플러그인을 만들다 보면 생소한 용어들에 부딛히게 됩니다. 물론 플러그인을 제작하기 위해서는 맥스에 대해서 알아야 하는 게 기본이긴 하지만 혹시라도 그렇지 못한 분들을 위해서 간단하게 기본 용어를 설명하도록 하겠습니다.
커맨드 패널(Command Pannel)
아래의 스샷은 맥스의 우측에 있는 폼뷰를 캡춰한 것입니다. 이 폼뷰 전체가 커맨드 패널인데요, 제일 위에 있는 탭 중 어떤 것이 선택되어 있느냐에 따라서 그 이름이 다릅니다. 커맨드 패널은 PropertySheet 이고 각 탭을 선택한 것은 Page 정도로 생각하면 되겠습니다. 생긴 것도 그와 유사하게 생겼죠? 아마도 내부 구현도 Tab 혹은 PropertySheet 를 이용했으리라 봅니다.
어쨌든 왼쪽에서 오른쪽 순으로 Create Pannel, Modify Pannel, Hierarchy Pannel, Motion Panne, Display Pannel, Utilities Pannel 이라고 불리웁니다. 마우스를 올려보면 이름이 나옵니다.
가지(Branch)
Branch 란 용어는 우리말로 하자면 '나무가지' 정도 되겠죠. 나무를 각 패널(Pannel) 이라고 하자면 위의 그림에서 Box, Cone, Sphere, GeoSphere 등의 하위기능이 가지라고 할 수 있습니다. 물론 맥스 자체에서는 Branch 라는 단어를 사용하지는 않는 것 같습니다만, SDK 에서는 자주 사용하더군요. 일단 알아두는 것이 좋을 것 같습니다.
1. 커맨드 패널에 플러그인 추가
현 단계까지 플러그인을 제작했고 맥스에 플러그인 디렉토리를 설정했다면 아래 스샷에 있는 여러 개의 윈도우 모양의 버튼을 누릅니다. 마우스를 올렸을 때 그 버튼에 대한 설명은 "Configure Button Sets" 라고 되어 있군요. "버튼 집합 설정하기" 정도가 되겠습니다. 즉 커맨트 패널에 나오는 플러그인 버튼 집합을 설정하는 메뉴라는 것입니다.
이 버튼을 누르면 다음과 같은 다이얼로그가 뜹니다.
1번은 커맨드 패널에 출력할 버튼의 개수를 나타냅니다. 현재 기본값은 9개로 설정되어 있을 것입니다. 우리는 우리의 플러그인 버튼을 추가할 것이기 때문에 10으로 값을 변경합니다. 그리고 왼쪽의 2번은 우리가 추가한 CustomPlugins 카테고리의 CMyPolygonCounter 플러그인의 이름이 있습니다.
오른쪽 버튼의 스크롤바를 내려 보면 다음과 같이 빈 버튼 자리가 있는 것을 볼 수 있습니다.
빈 버튼을 선택하면 노란색 테두리가 생기는게 보이시죠? 노란색 테두리가 생기면 왼쪽의 CMyPolygonCounter를 더블클릭합니다. 그러면 다음 그림처럼 버튼이 추가됩니다.
그런데 여기에서 주의할 점은 노란테두리가 reactor 나 MAXScript 등 다른 버튼에 있을 때 왼쪽을 더블클릭하면 그 버튼이 대체되어 버린다는 것입니다. 빈 공간을 선택하고 더블클릭해 주십시오.
이제 OK 를 누르면 다음과 같이 커맨드 패널이 변경되어 있는 것을 확인하실 수 있습니다.
이제 우리는 CMyPolygonCounter 버튼을 사용해서 우리의 플러그인을 실행할 수 있습니다.
2. CMyPolygonCounter::BeginEditParams
자 우리가 커맨드 패널의 CMyPolygonCounter 라는 버튼을 눌렀을 때 어떤 일이 발생할까요. 제목으로 벌써 눈치 채셨겠지만 UtilityObj::BeginEditParams 가 호출됩니다. 물론 우리가 재정의한 함수를 호출하게 됩니다. 이것의 선언을 살펴보도록 하죠.
virtual void BeginEditParams(Interface* ip, IUtil* iu) = 0
이것은 플러그인에 의해서 구현됩니다. SDK 설명으로는 "플러그인이 커맨드 패널의 가지(branch)에서 사용될 때 호출된다. 예를 들어 플러그인은 이 메서드에서 커맨드 패널에 롤업(rollup) 페이지를 추가할 수 있다" 라고 되어 있군요.
감을 잡으신 분도 있겠지만 이 메서드에서 다이얼로그를 생성하면 됩니다. 다음과 같이 코드를 작성할 수 있겠죠.
void CMyPolygonCounter::BeginEditParams(Interface *ip,IUtil *iu)
{
{
this->iu = iu;
this->ip = ip;
// 이미 다이얼로그가 존재하면
if(NULL != m_hDlg)
{
// 다이얼로그를 활성화한다
SetActiveWindow(m_hDlg);
}
// 다이얼로그가 존재하지 않으면
else
{
// 이미 다이얼로그가 존재하면
if(NULL != m_hDlg)
{
// 다이얼로그를 활성화한다
SetActiveWindow(m_hDlg);
}
// 다이얼로그가 존재하지 않으면
else
{
// 다이얼로그를 생성한다
m_hDlg = CreateDialogParam(hInstance,
MAKEINTRESOURCE(IDD_POLYGONCOUNTER),
ip->GetMAXHWnd();,
MyPolygonCounterDlgProc,
(LPARAM)this);
m_hDlg = CreateDialogParam(hInstance,
MAKEINTRESOURCE(IDD_POLYGONCOUNTER),
ip->GetMAXHWnd();,
MyPolygonCounterDlgProc,
(LPARAM)this);
ShowWindow(m_hDlg, SW_SHOW);
}
}
}
여기에서 왜 그냥 다이얼로그를 띄우지 않고 m_hDlg가 있는지를 확인하느냐에 대한 의문이 들 수 있습니다. 그것은 이 다이얼로그가 Modeless Dialog 이기 때문인데요, 폴리곤 카운터가 떠 있는 상태에서도 다른 편집 작업이 가능해야 한다는 이야기입니다. 즉 폴리곤 카운터의 플러그인 버튼을 다시 누른다고 해도 새로운 다이얼로그가 생성되는 것은 아니라는 의미입니다.
EndEditParams 가 호출된다고 해도 폴리곤 카운터 다이얼로그는 없어지지 않습니다. 그렇기 때문에 폴리곤 카운터가 떠 있는 상황에서는 버튼을 다시 누른다고 해도 그냥 활성화만 할 뿐이죠.
ip->GetMAXHWnd() 는 맥스가 실행되고 있는 윈도우 핸들을 반환합니다.
MyPolygonCounterDlgProc 는 다이얼로그 프로시저인데 아마도 CMyPolygonCounterDlgProc 로 생성되어 있을 것입니다. 거기에서 C를 뺐습니다.
3. CMyPolygonCounter::EndEditParams
이름에서 느낄 수 있듯이 폴리곤 카운터 플러그인 버튼이 해제될 때 호출됩니다. 이것이 해제되는 경우는 두 가지입니다. 첫번째는 다른 플러그인 버튼을 클릭했을 때이며, 두번째는 다른 커맨드 패널을 선택했을 경우입니다. 즉 버튼이 눌려있다는 표시인 노란색이 없어지는 경우를 말하는 것입니다.
BeginEditParams 에서도 언급했지만 폴리곤 카운터가 없어지는 것은 다이얼로그에서 IDCANCEL 이나 IDOK 통지가 발생했을 경우이지, EndEditParams 가 호출되는 경우가 아닙니다. 그래서 우리는 여기에서 아무런 작업도 수행하지 않습니다.
4. MyPolygonCounterDlgProc
//--- 다이얼로그 프로시저 -------------------------------------------------------
static BOOL CALLBACK MyPolygonCounterDlgProc(
HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
// 폴리곤 카운터 초기화 작업
theCMyPolygonCounter.Init(hDlg);
break;
static BOOL CALLBACK MyPolygonCounterDlgProc(
HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
// 폴리곤 카운터 초기화 작업
theCMyPolygonCounter.Init(hDlg);
break;
case WM_DESTROY :
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDCANCEL :
case IDOK :
// 폴리곤 카운터 종료 작업
theCMyPolygonCounter.Destroy(hDlg);
break;
}
switch(LOWORD(wParam))
{
case IDCANCEL :
case IDOK :
// 폴리곤 카운터 종료 작업
theCMyPolygonCounter.Destroy(hDlg);
break;
}
break;
default:
return FALSE;
return FALSE;
}
return TRUE;
}
}
위에서 theCMyPolygonCounter 는 폴리곤 카운터의 전역 인스턴스입니다. Init 와 Destroy 는 프로젝트를 생성할 때 자동으로 정의된 메서드입니다.
그런데 WM_DESTROY 가 아니라 Destroy 에서 종료처리를 하는 것에 대해서 의문을 가지실 것이라 생각합니다. 그 이유는 EndDialog 가 다이얼로그를 바로 해제하는 것이 아니기 때문입니다. 즉 WM_DESTORY 에서 종료 처리 작업을 하면 맥스를 종료할 때까지 종료 처리가 되지 않을 수 있습니다. 그러므로 theCMyPolygonCounter.Destroy(hDlg) 를 호출하여 종료 처리를 합니다.
5. CMyPolygonCounter::Init(HWND hDlg)
이 메서드는 초기화 작업을 수행합니다. 초기화작업이래야 별거 없고, 인터페이스에 폴리곤 카운터 다이얼로그를 등록하는 작업과 폴리곤 카운터 다이얼로그를 화면 중심에 배치하는 작업입니다.
void CMyPolygonCounter::Init(HWND hDlg)
{
{
// 다이얼로그 등록
ip->RegisterDlgWnd(hDlg);
ip->RegisterDlgWnd(hDlg);
// 맥스 윈도우 너비 구함
RECT Rect;
GetWindowRect(ip->GetMAXHWnd(), &Rect);
LONG lWidth = Rect.right - Rect.left;
LONG lHeight = Rect.bottom - Rect.top;
RECT Rect;
GetWindowRect(ip->GetMAXHWnd(), &Rect);
LONG lWidth = Rect.right - Rect.left;
LONG lHeight = Rect.bottom - Rect.top;
// 다이얼로그 너비 구함
RECT DlgRect;
GetWindowRect(hDlg, &DlgRect);
LONG lDlgWidth = DlgRect.right - DlgRect.left;
LONG lDlgHeight = DlgRect.bottom - DlgRect.top;
RECT DlgRect;
GetWindowRect(hDlg, &DlgRect);
LONG lDlgWidth = DlgRect.right - DlgRect.left;
LONG lDlgHeight = DlgRect.bottom - DlgRect.top;
// 다이얼로그의 위치 재설정
MoveWindow(hDlg, (lWidth - lDlgWidth) / 2,
(lHeight - lDlgHeight) / 2, lDlgWidth, lDlgHeight, TRUE);
}
MoveWindow(hDlg, (lWidth - lDlgWidth) / 2,
(lHeight - lDlgHeight) / 2, lDlgWidth, lDlgHeight, TRUE);
}
여기에서 주의해야 할 점은 m_hDlg 를 사용하지 않고 인자로 넘어온 hDlg 를 사용한다는 것입니다. 왜냐하면 WM_INITDIALOG 는 CreateDialogParams 가 값을 반환하기 전에, 즉 m_hDlg 가 할당되기 전에 호출되는 것이기 때문입니다.
6. CMyPolygonCounter::Destroy(HWND hDlg)
여기에서는 인터페이스에 등록했던 폴리곤 카운터 다이얼로그를 등록 해제하는 작업을 수행합니다. 또한 커맨드 패널에서 선택되어 있던 폴리곤 카운터 버튼을 선택 해제합니다.
void CMyPolygonCounter::Destroy(HWND hDlg)
{
{
// 다이얼로그 닫는다
EndDialog(hDlg, FALSE);
EndDialog(hDlg, FALSE);
// 다이얼로그 등록 해제
ip->UnRegisterDlgWnd(hDlg);
ip->UnRegisterDlgWnd(hDlg);
// 선택 상태를 해제한다
iu->CloseUtility();
iu->CloseUtility();
// 멤버 초기화
m_hDlg = NULL;
iu = NULL;
ip = NULL;
}
m_hDlg = NULL;
iu = NULL;
ip = NULL;
}
여기에서 iu->CloseUtility() 가 버튼의 선택을 해제하게 만드는 메서드입니다. 이 메서드를 호출하게 되면 EndEditParams 를 호출하게 됩니다. 만약 EndEditParams 내부에서 iu->CloseUtility() 를 호출하게 되면 서로를 계속해서 호출하게 된다는 점에 주의하시기 바랍니다.
그리고 m_hDlg = NULL 을 할당하는 것은 매우 중요합니다. 그렇게 하지 않으면 다시 BeginEditParams 를 했을 때 다이얼로그를 생성하지 않게 됩니다.
7. 정리
플러그인 버튼을 누를 때 BeginEditParams 가 호출되며, 여기에서 다이얼로그 생성 및 초기화 작업을 수행합니다. 그리고 다이얼로그에서 IDOK 나 IDCANCEL 통지가 발생했을 때 플러그인을 해제하고, 플러그인 버튼 선택을 해제합니다.
말로는 간단한데 직접 순서를 느낄 수 있는 가장 쉬운 방법은 MessageBox 를 사용해서 호출 순서를 출력해 보는 것입니다.
MyPolygonCounter.cpp 소스 코드를 첨부했습니다.
8. 주의 사항
MyPolygonCounter.dlu 파일을 쓰기 모드로 열 수 없다는 링크 에러가 나면 맥스 프로그램이 실행중인지를 확인해 보시기 바랍니다.
댓글 없음:
댓글 쓰기