I don't have zmod2 source to compile and/or test your plugin, but hope I see the overal logic behind.
Your tool is a floater that shows a width and/or other road-specific options for a spline you select in scene, right? I guess you don't actually need to collect a set of splines to store that much of information in your global CPSDLNodes. Well, you can actually use such a thing, but you can create it dynamically using a scene content when this info is needed. The key feature I would like to suggest is to move your per-spline options into user-defined options of the spline. You can give them specific names like
PSDL.RoadPlugin.Convert
('1' = convert, any other value = do not convert
PSDL.RoadPlugin.WidthRoad
(if value successfully converts to float, and float is in correct range (0.0f to 10.0f), consider it to be correct width specified;
PSDL.RoadPlugin.WidthSidewalk
...
and so on.
User will not need to create these options (yes, user could see them in regular "properties" window of zmodeler, can change or delete them), but the node itself will maintain them across through ZModeler logic. The spline will save and load these properties into .z3d, so you don't bother with this stuff.
Your tool can give a quick spot on scene nodes, collect splines that match criteria into array, check their user-defined options where applies (read given width values). You can move this into a routine like that:
collectSceneSplineNodes(core::IProcParams* pParams, bool bSelectedOnly, bool bConvertOnly, CPSDLNodes& arrOut);
It should not be a member of any class, so it can be accessed from any point of your code.
The routine will collect either selected only splines (e.g., when some scene node is selected or deselected, it will collect correct array of "still selected" scene splines, so your floater window can show properties for selected splines), and/or it can also check availability of non-zero "PSDL.RoadPlugin.Convert" user-defined property on a spline, so it will find only those splines that user wants to convert (e.g. before an export).
Code: Select all
ZRESULT collectSceneSplineNodes(core::IProcParams* pParams, bool bSelectedOnly, bool bConvertOnly, CPSDLNodes& arrOut);
{
//out array is empty by default; if it's not a case in your implementation for some reason, clear it:
//arrOut.deleteAll(); // add this method into CPSDLNodes too;
// scene navigator walks entire scene nodes, including
// hidden and disabled;
scene::CSceneNavigator nav;
if (ZFAILED(nav.initialize(pParams)))
return ZRESULT_FALSE;
//
// loop on nodes
while (nav.next())
{
if (!nav.isAccessible())
continue;
ZPtr<scene::ISplineNode> pSNode;
if (ZRESULT_OK == nav.queryNode(IID_(scene::ISplineNode), (void**)&pSNode))
{
//got a spline;
DWORD dwNodeStatus = nav.getNodeStatus();
float fWidth1 = -1.0f; //not available/not known;
float fWidth2 = -1.0f; //not available/not known;
bool bConvert = false;
long nType = -1;
ZPtr<core::ui::controls::IUserDefinedProperty> pUserProp;
ZString strValue;
if (ZRESULT_OK == pSNode->QueryInterface(IID_(core::ui::controls::IUserDefinedProperty), (void**)&pUserProp))
{
bool bConvert = ZRESULT_OK == pUserProp->getValueByName("PSDL.RoadPlugin.Convert", strValue) &&
strValue.length() > 0 && ((LPCTSTR)strValue)[0] != '0';
if (ZRESULT_OK != pUserProp->getValueByName("PSDL.RoadPlugin.WidthRoad", strValue) ||
strValue.length() == 0 || 1 != _stscanf(strValue, "%f", &fWidth1))
fWidth1 = -1.0f;//consider as not available;
if (ZRESULT_OK != pUserProp->getValueByName("PSDL.RoadPlugin.WidthSidewalk", strValue) ||
strValue.length() == 0 || 1 != _stscanf(strValue, "%f", &fWidth2))
fWidth2 = -1.0f;//consider as not available;
if (ZRESULT_OK != pUserProp->getValueByName("PSDL.RoadPlugin.Type", strValue) ||
strValue.length() == 0 || 1 != _stscanf(strValue, "%li", &nType))
nType = -1;//consider as not available;
}
//
// determine whether this spline shold go into [out] array:
if ((!bConvertOnly || bConvertOnly && bConvert) &&
(!bSelectedOnly || bSelectedOnly && (0 != (dwNodeStatus & STATUS_SELECTED))))
// refine "add" routine to accept initial parameters: "convert", "type", "width1", "width2":
// let it refine values, since -1.0f could appear as width (not defined), type could
// be -1 as "not defined" too)))
arrOut.add(pSNode, bConvert, nType, fWidth1, fWidth2);
}//is spline
}//while nodes
return ZRESULT_OK;
}
Your context tool will not deal with CPSDLNodes, but will set on/off user-defined property "PSDL.RoadPlugin.Convert" on a given spline(s):
Code: Select all
ZRESULT CPSDLRoadsContextTool::apply(core::IProcParams *pParams, DWORD *pRetVal)
{
resetError();
core::eLevel lev;
pParams->getLevel(lev);
if (SUBLEVEL(lev))
return ZRESULT_FALSE;
scene::tNodeSeq seq;
scene::CSubsetIterator it;
ZPtr<scene::ISplineNode> pNode;
if (!it.initialize(pParams))
return ZRESULT_FALSE;
while (it.next())
{
if (!it.isAllowEditing())
continue;
pNode = NULL;
// stop on spline node only:
if (ZRESULT_OK == it.queryNode(IID_(scene::ISplineNode), (void**)&pNode))
break;
}
if (!pNode)
return ZRESULT_FALSE;
//
// query current user-defined value:
ZPtr<core::ui::controls::IUserDefinedOptions> pUserOpt;
if (ZRESULT_OK == pNode->QueryInterface(IID_(core::ui::controls::IUserDefinedOptions), (void**)&pUserOpt))
{
ZString strValue;
ZString strNewValue("1");//by default your tool will enable conversion;
if (ZRESULT_OK == pUserOpt->getOptionValueByName("PSDL.RoadPlugin.Convert", strValue) &&
strValue.length() > 0 && ((LPCTSTR)strValue)[0] != '0')
{
// convert property is already available and it's On; so context tool action
// would be to turn if off:
strNewValue[0] = '0';
}
// adding option that matches by name to any existing option will "update" value
// on existing, so it's safe to use "add" in both cases: option is already available
// and option is not available yet;
pUserOpt->addOption("PSDL.RoadPlugin.Convert", core::ui::controls::checkBox, strNewValue);
}
//
// ...you can also force your floater window to update, so it can reflect changes
//
return ZRESULT_OK;
}
The respective queryLayout will change, sine it will check for a property on a spline node:
Code: Select all
ZRESULT CPSDLRoadsContextTool::queryLayout(scene::INode *pNode, core::eLevel level, core::ui::IViewport *pViewport)
{
ZPtr<scene::ISplineNode> pSplineNode;
if (ZRESULT_OK != pNode->QueryInterface(IID_(scene::ISplineNode), (void**) &pSplineNode))
return ZRESULT_FALSE;
// given node is a splien node; check for conversion property:
ZPtr<core::ui::controls::IUserDefinedOptions> pUserOpt;
ZString strValue;
if (ZRESULT_OK == pNode->QueryInterface(IID_(core::ui::controls::IUserDefinedOptions), (void**)&pUserOpt) &&
ZRESULT_OK == pUserOpt->getOptionValueByName("PSDL.RoadPlugin.Convert", strValue) &&
strValue.length() > 0 && ((LPCTSTR)strValue)[0] != '0')
// valid property with valid 'on' value:
g_bCheckState = true;
else
g_bCheckState = false;
return ZRESULT_OK;
}
and your context tool does not need setStatus and changeStatus. the checkStatus(DWORD nBit) should look like this:
return (nBit == STATUS_ACTIVE && g_bCheckState) ? ZRESULT_OK : ZRESULT_FALSE;
Concerning your tool, there are some notes to mention:
1. event handler should always return ZRESULT_FALSE. Returning ZRESULT_OK will stop processing and other event handlers will not get notified.
2. "scene" event handing will need
onNodeSelection only, all other can simply return ZRESULT_FALSE;
3. handling "zm" events becomes not necessary.
all of your
Code: Select all
void Update(void);
void OnCheckRadio(UINT nID);
void OnEnChange(UINT nID);
should start with a rows of code like that:
Code: Select all
CPSDLNodes arrSplines;
ZPtr<core::IProcParams> pParmas;
g_pZModeler->getProcParams(&pParams);
if (ZRESULT_OK == collectSceneSplineNodes(pParams, true, false, arrSplines))
{
//
// the rest of implementation follows here;
}