[Closed] Highlighting pickbutton using SDK?
I’m trying to make an as-stripped-down-as-possible utility plugin where a user can select a node and store it. I have most of the functionality down, the only things I still need are:
- how to get the button to highlight between clicking it and selection of a node, and
- the most practical way to store the value of the selected node within the plugin.
Of course, if you see anything else that I’m not doing right, or that can be done in a better way, please let me know!
#include "includes.h"
class TestUtil : public UtilityObj {
public:
IUtil *iu;
Interface *ip;
HWND hPanel;
TestUtil();
void BeginEditParams(Interface *ip,IUtil *iu);
void EndEditParams(Interface *ip,IUtil *iu);
void DeleteThis() {}
void TestUtil::Init(HWND hWnd) { }
void TestUtil::Destroy(HWND hWnd) { }
}; static TestUtil theTestUtil;
TestUtil::TestUtil() {
iu = NULL;
ip = NULL;
hPanel = NULL;
}
class TestUtilClassDesc:public ClassDesc2 {
public:
int IsPublic() { return 1; }
void *Create(BOOL loading = FALSE) { return &theTestUtil; }
const TCHAR *ClassName() { return GetString(IDS_CLASS_NAME); }
SClass_ID SuperClassID() { return UTILITY_CLASS_ID; }
Class_ID ClassID() { return ImpressObjects_CLASS_ID; }
const TCHAR* Category() { return _T(""); }
}; static TestUtilClassDesc TestUtilDesc;
ClassDesc2* GetTestUtilDesc() {return &TestUtilDesc;}
class TestUtilPickModeCallback : public PickModeCallback {
public:
BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags);
BOOL Pick(IObjParam *ip,ViewExp *vpt);
}; static TestUtilPickModeCallback thePickMode;
BOOL TestUtilPickModeCallback::HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags) {
return ip->PickNode(hWnd, m) ? TRUE : FALSE;
}
BOOL TestUtilPickModeCallback::Pick(IObjParam *ip,ViewExp *vpt) {
INode *node = vpt->GetClosestHit();
mprintf("picked %s
", node->GetName());
return TRUE;
}
static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_PICKOBJ: {
theTestUtil.ip->SetPickMode(&thePickMode); break;
}
}
default: return FALSE;
}
}
return TRUE;
}
I’m not sure how to store data inside a Utility plugin, but I can help you with the pickbutton.
There are a couple of virtual functions in a PickModeCallback that you can override to enable/disable the check state.
Here are the functions:
void EnterMode(IObjParam *ip);
void ExitMode(IObjParam *ip);
And here’s how to enable/disable the check state:
void TestUtilPickModeCallback::EnterMode(IObjParam *ip)
{
pickButton->SetCheck(TRUE);
}
void TestUtilPickModeCallback::ExitMode(IObjParam *ip)
{
pickButton->SetCheck(FALSE);
}
The “pickButton” variable is a pointer to your ICustButton. You can get the ICustButton from your control with:
pickButton = GetICustButton(GetDlgItem([your hwnd], [your IDC_* name]));
(Replace the stuff in the square brackets with the appropriate values)
If I have correctly understood what you have told me (checking against some of the sample files as well to make sure I am on the right track), that brings me to the following. Unfortunately, it crashes as soon as I click on the button, so I must be missing something or doing something wrong.
#include "includes.h"
class TestUtil : public UtilityObj {
public:
IUtil *iu;
Interface *ip;
HWND hPanel;
ICustButton *pickButton;
TestUtil();
void BeginEditParams(Interface *ip,IUtil *iu);
void EndEditParams(Interface *ip,IUtil *iu);
void DeleteThis() {}
void Init (HWND hWnd);
void Destroy (HWND hWnd) {};
};
static TestUtil theTestUtil;
TestUtil::TestUtil() {
iu = NULL;
ip = NULL;
hPanel = NULL;
pickButton = NULL;
}
class TestUtilClassDesc:public ClassDesc2 {
public:
int IsPublic() { return 1; }
void *Create(BOOL loading = FALSE) { return &theTestUtil; }
const TCHAR *ClassName() { return GetString(IDS_CLASS_NAME); }
SClass_ID SuperClassID() { return UTILITY_CLASS_ID; }
Class_ID ClassID() { return ImpressObjects_CLASS_ID; }
const TCHAR* Category() { return _T(""); }
};
static TestUtilClassDesc TestUtilDesc;
ClassDesc2* GetTestUtilDesc() {return &TestUtilDesc;}
void TestUtil::Init(HWND hWnd) {
pickButton = GetICustButton(GetDlgItem(hWnd, IDC_PICKOBJ));
};
class TestUtilPickModeCallback : public PickModeCallback {
public:
void EnterMode(IObjParam *ip);
void ExitMode(IObjParam *ip);
BOOL HitTest(IObjParam *ip, HWND hWnd, ViewExp *vpt, IPoint2 m, int flags);
BOOL Pick(IObjParam *ip, ViewExp *vpt);
};
static TestUtilPickModeCallback thePickMode;
void TestUtilPickModeCallback::EnterMode (IObjParam *ip) { theTestUtil.pickButton->SetCheck(TRUE); }
void TestUtilPickModeCallback::ExitMode (IObjParam *ip) { theTestUtil.pickButton->SetCheck(FALSE); };
BOOL TestUtilPickModeCallback::HitTest(IObjParam *ip, HWND hWnd,ViewExp *vpt, IPoint2 m, int flags) {
return ip->PickNode(hWnd, m) ? TRUE : FALSE;
}
BOOL TestUtilPickModeCallback::Pick(IObjParam *ip, ViewExp *vpt) {
INode *node = vpt->GetClosestHit();
mprintf("picked %s
", node->GetName());
return TRUE;
}
static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_PICKOBJ: {
theTestUtil.ip->SetPickMode(&thePickMode); break;
}
break;
}
default: return FALSE;
}
}
return TRUE;
}
void TestUtil::BeginEditParams(Interface *ip, IUtil *iu) {
this->iu = iu;
this->ip = ip;
hPanel = ip->AddRollupPage(hInstance, MAKEINTRESOURCE(IDD_PANEL), DlgProc, GetString(IDS_PARAMS), 0);
}
void TestUtil::EndEditParams(Interface *ip, IUtil *iu) {
this->iu = NULL;
this->ip = NULL;
ip->DeleteRollupPage(hPanel);
hPanel = NULL;
}
there is a clear code in touch.cpp sdk example.
it shows how to pick a node and what to do with a pickbutton
Not sure what I’m supposed to be looking at from that. I tried editing in some things that looked relevant… button still doesn’t highlight when clicked, and still crashes. The original code I had wouldn’t highlight the button but it at least registered the node being selected. Since then I’ve added a bunch of stuff I don’t understand and it doesn’t work at all. So, basically the opposite of what I was trying to do
#include "includes.h"
class TestUtil : public UtilityObj {
public:
IUtil *iu;
Interface *ip;
HWND hPanel;
static ICustButton *pickButton;
TestUtil();
void BeginEditParams(Interface *ip, IUtil *iu);
void EndEditParams(Interface *ip, IUtil *iu);
void DeleteThis() { }
void Init(HWND hWnd);
void Destroy (HWND hWnd) { }
INode *tObj;
};
static TestUtil theTestUtil;
ICustButton *TestUtil::pickButton = NULL;
TestUtil::TestUtil() {
iu = NULL;
ip = NULL;
hPanel = NULL;
//pickButton = NULL;
}
class TestUtilClassDesc:public ClassDesc2 {
public:
int IsPublic() { return 1; }
void *Create(BOOL loading = FALSE) { return &theTestUtil; }
const TCHAR *ClassName() { return GetString(IDS_CLASS_NAME); }
SClass_ID SuperClassID() { return UTILITY_CLASS_ID; }
Class_ID ClassID() { return ImpressObjects_CLASS_ID; }
const TCHAR* Category() { return _T(""); }
};
static TestUtilClassDesc TestUtilDesc;
ClassDesc2* GetTestUtilDesc() {return &TestUtilDesc;}
void TestUtil::Init(HWND hWnd) {
pickButton = GetICustButton(GetDlgItem(hWnd, IDC_PICKOBJ));
};
class TestUtilPickModeCallback : public PickModeCallback {
TestUtil *testObj;
public:
void EnterMode(IObjParam *ip);
void ExitMode(IObjParam *ip);
BOOL HitTest(IObjParam *ip, HWND hWnd, ViewExp *vpt, IPoint2 m, int flags);
BOOL Pick(IObjParam *ip, ViewExp *vpt);
};
static TestUtilPickModeCallback thePickMode;
static int pickMode = 0;
static CommandMode* lastMode = NULL;
static void SetPickMode(PickModeCallback* p, int w = 0) {
if (pickMode || !p) {
pickMode = 0;
GetCOREInterface()->PushCommandMode(lastMode);
lastMode = NULL;
GetCOREInterface()->ClearPickMode();
} else {
pickMode = w;
lastMode = GetCOREInterface()->GetCommandMode();
GetCOREInterface()->SetPickMode(p);
}
}
void TestUtilPickModeCallback::EnterMode (IObjParam *ip) { theTestUtil.pickButton->SetCheck(TRUE); }
void TestUtilPickModeCallback::ExitMode (IObjParam *ip) { theTestUtil.pickButton->SetCheck(FALSE); };
BOOL TestUtilPickModeCallback::HitTest(IObjParam *ip, HWND hWnd,ViewExp *vpt, IPoint2 m, int flags) {
return ip->PickNode(hWnd, m) ? TRUE : FALSE;
}
BOOL TestUtilPickModeCallback::Pick(IObjParam *ip, ViewExp *vpt) {
if (vpt->HitCount() == 0)
return FALSE;
INode *node;
if ((node = vpt->GetClosestHit()) != NULL) {
mprintf("picked %s
", node->GetName());
SetPickMode(NULL);
testObj->pickButton->SetCheck(FALSE);
HWND hw = testObj->hPanel;
Static_SetText(GetDlgItem(hw,IDS_PARAMS), testObj->tObj->GetName());
}
return TRUE;
}
static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_PICKOBJ: {
theTestUtil.ip->SetPickMode(&thePickMode); break;
}
break;
}
default: return FALSE;
}
}
return TRUE;
}
void TestUtil::BeginEditParams(Interface *ip, IUtil *iu) {
this->iu = iu;
this->ip = ip;
hPanel = ip->AddRollupPage(hInstance, MAKEINTRESOURCE(IDD_PANEL), DlgProc, GetString(IDS_PARAMS), 0);
}
void TestUtil::EndEditParams(Interface *ip, IUtil *iu) {
this->iu = NULL;
this->ip = NULL;
ip->DeleteRollupPage(hPanel);
hPanel = NULL;
}
You have to step through your code. Put breakpoints where the crashing is occurring, and then use VS to check the value of the variables. Your pickbutton is probably NULL for whatever reason.
I have not been able to get breakpoints to work. When I try to run my build from inside VS, the following happens:
-
I get a message stating “Debugging information for ‘3dsmax.exe cannot be found or does not match. Cannot find or open the PDB file. Do you want to continue debugging?” (I choose yes)
-
In my breakpoints window, all breakpoints are highlighted with yellow triangles with exclamation points and state “The breakpoint will not currently be hit. No symbols have been loaded for this document”.
-
In my module window, every entry reads either “Cannot find or open the PDB file” or occasionally “Binary was not built with debug information” under Symbol Status.
This occurs regardless of whether I am using Debug, Hybrid, or Release build. I have made modifications to each of these configurations because they did not compile properly as packaged, but I was getting these errors even before I made my changes.
So I need to either figure out why I am having these issues, or use some other means of debugging. Up to this point I have been outputting strings to the maxscript listener, but in the case of an error which causes Max itself to crash, this is not a viable solution.
Just to confirm…
You are building in debug mode, and using 3dsmax.exe as the debugging program…and after 3dsmax loads (not by you running it manually, but by it being automatically launched by VS after your plugin compiles) and you open your plugin in 3dsmax, you get an error saying debug symbols not loaded?
If you click here I’ve screenshotted some of the relevant property pages of a working plugin of mine…do you see any major discrepancies between what you have and what’s in these screens?
I’m not in expert on the ins and outs of VS, but when I use the plugin wizard included in the 3dsmax SDK, after re-targeting the include/lib directories, plugins built in Debug mode always debug fine (breakpoints, stack, etc). Is it possible you switched something off by accident?
The differences I can see are as follows
My General/Intermediate Directory is:
$(Platform)$(Configuration)\
I’m not using the Unicode Character Set, but then I’m also running VS2010 for Max 2012
I can’t see all of your C++/General/Additional Include Directories, but mine are:
.\midl$(Platform);C:\Program Files (x86)\Autodesk\3ds Max 2012 SDK\include;%(AdditionalIncludeDirectories)
My option directly below that is “Resolve #using References” rather than “Additional #using References”, and I don’t have anything there.
C++/General/Warning Level is Level3 (/W3) and Treat Warnings as Errors is No (/WX-) – I’m not sure if the value you are using is something I have available with my version.
My Linker/General/Output File is the explicit file directory and name.
Linker/General/Enable Incremental linking is:
No (/INCREMENTAL:NO) – whereas in yours the cell is blank.
Beyond that things look pretty much the same. There are some fields which are too long to fit in the space visible on screen, but I doubt they will be of much significance.
So, what am I doing wrong here?
Are your .pdb files going to the 3dsmax plugin directory (ie, same place the plugin file goes)?