16 #pragma warning(disable: 4800) 18 void SetICustEdit(HWND wnd,
int nIDDlgItem, TCHAR *text)
20 ICustEdit *edit = GetICustEdit(GetDlgItem(wnd, nIDDlgItem));
22 ReleaseICustEdit(edit);
25 void SetICustEdit(HWND wnd,
int nIDDlgItem,
int n)
28 _stprintf(text, _T(
"%d"), n);
29 ICustEdit *edit = GetICustEdit(GetDlgItem(wnd, nIDDlgItem));
31 ReleaseICustEdit(edit);
34 TCHAR *GetICustEditT(HWND wnd)
36 static TCHAR buffer[2084];
37 ICustEdit *edit = GetICustEdit(wnd);
38 edit->GetText(buffer, 2084);
39 ReleaseICustEdit(edit);
43 int GetICustEditI(HWND wnd, BOOL *valid)
45 ICustEdit *edit = GetICustEdit(wnd);
46 int i = edit->GetInt(valid);
47 ReleaseICustEdit(edit);
51 void ChunkSave(ISave *isave,
int chunkid,
int value)
54 isave->BeginChunk(chunkid);
55 isave->Write(&value,
sizeof(
int), &nb);
59 void ChunkSave(ISave *isave,
int chunkid, ULONG value)
62 isave->BeginChunk(chunkid);
63 isave->Write(&value,
sizeof(ULONG), &nb);
67 void ChunkSave(ISave *isave,
int chunkid,
bool value)
70 isave->BeginChunk(chunkid);
71 isave->Write(&value,
sizeof(
bool), &nb);
75 void ChunkSave(ISave *isave,
int chunkid, TCHAR *value)
78 isave->BeginChunk(chunkid);
79 int length = _tcslen(value) + 1;
80 isave->Write(&length,
sizeof(
int), &nb);
81 isave->Write(value, length *
sizeof(TCHAR), &nb);
85 TCHAR *ChunkLoadString(ILoad *iload, TCHAR *buffer,
int maxLength)
90 res = iload->Read(&length,
sizeof(
int), &nb);
91 assert(res == IO_OK && length <= maxLength);
92 res = iload->Read(buffer, length *
sizeof(TCHAR), &nb);
97 int ChunkLoadInt(ILoad *iload)
102 res = iload->Read(&value,
sizeof(
int), &nb);
103 assert(res == IO_OK);
107 int ChunkLoadULONG(ILoad *iload)
111 res = iload->Read(&value,
sizeof(ULONG), &nb);
112 assert(res == IO_OK);
116 bool ChunkLoadBool(ILoad *iload)
121 res = iload->Read(&value,
sizeof(
bool), &nb);
122 assert(res == IO_OK);
126 void showAnimControls(HWND hWnd, BOOL val) {
127 ShowWindow(GetDlgItem(hWnd, IDC_EF_LABEL), val);
128 ShowWindow(GetDlgItem(hWnd, IDC_EF), val);
129 ShowWindow(GetDlgItem(hWnd, IDC_SF_LABEL), val);
130 SetWindowText(GetDlgItem(hWnd, IDC_EXP_SEL_FRAMES),
131 val ? _T(
"Use Range:") : _T(
"Use Frame:"));
134 void enableAnimControls(HWND hWnd, BOOL val) {
135 EnableWindow(GetDlgItem(hWnd, IDC_SF_LABEL), val);
136 EnableWindow(GetDlgItem(hWnd, IDC_SF), val);
137 EnableWindow(GetDlgItem(hWnd, IDC_EF_LABEL), val);
138 EnableWindow(GetDlgItem(hWnd, IDC_EF), val);
141 void enableChooserControls(HWND hWnd, BOOL val) {
142 EnableWindow(GetDlgItem(hWnd, IDC_LIST_EXPORT), val);
143 EnableWindow(GetDlgItem(hWnd, IDC_ADD_EXPORT), val);
144 EnableWindow(GetDlgItem(hWnd, IDC_REMOVE_EXPORT), val);
148 #define ANIM_RAD_NONE 0 149 #define ANIM_RAD_EXPALL 1 150 #define ANIM_RAD_EXPSEL 2 151 #define ANIM_RAD_ALL 3 152 void enableAnimRadios(HWND hWnd,
int mask) {
153 EnableWindow(GetDlgItem(hWnd, IDC_EXP_ALL_FRAMES), mask & ANIM_RAD_EXPALL);
154 EnableWindow(GetDlgItem(hWnd, IDC_EXP_SEL_FRAMES), mask & ANIM_RAD_EXPSEL);
165 ph(instance), hWnd(wnd) {}
167 #if MAX_VERSION_MAJOR < 15 168 virtual TCHAR *dialogTitle() {
return _T(
"Objects to Export");}
169 virtual TCHAR *buttonText() {
return _T(
"Select");}
171 virtual const MCHAR *dialogTitle() {
return _M(
"Objects to Export");}
172 virtual const MCHAR *buttonText() {
return _M(
"Select");}
175 virtual int filter(INode *node);
176 virtual void proc(INodeTab &nodeTab);
181 int AddNodeCB::filter(INode *node) {
184 Object *obj = node->EvalWorldState(0).obj;
185 Control *c = node->GetTMController();
187 bool is_bone = (node->GetBoneNodeOnOff() ||
189 ((c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) ||
190 (c->ClassID() == BIPBODY_CONTROL_CLASS_ID) ||
191 (c->ClassID() == FOOTPRINT_CLASS_ID))));
194 if (IsDlgButtonChecked(hWnd, IDC_ANIMATION) == BST_CHECKED)
195 return is_bone && !ph->FindNode(node->GetHandle());
198 is_bone || ((obj->SuperClassID() == HELPER_CLASS_ID && obj->ClassID() != MaxEggPlugin_CLASS_ID)) ||
199 ((obj->SuperClassID() == GEOMOBJECT_CLASS_ID &&
200 obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) ||
201 (obj->SuperClassID() == SHAPE_CLASS_ID &&
202 obj->ClassID() == EDITABLE_SURF_CLASS_ID &&
203 GetNURBSSet(obj, 0, getSet, TRUE) &&
204 getSet.GetNURBSObject(0)->GetType() == kNCVCurve)) &&
205 !ph->FindNode(node->GetHandle()));
209 void AddNodeCB::proc(INodeTab &nodeTab) {
211 for (
int i = 0; i < nodeTab.Count(); i++)
212 ph->AddNode(nodeTab[i]->GetHandle());
213 ph->RefreshNodeList(hWnd);
224 ph(instance), hWnd(wnd) {}
226 #if MAX_VERSION_MAJOR < 15 227 virtual TCHAR *dialogTitle() {
return _T(
"Objects to Remove");}
228 virtual TCHAR *buttonText() {
return _T(
"Remove");}
230 virtual const MCHAR *dialogTitle() {
return _M(
"Objects to Remove");}
231 virtual const MCHAR *buttonText() {
return _M(
"Remove");}
234 virtual int filter(INode *node) {
return (node && ph->FindNode(node->GetHandle()));}
235 virtual void proc(INodeTab &nodeTab);
240 void RemoveNodeCB::proc(INodeTab &nodeTab) {
241 for (
int i = 0; i < nodeTab.Count(); i++)
242 ph->RemoveNodeByHandle(nodeTab[i]->GetHandle());
243 ph->RefreshNodeList(hWnd);
246 MaxEggOptions::MaxEggOptions() {
247 _max_interface = NULL;
248 _anim_type = MaxEggOptions::AT_model;
249 _start_frame = INT_MIN;
250 _end_frame = INT_MIN;
251 _double_sided =
false;
257 _path_replace->_path_store = PS_relative;
258 _export_whole_scene =
true;
259 _export_all_frames =
true;
263 INT_PTR CALLBACK MaxOptionsDialogProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
265 TCHAR tempFilename[2048];
268 if ( !imp && message != WM_INITDIALOG )
return FALSE;
274 SetWindowLongPtr(hWnd,GWLP_USERDATA,lParam);
280 EndDialog(hWnd, FALSE);
285 switch( LOWORD(wParam) ) {
287 if (HIWORD(wParam) == BN_CLICKED) {
288 SetWindowText(GetDlgItem(hWnd, IDC_EXPORT_SELECTED),
289 _T(
"Export Meshes:"));
290 enableAnimRadios(hWnd, ANIM_RAD_NONE);
291 showAnimControls(hWnd, TRUE);
292 enableAnimControls(hWnd, FALSE);
293 if (imp->_prev_type == MaxEggOptions::AT_chan) imp->ClearNodeList(hWnd);
294 imp->_prev_type = MaxEggOptions::AT_model;
301 if (HIWORD(wParam) == BN_CLICKED) {
302 SetWindowText(GetDlgItem(hWnd, IDC_EXPORT_SELECTED),
303 _T(
"Export Bones:"));
304 enableAnimRadios(hWnd, ANIM_RAD_ALL);
305 showAnimControls(hWnd, TRUE);
306 enableAnimControls(hWnd, IsDlgButtonChecked(hWnd, IDC_EXP_SEL_FRAMES));
307 if (imp->_prev_type != MaxEggOptions::AT_chan) imp->ClearNodeList(hWnd);
308 imp->_prev_type = MaxEggOptions::AT_chan;
309 CheckRadioButton(hWnd, IDC_EXP_ALL_FRAMES, IDC_EXP_SEL_FRAMES, IDC_EXP_ALL_FRAMES);
315 if (HIWORD(wParam) == BN_CLICKED) {
316 SetWindowText(GetDlgItem(hWnd, IDC_EXPORT_SELECTED),
317 _T(
"Export Models:"));
318 enableAnimRadios(hWnd, ANIM_RAD_ALL);
319 showAnimControls(hWnd, TRUE);
320 enableAnimControls(hWnd, IsDlgButtonChecked(hWnd, IDC_EXP_SEL_FRAMES));
321 if (imp->_prev_type == MaxEggOptions::AT_chan) imp->ClearNodeList(hWnd);
322 imp->_prev_type = MaxEggOptions::AT_both;
323 CheckRadioButton(hWnd, IDC_EXP_ALL_FRAMES, IDC_EXP_SEL_FRAMES, IDC_EXP_ALL_FRAMES);
329 if (HIWORD(wParam) == BN_CLICKED) {
330 SetWindowText(GetDlgItem(hWnd, IDC_EXPORT_SELECTED),
331 _T(
"Export Meshes:"));
332 enableAnimRadios(hWnd, ANIM_RAD_EXPSEL);
333 showAnimControls(hWnd, FALSE);
334 enableAnimControls(hWnd, TRUE);
335 CheckRadioButton(hWnd, IDC_EXP_ALL_FRAMES, IDC_EXP_SEL_FRAMES, IDC_EXP_SEL_FRAMES);
336 if (imp->_prev_type == MaxEggOptions::AT_chan) imp->ClearNodeList(hWnd);
337 imp->_prev_type = MaxEggOptions::AT_pose;
342 case IDC_EXP_ALL_FRAMES:
343 if (HIWORD(wParam) == BN_CLICKED) {
344 enableAnimControls(hWnd, FALSE);
345 if(imp->_prev_type == MaxEggOptions::AT_chan)
347 CheckRadioButton(hWnd, IDC_MODEL, IDC_POSE, IDC_ANIMATION);
349 if(imp->_prev_type == MaxEggOptions::AT_both)
351 CheckRadioButton(hWnd, IDC_MODEL, IDC_POSE, IDC_BOTH);
357 case IDC_EXP_SEL_FRAMES:
358 if (HIWORD(wParam) == BN_CLICKED) {
359 enableAnimControls(hWnd, TRUE);
360 if(imp->_prev_type == MaxEggOptions::AT_chan)
362 CheckRadioButton(hWnd, IDC_MODEL, IDC_POSE, IDC_ANIMATION);
364 if(imp->_prev_type == MaxEggOptions::AT_both)
366 CheckRadioButton(hWnd, IDC_MODEL, IDC_POSE, IDC_BOTH);
373 if (HIWORD(wParam) == BN_CLICKED) {
374 enableChooserControls(hWnd, FALSE);
379 case IDC_EXPORT_SELECTED:
380 if (HIWORD(wParam) == BN_CLICKED) {
381 enableChooserControls(hWnd, TRUE);
390 if (!imp->_choosing_nodes) {
392 imp->_choosing_nodes =
true;
393 imp->_max_interface->DoHitByNameDialog(&PickCB);
394 imp->_choosing_nodes =
false;
399 case IDC_REMOVE_EXPORT:
401 if (!imp->_choosing_nodes) {
402 imp->_choosing_nodes =
true;
404 imp->_max_interface->DoHitByNameDialog(&PickCB);
405 imp->_choosing_nodes =
false;
411 if (imp->UpdateFromUI(hWnd)) EndDialog(hWnd, TRUE);
414 EndDialog(hWnd, FALSE);
418 _tcscpy(tempFilename, GetICustEditT(GetDlgItem(hWnd, IDC_FILENAME)));
420 memset(&ofn, 0,
sizeof(ofn));
422 ofn.lpstrFile = tempFilename;
423 ofn.lStructSize =
sizeof(ofn);
424 ofn.hwndOwner = hWnd;
425 ofn.Flags = OFN_HIDEREADONLY | OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST;
426 ofn.lpstrDefExt = _T(
"egg");
427 ofn.lpstrFilter = _T(
"Panda3D Egg Files (*.egg)\0*.egg\0All Files (*.*)\0*.*\0");
429 SetFocus(GetDlgItem(hWnd, IDC_FILENAME));
430 if (GetSaveFileName(&ofn))
431 SetICustEdit(hWnd, IDC_FILENAME, ofn.lpstrFile);
439 if (IsDlgButtonChecked(hWnd, IDC_CHECK1))
440 if (MessageBox(hWnd, _T(
"Warning: Exporting double-sided polygons can severely hurt ")
441 _T(
"performance in Panda3D.\n\nAre you sure you want to export them?"),
442 _T(
"Panda3D Exporter"), MB_YESNO | MB_ICONQUESTION) != IDYES)
443 CheckDlgButton(hWnd, IDC_CHECK1, BST_UNCHECKED);
456 void MaxOptionsDialog::SetAnimRange() {
458 Interval anim_range = _max_interface->GetAnimRange();
459 _min_frame = anim_range.Start()/GetTicksPerFrame();
460 _max_frame = anim_range.End()/GetTicksPerFrame();
463 MaxOptionsDialog::MaxOptionsDialog() :
468 _choosing_nodes(
false),
473 MaxOptionsDialog::~MaxOptionsDialog ()
478 void MaxOptionsDialog::UpdateUI(HWND hWnd) {
479 int typeButton = IDC_MODEL;
480 int anim_exp = _export_all_frames ? IDC_EXP_ALL_FRAMES : IDC_EXP_SEL_FRAMES;
481 int model_exp = _export_whole_scene ? IDC_EXPORT_ALL : IDC_EXPORT_SELECTED;
483 switch (_anim_type) {
484 case MaxEggOptions::AT_chan: typeButton = IDC_ANIMATION;
break;
485 case MaxEggOptions::AT_both: typeButton = IDC_BOTH;
break;
486 case MaxEggOptions::AT_pose: typeButton = IDC_POSE;
break;
487 case MaxEggOptions::AT_model: typeButton = IDC_MODEL;
break;
490 _prev_type = _anim_type;
492 CheckRadioButton(hWnd, IDC_MODEL, IDC_POSE, typeButton);
493 SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(typeButton, BN_CLICKED), 0);
494 CheckRadioButton(hWnd, IDC_EXPORT_ALL, IDC_EXPORT_SELECTED, model_exp);
495 SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(model_exp, BN_CLICKED), 0);
496 CheckRadioButton(hWnd, IDC_EXP_ALL_FRAMES, IDC_EXP_SEL_FRAMES, anim_exp);
497 SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(anim_exp, BN_CLICKED), 0);
499 CheckDlgButton(hWnd, IDC_CHECK1,
500 _double_sided ? BST_CHECKED : BST_UNCHECKED);
503 SetICustEdit(hWnd, IDC_FILENAME, _file_name);
504 if (_start_frame != INT_MIN) {
505 SetICustEdit(hWnd, IDC_SF, _start_frame);
506 SetICustEdit(hWnd, IDC_EF, _end_frame);
508 SetICustEdit(hWnd, IDC_SF, _min_frame);
509 SetICustEdit(hWnd, IDC_EF, _max_frame);
512 RefreshNodeList(hWnd);
515 void MaxOptionsDialog::ClearNodeList(HWND hWnd) {
517 RefreshNodeList(hWnd);
520 void MaxOptionsDialog::RefreshNodeList(HWND hWnd) {
522 HWND nodeLB = GetDlgItem(hWnd, IDC_LIST_EXPORT);
523 SendMessage(nodeLB, LB_RESETCONTENT, 0, 0);
524 for (
int i = 0; i < _node_list.size(); i++) {
525 INode *temp = _max_interface->GetINodeByHandle(_node_list[i]);
526 const TCHAR *name = _T(
"Unknown Node");
527 if (temp) name = temp->GetName();
528 SendMessage(nodeLB, LB_ADDSTRING, 0, (LPARAM)name);
532 bool MaxOptionsDialog::UpdateFromUI(HWND hWnd) {
534 Anim_Type newAnimType;
535 int newSF = INT_MIN, newEF = INT_MIN;
538 if (IsDlgButtonChecked(hWnd, IDC_MODEL)) newAnimType = MaxEggOptions::AT_model;
539 else if (IsDlgButtonChecked(hWnd, IDC_ANIMATION)) newAnimType = MaxEggOptions::AT_chan;
540 else if (IsDlgButtonChecked(hWnd, IDC_BOTH)) newAnimType = MaxEggOptions::AT_both;
541 else newAnimType = MaxEggOptions::AT_pose;
543 if (newAnimType != MaxEggOptions::AT_model && IsDlgButtonChecked(hWnd, IDC_EXP_SEL_FRAMES)) {
544 newSF = GetICustEditI(GetDlgItem(hWnd, IDC_SF), &valid);
546 MessageBox(hWnd, _T(
"Start Frame must be an integer"), _T(
"Invalid Value"), MB_OK | MB_ICONEXCLAMATION);
549 if (newSF < _min_frame) {
550 _stprintf(msg, _T(
"Start Frame must be at least %d"), _min_frame);
551 MessageBox(hWnd, msg, _T(
"Invalid Value"), MB_OK | MB_ICONEXCLAMATION);
554 if (newSF > _max_frame) {
555 _stprintf(msg, _T(
"Start Frame must be at most %d"), _max_frame);
556 MessageBox(hWnd, msg, _T(
"Invalid Value"), MB_OK | MB_ICONEXCLAMATION);
559 if (newAnimType != MaxEggOptions::AT_pose) {
560 newEF = GetICustEditI(GetDlgItem(hWnd, IDC_EF), &valid);
562 MessageBox(hWnd, _T(
"End Frame must be an integer"), _T(
"Invalid Value"), MB_OK | MB_ICONEXCLAMATION);
565 if (newEF > _max_frame) {
566 _stprintf(msg, _T(
"End Frame must be at most %d"), _max_frame);
567 MessageBox(hWnd, msg, _T(
"Invalid Value"), MB_OK | MB_ICONEXCLAMATION);
571 MessageBox(hWnd, _T(
"End Frame must be greater than the start frame"), _T(
"Invalid Value"), MB_OK | MB_ICONEXCLAMATION);
577 TCHAR *temp = GetICustEditT(GetDlgItem(hWnd, IDC_FILENAME));
578 if (!_tcslen(temp)) {
579 MessageBox(hWnd, _T(
"The filename cannot be empty"), _T(
"Invalid Value"), MB_OK | MB_ICONEXCLAMATION);
583 if (_tcslen(temp) < 4 || _tcsncmp(_T(
".egg"), temp+(_tcslen(temp) - 4), 4)) {
584 _stprintf(_file_name, _T(
"%s.egg"), temp);
586 _tcscpy(_file_name, temp);
589 temp = _tcsrchr(_file_name,
'\\');
590 if (!temp) temp = _file_name;
593 if (_tcslen(temp) >
sizeof(_short_name))
594 _stprintf(_short_name, _T(
"%.*s..."),
sizeof(_short_name)-4, temp);
596 _tcscpy(_short_name, temp);
597 _short_name[_tcslen(_short_name) - 4] = NULL;
600 _start_frame = newSF;
602 _anim_type = newAnimType;
603 _double_sided = IsDlgButtonChecked(hWnd, IDC_CHECK1);
606 _export_whole_scene = IsDlgButtonChecked(hWnd, IDC_EXPORT_ALL);
607 _export_all_frames = IsDlgButtonChecked(hWnd, IDC_EXP_ALL_FRAMES);
612 bool MaxOptionsDialog::FindNode(ULONG INodeHandle) {
613 for (
int i = 0; i < _node_list.size(); i++)
614 if (_node_list[i] == INodeHandle)
return true;
618 void MaxOptionsDialog::AddNode(ULONG INodeHandle) {
619 if (FindNode(INodeHandle))
return;
620 _node_list.push_back(INodeHandle);
623 void MaxOptionsDialog::CullBadNodes() {
624 if (!_max_interface)
return;
625 std::vector<ULONG> good;
626 for (
int i=0; i<_node_list.size(); i++) {
627 ULONG handle = _node_list[i];
628 if (_max_interface->GetINodeByHandle(handle)) {
629 good.push_back(handle);
635 void MaxOptionsDialog::RemoveNode(
int i) {
636 if (i >= 0 && i < _node_list.size()) {
637 for (
int j = i+1; j < _node_list.size(); j++)
638 _node_list[i++] = _node_list[j++];
639 _node_list.pop_back();
643 void MaxOptionsDialog::RemoveNodeByHandle(ULONG INodeHandle) {
644 for (
int i = 0; i < _node_list.size(); i++) {
645 if (_node_list[i] == INodeHandle) {
652 IOResult MaxOptionsDialog::Save(ISave *isave) {
653 isave->BeginChunk(CHUNK_EGG_EXP_OPTIONS);
654 ChunkSave(isave, CHUNK_ANIM_TYPE, _anim_type);
655 ChunkSave(isave, CHUNK_FILENAME, _file_name);
656 ChunkSave(isave, CHUNK_SHORTNAME, _short_name);
657 ChunkSave(isave, CHUNK_SF, _start_frame);
658 ChunkSave(isave, CHUNK_EF, _end_frame);
659 ChunkSave(isave, CHUNK_DBL_SIDED, _double_sided);
660 ChunkSave(isave, CHUNK_EGG_CHECKED, _checked);
661 ChunkSave(isave, CHUNK_ALL_FRAMES, _export_all_frames);
662 ChunkSave(isave, CHUNK_EXPORT_FULL, _export_whole_scene);
664 isave->BeginChunk(CHUNK_NODE_LIST);
665 for (
int i = 0; i < _node_list.size(); i++)
666 ChunkSave(isave, CHUNK_NODE_HANDLE, _node_list[i]);
672 IOResult MaxOptionsDialog::Load(ILoad *iload) {
673 IOResult res = iload->OpenChunk();
675 while (res == IO_OK) {
676 switch(iload->CurChunkID()) {
677 case CHUNK_ANIM_TYPE: _anim_type = (Anim_Type)ChunkLoadInt(iload);
break;
678 case CHUNK_FILENAME: ChunkLoadString(iload, _file_name,
sizeof(_file_name));
break;
679 case CHUNK_SHORTNAME: ChunkLoadString(iload, _short_name,
sizeof(_short_name));
break;
680 case CHUNK_SF: _start_frame = ChunkLoadInt(iload);
break;
681 case CHUNK_EF: _end_frame = ChunkLoadInt(iload);
break;
682 case CHUNK_DBL_SIDED: _double_sided = ChunkLoadBool(iload);
break;
683 case CHUNK_EGG_CHECKED: _checked = ChunkLoadBool(iload);
break;
684 case CHUNK_ALL_FRAMES: _export_all_frames = ChunkLoadBool(iload);
break;
685 case CHUNK_EXPORT_FULL: _export_whole_scene = ChunkLoadBool(iload);
break;
687 case CHUNK_NODE_LIST:
688 res = iload->OpenChunk();
689 while (res == IO_OK) {
690 if (iload->CurChunkID() == CHUNK_NODE_HANDLE) AddNode(ChunkLoadULONG(iload));
692 res = iload->OpenChunk();
697 res = iload->OpenChunk();
700 if (res == IO_END)
return IO_OK;
This encapsulates the user's command-line request to replace existing, incorrect pathnames to models ...