Browse Source

Redone max exporter as a plugin rather than an export menu option

Philip Saltzman 20 years ago
parent
commit
e558fae66f

+ 1 - 1
pandatool/src/maxegg/DllEntry.cpp

@@ -13,7 +13,7 @@
 
 
 #include "MaxEgg.h"
 #include "MaxEgg.h"
 
 
-extern ClassDesc2* GetMaxEggPluginDesc();
+extern ClassDesc* GetMaxEggPluginDesc();
 
 
 HINSTANCE hInstance;
 HINSTANCE hInstance;
 int controlsInit = FALSE;
 int controlsInit = FALSE;

+ 435 - 314
pandatool/src/maxegg/MaxEgg.cpp

@@ -19,46 +19,53 @@
 #include <crtdbg.h>
 #include <crtdbg.h>
 
 
 // Discreet-Generated ID for this app.
 // Discreet-Generated ID for this app.
-#define MaxEggPlugin_CLASS_ID	Class_ID(0x7ac0d6b7, 0x55731ef6)
-// Our version number * 100
-#define MAX_EGG_VERSION_NUMBER 100 
 #define MNEG Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM2
 #define MNEG Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM2
 #define MNEG_GEOMETRY_GENERATION Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM3
 #define MNEG_GEOMETRY_GENERATION Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM3
 
 
+//Disable the forcing int to true or false performance warning
+#pragma warning(disable: 4800)
+
 /* MaxEggPluginClassDesc - A class that describes 3DS Plugin support.
 /* MaxEggPluginClassDesc - A class that describes 3DS Plugin support.
-   This basically says "Yes, I can export files. Use me!"
+   This basically says "Yes, I am a helper object!"
 */
 */
-class MaxEggPluginClassDesc:public ClassDesc2 
+class MaxEggPluginClassDesc : public ClassDesc 
 {
 {
 public:
 public:
-  int IsPublic() { return TRUE; }
-  void *Create(BOOL loading = FALSE) { return new MaxEggPlugin(); }
-  const TCHAR *ClassName() { return GetString(IDS_CLASS_NAME); }
-  SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
-  Class_ID ClassID() { return MaxEggPlugin_CLASS_ID; }
-  const TCHAR *Category() { return GetString(IDS_CATEGORY); }
-
+  int          IsPublic() { return TRUE; }
+  void         *Create(BOOL loading = FALSE) { return new MaxEggPlugin(); }
+  const TCHAR  *ClassName() { return GetString(IDS_CLASS_NAME); }
+  SClass_ID    SuperClassID() { return HELPER_CLASS_ID; }
+  Class_ID     ClassID() { return MaxEggPlugin_CLASS_ID; }
+  const TCHAR  *Category() { return GetString(IDS_CATEGORY); }
   // returns fixed parsable name (scripter-visible name)
   // returns fixed parsable name (scripter-visible name)
   const TCHAR *InternalName() { return _T("MaxEggPlugin"); }
   const TCHAR *InternalName() { return _T("MaxEggPlugin"); }
-  // returns owning module handle
-  HINSTANCE HInstance() { return hInstance; }
 };
 };
 
 
-// Our static instance of the above class
+// Our private global instance of the above class
 static MaxEggPluginClassDesc MaxEggPluginDesc;
 static MaxEggPluginClassDesc MaxEggPluginDesc;
 
 
-// The function that I believe Max calls, when looking for information as
-// to what this plugin does.
-ClassDesc2* GetMaxEggPluginDesc() { return &MaxEggPluginDesc; }
+// Called by LibClassDesc to find out what the plugin is
+ClassDesc* GetMaxEggPluginDesc() { return &MaxEggPluginDesc; }
+
+// Initialize class-static variables
+Mesh MaxEggPlugin::mesh;
+short MaxEggPlugin::meshBuilt=0;
+HWND MaxEggPlugin::hMaxEggParams = NULL;
+IObjParam *MaxEggPlugin::iObjParams;
 
 
 /* MaxEggPluginOptionsDlgProc() - This is the callback function for the
 /* MaxEggPluginOptionsDlgProc() - This is the callback function for the
    dialog box that appears at the beginning of the conversion process.
    dialog box that appears at the beginning of the conversion process.
  */
  */
+
 BOOL CALLBACK MaxEggPluginOptionsDlgProc( HWND hWnd, UINT message, 
 BOOL CALLBACK MaxEggPluginOptionsDlgProc( HWND hWnd, UINT message, 
 					  WPARAM wParam, LPARAM lParam ) 
 					  WPARAM wParam, LPARAM lParam ) 
 {
 {
+  MaxEggExpOptions *tempEgg;
+  int sel, res;
+
   //We pass in our plugin through the lParam variable. Let's convert it back.
   //We pass in our plugin through the lParam variable. Let's convert it back.
   MaxEggPlugin *imp = (MaxEggPlugin*)GetWindowLongPtr(hWnd,GWLP_USERDATA); 
   MaxEggPlugin *imp = (MaxEggPlugin*)GetWindowLongPtr(hWnd,GWLP_USERDATA); 
+  if ( !imp && message != WM_INITDIALOG ) return FALSE;
 
 
   switch(message) 
   switch(message) 
     {
     {
@@ -66,345 +73,459 @@ BOOL CALLBACK MaxEggPluginOptionsDlgProc( HWND hWnd, UINT message,
     case WM_INITDIALOG:
     case WM_INITDIALOG:
       // this line is very necessary to pass the plugin as the lParam
       // this line is very necessary to pass the plugin as the lParam
       SetWindowLongPtr(hWnd,GWLP_USERDATA,lParam); 
       SetWindowLongPtr(hWnd,GWLP_USERDATA,lParam); 
-      CenterWindow(hWnd,GetParent(hWnd));
-      return TRUE;
-      break;
-    // Closing the window is equivalent to canceling the export
-    case WM_CLOSE:
-      imp->confirmExport = false;
-      EndDialog(hWnd, 0);
-      return TRUE;
-      break;
-    // If we get here, this means thatone of our controls was modified.
+      SetDlgFont( hWnd, imp->iObjParams->GetAppHFont() );
+      MaxEggPlugin::hMaxEggParams = hWnd;
+      return TRUE; break;
+    
+    case WM_MOUSEACTIVATE:
+      imp->iObjParams->RealizeParamPanel();
+      return TRUE; break;
+
+    case WM_LBUTTONDOWN:
+    case WM_LBUTTONUP:
+    case WM_MOUSEMOVE:
+      imp->iObjParams->RollupMouseMessage(hWnd,message,wParam,lParam);
+      return TRUE; break;
+     
+    // A control was modified
     case WM_COMMAND:
     case WM_COMMAND:
-      //The control in question will be found in the lower word of the wParam 
-      // long.
-      switch( LOWORD(wParam) ) 
-	{
-	// The checkbox for toggling whether this model has animations
-	case IDC_ANIMATION:
-	  // sets the plugin's animation parameter
-	  imp->animation = !imp->animation;
-	  CheckDlgButton( hWnd, IDC_ANIMATION, 
-			  imp->animation ? BST_CHECKED : BST_UNCHECKED );
-	  
-	  // Enables/disables the animation options depending on how the 
-	  // animation checkbox is checked
-	  EnableWindow(GetDlgItem(hWnd, IDC_MODEL), imp->animation);
-	  EnableWindow(GetDlgItem(hWnd, IDC_CHAN), imp->animation);
-	  EnableWindow(GetDlgItem(hWnd, IDC_POSE), imp->animation);
-	  EnableWindow(GetDlgItem(hWnd, IDC_STROBE), imp->animation);
-	  EnableWindow(GetDlgItem(hWnd, IDC_BOTH), imp->animation);
-	  EnableWindow(GetDlgItem(hWnd, IDC_SF), imp->animation);
-	  EnableWindow(GetDlgItem(hWnd, IDC_EF), imp->animation);
-
-	  // if this is the first time the animation checkbox has been checked,
-	  // then there is no animation type set, so set the animation type to
-	  // "model" 
-	  if (imp->anim_type == MaxEggPlugin::AT_none) {
-	    CheckDlgButton( hWnd, IDC_MODEL, BST_CHECKED );
-	    imp->anim_type = MaxEggPlugin::AT_model;
-	  }
-	  return TRUE;
-	  break;
-
-	// The radio buttons for what type of animation will exported
-	// The animation type is set by these buttons
-	case IDC_MODEL:
-	  imp->anim_type = MaxEggPlugin::AT_model;
-	  break;
-	case IDC_CHAN:
-	  imp->anim_type = MaxEggPlugin::AT_chan;
-	  break;
-	case IDC_POSE:
-	  imp->anim_type = MaxEggPlugin::AT_pose;
-	  break;
-	case IDC_STROBE:
-	  imp->anim_type = MaxEggPlugin::AT_strobe;
-	  break;
-	case IDC_BOTH:
-	  imp->anim_type = MaxEggPlugin::AT_both;
-	  break;
-	  
-	//The checkbox that toggles wether to make a .BAM file or not.
-	case IDC_MAKE_BAM:
-	  imp->makeBam = !imp->makeBam; 
-	  CheckDlgButton( hWnd, IDC_MAKE_BAM,
-			  imp->makeBam ? BST_CHECKED : BST_UNCHECKED );
-	  return TRUE;
-	  break;
-	// Ckicking the cancel button obviously cancels the export
-	case IDC_CANCEL:
-	  imp->confirmExport = false;
-	  EndDialog(hWnd, 0);
-	  return TRUE;
-	  break;
-	// Clicking the done button is the only way to continue with the export
-	case IDC_DONE:
-	  imp->confirmExport = true;
-	  EndDialog(hWnd, 0);
-	  return TRUE;
-	  break;
-	}
-      break;
+      //The modified control is found in the lower word of the wParam long.
+      switch( LOWORD(wParam) ) {
+        case IDC_OVERWRITE_CHECK:
+          imp->autoOverwrite = 
+            (IsDlgButtonChecked(hWnd, IDC_OVERWRITE_CHECK) == BST_CHECKED);
+          return TRUE; break;
+        case IDC_PVIEW_CHECK:
+          imp->pview = 
+            (IsDlgButtonChecked(hWnd, IDC_PVIEW_CHECK) == BST_CHECKED);
+          return TRUE; break;
+        case IDC_LOGGING:
+          imp->logOutput = 
+            (IsDlgButtonChecked(hWnd, IDC_LOGGING) == BST_CHECKED);
+          return TRUE; break;
+        case IDC_ADD_EGG:
+          tempEgg = new MaxEggExpOptions();
+          tempEgg->maxInterface = imp->iObjParams;
+          tempEgg->SetAnimRange();
+          res = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EGG_DETAILS), 
+                               hWnd, MaxEggExpOptionsProc, (LPARAM)tempEgg);
+          tempEgg->maxInterface = NULL;
+          if (res == TRUE) {
+            imp->SaveCheckState();
+            imp->AddEgg(tempEgg);
+            imp->UpdateUI();
+          }
+          else delete tempEgg;
+          return TRUE; break;
+        case IDC_EDIT_EGG:
+          sel = ListView_GetSelectionMark(GetDlgItem(hWnd, IDC_LIST_EGGS));
+          if (sel != -1) {
+            MaxEggExpOptions *tempEgg = imp->GetEgg(sel);
+            if (tempEgg) {
+              tempEgg->maxInterface = imp->iObjParams;
+              tempEgg->SetAnimRange();
+              tempEgg->CullBadNodes();
+              DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EGG_DETAILS), 
+                             hWnd, MaxEggExpOptionsProc, (LPARAM)tempEgg);
+              tempEgg->maxInterface = NULL;
+            }
+            imp->SaveCheckState();
+            imp->UpdateUI();
+          }
+          return TRUE; break;
+        case IDC_REMOVE_EGG:
+          sel = ListView_GetSelectionMark(GetDlgItem(hWnd, IDC_LIST_EGGS));
+          if (sel != -1) {
+            imp->SaveCheckState();
+            imp->RemoveEgg(sel);
+            imp->UpdateUI();
+          }
+          return TRUE; break;
+        case IDC_EXPORT:
+          imp->DoExport();
+          return TRUE; break;
+      }
     }
     }
   return FALSE;
   return FALSE;
 }
 }
 
 
 /* MaxEggPlugin() - Uninteresting constructor.
 /* MaxEggPlugin() - Uninteresting constructor.
  */
  */
-MaxEggPlugin::MaxEggPlugin() 
+MaxEggPlugin::MaxEggPlugin() :
+autoOverwrite(false), pview(true), logOutput(false), numEggs(0), maxEggs(5)
 {
 {
-  makeBam = false;
-  animation = false;
-  anim_type = AT_none;
+  eggList = new MaxEggExpOptions*[maxEggs];
+  BuildMesh();
 }
 }
 
 
-/* ~MaxEggPlugin() - Uninteresting destructor.
- */
-MaxEggPlugin::~MaxEggPlugin() 
-{
+MaxEggPlugin::~MaxEggPlugin() {
+  for (int i = 0; i < numEggs; i++) delete eggList[i];
+  delete [] eggList;
 }
 }
 
 
-/* ExtCount() - Returns the number of extensions this exporter produces. 
-   That's only one, .EGG files.
- */
-int MaxEggPlugin::ExtCount() 
-{
-  return 1;
+void MaxEggPlugin::AddEgg(MaxEggExpOptions *newEgg) {
+  if (numEggs >= maxEggs) {
+    MaxEggExpOptions **newList;
+    maxEggs *= 2;
+    newList = new MaxEggExpOptions*[maxEggs];
+    for (int i = 0; i < numEggs; i++) newList[i] = eggList[i];
+    delete [] eggList;
+    eggList = newList;
+  }
+
+  eggList[numEggs++] = newEgg;
 }
 }
 
 
-/* Ext(int) - Returns the nth extension. Since there's only one, it always 
-   returns "egg"
-*/
-const TCHAR *MaxEggPlugin::Ext(int n) 
+void MaxEggPlugin::RemoveEgg(int i) {
+  if (i >= 0 && i < numEggs) {
+    delete eggList[i];
+    for (int j = i+1; j < numEggs;) eggList[i++] = eggList[j++];
+    --numEggs;
+  }
+}
+
+void MaxEggPlugin::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev )
 {
 {
-  return _T("egg");
+  iObjParams = ip;
+  if ( !hMaxEggParams ) {
+    hMaxEggParams = ip->AddRollupPage(hInstance, 
+		                                  MAKEINTRESOURCE(IDD_PANEL),
+			                                MaxEggPluginOptionsDlgProc, 
+			                                GetString(IDS_PARAMS), 
+			                                (LPARAM)this );
+    ip->RegisterDlgWnd(hMaxEggParams);
+  } else {
+    SetWindowLongPtr( hMaxEggParams, GWLP_USERDATA, (LPARAM)this );
+  }
+
+  UpdateUI();
 }
 }
 
 
-/* LongDesc() - A long description of the files we export.  Curiously, this 
-   isn't for the nth extension, rather a one-description-fits-all thing.
-*/
-const TCHAR *MaxEggPlugin::LongDesc() 
+void MaxEggPlugin::EndEditParams( IObjParam *ip, ULONG flags,Animatable *prev)
 {
 {
-  return _T("Panda3D .egg file");
+  SaveCheckState();
+  if ( flags&END_EDIT_REMOVEUI ) {		
+    ip->UnRegisterDlgWnd(hMaxEggParams);
+    ip->DeleteRollupPage(hMaxEggParams);
+    hMaxEggParams = NULL;				
+  } else {
+    SetWindowLongPtr( hMaxEggParams, GWLP_USERDATA, NULL );
+  }
+  iObjParams = NULL;
 }
 }
 
 
-/**
- * A short description of the files we export.  Curiously, this isn't
- * for the nth extension, rather a one-description-fits-all thing.
- */
-const TCHAR *MaxEggPlugin::ShortDesc() 
-{			
-  return _T("Panda3D");
+void MaxEggPlugin::SaveCheckState() {
+  if (!hMaxEggParams) return;
+  HWND lv = GetDlgItem(hMaxEggParams, IDC_LIST_EGGS);
+  for (int i = 0; i < numEggs; i++)
+    eggList[i]->checked = ListView_GetCheckState(lv, i);
 }
 }
 
 
-/**
- * Who wrote this.
- */
-const TCHAR *MaxEggPlugin::AuthorName() 
-{			
-  return _T("Steven \"Sauce\" Osman");
+void MaxEggPlugin::UpdateUI() {
+  HWND lv = GetDlgItem(hMaxEggParams, IDC_LIST_EGGS);
+  LV_COLUMN pCol;
+
+  if (ListView_GetColumnWidth(lv, 1) <= 0 || ListView_GetColumnWidth(lv, 1) > 10000) {
+    //Columns have not been setup, so initialize the control
+    ListView_SetExtendedListViewStyleEx(lv, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT, 
+                                            LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT);
+
+    pCol.fmt = LVCFMT_LEFT;
+    pCol.cx = 96;
+    pCol.pszText = "Filename";
+    pCol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
+    pCol.iSubItem = 0;
+    ListView_InsertColumn(lv, 0, &pCol);
+
+    pCol.cx = 44;
+    pCol.pszText = "Type";
+    ListView_InsertColumn(lv, 1, &pCol);
+  }
+
+  //Add the eggs to the list
+  ListView_DeleteAllItems(lv);
+  LV_ITEM Item;
+  Item.mask = LVIF_TEXT;
+
+  for (int i = 0; i < numEggs; i++) {
+    Item.iItem = i;
+    Item.iSubItem = 0;
+    Item.pszText = eggList[i]->shortName;
+    ListView_InsertItem(lv, &Item);
+    Item.iSubItem = 1;
+    switch(eggList[i]->anim_type) {
+      case MaxEggExpOptions::Anim_Type::AT_chan: Item.pszText = "Animation"; break;
+      case MaxEggExpOptions::Anim_Type::AT_both: Item.pszText = "Both"; break;
+      case MaxEggExpOptions::Anim_Type::AT_pose: Item.pszText = "Pose"; break;
+      case MaxEggExpOptions::Anim_Type::AT_model:
+      default:                                   Item.pszText = "Model"; break;
+    }
+    ListView_SetItem(lv, &Item);
+    ListView_SetCheckState(lv, i, eggList[i]->checked);
+  }
+
+  // Set the "Overwrite Existing Files" and "Pview" checkboxes
+  CheckDlgButton(hMaxEggParams, IDC_OVERWRITE_CHECK, 
+                 autoOverwrite ? BST_CHECKED : BST_UNCHECKED);
+  CheckDlgButton(hMaxEggParams, IDC_PVIEW_CHECK, 
+                 pview ? BST_CHECKED : BST_UNCHECKED);
+  CheckDlgButton(hMaxEggParams, IDC_LOGGING, 
+                 logOutput ? BST_CHECKED : BST_UNCHECKED);
 }
 }
 
 
-/**
- * Who owns this.
- */
-const TCHAR *MaxEggPlugin::CopyrightMessage() 
+void MaxEggPlugin::DoExport() {
+  int good = 0, bad = 0;
+  char msg[2048];
+  strcpy(msg, "The following exports failed:\n");
+
+  SaveCheckState();
+
+  for (int i = 0; i < numEggs; i++) {
+    if (eggList[i]->checked) {
+      if (!eggList[i]->DoExport(iObjParams, autoOverwrite, logOutput)) {
+        ++bad;
+        strcat(msg, eggList[i]->shortName);
+        strcat(msg, ".egg\n");
+      }
+      else ++good;
+    }
+  }
+
+  if (bad == 0)
+    strcpy(msg, "All eggs exported successfully");
+  else
+    strcat(msg, "\nAll other eggs exported successfully");
+
+  UINT mask = MB_OK;
+  if (bad > 0) mask |= MB_ICONEXCLAMATION;
+  else mask |= MB_ICONINFORMATION;
+
+  MessageBox(hMaxEggParams, msg, "Panda3D Export results", mask);
+
+  if (pview && good > 0) {
+    char pviewMsg[2048];
+    int pviewSkipped = 0;
+    strcpy(pviewMsg, "The following eggs were skipped since animations cannot be pviewed:\n");
+    for (i = 0; i < numEggs; i++)
+      if (eggList[i]->checked && eggList[i]->successful)
+        if (eggList[i]->anim_type != MaxEggExpOptions::AT_chan) {
+          char buf[1024];
+          PROCESS_INFORMATION pi;
+          STARTUPINFO si;
+
+          memset(&si,0,sizeof(si));
+          si.cb= sizeof(si);
+
+          sprintf(buf, "Pview %s.egg?", eggList[i]->shortName);
+          if (MessageBox(hMaxEggParams, buf, "Panda3D exporter", MB_YESNO | MB_ICONQUESTION) == IDYES) {
+            char cmdLine[2048];
+            sprintf(cmdLine, "pview \"%s\"", eggList[i]->filename);
+            CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, CREATE_NEW_CONSOLE,
+                          NULL, NULL, &si, &pi);
+          }
+        }
+        else {
+          pviewSkipped++;    
+          strcat(pviewMsg, eggList[i]->shortName);
+          strcat(pviewMsg, ".egg\n");
+        }
+    
+    if (pviewSkipped > 0) {
+      strcat(pviewMsg, "\nExport animations using the \"both\" option to pview them.");
+      MessageBox(hMaxEggParams, pviewMsg, "Panda3D exporter", MB_OK | MB_ICONINFORMATION);
+    }
+  }
+
+}
+
+
+void MaxEggPlugin::BuildMesh()
+{
+  int i;
+  if(meshBuilt) return;
+  
+  mesh.setNumVerts(252);
+  mesh.setNumFaces(84);
+  mesh.setSmoothFlags(0);
+  mesh.setNumTVerts (0);
+  mesh.setNumTVFaces (0);
+  
+  for (i=0; i<252; i++) 
+    mesh.setVert(i, meshVerts[i][0]*10, meshVerts[i][1]*10, meshVerts[i][2]*10);
+  for (i=0; i<84; i++) {
+    mesh.faces[i].setEdgeVisFlags(1, 1, 0);
+    mesh.faces[i].setSmGroup(0);
+    mesh.faces[i].setVerts(i*3, i*3+1, i*3+2);
+  }
+
+  mesh.InvalidateGeomCache();
+  mesh.BuildStripsAndEdges();
+  
+  meshBuilt = TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+// The creation callback - sets the initial position of the helper in the scene.
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class MaxEggPluginCreateMouseCallBack: public CreateMouseCallBack 
+{
+public:
+  int proc( ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat );
+};
+
+int MaxEggPluginCreateMouseCallBack::proc(ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat ) 
 {	
 {	
-  return _T("Copyright (C) 2003 Carnegie Mellon University, Entertainment Technology Center");
+  if (msg==MOUSE_POINT||msg==MOUSE_MOVE) {
+    switch(point) {
+    case 0:
+      mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
+      break;
+    case 1:
+      mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
+      if (msg==MOUSE_POINT) return CREATE_STOP;
+      break;			
+    }
+  } else if (msg == MOUSE_ABORT) {		
+    return CREATE_ABORT; 
+  }
+  return CREATE_CONTINUE;
 }
 }
 
 
-/**
- * Who cares?
- */
-const TCHAR *MaxEggPlugin::OtherMessage1() 
+static MaxEggPluginCreateMouseCallBack MaxEggCreateMouseCB;
+
+CreateMouseCallBack* MaxEggPlugin::GetCreateMouseCallBack() 
+{ return &MaxEggCreateMouseCB; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+//Boilerplate functions for dealing with the display of the plugin
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void MaxEggPlugin::GetMat(TimeValue t, INode* inode, ViewExp* vpt, Matrix3& tm) 
 {
 {
-  return _T("Modified by Ken Strickland");
+  tm = inode->GetObjectTM(t);
+  tm.NoScale();
+  float scaleFactor = vpt->NonScalingObjectSize()*vpt->GetVPWorldWidth(tm.GetTrans())/(float)360.0;
+  tm.Scale(Point3(scaleFactor,scaleFactor,scaleFactor));
 }
 }
 
 
-/**
- * Who knows?
- */
-const TCHAR *MaxEggPlugin::OtherMessage2() 
-{		
-  return _T("Who's got the funk? We do!");
+void MaxEggPlugin::GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel )
+{
+  box = mesh.getBoundingBox(tm);
 }
 }
 
 
-/**
- * Returns version * 100.  defined in MAX_EGG_VERSION_NUMBER
- */
-unsigned int MaxEggPlugin::Version() 
-{				
-  return MAX_EGG_VERSION_NUMBER;
+void MaxEggPlugin::GetLocalBoundBox(TimeValue t, INode* inode, ViewExp* vpt, Box3& box ) 
+{
+  Matrix3 m = inode->GetObjectTM(t);
+  Point3 pt;
+  Point3 q[4];
+  float scaleFactor = vpt->GetVPWorldWidth(m.GetTrans())/360.0f;
+  box = mesh.getBoundingBox();
+  box.Scale(scaleFactor);
 }
 }
 
 
-/**
- * No about dialog box right now.
- */
-void MaxEggPlugin::ShowAbout(HWND hWnd) 
-{			
+void MaxEggPlugin::GetWorldBoundBox(TimeValue t, INode* inode, ViewExp* vpt, Box3& box )
+{
+  int i, nv; Matrix3 tm; Point3 pt;
+  GetMat(t,inode,vpt,tm);
+  nv = mesh.getNumVerts();
+  box.Init();
+  for (i=0; i<nv; i++) 
+    box += tm*mesh.getVert(i);
 }
 }
 
 
-/**
- * We'll support all options by default.
- */
-BOOL MaxEggPlugin::SupportsOptions(int ext, DWORD options) 
+int MaxEggPlugin::HitTest(TimeValue t, INode *inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt) 
 {
 {
-  // According to the maxsdk help, there is only one option which is 
-  // SCENE_EXPORT_SELECTED.  This should return false until the code
-  // for converting only selected objects to egg is written
-  
-  return false;
+  HitRegion hitRegion;
+  DWORD	savedLimits;
+  Matrix3 m;
+  GraphicsWindow *gw = vpt->getGW();	
+  Material *mtl = gw->getMaterial();
+  MakeHitRegion(hitRegion,type,crossing,4,p);	
+  gw->setRndLimits(((savedLimits = gw->getRndLimits()) | GW_PICK) & ~GW_ILLUM);
+  GetMat(t,inode,vpt,m);
+  gw->setTransform(m);
+  gw->clearHitCode();
+  if (mesh.select( gw, mtl, &hitRegion, flags & HIT_ABORTONHIT )) 
+    return TRUE;
+  return FALSE;
 }
 }
 
 
-/*!
- * This method creates and triggers the exporter.  Basically it takes the
- * user's options and builds a command-line parameter list from it.
- * It then invokes the converter pretending it was invoked as a standalone
- * program.  BIG WARNING:  The converter stuff often does exit() if the
- * command line arguments are displeasing.
- */
-int MaxEggPlugin::DoExport(const TCHAR *ptcOutputFilename,ExpInterface *ei,
-			   Interface *pMaxInterface,
-			   BOOL suppressPrompts, DWORD options) 
+int MaxEggPlugin::Display(TimeValue t, INode* inode, ViewExp *vpt, int flags) 
 {
 {
-  MaxToEgg *pmteConverter = new MaxToEgg();
-  char *apcParameters[64];
-  char acOutputFilename[MAX_PATH];
-  int iParameterCount=0;
-
-  //Initialize our global error logger
-  Logger::globalLoggingInstance = new Logger( Logger::PIPE_TO_FILE, 
-					      "MaxEggLog.txt" );
-
-  //Set the various logging levels for the subsystems.
-  Logger::SetOneErrorMask( ME, Logger::SAT_ALL );
-  Logger::SetOneErrorMask( MTE, Logger::SAT_NULL_ERROR | 
-			   Logger::SAT_CRITICAL_ERROR | 
-			   Logger::SAT_PARAMETER_INVALID_ERROR | 
-			   Logger::SAT_OTHER_ERROR | Logger::SAT_HIGH_LEVEL );
-  Logger::SetOneErrorMask( MTEC, Logger::SAT_NULL_ERROR | 
-			   Logger::SAT_CRITICAL_ERROR |
-			   Logger::SAT_PARAMETER_INVALID_ERROR | 
-			   Logger::SAT_OTHER_ERROR | Logger::SAT_HIGH_LEVEL |
-			   Logger::SAT_MEDIUM_LEVEL | Logger::SAT_LOW_LEVEL |
-			   Logger::SAT_DEBUG_SPAM_LEVEL );
-  Logger::SetOneErrorMask( MNEG, Logger::SAT_NULL_ERROR |
-			   Logger::SAT_CRITICAL_ERROR |
-			   Logger::SAT_PARAMETER_INVALID_ERROR | 
-			   Logger::SAT_OTHER_ERROR | Logger::SAT_HIGH_LEVEL |
-			   Logger::SAT_MEDIUM_LEVEL | Logger::SAT_LOW_LEVEL );
-  Logger::SetOneErrorMask( MNEG_GEOMETRY_GENERATION, Logger::SAT_NULL_ERROR |
-			   Logger::SAT_CRITICAL_ERROR |
-			   Logger::SAT_PARAMETER_INVALID_ERROR | 
-			   Logger::SAT_OTHER_ERROR | Logger::SAT_HIGH_LEVEL |
-			   Logger::SAT_MEDIUM_LEVEL | Logger::SAT_LOW_LEVEL );
-  Logger::SetOneErrorMask( LLOGGING, Logger::SAT_ALL );
-	
-  Logger::FunctionEntry( "MaxEggPlugin::DoExport" );
-
-  // Copy the output filename so that it can be modified if necessary
-  strncpy(acOutputFilename,ptcOutputFilename,MAX_PATH-1);
-  acOutputFilename[MAX_PATH-1]=0;
-
-  // Panda reaaaaaaaaly wants the extension to be in lower case.
-  // So if we see a .egg at the end, lower case it.
-  if ((strlen(acOutputFilename)>4) &&
-      (stricmp(acOutputFilename+strlen(acOutputFilename)-4,".egg")==0)) {
-    strlwr(acOutputFilename+strlen(acOutputFilename)-4);
-  }
-  
-  pmteConverter->SetMaxInterface(pMaxInterface);
-  
-  // Set the command-line arguments
-  // ARGV[0] = program name
-  apcParameters[iParameterCount++]="MaxEggPlugin";
+  Matrix3 m;
+  GraphicsWindow *gw = vpt->getGW();
+  Material *mtl = gw->getMaterial();
   
   
-  confirmExport = false;
-  if(!suppressPrompts)
-    // Displays the dialog box that retrieves the export options
-    DialogBoxParam(hInstance, 
-		   MAKEINTRESOURCE(IDD_PANEL), 
-		   pMaxInterface->GetMAXHWnd(), 
-		   MaxEggPluginOptionsDlgProc, (LPARAM)this);
-  
-  // Stops the export if the user chooses to cancel
-  if (!confirmExport)
-    return true;
-  
-  // ARGV[1] = Input file
-  // Use a bogus input filename that exists
-  apcParameters[iParameterCount++]="nul.max";
-  
-  // ARGV[2,3] = Output file
-  // Pass in the output filename
-  // Output file has to be passed in with the -o parameter in order to be able 
-  // to overwrite an existing file
-  apcParameters[iParameterCount++]="-o";
-  apcParameters[iParameterCount++]=acOutputFilename;
-  
-  // ARGV[4,5] = Animation options (if animation is checked)
-  // Check if there is an animation to be saved and what type of animation it
-  // will be saved as.  Then set the animation option.
-  if (animation) {
-    apcParameters[iParameterCount++]="-a";
-    switch (anim_type) 
-      {
-      case AT_model:
-	apcParameters[iParameterCount++]="model";
-	break;
-      case AT_chan:
-	apcParameters[iParameterCount++]="chan";
-	break;
-      case AT_pose:
-	apcParameters[iParameterCount++]="pose";
-	break;
-      case AT_strobe:
-	apcParameters[iParameterCount++]="strobe";
-	break;
-      case AT_both:
-	apcParameters[iParameterCount++]="both";
-	break;
-      default:
-	apcParameters[iParameterCount++]="none";
-	break;
-      }
-  }
-  apcParameters[iParameterCount]=0;
+  GetMat(t,inode,vpt,m);
+  gw->setTransform(m);
+  DWORD rlim = gw->getRndLimits();
+  gw->setRndLimits(GW_WIREFRAME|GW_BACKCULL);
+  if (inode->Selected()) 
+    gw->setColor( LINE_COLOR, GetSelColor());
+  else if(!inode->IsFrozen())
+    gw->setColor( LINE_COLOR, GetUIColor(COLOR_TAPE_OBJ));
+  mesh.render( gw, mtl, NULL, COMP_ALL);
+  return 0;
+}
 
 
-  // Parse the command line and run the converter
-  pmteConverter->parse_command_line(iParameterCount, apcParameters);
-  pmteConverter->Run();
-  
-  bool bSuccessful = pmteConverter->IsSuccessful();
+RefResult MaxEggPlugin::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message ) 
+{
+  UpdateUI();
+  return REF_SUCCEED;
+}
+
+ObjectState MaxEggPlugin::Eval(TimeValue time)
+{
+  return ObjectState(this);
+}
+
+Interval MaxEggPlugin::ObjectValidity(TimeValue t) 
+{
+  Interval ivalid;
+  ivalid.SetInfinite();
+  return ivalid;	
+}
+
+RefTargetHandle MaxEggPlugin::Clone(RemapDir& remap) 
+{
+  MaxEggPlugin* newob = new MaxEggPlugin();
+  return(newob);
+}
+
+///////////////////////////////////////////////////////////
+// Loading and saving the plugin
+///////////////////////////////////////////////////////////
+
+IOResult MaxEggPlugin::Save(ISave *isave) {
+  SaveCheckState();
+  for (int i = 0; i < numEggs; i++)
+    eggList[i]->Save(isave);
+  ChunkSave(isave,   CHUNK_OVERWRITE_FLAG,  autoOverwrite);
+  ChunkSave(isave,   CHUNK_PVIEW_FLAG,      pview);
+  ChunkSave(isave,   CHUNK_LOG_OUTPUT,      logOutput);
+
+  return IO_OK;
+}
+
+IOResult MaxEggPlugin::Load(ILoad *iload) {
+  IOResult res = iload->OpenChunk();
+  MaxEggExpOptions *temp;
   
   
-  // Display a message box telling that the export is completed
-  if (bSuccessful)
-    MessageBox(pMaxInterface->GetMAXHWnd(), 
-	       "Export to EGG completed successfully.", "Panda3D Converter",
-	       MB_OK);
-  else
-    MessageBox(pMaxInterface->GetMAXHWnd(), "Export unsuccessful.", 
-	       "Panda3D Converter", MB_OK);
-		Logger::Log(MTEC, Logger::SAT_MEDIUM_LEVEL, "After finished mbox");
-
-  // This was put in try block because originally deleting pmteConverter 
-  // would throw an exception.  That no longer happens, but this is still
-  // here for good measure
-  try {
-		Logger::Log(MTEC, Logger::SAT_MEDIUM_LEVEL, "before deleting pmteconverter");
-    delete pmteConverter; 
-  } catch (...) {
-		Logger::Log(MTEC, Logger::SAT_MEDIUM_LEVEL, "before error message window");
-    MessageBox(pMaxInterface->GetMAXHWnd(), "I just got an unknown exception.",
-	       "Panda3D Converter", MB_OK);
+  while (res == IO_OK) {
+    switch(iload->CurChunkID()) {
+    case CHUNK_OVERWRITE_FLAG: autoOverwrite = ChunkLoadBool(iload); break;
+    case CHUNK_PVIEW_FLAG:     pview = ChunkLoadBool(iload); break;
+    case CHUNK_LOG_OUTPUT:     logOutput = ChunkLoadBool(iload); break;
+    case CHUNK_EGG_EXP_OPTIONS:
+      temp = new MaxEggExpOptions();
+      temp->Load(iload);
+      AddEgg(temp);
+      break;
+    }
+    iload->CloseChunk();
+    res = iload->OpenChunk();
   }
   }
-		Logger::Log(MTEC, Logger::SAT_MEDIUM_LEVEL, "before logger function exit");
-  Logger::FunctionExit();
-  //Free the error logger
-  if ( Logger::globalLoggingInstance )
-    delete Logger::globalLoggingInstance;
  
  
-  return bSuccessful;
+  return IO_OK;
 }
 }

+ 322 - 38
pandatool/src/maxegg/MaxEgg.h

@@ -19,12 +19,14 @@
 
 
 //Includes & Definitions
 //Includes & Definitions
 #include "MaxToEgg.h"
 #include "MaxToEgg.h"
+#include "MaxEggExpOptions.h"
 #include "windef.h"
 #include "windef.h"
 
 
 /* Error-Reporting Includes
 /* Error-Reporting Includes
  */
  */
 #include "Logger.h"
 #include "Logger.h"
 #define ME Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM6
 #define ME Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM6
+#define MaxEggPlugin_CLASS_ID	Class_ID(0x7ac0d6b7, 0x55731ef6)
 
 
 /* Externed Globals
 /* Externed Globals
  */
  */
@@ -42,51 +44,333 @@ extern TCHAR *GetString(int id);
    a suitable interface to Max files can be connected from a standalone
    a suitable interface to Max files can be connected from a standalone
    program instead of a plugin.
    program instead of a plugin.
 */
 */
-class MaxEggPlugin : public SceneExport 
+class MaxEggPlugin : public HelperObject 
 {
 {
+  MaxEggExpOptions **eggList;
+  int numEggs;
+  int maxEggs;
+
  public:
  public:
-  static HWND hParams;
-  bool confirmExport;
-  bool makeBam;
-  bool animation;
-  enum Anim_Type {
-    AT_none,
-    AT_model,
-    AT_chan,
-    AT_pose,
-    AT_strobe,
-    AT_both
-  };
-  Anim_Type anim_type;
+  bool autoOverwrite;
+  bool pview;
+  bool logOutput;
   
   
-  // Number of extensions supported
-  int ExtCount();
-  // Extension #n (i.e. "3DS")
-  const TCHAR *Ext(int n);					
-  // Long ASCII description (i.e. "Autodesk 3D Studio File")
-  const TCHAR *LongDesc();
-  // Short ASCII description (i.e. "3D Studio")
-  const TCHAR *ShortDesc();
-  // ASCII Author name
-  const TCHAR *AuthorName();
-  // ASCII Copyright message
-  const TCHAR *CopyrightMessage();
-  // Other message #1
-  const TCHAR *OtherMessage1();
-  // Other message #2
-  const TCHAR *OtherMessage2();
-  // Version number * 100 (i.e. v3.01 = 301)
-  unsigned int Version();
-  // Show DLL's "About..." box
-  void ShowAbout(HWND hWnd);
-
-  BOOL SupportsOptions(int ext, DWORD options);
-  int DoExport(const TCHAR *name,ExpInterface *ei,
-	       Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);
+  // Class vars
+  static Mesh mesh;           // This plugin generates no geometry, this mesh is not passed on to 3D Studio.
+  static short meshBuilt;
+  static HWND hMaxEggParams;
+  static IObjParam *iObjParams;
 
 
   //Constructor/Destructor
   //Constructor/Destructor
   MaxEggPlugin();
   MaxEggPlugin();
   virtual ~MaxEggPlugin();
   virtual ~MaxEggPlugin();
+
+  //Other class Methods
+  void DoExport();
+  void UpdateUI();
+  void SaveCheckState();
+  void BuildMesh();
+
+  void AddEgg(MaxEggExpOptions *newEgg);
+  void RemoveEgg(int i);
+  MaxEggExpOptions *GetEgg(int i) { return (i >= 0 && i < numEggs) ? eggList[i] : NULL; }
+
+  // Required implimented virtual methods:
+  // inherited virtual methods for Reference-management
+  RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message );
+  void GetMat(TimeValue t, INode* inod, ViewExp *vpt, Matrix3& mat);
+
+  // From BaseObject
+  int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt);
+  int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags);
+  CreateMouseCallBack* GetCreateMouseCallBack();
+  void BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev);
+  void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next);
+  TCHAR *GetObjectName() { return GetString(IDS_LIBDESCRIPTION); }
+
+  // From Object
+  ObjectState Eval(TimeValue time);
+  void InitNodeName(TSTR& s) { s = GetString(IDS_CLASS_NAME); }
+  Interval ObjectValidity(TimeValue time);
+  void Invalidate();
+  int DoOwnSelectHilite() { return 1; }
+
+  // From GeomObject
+  int IntersectRay(TimeValue t, Ray& r, float& at) { return 0; }
+  void GetWorldBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box );
+  void GetLocalBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box );
+  void GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel );
+
+  // Animatable methods
+  void DeleteThis() { delete this; }
+  Class_ID ClassID() { return MaxEggPlugin_CLASS_ID; }
+  void GetClassName(TSTR& s) { s = TSTR(GetString(IDS_CLASS_NAME)); }
+  TSTR SubAnimName(int i) { return TSTR(GetString(IDS_CLASS_NAME)); }
+  
+  // From ref
+  RefTargetHandle Clone(RemapDir& remap = NoRemap());
+
+  // IO
+  IOResult Save(ISave *isave);
+  IOResult Load(ILoad *iload);
+
 };
 };
 
 
+
+
+const double meshVerts[252][3] = {
+	  {0.729464, 0.714986, -0.919852},
+      {0.466137, 0.160201, -0.594656},
+      {-0.265897, 0.714986, -1.14704},
+      {0.466137, 0.160201, -0.594656},
+      {-0.177333, 0.160201, -0.741523},
+      {-0.265897, 0.714986, -1.14704},
+      {-0.265897, 0.714986, -1.14704},
+      {-0.177333, 0.160201, -0.741523},
+      {-1.06411, 0.714986, -0.510479},
+      {-0.177333, 0.160201, -0.741523},
+      {-0.693356, 0.160201, -0.330009},
+      {-1.06411, 0.714986, -0.510479},
+      {-1.06411, 0.714986, 0.510479},
+      {-1.06411, 0.714986, -0.510479},
+      {-0.693356, 0.160201, 0.330009},
+      {-0.693356, 0.160201, -0.330009},
+      {-0.693356, 0.160201, 0.330009},
+      {-1.06411, 0.714986, -0.510479},
+      {-0.265897, 0.714986, 1.14704},
+      {-1.06411, 0.714986, 0.510479},
+      {-0.177333, 0.160201, 0.741523},
+      {-0.693356, 0.160201, 0.330009},
+      {-0.177333, 0.160201, 0.741523},
+      {-1.06411, 0.714986, 0.510479},
+      {0.729464, 0.714986, 0.919852},
+      {-0.265897, 0.714986, 1.14704},
+      {0.466137, 0.160201, 0.594656},
+      {-0.177333, 0.160201, 0.741523},
+      {0.466137, 0.160201, 0.594656},
+      {-0.265897, 0.714986, 1.14704},
+      {1.17244, 0.714986, 0.0},
+      {0.729464, 0.714986, 0.919852},
+      {0.752508, 0.160201, 0.0},
+      {0.466137, 0.160201, 0.594656},
+      {0.752508, 0.160201, 0.0},
+      {0.729464, 0.714986, 0.919852},
+      {0.729464, 0.714986, -0.919852},
+      {1.17244, 0.714986, 0.0},
+      {0.466137, 0.160201, -0.594656},
+      {0.752508, 0.160201, 0.0},
+      {0.466137, 0.160201, -0.594656},
+      {1.17244, 0.714986, 0.0},
+      {-0.286334, 1.44995, -1.26822},
+      {0.814187, 1.44995, -1.01703},
+      {-0.265897, 0.714986, -1.14704},
+      {0.729464, 0.714986, -0.919852},
+      {-0.265897, 0.714986, -1.14704},
+      {0.814187, 1.44995, -1.01703},
+      {-1.16888, 1.44995, -0.564412},
+      {-0.286334, 1.44995, -1.26822},
+      {-1.06411, 0.714986, -0.510479},
+      {-0.265897, 0.714986, -1.14704},
+      {-1.06411, 0.714986, -0.510479},
+      {-0.286334, 1.44995, -1.26822},
+      {-1.16888, 1.44995, 0.564411},
+      {-1.16888, 1.44995, -0.564412},
+      {-1.06411, 0.714986, 0.510479},
+      {-1.06411, 0.714986, -0.510479},
+      {-1.06411, 0.714986, 0.510479},
+      {-1.16888, 1.44995, -0.564412},
+      {-1.16888, 1.44995, 0.564411},
+      {-1.06411, 0.714986, 0.510479},
+      {-0.286334, 1.44995, 1.26822},
+      {-1.06411, 0.714986, 0.510479},
+      {-0.265897, 0.714986, 1.14704},
+      {-0.286334, 1.44995, 1.26822},
+      {-0.286334, 1.44995, 1.26822},
+      {-0.265897, 0.714986, 1.14704},
+      {0.814187, 1.44995, 1.01703},
+      {-0.265897, 0.714986, 1.14704},
+      {0.729464, 0.714986, 0.919852},
+      {0.814187, 1.44995, 1.01703},
+      {1.30396, 1.44995, 0.0},
+      {0.814187, 1.44995, 1.01703},
+      {1.17244, 0.714986, 0.0},
+      {0.729464, 0.714986, 0.919852},
+      {1.17244, 0.714986, 0.0},
+      {0.814187, 1.44995, 1.01703},
+      {1.30396, 1.44995, 0.0},
+      {1.17244, 0.714986, 0.0},
+      {0.814187, 1.44995, -1.01703},
+      {1.17244, 0.714986, 0.0},
+      {0.729464, 0.714986, -0.919852},
+      {0.814187, 1.44995, -1.01703},
+      {-0.286334, 1.44995, -1.26822},
+      {-0.227573, 2.16723, -1.05763},
+      {0.814187, 1.44995, -1.01703},
+      {0.814187, 1.44995, -1.01703},
+      {-0.227573, 2.16723, -1.05763},
+      {0.690208, 2.16723, -0.848157},
+      {-1.16888, 1.44995, -0.564412},
+      {-0.963577, 2.16723, -0.470692},
+      {-0.286334, 1.44995, -1.26822},
+      {-0.286334, 1.44995, -1.26822},
+      {-0.963577, 2.16723, -0.470692},
+      {-0.227573, 2.16723, -1.05763},
+      {-1.16888, 1.44995, -0.564412},
+      {-1.16888, 1.44995, 0.564411},
+      {-0.963577, 2.16723, -0.470692},
+      {-1.16888, 1.44995, 0.564411},
+      {-0.963577, 2.16723, 0.470692},
+      {-0.963577, 2.16723, -0.470692},
+      {-1.16888, 1.44995, 0.564411},
+      {-0.286334, 1.44995, 1.26822},
+      {-0.963577, 2.16723, 0.470692},
+      {-0.286334, 1.44995, 1.26822},
+      {-0.227574, 2.16723, 1.05763},
+      {-0.963577, 2.16723, 0.470692},
+      {-0.286334, 1.44995, 1.26822},
+      {0.814187, 1.44995, 1.01703},
+      {-0.227574, 2.16723, 1.05763},
+      {0.814187, 1.44995, 1.01703},
+      {0.690208, 2.16723, 0.848157},
+      {-0.227574, 2.16723, 1.05763},
+      {0.814187, 1.44995, 1.01703},
+      {1.30396, 1.44995, 0.0},
+      {0.690208, 2.16723, 0.848157},
+      {1.30396, 1.44995, 0.0},
+      {1.09866, 2.16723, 0.0},
+      {0.690208, 2.16723, 0.848157},
+      {0.814187, 1.44995, -1.01703},
+      {0.690208, 2.16723, -0.848157},
+      {1.30396, 1.44995, 0.0},
+      {1.30396, 1.44995, 0.0},
+      {0.690208, 2.16723, -0.848157},
+      {1.09866, 2.16723, 0.0},
+      {-0.227573, 2.16723, -1.05763},
+      {-0.154893, 2.72566, -0.759032},
+      {0.690208, 2.16723, -0.848157},
+      {0.690208, 2.16723, -0.848157},
+      {-0.154893, 2.72566, -0.759032},
+      {0.50377, 2.72566, -0.608696},
+      {-0.963577, 2.16723, -0.470692},
+      {-0.683099, 2.72566, -0.337801},
+      {-0.227573, 2.16723, -1.05763},
+      {-0.227573, 2.16723, -1.05763},
+      {-0.683099, 2.72566, -0.337801},
+      {-0.154893, 2.72566, -0.759032},
+      {-0.963577, 2.16723, -0.470692},
+      {-0.963577, 2.16723, 0.470692},
+      {-0.683099, 2.72566, -0.337801},
+      {-0.963577, 2.16723, 0.470692},
+      {-0.683099, 2.72566, 0.337801},
+      {-0.683099, 2.72566, -0.337801},
+      {-0.963577, 2.16723, 0.470692},
+      {-0.227574, 2.16723, 1.05763},
+      {-0.683099, 2.72566, 0.337801},
+      {-0.227574, 2.16723, 1.05763},
+      {-0.154893, 2.72566, 0.759032},
+      {-0.683099, 2.72566, 0.337801},
+      {-0.227574, 2.16723, 1.05763},
+      {0.690208, 2.16723, 0.848157},
+      {-0.154893, 2.72566, 0.759032},
+      {0.690208, 2.16723, 0.848157},
+      {0.50377, 2.72566, 0.608696},
+      {-0.154893, 2.72566, 0.759032},
+      {0.690208, 2.16723, 0.848157},
+      {1.09866, 2.16723, 0.0},
+      {0.50377, 2.72566, 0.608696},
+      {1.09866, 2.16723, 0.0},
+      {0.796903, 2.72566, 0.0},
+      {0.50377, 2.72566, 0.608696},
+      {1.09866, 2.16723, 0.0},
+      {0.690208, 2.16723, -0.848157},
+      {0.796903, 2.72566, 0.0},
+      {0.690208, 2.16723, -0.848157},
+      {0.50377, 2.72566, -0.608696},
+      {0.796903, 2.72566, 0.0},
+      {0.50377, 2.72566, -0.608696},
+      {-0.154893, 2.72566, -0.759032},
+      {0.259722, 3.11175, -0.299638},
+      {-0.154893, 2.72566, -0.759032},
+      {-0.0645132, 3.11175, -0.373643},
+      {0.259722, 3.11175, -0.299638},
+      {-0.154893, 2.72566, -0.759032},
+      {-0.683099, 2.72566, -0.337801},
+      {-0.0645132, 3.11175, -0.373643},
+      {-0.683099, 2.72566, -0.337801},
+      {-0.324529, 3.11175, -0.166287},
+      {-0.0645132, 3.11175, -0.373643},
+      {-0.683099, 2.72566, -0.337801},
+      {-0.683099, 2.72566, 0.337801},
+      {-0.324529, 3.11175, -0.166287},
+      {-0.683099, 2.72566, 0.337801},
+      {-0.324529, 3.11175, 0.166287},
+      {-0.324529, 3.11175, -0.166287},
+      {-0.683099, 2.72566, 0.337801},
+      {-0.154893, 2.72566, 0.759032},
+      {-0.324529, 3.11175, 0.166287},
+      {-0.154893, 2.72566, 0.759032},
+      {-0.0645132, 3.11175, 0.373642},
+      {-0.324529, 3.11175, 0.166287},
+      {-0.154893, 2.72566, 0.759032},
+      {0.50377, 2.72566, 0.608696},
+      {-0.0645132, 3.11175, 0.373642},
+      {0.50377, 2.72566, 0.608696},
+      {0.259722, 3.11175, 0.299638},
+      {-0.0645132, 3.11175, 0.373642},
+      {0.50377, 2.72566, 0.608696},
+      {0.796903, 2.72566, 0.0},
+      {0.259722, 3.11175, 0.299638},
+      {0.796903, 2.72566, 0.0},
+      {0.40402, 3.11175, 0.0},
+      {0.259722, 3.11175, 0.299638},
+      {0.796903, 2.72566, 0.0},
+      {0.50377, 2.72566, -0.608696},
+      {0.40402, 3.11175, 0.0},
+      {0.50377, 2.72566, -0.608696},
+      {0.259722, 3.11175, -0.299638},
+      {0.40402, 3.11175, 0.0},
+      {-0.177333, 0.160201, -0.741523},
+      {0.466137, 0.160201, -0.594656},
+      {-0.00334214, 0.00443203, 0.0},
+      {-0.693356, 0.160201, -0.330009},
+      {-0.177333, 0.160201, -0.741523},
+      {-0.00334214, 0.00443203, 0.0},
+      {-0.693356, 0.160201, 0.330009},
+      {-0.693356, 0.160201, -0.330009},
+      {-0.00334214, 0.00443203, 0.0},
+      {-0.177333, 0.160201, 0.741523},
+      {-0.693356, 0.160201, 0.330009},
+      {-0.00334214, 0.00443203, 0.0},
+      {0.466137, 0.160201, 0.594656},
+      {-0.177333, 0.160201, 0.741523},
+      {-0.00334214, 0.00443203, 0.0},
+      {0.752508, 0.160201, 0.0},
+      {0.466137, 0.160201, 0.594656},
+      {-0.00334214, 0.00443203, 0.0},
+      {0.466137, 0.160201, -0.594656},
+      {0.752508, 0.160201, 0.0},
+      {-0.00334214, 0.00443203, 0.0},
+      {0.259722, 3.11175, -0.299638},
+      {-0.0645132, 3.11175, -0.373643},
+      {0.0207683, 3.20912, 0.0},
+      {-0.0645132, 3.11175, -0.373643},
+      {-0.324529, 3.11175, -0.166287},
+      {0.0207683, 3.20912, 0.0},
+      {-0.324529, 3.11175, -0.166287},
+      {-0.324529, 3.11175, 0.166287},
+      {0.0207683, 3.20912, 0.0},
+      {-0.324529, 3.11175, 0.166287},
+      {-0.0645132, 3.11175, 0.373642},
+      {0.0207683, 3.20912, 0.0},
+      {-0.0645132, 3.11175, 0.373642},
+      {0.259722, 3.11175, 0.299638},
+      {0.0207683, 3.20912, 0.0},
+      {0.259722, 3.11175, 0.299638},
+      {0.40402, 3.11175, 0.0},
+      {0.0207683, 3.20912, 0.0},
+      {0.40402, 3.11175, 0.0},
+      {0.259722, 3.11175, -0.299638},
+      {0.0207683, 3.20912, 0.0}
+  };
+  
 #endif // __MaxEggPlugin__H
 #endif // __MaxEggPlugin__H

+ 219 - 180
pandatool/src/maxegg/MaxEgg.rc

@@ -1,180 +1,219 @@
-// Microsoft Visual C++ generated resource script.
-//
-#include "resource.h"
-
-#define APSTUDIO_READONLY_SYMBOLS
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 2 resource.
-//
-#include "afxres.h"
-
-/////////////////////////////////////////////////////////////////////////////
-#undef APSTUDIO_READONLY_SYMBOLS
-
-/////////////////////////////////////////////////////////////////////////////
-// English (U.S.) resources
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-#ifdef _WIN32
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-#endif //_WIN32
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Dialog
-//
-
-IDD_PANEL DIALOGEX 0, 0, 374, 169
-STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
-    WS_SYSMENU
-EXSTYLE WS_EX_TOOLWINDOW
-FONT 8, "MS Sans Serif", 0, 0, 0x1
-BEGIN
-    CTEXT           "MaxToEgg Exporter",IDC_STATIC,7,7,360,10
-    CTEXT           "By Corey Revilla, Ken Strickland, and Steve Osman.",
-                    IDC_STATIC,7,21,360,19
-    CONTROL         "Also create .BAM file",IDC_MAKE_BAM,"Button",
-                    BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,35,138,81,10
-    PUSHBUTTON      "Done",IDC_DONE,303,139,50,14
-    GROUPBOX        "",IDC_STATIC,21,38,333,93
-    CONTROL         "Animation",IDC_ANIMATION,"Button",BS_AUTOCHECKBOX | 
-                    WS_TABSTOP,27,38,49,10
-    CONTROL         "model only",IDC_MODEL,"Button",BS_AUTORADIOBUTTON | 
-                    WS_DISABLED | WS_GROUP | WS_TABSTOP,35,51,60,12
-    CONTROL         "animation channel only",IDC_CHAN,"Button",
-                    BS_AUTORADIOBUTTON | WS_DISABLED | WS_TABSTOP,35,65,92,
-                    12
-    CONTROL         "pose",IDC_POSE,"Button",BS_AUTORADIOBUTTON | 
-                    WS_DISABLED | WS_TABSTOP,35,80,60,12
-    CONTROL         "strobe",IDC_STROBE,"Button",BS_AUTORADIOBUTTON | 
-                    WS_DISABLED | WS_TABSTOP,35,95,60,12
-    CONTROL         "model and animation channel",IDC_BOTH,"Button",
-                    BS_AUTORADIOBUTTON | WS_DISABLED | WS_TABSTOP,35,110,110,
-                    12
-    EDITTEXT        IDC_CN,254,54,71,12,ES_AUTOHSCROLL | WS_DISABLED
-    EDITTEXT        IDC_SF,303,70,22,12,ES_AUTOHSCROLL | WS_DISABLED
-    EDITTEXT        IDC_IF,303,103,22,12,ES_AUTOHSCROLL | WS_DISABLED
-    EDITTEXT        IDC_EF,303,87,22,12,ES_AUTOHSCROLL | WS_DISABLED
-    LTEXT           "Character Name",IDC_CN_LABEL,187,56,54,10,WS_DISABLED
-    LTEXT           "Start Frame",IDC_SF_LABEL,187,72,48,10,WS_DISABLED
-    LTEXT           "End Frame",IDC_EF_LABEL,187,88,45,10,WS_DISABLED
-    LTEXT           "Frame Increment",IDC_IF_LABEL,187,105,64,10,WS_DISABLED
-    PUSHBUTTON      "Cancel",IDC_CANCEL,250,139,50,14
-END
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// DESIGNINFO
-//
-
-#ifdef APSTUDIO_INVOKED
-GUIDELINES DESIGNINFO 
-BEGIN
-    IDD_PANEL, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 367
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 162
-    END
-END
-#endif    // APSTUDIO_INVOKED
-
-
-#ifdef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// TEXTINCLUDE
-//
-
-1 TEXTINCLUDE 
-BEGIN
-    "resource.h\0"
-END
-
-2 TEXTINCLUDE 
-BEGIN
-    "#include ""afxres.h""\r\n"
-    "\0"
-END
-
-3 TEXTINCLUDE 
-BEGIN
-    "\r\n"
-    "\0"
-END
-
-#endif    // APSTUDIO_INVOKED
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Version
-//
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION 3,0,0,0
- PRODUCTVERSION 3,0,0,0
- FILEFLAGSMASK 0x3fL
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x40004L
- FILETYPE 0x2L
- FILESUBTYPE 0x0L
-BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-        BLOCK "040904b0"
-        BEGIN
-            VALUE "FileVersion", "4.0.0.0"
-            VALUE "InternalName", "MaxEgg"
-//            VALUE "OriginalFilename", "MaxEgg.dle"
-            VALUE "ProductName", "3ds max"
-            VALUE "ProductVersion", "4.0.0.0"
-            VALUE "FileDescription", "Panda3D .egg exporter"
-            VALUE "Comments", "TECH: "
-            VALUE "LegalTrademarks", "3D Studio MAX, Biped, Character Studio, Heidi, Kinetix and Physique are registered trademarks and 3ds max, combustion, Discreet, DWG Unplugged, DXF, FLI and FLC are trademarks of Autodesk, Inc."
-        END
-    END
-    BLOCK "VarFileInfo"
-    BEGIN
-        VALUE "Translation", 0x409, 1200
-    END
-END
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// String Table
-//
-
-STRINGTABLE 
-BEGIN
-    IDS_LIBDESCRIPTION      "Panda3D .egg exporter"
-    IDS_CATEGORY            "egg"
-    IDS_CLASS_NAME          "MaxEgg"
-    IDS_PARAMS              "Parameters"
-    IDS_SPIN                "Spin"
-END
-
-#endif    // English (U.S.) resources
-/////////////////////////////////////////////////////////////////////////////
-
-
-
-#ifndef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 3 resource.
-//
-
-
-/////////////////////////////////////////////////////////////////////////////
-#endif    // not APSTUDIO_INVOKED
-
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_PANEL DIALOGEX 0, 0, 109, 192
+STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    CTEXT           "Panda 3D EGG Exporter",IDC_PANEL_TITLE,6,6,96,11,
+                    WS_BORDER
+    LTEXT           "Eggs",IDC_EGGS_LABEL,6,19,62,8
+    CONTROL         "Custom1",IDC_LIST_EGGS,"SysListView32",LVS_REPORT | 
+                    LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | 
+                    WS_TABSTOP,6,28,96,69,WS_EX_CLIENTEDGE
+    CONTROL         "Add...",IDC_ADD_EGG,"CustButton",WS_TABSTOP,6,102,24,12
+    CONTROL         "Edit...",IDC_EDIT_EGG,"CustButton",WS_TABSTOP,36,102,30,
+                    12
+    CONTROL         "Remove",IDC_REMOVE_EGG,"CustButton",WS_TABSTOP,72,102,
+                    30,12
+    CONTROL         "Overwrite Existing Files",IDC_OVERWRITE_CHECK,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,6,126,103,8,
+                    WS_EX_TRANSPARENT
+    CONTROL         "Export Now",IDC_EXPORT,"CustButton",WS_TABSTOP,5,164,96,
+                    18
+    CONTROL         "Pview Output",IDC_PVIEW_CHECK,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,6,138,103,8,WS_EX_TRANSPARENT
+    CONTROL         "Log Output",IDC_LOGGING,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,6,148,90,11
+END
+
+IDD_EGG_DETAILS DIALOGEX 0, 0, 256, 169
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Export Settings"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    LTEXT           "Export Type:",IDC_STATIC,6,5,54,8
+    CONTROL         "Model",IDC_MODEL,"Button",BS_AUTORADIOBUTTON,54,4,38,12
+    CONTROL         "Animation",IDC_ANIMATION,"Button",BS_AUTORADIOBUTTON,96,
+                    4,50,12
+    CONTROL         "Both",IDC_BOTH,"Button",BS_AUTORADIOBUTTON,150,4,35,12
+    CONTROL         "Pose",IDC_POSE,"Button",BS_AUTORADIOBUTTON,186,4,36,12
+    GROUPBOX        "Options",IDC_STATIC,6,18,246,126,WS_GROUP
+    LTEXT           "Filename",IDC_STATIC,12,30,66,8
+    CONTROL         "Custom1",IDC_FILENAME,"CustEdit",WS_TABSTOP,12,42,180,
+                    12
+    CONTROL         "Browse...",IDC_BROWSE,"CustButton",WS_TABSTOP,198,42,48,
+                    12
+    CONTROL         "Export Entire Scene",IDC_EXPORT_ALL,"Button",
+                    BS_AUTORADIOBUTTON | WS_GROUP,12,60,114,8
+    CONTROL         "Export Meshes:",IDC_EXPORT_SELECTED,"Button",
+                    BS_AUTORADIOBUTTON,12,71,114,8
+    LISTBOX         IDC_LIST_EXPORT,12,84,114,36,LBS_SORT | 
+                    LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_VSCROLL | 
+                    WS_TABSTOP
+    CONTROL         "Add...",IDC_ADD_EXPORT,"CustButton",WS_TABSTOP,12,126,
+                    54,12
+    CONTROL         "Remove...",IDC_REMOVE_EXPORT,"CustButton",WS_TABSTOP,72,
+                    126,54,12
+    CONTROL         "Export All Frames",IDC_EXP_ALL_FRAMES,"Button",
+                    BS_AUTORADIOBUTTON | WS_GROUP,144,60,96,8
+    CONTROL         "Use Range:",IDC_EXP_SEL_FRAMES,"Button",
+                    BS_AUTORADIOBUTTON | BS_VCENTER,144,70,102,11
+    LTEXT           "Start Frame",IDC_SF_LABEL,144,86,42,8,WS_DISABLED
+    CONTROL         "Custom4",IDC_SF,"CustEdit",WS_DISABLED | WS_TABSTOP,186,
+                    84,48,12
+    LTEXT           "End Frame",IDC_EF_LABEL,144,103,42,8,WS_DISABLED
+    CONTROL         "Custom5",IDC_EF,"CustEdit",WS_DISABLED | WS_TABSTOP,186,
+                    101,48,12
+    CONTROL         "Double-sided Polygons",IDC_CHECK1,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,144,126,102,12
+    CONTROL         "OK",IDC_OK,"CustButton",WS_TABSTOP,162,150,42,12
+    CONTROL         "Cancel",IDC_CANCEL,"CustButton",WS_TABSTOP,210,150,42,
+                    12
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO 
+BEGIN
+    IDD_PANEL, DIALOG
+    BEGIN
+        BOTTOMMARGIN, 182
+    END
+
+    IDD_EGG_DETAILS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 249
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 162
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 3,0,0,0
+ PRODUCTVERSION 3,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "FileVersion", "4.0.0.0"
+            VALUE "InternalName", "MaxEgg"
+            VALUE "OriginalFilename", "MaxEgg.dle"
+            VALUE "ProductName", "3ds max"
+            VALUE "ProductVersion", "4.0.0.0"
+            VALUE "FileDescription", "Panda3D .egg exporter"
+            VALUE "Comments", "TECH: "
+            VALUE "LegalTrademarks", "3D Studio MAX, Biped, Character Studio, Heidi, Kinetix and Physique are registered trademarks and 3ds max, combustion, Discreet, DWG Unplugged, DXF, FLI and FLC are trademarks of Autodesk, Inc."
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE 
+BEGIN
+    IDS_LIBDESCRIPTION      "Panda3D .egg exporter"
+    IDS_CATEGORY            "Exporters"
+    IDS_CLASS_NAME          "Panda3D"
+    IDS_PARAMS              "Parameters"
+    IDS_SPIN                "Spin"
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+

+ 768 - 0
pandatool/src/maxegg/MaxEggExpOptions.cpp

@@ -0,0 +1,768 @@
+/*
+  MaxEgg.cpp 
+  Created by Phillip Saltzman, 2/15/05
+  Carnegie Mellon University, Entetainment Technology Center
+
+  This file implements the classes that are used to choose
+  what to export from 3D Studio max
+*/
+
+#include "MaxEggExpOptions.h"
+
+//Disable the forcing int to true or false performance warning
+#pragma warning(disable: 4800)
+
+void SetICustEdit(HWND wnd, int nIDDlgItem, char *text)
+{ 
+  ICustEdit *edit = GetICustEdit(GetDlgItem(wnd, nIDDlgItem));
+  edit->SetText(text);
+  ReleaseICustEdit(edit);
+}
+
+void SetICustEdit(HWND wnd, int nIDDlgItem, int n)
+{
+  char text[80];
+  sprintf(text, "%d", n);
+  ICustEdit *edit = GetICustEdit(GetDlgItem(wnd, nIDDlgItem));
+  edit->SetText(text);
+  ReleaseICustEdit(edit);
+}
+
+char *GetICustEditT(HWND wnd)
+{
+  static char buffer[2084];
+  ICustEdit *edit = GetICustEdit(wnd);
+  edit->GetText(buffer,2084);
+  ReleaseICustEdit(edit);
+  return buffer;
+}
+
+int GetICustEditI(HWND wnd, BOOL *valid)
+{
+  ICustEdit *edit = GetICustEdit(wnd);
+  int i = edit->GetInt(valid);
+  ReleaseICustEdit(edit);
+  return i;
+}
+
+void ChunkSave(ISave *isave, int chunkid, int value)
+{
+  ULONG nb;
+  isave->BeginChunk(chunkid);
+  isave->Write(&value, sizeof(int), &nb);
+  isave->EndChunk();
+}
+
+void ChunkSave(ISave *isave, int chunkid, ULONG value)
+{
+  ULONG nb;
+  isave->BeginChunk(chunkid);
+  isave->Write(&value, sizeof(ULONG), &nb);
+  isave->EndChunk();
+}
+
+void ChunkSave(ISave *isave, int chunkid, bool value)
+{
+  ULONG nb;
+  isave->BeginChunk(chunkid);
+  isave->Write(&value, sizeof(bool), &nb);
+  isave->EndChunk();
+}
+
+void ChunkSave(ISave *isave, int chunkid, char *value)
+{
+  ULONG nb;
+  isave->BeginChunk(chunkid);
+  int bytes = strlen(value) + 1;
+  isave->Write(&bytes, sizeof(int), &nb);
+  isave->Write(value, bytes, &nb);
+  isave->EndChunk();
+}
+
+char *ChunkLoadString(ILoad *iload, char *buffer, int maxBytes)
+{
+  ULONG nb;
+  int bytes;
+  IOResult res;
+  res = iload->Read(&bytes, sizeof(int), &nb);
+  assert(res == IO_OK && bytes <= maxBytes);
+  res = iload->Read(buffer, bytes, &nb);
+  assert(res == IO_OK);
+  return buffer;
+}
+
+int ChunkLoadInt(ILoad *iload)
+{
+  ULONG nb;
+  int value;
+  IOResult res;
+  res = iload->Read(&value, sizeof(int), &nb);
+  assert(res == IO_OK);
+  return value;
+}
+
+int ChunkLoadULONG(ILoad *iload)
+{
+  ULONG nb, value;
+  IOResult res;
+  res = iload->Read(&value, sizeof(ULONG), &nb);
+  assert(res == IO_OK);
+  return value;
+}
+
+bool ChunkLoadBool(ILoad *iload)
+{
+  ULONG nb;
+  bool value;
+  IOResult res;
+  res = iload->Read(&value, sizeof(bool), &nb);
+  assert(res == IO_OK);
+  return value;
+}
+
+void showAnimControls(HWND hWnd, BOOL val) {
+  ShowWindow(GetDlgItem(hWnd, IDC_EF_LABEL), val);
+  ShowWindow(GetDlgItem(hWnd, IDC_EF), val);
+  ShowWindow(GetDlgItem(hWnd, IDC_SF_LABEL), val);
+  SetWindowText(GetDlgItem(hWnd, IDC_EXP_SEL_FRAMES),
+                val ? "Use Range:" : "Use Frame:");
+}
+
+void enableAnimControls(HWND hWnd, BOOL val) {
+  EnableWindow(GetDlgItem(hWnd, IDC_SF_LABEL), val);
+  EnableWindow(GetDlgItem(hWnd, IDC_SF), val);
+  EnableWindow(GetDlgItem(hWnd, IDC_EF_LABEL), val);
+  EnableWindow(GetDlgItem(hWnd, IDC_EF), val);
+}
+
+void enableChooserControls(HWND hWnd, BOOL val) {
+  EnableWindow(GetDlgItem(hWnd, IDC_LIST_EXPORT), val);
+  EnableWindow(GetDlgItem(hWnd, IDC_ADD_EXPORT), val);
+  EnableWindow(GetDlgItem(hWnd, IDC_REMOVE_EXPORT), val);
+}
+
+#define ANIM_RAD_NONE 0
+#define ANIM_RAD_EXPALL 1
+#define ANIM_RAD_EXPSEL 2
+#define ANIM_RAD_ALL    3
+void enableAnimRadios(HWND hWnd, int mask) {
+  EnableWindow(GetDlgItem(hWnd, IDC_EXP_ALL_FRAMES), mask & ANIM_RAD_EXPALL);
+  EnableWindow(GetDlgItem(hWnd, IDC_EXP_SEL_FRAMES), mask & ANIM_RAD_EXPSEL);
+}
+
+// Handles the work of actually picking the target.
+class AddNodeCB : public HitByNameDlgCallback
+{
+public:
+  MaxEggExpOptions *ph; //Pointer to the parent class
+  HWND hWnd;            //Handle to the parent dialog
+
+  AddNodeCB (MaxEggExpOptions *instance, HWND wnd) : 
+    ph(instance), hWnd(wnd) {}
+
+  virtual TCHAR *dialogTitle() {return _T("Objects to Export");}
+  virtual TCHAR *buttonText()  {return _T("Select");}
+  virtual int filter(INode *node);
+  virtual void proc(INodeTab &nodeTab);
+};
+
+//This tells what should be in the list
+//Allow only triangular objects, nurbs, and joints
+int AddNodeCB::filter(INode *node) {
+  if (!node) return 0;
+  
+  Object *obj = node->EvalWorldState(0).obj;
+  Control *c = node->GetTMController();
+  NURBSSet getSet;
+  bool is_bone = (node->GetBoneNodeOnOff() ||     //True for bones
+     (c &&                                          //True for bipeds
+     ((c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) ||
+     (c->ClassID() == BIPBODY_CONTROL_CLASS_ID) ||
+     (c->ClassID() == FOOTPRINT_CLASS_ID))));
+
+  
+  if (IsDlgButtonChecked(hWnd, IDC_ANIMATION) == BST_CHECKED)
+    return is_bone && !ph->FindNode(node->GetHandle());
+  else
+    return (
+    !is_bone &&
+    ((obj->SuperClassID() == GEOMOBJECT_CLASS_ID && //Allow geometrics
+      obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) ||
+     (obj->SuperClassID() == SHAPE_CLASS_ID &&      //Allow CV NURBS
+      obj->ClassID() == EDITABLE_SURF_CLASS_ID &&
+      GetNURBSSet(obj, 0, getSet, TRUE) &&
+      getSet.GetNURBSObject(0)->GetType() == kNCVCurve)) &&
+    !ph->FindNode(node->GetHandle()));             //Only allow items not already selected
+}
+
+//Adds all of the selected items to the list
+void AddNodeCB::proc(INodeTab &nodeTab) {
+  for (int i = 0; i < nodeTab.Count(); i++)
+    ph->AddNode(nodeTab[i]->GetHandle());
+  ph->RefreshNodeList(hWnd);
+}
+
+//This callback class generates a list of nodes that have previously been selected
+class RemoveNodeCB : public HitByNameDlgCallback
+{
+public:
+  MaxEggExpOptions *ph; //Pointer to the parent class
+  HWND hWnd;            //Handle to the parent dialog
+
+  RemoveNodeCB (MaxEggExpOptions *instance, HWND wnd) : 
+    ph(instance), hWnd(wnd) {}
+
+  virtual TCHAR *dialogTitle() {return _T("Objects to Remove");}
+  virtual TCHAR *buttonText()  {return _T("Remove");}
+  virtual int filter(INode *node) {return (node && ph->FindNode(node->GetHandle()));}
+  virtual void proc(INodeTab &nodeTab);
+};
+
+
+//Adds all of the selected items to the list
+void RemoveNodeCB::proc(INodeTab &nodeTab) {
+  for (int i = 0; i < nodeTab.Count(); i++)
+    ph->RemoveNodeByHandle(nodeTab[i]->GetHandle());
+  ph->RefreshNodeList(hWnd);
+}
+
+MaxEggExpOptions::MaxEggExpOptions () :
+checked(true), anim_type(AT_model), sf(INT_MIN), ef(INT_MIN), 
+expWholeScene(true), dblSided(false), numNodes(0), maxNodes(5),
+choosingNodes(false), prev_type(AT_model) {
+  *filename = NULL;
+  nodeList = new ULONG[maxNodes];
+}
+
+BOOL CALLBACK MaxEggExpOptionsProc( HWND hWnd, UINT message, 
+					                          WPARAM wParam, LPARAM lParam ) 
+{
+  char tempFilename[2048];
+  //We pass in our plugin through the lParam variable. Let's convert it back.
+  MaxEggExpOptions *imp = (MaxEggExpOptions*)GetWindowLongPtr(hWnd,GWLP_USERDATA); 
+  if ( !imp && message != WM_INITDIALOG ) return FALSE;
+
+  switch(message) 
+    {
+    // When we start, center the window.
+    case WM_INITDIALOG:
+      // this line is very necessary to pass the plugin as the lParam
+      SetWindowLongPtr(hWnd,GWLP_USERDATA,lParam); 
+      ((MaxEggExpOptions*)lParam)->UpdateUI(hWnd);
+      return TRUE; break;
+    
+    case WM_CLOSE:
+      EndDialog(hWnd, FALSE);
+      return TRUE; break;
+
+    // A control was modified
+    case WM_COMMAND:
+      //The modified control is found in the lower word of the wParam long.
+      switch( LOWORD(wParam) ) {
+        case IDC_MODEL:
+          if (HIWORD(wParam) == BN_CLICKED) {
+            SetWindowText(GetDlgItem(hWnd, IDC_EXPORT_SELECTED),
+              "Export Meshes:");
+            enableAnimRadios(hWnd, ANIM_RAD_NONE);
+            showAnimControls(hWnd, TRUE);
+            enableAnimControls(hWnd, FALSE);
+            if (imp->prev_type == MaxEggExpOptions::AT_chan) imp->ClearNodeList(hWnd);
+            imp->prev_type = MaxEggExpOptions::AT_model;
+            return TRUE;
+          }
+          break;
+
+        case IDC_ANIMATION:
+          if (HIWORD(wParam) == BN_CLICKED) {
+            SetWindowText(GetDlgItem(hWnd, IDC_EXPORT_SELECTED),
+              "Export Bones:");
+            enableAnimRadios(hWnd, ANIM_RAD_ALL);
+            showAnimControls(hWnd, TRUE);
+            enableAnimControls(hWnd, IsDlgButtonChecked(hWnd, IDC_EXP_SEL_FRAMES));
+            if (imp->prev_type != MaxEggExpOptions::AT_chan) imp->ClearNodeList(hWnd);
+            imp->prev_type = MaxEggExpOptions::AT_chan;
+            return TRUE;
+          }
+          break;
+        case IDC_BOTH:
+          if (HIWORD(wParam) == BN_CLICKED) {
+            SetWindowText(GetDlgItem(hWnd, IDC_EXPORT_SELECTED),
+              "Export Models:");
+            enableAnimRadios(hWnd, ANIM_RAD_ALL);
+            showAnimControls(hWnd, TRUE);
+            enableAnimControls(hWnd, IsDlgButtonChecked(hWnd, IDC_EXP_SEL_FRAMES));
+            if (imp->prev_type == MaxEggExpOptions::AT_chan) imp->ClearNodeList(hWnd);
+            imp->prev_type = MaxEggExpOptions::AT_both;
+            return TRUE;
+          }
+          break;
+        case IDC_POSE:
+          if (HIWORD(wParam) == BN_CLICKED) {
+            SetWindowText(GetDlgItem(hWnd, IDC_EXPORT_SELECTED),
+              "Export Meshes:");
+            enableAnimRadios(hWnd, ANIM_RAD_EXPSEL);
+            showAnimControls(hWnd, FALSE);
+            enableAnimControls(hWnd, TRUE);
+            CheckRadioButton(hWnd, IDC_EXP_ALL_FRAMES, IDC_EXP_SEL_FRAMES, IDC_EXP_SEL_FRAMES);
+            if (imp->prev_type == MaxEggExpOptions::AT_chan) imp->ClearNodeList(hWnd);
+            imp->prev_type = MaxEggExpOptions::AT_pose;
+            return TRUE;
+          }
+          break;
+        case IDC_EXP_ALL_FRAMES:
+          if (HIWORD(wParam) == BN_CLICKED) {
+            enableAnimControls(hWnd, FALSE);
+            return TRUE;
+          }
+          break;
+
+        case IDC_EXP_SEL_FRAMES:
+          if (HIWORD(wParam) == BN_CLICKED) {
+            enableAnimControls(hWnd, TRUE);
+            return TRUE;
+          }
+          break;
+
+        case IDC_EXPORT_ALL:
+          if (HIWORD(wParam) == BN_CLICKED) {
+            enableChooserControls(hWnd, FALSE);
+            return TRUE;
+          }
+          break;
+
+        case IDC_EXPORT_SELECTED:
+          if (HIWORD(wParam) == BN_CLICKED) {
+            enableChooserControls(hWnd, TRUE);
+            return TRUE;
+          }
+          break;
+
+        case IDC_ADD_EXPORT:
+        {
+          if (!imp->choosingNodes) {
+            AddNodeCB PickCB(imp, hWnd);
+            imp->choosingNodes = true;
+            imp->maxInterface->DoHitByNameDialog(&PickCB);
+            imp->choosingNodes = false;
+          }
+        }
+        return TRUE; break;
+
+        case IDC_REMOVE_EXPORT:
+        {
+          if (!imp->choosingNodes) {
+            imp->choosingNodes = true;
+            RemoveNodeCB PickCB(imp, hWnd);
+            imp->maxInterface->DoHitByNameDialog(&PickCB);
+            imp->choosingNodes = false;
+          }
+        }
+        return TRUE; break;
+
+        case IDC_OK:
+          if (imp->UpdateFromUI(hWnd)) EndDialog(hWnd, TRUE);
+          return TRUE; break;
+        case IDC_CANCEL:
+          EndDialog(hWnd, FALSE);
+          return TRUE; break;
+        case IDC_BROWSE:
+          OPENFILENAME ofn;
+          strcpy(tempFilename, GetICustEditT(GetDlgItem(hWnd, IDC_FILENAME)));
+
+          memset(&ofn, 0, sizeof(ofn));
+          ofn.nMaxFile = 2047;
+          ofn.lpstrFile = tempFilename;
+          ofn.lStructSize = sizeof(ofn);
+          ofn.hwndOwner = hWnd;
+          ofn.Flags = OFN_HIDEREADONLY | OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST;
+          ofn.lpstrDefExt = "egg";
+          ofn.lpstrFilter = "Panda3D Egg Files (*.egg)\0*.egg\0All Files (*.*)\0*.*\0";
+
+          SetFocus(GetDlgItem(hWnd, IDC_FILENAME));
+          if (GetSaveFileName(&ofn))
+            SetICustEdit(hWnd, IDC_FILENAME, ofn.lpstrFile);
+          //else {
+          //  char buf[255];
+          //  sprintf(buf, "%d", CommDlgExtendedError());
+          //  MessageBox(hWnd, buf, "Error on GetSaveFileName", MB_OK);
+          //}
+          return TRUE; break;
+        case IDC_CHECK1:
+          if (IsDlgButtonChecked(hWnd, IDC_CHECK1))
+            if (MessageBox(hWnd, "Warning: Exporting double-sided polygons can severely hurt "
+              "performance in Panda3D.\n\nAre you sure you want to export them?",
+              "Panda3D Exporter", MB_YESNO | MB_ICONQUESTION) != IDYES)
+              CheckDlgButton(hWnd, IDC_CHECK1, BST_UNCHECKED);
+          return TRUE; break;
+        default:
+          //char buf[255];
+          //sprintf(buf, "%d", LOWORD(wParam));
+          //MessageBox(hWnd, buf, "Unknown WParam", MB_OK);
+          break;
+      }
+    }
+  return FALSE;
+}
+
+void MaxEggExpOptions::SetAnimRange() {
+  // Get the start and end frames and the animation frame rate from Max
+  Interval anim_range = maxInterface->GetAnimRange();
+  minFrame = anim_range.Start()/GetTicksPerFrame();
+  maxFrame = anim_range.End()/GetTicksPerFrame();
+}
+
+void MaxEggExpOptions::UpdateUI(HWND hWnd) {
+  int typeButton = IDC_MODEL;
+  int anim_exp = sf == INT_MIN ? IDC_EXP_ALL_FRAMES : IDC_EXP_SEL_FRAMES;
+  int model_exp = expWholeScene ? IDC_EXPORT_ALL : IDC_EXPORT_SELECTED;
+
+  switch (anim_type) {
+    case AT_chan: typeButton = IDC_ANIMATION; break;
+    case AT_both: typeButton = IDC_BOTH; break;
+    case AT_pose: typeButton = IDC_POSE; break;
+  }
+
+  prev_type = anim_type;
+
+  CheckRadioButton(hWnd, IDC_MODEL, IDC_POSE, typeButton);
+  SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(typeButton, BN_CLICKED), 0);
+  CheckRadioButton(hWnd, IDC_EXPORT_ALL, IDC_EXPORT_SELECTED, model_exp);
+  SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(model_exp, BN_CLICKED), 0);
+  CheckRadioButton(hWnd, IDC_EXP_ALL_FRAMES, IDC_EXP_SEL_FRAMES, anim_exp);
+  SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(anim_exp, BN_CLICKED), 0);
+
+  CheckDlgButton(hWnd, IDC_CHECK1,
+                 dblSided ? BST_CHECKED : BST_UNCHECKED);
+
+  SetICustEdit(hWnd, IDC_FILENAME, filename);
+  if (sf != INT_MIN) {
+    SetICustEdit(hWnd, IDC_SF, sf);
+    SetICustEdit(hWnd, IDC_EF, ef);
+  }
+  else {
+    SetICustEdit(hWnd, IDC_SF, minFrame);
+    SetICustEdit(hWnd, IDC_EF, maxFrame);
+  }
+
+  RefreshNodeList(hWnd);
+}
+
+void MaxEggExpOptions::ClearNodeList(HWND hWnd) {
+  numNodes = 0;
+  RefreshNodeList(hWnd);
+}
+
+void MaxEggExpOptions::RefreshNodeList(HWND hWnd) {
+  //Clear and repopulate the node box
+  HWND nodeLB = GetDlgItem(hWnd, IDC_LIST_EXPORT);
+  SendMessage(nodeLB, LB_RESETCONTENT, 0, 0);
+  for (int i = 0; i < numNodes; i++) {
+    INode *temp = maxInterface->GetINodeByHandle(GetNode(i));
+    TCHAR *name = _T("Unknown Node");
+    if (temp) name = temp->GetName();
+    SendMessage(nodeLB, LB_ADDSTRING, 0, (LPARAM)name);
+  }
+}
+
+bool MaxEggExpOptions::UpdateFromUI(HWND hWnd) {
+  BOOL valid;
+  Anim_Type newAnimType;
+  int newSF = INT_MIN, newEF = INT_MIN;
+  char msg[1024];
+
+  if (IsDlgButtonChecked(hWnd, IDC_MODEL))          newAnimType = AT_model;
+  else if (IsDlgButtonChecked(hWnd, IDC_ANIMATION)) newAnimType = AT_chan;
+  else if (IsDlgButtonChecked(hWnd, IDC_BOTH))      newAnimType = AT_both;
+  else                                              newAnimType = AT_pose;
+
+  if (newAnimType != AT_model && IsDlgButtonChecked(hWnd, IDC_EXP_SEL_FRAMES)) {
+    newSF = GetICustEditI(GetDlgItem(hWnd, IDC_SF), &valid);
+    if (!valid) {
+      MessageBox(hWnd, "Start Frame must be an integer", "Invalid Value", MB_OK | MB_ICONEXCLAMATION);
+      return false;
+    }
+    if (newSF < minFrame) {
+      sprintf(msg, "Start Frame must be at least %d", minFrame);
+      MessageBox(hWnd, msg, "Invalid Value", MB_OK | MB_ICONEXCLAMATION);
+      return false;
+    }
+    if (newSF > maxFrame) {
+      sprintf(msg, "Start Frame must be at most %d", maxFrame);
+      MessageBox(hWnd, msg, "Invalid Value", MB_OK | MB_ICONEXCLAMATION);
+      return false;
+    }
+    if (newAnimType != AT_pose) {
+      newEF = GetICustEditI(GetDlgItem(hWnd, IDC_EF), &valid);
+      if (!valid) {
+        MessageBox(hWnd, "End Frame must be an integer", "Invalid Value", MB_OK | MB_ICONEXCLAMATION);
+        return false;
+      }
+      if (newEF > maxFrame) {
+        sprintf(msg, "End Frame must be at most %d", maxFrame);
+        MessageBox(hWnd, msg, "Invalid Value", MB_OK | MB_ICONEXCLAMATION);
+        return false;
+      }
+      if (newEF < newSF) {
+        MessageBox(hWnd, "End Frame must be greater than the start frame", "Invalid Value", MB_OK | MB_ICONEXCLAMATION);
+        return false;
+      }
+    }
+  }
+
+  char *temp = GetICustEditT(GetDlgItem(hWnd, IDC_FILENAME));
+  if (!strlen(temp)) {
+    MessageBox(hWnd, "The filename cannot be empty", "Invalid Value", MB_OK | MB_ICONEXCLAMATION);
+    return false;
+  }
+
+  if (strlen(temp) < 4 || strncmp(".egg", temp+(strlen(temp) - 4), 4))
+    sprintf(filename, "%s.egg", temp);
+  else strcpy(filename, temp);
+
+  temp = strrchr(filename, '\\');
+  if (!temp) temp = filename;
+  else temp++;
+
+  if (strlen(temp) > sizeof(shortName))
+    sprintf(shortName, "%.*s...", sizeof(shortName)-4, temp);
+  else {
+    strcpy(shortName, temp);
+    shortName[strlen(shortName) - 4] = NULL; //Cut off the .egg
+  }
+  
+  dblSided = IsDlgButtonChecked(hWnd, IDC_CHECK1);
+  expWholeScene = IsDlgButtonChecked(hWnd, IDC_EXPORT_ALL);
+  sf = newSF; ef = newEF; anim_type = newAnimType;
+  return true;
+}
+
+bool MaxEggExpOptions::FindNode(ULONG INodeHandle) {
+  for (int i = 0; i < numNodes; i++) 
+    if (nodeList[i] == INodeHandle) return true;
+  return false;
+}
+
+void MaxEggExpOptions::AddNode(ULONG INodeHandle) {
+  if (FindNode(INodeHandle)) return; 
+  if (numNodes >= maxNodes) {
+    ULONG *newList;
+    maxNodes *= 2;
+    newList = new ULONG[maxNodes];
+    for (int i = 0; i < numNodes; i++) newList[i] = nodeList[i];
+    delete [] nodeList;
+    nodeList = newList;
+  }
+
+  nodeList[numNodes++] = INodeHandle;
+}
+
+void MaxEggExpOptions::CullBadNodes() {
+  if (!maxInterface) return;
+  int i = 0, j = 0;
+  while (j < numNodes) {
+    if (!maxInterface->GetINodeByHandle(nodeList[i])) j++;
+    else nodeList[i++] = nodeList[j++];
+  }
+  numNodes = i;
+}
+
+void MaxEggExpOptions::RemoveNode(int i) {
+  if (i >= 0 && i < numNodes) {
+    for (int j = i+1; j < numNodes;) nodeList[i++] = nodeList[j++];
+    --numNodes;
+  }
+}
+
+void MaxEggExpOptions::RemoveNodeByHandle(ULONG INodeHandle) {
+  for (int i = 0; i < numNodes; i++)
+    if (nodeList[i] == INodeHandle) {
+      RemoveNode(i);
+      return;
+    }
+}
+
+IOResult MaxEggExpOptions::Save(ISave *isave) {
+  isave->BeginChunk(CHUNK_EGG_EXP_OPTIONS);
+  ChunkSave(isave, CHUNK_ANIM_TYPE, anim_type);
+  ChunkSave(isave, CHUNK_FILENAME, filename);
+  ChunkSave(isave, CHUNK_SHORTNAME, shortName);
+  ChunkSave(isave, CHUNK_SF, sf);
+  ChunkSave(isave, CHUNK_EF, ef);
+  ChunkSave(isave, CHUNK_DBL_SIDED, dblSided);
+  ChunkSave(isave, CHUNK_EGG_CHECKED, checked);
+  ChunkSave(isave, CHUNK_EXPORT_FULL, expWholeScene);
+  isave->BeginChunk(CHUNK_NODE_LIST);
+  for (int i = 0; i < numNodes; i++)
+    ChunkSave(isave, CHUNK_NODE_HANDLE, nodeList[i]);
+  isave->EndChunk();
+  isave->EndChunk();
+
+  return IO_OK;
+} 
+
+IOResult MaxEggExpOptions::Load(ILoad *iload) {
+  IOResult res = iload->OpenChunk();
+  
+  while (res == IO_OK) {
+    switch(iload->CurChunkID()) {
+    case CHUNK_ANIM_TYPE: anim_type = (Anim_Type)ChunkLoadInt(iload); break;
+    case CHUNK_FILENAME: ChunkLoadString(iload, filename, sizeof(filename)); break;
+    case CHUNK_SHORTNAME: ChunkLoadString(iload, shortName, sizeof(shortName)); break;
+    case CHUNK_SF: sf = ChunkLoadInt(iload); break;
+    case CHUNK_EF: ef = ChunkLoadInt(iload); break;
+    case CHUNK_DBL_SIDED: dblSided = ChunkLoadBool(iload); break;
+    case CHUNK_EGG_CHECKED: checked = ChunkLoadBool(iload); break;
+    case CHUNK_EXPORT_FULL: expWholeScene = ChunkLoadBool(iload); break;
+    case CHUNK_NODE_LIST:
+      res = iload->OpenChunk();
+      while (res == IO_OK) {
+        if (iload->CurChunkID() == CHUNK_NODE_HANDLE) AddNode(ChunkLoadULONG(iload));
+        iload->CloseChunk();
+        res = iload->OpenChunk();
+      }
+    break;
+    }
+    iload->CloseChunk();
+    res = iload->OpenChunk();
+  }
+  
+  if (res == IO_END) return IO_OK;
+  return IO_ERROR;
+}
+
+/*!
+ * This method creates and triggers the exporter.  Basically it takes the
+ * user's options and builds a command-line parameter list from it.
+ * It then invokes the converter pretending it was invoked as a standalone
+ * program.  BIG WARNING:  The converter stuff often does exit() if the
+ * command line arguments are displeasing.
+ */
+bool MaxEggExpOptions::DoExport(IObjParam *ip, bool autoOverwrite, bool saveLog) 
+{
+  MaxToEgg *pmteConverter = new MaxToEgg();
+  char *apcParameters[64];
+  char pszSF[10], pszEF[10];
+  char acOutputFilename[MAX_PATH];
+  char curFilename[MAX_PATH];
+  int iParameterCount=0;
+
+  //Initialize our global error logger
+  if (saveLog)
+    Logger::globalLoggingInstance = new Logger( Logger::PIPE_TO_FILE, "MaxEggLog.txt" );
+  else Logger::globalLoggingInstance = new Logger( Logger::PIPE_TO_DEV_NULL, "" );
+
+  //Set the various logging levels for the subsystems.
+  Logger::SetOneErrorMask( ME, Logger::SAT_ALL );
+  Logger::SetOneErrorMask( MTE, Logger::SAT_NULL_ERROR | 
+			   Logger::SAT_CRITICAL_ERROR | 
+			   Logger::SAT_PARAMETER_INVALID_ERROR | 
+			   Logger::SAT_OTHER_ERROR | Logger::SAT_HIGH_LEVEL );
+  Logger::SetOneErrorMask( MTEC, Logger::SAT_NULL_ERROR | 
+			   Logger::SAT_CRITICAL_ERROR |
+			   Logger::SAT_PARAMETER_INVALID_ERROR | 
+			   Logger::SAT_OTHER_ERROR | Logger::SAT_HIGH_LEVEL |
+			   Logger::SAT_MEDIUM_LEVEL | Logger::SAT_LOW_LEVEL |
+			   Logger::SAT_DEBUG_SPAM_LEVEL );
+  Logger::SetOneErrorMask( MNEG, Logger::SAT_NULL_ERROR |
+			   Logger::SAT_CRITICAL_ERROR |
+			   Logger::SAT_PARAMETER_INVALID_ERROR | 
+			   Logger::SAT_OTHER_ERROR | Logger::SAT_HIGH_LEVEL |
+			   Logger::SAT_MEDIUM_LEVEL | Logger::SAT_LOW_LEVEL );
+  Logger::SetOneErrorMask( MNEG_GEOMETRY_GENERATION, Logger::SAT_NULL_ERROR |
+			   Logger::SAT_CRITICAL_ERROR |
+			   Logger::SAT_PARAMETER_INVALID_ERROR | 
+			   Logger::SAT_OTHER_ERROR | Logger::SAT_HIGH_LEVEL |
+			   Logger::SAT_MEDIUM_LEVEL | Logger::SAT_LOW_LEVEL );
+  Logger::SetOneErrorMask( LLOGGING, Logger::SAT_ALL );
+	
+  Logger::FunctionEntry( "MaxEggPlugin::DoExport" );
+
+  // Copy the output filename so that it can be modified if necessary
+  strncpy(acOutputFilename, filename, MAX_PATH-1);
+  acOutputFilename[MAX_PATH-1]=0;
+
+  // Panda reaaaaaaaaly wants the extension to be in lower case.
+  // So if we see a .egg at the end, lower case it.
+  if ((strlen(acOutputFilename)>4) &&
+      (stricmp(acOutputFilename+strlen(acOutputFilename)-4,".egg")==0)) {
+    strlwr(acOutputFilename+strlen(acOutputFilename)-4);
+  }
+  pmteConverter->SetMaxInterface((Interface*)ip);
+  
+  // Set the command-line arguments
+  // ARGV[0] = program name
+  apcParameters[iParameterCount++]="MaxEggPlugin";
+  
+  // ARGV[1] = Input file
+  // Use a bogus input filename that exists
+  apcParameters[iParameterCount++]="nul.max";
+  
+  // ARGV[2,3] = Animation options
+  // Check if there is an animation to be saved and what type of animation it
+  // will be saved as.  Then set the animation option.
+  apcParameters[iParameterCount++]="-a";
+  switch (anim_type) {
+    case AT_chan: apcParameters[iParameterCount++]="chan"; break;
+    case AT_pose: apcParameters[iParameterCount++]="pose"; break;
+    case AT_both: apcParameters[iParameterCount++]="both"; break;
+    case AT_model: 
+    default: apcParameters[iParameterCount++]="model"; break;
+  }
+
+  //Start Frame
+  //If the export options need a start frame and we have one
+  //then use it
+  if (sf != INT_MIN && anim_type != AT_model) {
+    sprintf(pszSF, "%d", sf);
+    apcParameters[iParameterCount++] = "-sf";
+    apcParameters[iParameterCount++] = pszSF;
+  }
+
+  //Start Frame
+  //If the export options need an end frame and we have one
+  //then use it
+  if (sf != INT_MIN && (anim_type == AT_chan || anim_type == AT_both)) {
+    sprintf(pszEF, "%d", ef);
+    apcParameters[iParameterCount++] = "-ef";
+    apcParameters[iParameterCount++] = pszEF;
+  }
+
+  // Doublesided: This option may be diabled in the converter
+  // but this is here for when it is enabled
+  if (dblSided) apcParameters[iParameterCount++] = "-bface";
+
+  // Final ARGV = Output file
+  // Pass in the output filename
+  // Output file has to be passed in with the -o parameter in order to be able 
+  // to overwrite an existing file
+  if (autoOverwrite) apcParameters[iParameterCount++]="-o";
+  apcParameters[iParameterCount++]=acOutputFilename;
+
+  apcParameters[iParameterCount]=0;
+
+  // Parse the command line and run the converter
+  pmteConverter->parse_command_line(iParameterCount, apcParameters);
+  if (expWholeScene) pmteConverter->Run(NULL, 0);
+  else pmteConverter->Run(nodeList, numNodes);
+  
+  successful = pmteConverter->IsSuccessful();
+  
+  // This was put in try block because originally deleting pmteConverter 
+  // would throw an exception.  That no longer happens, but this is still
+  // here for good measure
+  try {
+		Logger::Log(MTEC, Logger::SAT_MEDIUM_LEVEL, "before deleting pmteconverter");
+    delete pmteConverter; 
+  } catch (...) {
+		Logger::Log(MTEC, Logger::SAT_MEDIUM_LEVEL, "before error message window");
+    MessageBox(ip->GetMAXHWnd(), "I just got an unknown exception.",
+	       "Panda3D Converter", MB_OK);
+  }
+		Logger::Log(MTEC, Logger::SAT_MEDIUM_LEVEL, "before logger function exit");
+  Logger::FunctionExit();
+  //Free the error logger
+  if ( Logger::globalLoggingInstance )
+    delete Logger::globalLoggingInstance;
+ 
+  return successful;
+}

+ 114 - 0
pandatool/src/maxegg/MaxEggExpOptions.h

@@ -0,0 +1,114 @@
+/*
+  MaxEggExpOptions.h 
+  Created by Phillip Saltzman, 2/15/05
+  Carnegie Mellon University, Entetainment Technology Center
+
+  This file contains a class that allows users to specify
+  export options, and then execute the export
+*/
+
+#ifndef __MaxEggExpOptions__H
+#define __MaxEggExpOptions__H
+
+#pragma conform(forScope, off)
+
+//Includes & Definitions
+#include "maxToEgg.h"
+#include "windef.h"
+
+/* Error-Reporting Includes */
+#include "Logger.h"
+#define ME Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM6
+#define MNEG Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM2
+#define MNEG_GEOMETRY_GENERATION Logger::ST_MAP_ME_TO_APP_SPECIFIC_SYSTEM3
+
+/* Externed Globals */
+extern HINSTANCE hInstance;
+
+//Save/load chunk definitions
+#define CHUNK_OVERWRITE_FLAG  0x1000
+#define CHUNK_PVIEW_FLAG      0x1001
+#define CHUNK_LOG_OUTPUT      0x1002
+#define CHUNK_EGG_EXP_OPTIONS 0x1100
+#define CHUNK_ANIM_TYPE       0x1101
+#define CHUNK_EGG_CHECKED     0x1102
+#define CHUNK_DBL_SIDED       0x1103
+#define CHUNK_SF              0x1104
+#define CHUNK_EF              0x1105
+#define CHUNK_FILENAME        0x1106
+#define CHUNK_SHORTNAME       0x1107
+#define CHUNK_EXPORT_FULL     0x1108
+#define CHUNK_NODE_LIST       0x1200
+#define CHUNK_NODE_HANDLE     0x1201
+
+//Global functions
+void ChunkSave(ISave *isave, int chunkid, int value);
+void ChunkSave(ISave *isave, int chunkid, bool value);
+void ChunkSave(ISave *isave, int chunkid, char *value);
+char *ChunkLoadString(ILoad *iload, char *buffer, int maxBytes);
+int ChunkLoadInt(ILoad *iload);
+bool ChunkLoadBool(ILoad *iload);
+void SetICustEdit(HWND wnd, int nIDDlgItem, char *text);
+BOOL CALLBACK MaxEggExpOptionsProc( HWND hWnd, UINT message, 
+					                          WPARAM wParam, LPARAM lParam );
+
+class MaxEggExpOptions
+{
+  friend class MaxEggPlugin;
+  public:
+    enum Anim_Type {
+    AT_none,
+    AT_model,
+    AT_chan,
+    AT_pose,
+    AT_strobe,
+    AT_both
+  };
+
+ protected:
+  Anim_Type anim_type;
+  int sf, ef;
+  int minFrame, maxFrame;
+  bool dblSided, expWholeScene;
+  char shortName[256];
+
+  bool successful;
+
+  ULONG *nodeList;
+  int numNodes;
+  int maxNodes;
+
+ public:
+  bool checked;
+  char filename[2048];
+  IObjParam *maxInterface;
+  bool choosingNodes;
+  Anim_Type prev_type;
+
+  MaxEggExpOptions();
+  virtual ~MaxEggExpOptions() { delete [] nodeList;}
+
+  bool DoExport(IObjParam *ip, bool autoOverwrite, bool saveLog);
+  
+  void UpdateUI(HWND hWnd);
+  bool UpdateFromUI(HWND hWnd);
+  void RefreshNodeList(HWND hWnd);
+  void SetAnimRange();
+
+  bool FindNode(ULONG INodeHandle); //returns true if the node is already in the list
+  void AddNode(ULONG INodeHandle);
+  void RemoveNode(int i);
+  void RemoveNodeByHandle(ULONG INodeHandle);
+  void ClearNodeList(HWND hWnd);
+  void CullBadNodes();
+
+  ULONG GetNode(int i) { return (i >= 0 && i < maxNodes) ? nodeList[i] : ULONG_MAX; }
+
+  IOResult Load(ILoad *iload);
+  IOResult Save(ISave *isave);
+
+  //int DoExport(const TCHAR *name,ExpInterface *ei,
+	//       Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);
+};
+
+#endif // __MaxEggExpOptions__H

+ 42 - 5
pandatool/src/maxegg/MaxToEgg.cpp

@@ -28,13 +28,17 @@ MaxToEgg::MaxToEgg() : SomethingToEgg("3D Studio Max",".max")
   add_transform_options();
   add_transform_options();
 
 
   set_program_description("This program converts 3D Studio Max model files to egg.");
   set_program_description("This program converts 3D Studio Max model files to egg.");
-  add_option("p", "", 0,
+  /*add_option("p", "", 0,
 	     "Generate polygon output only.  Convert scene to triangle mesh "
 	     "Generate polygon output only.  Convert scene to triangle mesh "
-	     "before converting.", &MaxToEgg::dispatch_none, &alertOnBegin);
+	     "before converting.", &MaxToEgg::dispatch_none, &alertOnBegin);*/
+  add_option("bface", "", 0,
+	     "Export all polygons as double-sided ",
+	     &MaxToEgg::dispatch_none, &doubleSided);
   //Fill in the member variables.
   //Fill in the member variables.
   pMaxInterface = null;
   pMaxInterface = null;
   successfulOutput = false;
   successfulOutput = false;
-  alertOnBegin = false;
+  confirmExport = true;
+  doubleSided = false;
 }
 }
 
 
 /* IsSuccessful() - Indicates if conversion was successful.
 /* IsSuccessful() - Indicates if conversion was successful.
@@ -52,7 +56,7 @@ char *MaxToEgg::MyClassName()
 /* Run() - Runs the conversion.  Creates a MaxToEggConverter, populates it
 /* Run() - Runs the conversion.  Creates a MaxToEggConverter, populates it
    with the scene graph, and then writes out the egg file.
    with the scene graph, and then writes out the egg file.
 */
 */
-void MaxToEgg::Run() 
+void MaxToEgg::Run(ULONG *selection_list, int len) 
 {
 {
   MaxToEggConverter converter;
   MaxToEggConverter converter;
 
 
@@ -66,9 +70,11 @@ void MaxToEgg::Run()
   converter.set_egg_data( &_data, false );
   converter.set_egg_data( &_data, false );
   // applies the parameters from the command line options
   // applies the parameters from the command line options
   apply_parameters(converter);
   apply_parameters(converter);
+  converter.set_double_sided(doubleSided);
+  converter.set_selection_list(selection_list, len);
   
   
   //Now, do the actual file conversion.
   //Now, do the actual file conversion.
-  if (converter.convert_file(_input_filename)) {
+  if (confirmExport && converter.convert_file(_input_filename)) {
     successfulOutput=true;
     successfulOutput=true;
     write_egg_file();
     write_egg_file();
     Logger::Log( MTE, Logger::SAT_DEBUG_SPAM_LEVEL, "Egg file written!" );
     Logger::Log( MTE, Logger::SAT_DEBUG_SPAM_LEVEL, "Egg file written!" );
@@ -86,3 +92,34 @@ void MaxToEgg::SetMaxInterface(Interface *pInterface)
 {
 {
   pMaxInterface = pInterface;
   pMaxInterface = pInterface;
 }
 }
+
+bool MaxToEgg::handle_args(Args &args) {
+  char msg[3072];
+
+  //If "auto overwrite" was not checked, ask if the user wishes to overwrite the file
+  if (_allow_last_param && !_got_output_filename && args.size() > 1) {
+    _got_output_filename = true;
+    _output_filename = Filename::from_os_specific(args.back());
+    args.pop_back();
+
+    if (!verify_output_file_safe()) {
+      sprintf(msg, "Overwrite file \"%s\"?", _output_filename.to_os_specific().c_str());
+      confirmExport = 
+        ( MessageBox(pMaxInterface->GetMAXHWnd(), msg, "Panda3D Exporter", MB_YESNO | MB_ICONQUESTION)
+          == IDYES );
+    }
+  }
+
+  //Attempt to catch if the output file is read-only
+  if (confirmExport) {
+    _output_filename.make_dir();
+    if (access(Filename(_output_filename.get_dirname()).to_os_specific().c_str(), 2) ||
+        (access(_output_filename.to_os_specific().c_str(), 2) && errno != ENOENT)) {
+      sprintf(msg, "Error attempting to open \"%s\"\n\nDestination file is in use or read-only", _output_filename.to_os_specific().c_str());
+      MessageBox(pMaxInterface->GetMAXHWnd(), msg, "Panda3D Exporter", MB_OK | MB_ICONERROR);
+      confirmExport = false;
+    }
+  }
+
+  return SomethingToEgg::handle_args(args);
+}

+ 4 - 2
pandatool/src/maxegg/MaxToEgg.h

@@ -36,7 +36,7 @@ class MaxToEgg : public SomethingToEgg
  protected:
  protected:
   // If true, various windows pop-up alerts will announce when certain tasks
   // If true, various windows pop-up alerts will announce when certain tasks
   // begin.
   // begin.
-  bool alertOnBegin;
+  bool confirmExport;
   // The local pointer to the 3ds max interface we keep around to get the
   // The local pointer to the 3ds max interface we keep around to get the
   // scene graph. If we ever get this to run standalone, we'll need an
   // scene graph. If we ever get this to run standalone, we'll need an
   // alternate way to set this other than through MaxEggPlugin. 
   // alternate way to set this other than through MaxEggPlugin. 
@@ -44,6 +44,8 @@ class MaxToEgg : public SomethingToEgg
   // False initially, but premanently switches to true when a file is 
   // False initially, but premanently switches to true when a file is 
   // sucessfully converted.
   // sucessfully converted.
   bool successfulOutput;
   bool successfulOutput;
+  bool doubleSided;
+  virtual bool handle_args(Args &args);
 
 
  public:
  public:
   MaxToEgg();
   MaxToEgg();
@@ -51,7 +53,7 @@ class MaxToEgg : public SomethingToEgg
   bool IsSuccessful();
   bool IsSuccessful();
   //Returns a pointer to the class name.
   //Returns a pointer to the class name.
   char *MyClassName();
   char *MyClassName();
-  void Run();
+  void Run(ULONG *selection_list, int len);
   void SetMaxInterface(Interface *pInterface);
   void SetMaxInterface(Interface *pInterface);
 };
 };
 
 

+ 17 - 4
pandatool/src/maxegg/MaxToEggConverter.h

@@ -25,7 +25,11 @@
 
 
 /* 3ds Max Includes, with bonus(!) memory protection
 /* 3ds Max Includes, with bonus(!) memory protection
  */
  */
+
 #ifdef MAX5
 #ifdef MAX5
+//Disable the "Too many actual parameters in istdplug.h" warning in Max5
+#pragma warning(push)
+#pragma warning(disable: 4002)
 #include "pre_max_include.h"
 #include "pre_max_include.h"
 #endif
 #endif
 #include "Max.h"
 #include "Max.h"
@@ -36,10 +40,13 @@
 #include "resource.h"
 #include "resource.h"
 #include "stdmat.h"
 #include "stdmat.h"
 #include "phyexp.h"
 #include "phyexp.h"
+#include "surf_api.h"
 #ifdef MAX5
 #ifdef MAX5
 #include "post_max_include.h"
 #include "post_max_include.h"
+#pragma warning(pop)
 #endif
 #endif
 
 
+
 /* Panda Includes
 /* Panda Includes
  */
  */
 #include "eggCoordinateSystem.h"
 #include "eggCoordinateSystem.h"
@@ -49,6 +56,7 @@
 #include "eggTexture.h"
 #include "eggTexture.h"
 #include "eggVertex.h"
 #include "eggVertex.h"
 #include "eggVertexPool.h"
 #include "eggVertexPool.h"
+#include "eggNurbsCurve.h"
 #include "pandatoolbase.h"
 #include "pandatoolbase.h"
 #include "somethingToEgg.h"
 #include "somethingToEgg.h"
 #include "somethingToEggConverter.h"
 #include "somethingToEggConverter.h"
@@ -95,8 +103,13 @@ class MaxToEggConverter : public SomethingToEggConverter {
   //Sets the interface to 3dsMax.
   //Sets the interface to 3dsMax.
   void setMaxInterface(Interface *pInterface);
   void setMaxInterface(Interface *pInterface);
 
 
+  void set_selection_list(ULONG *list, int len) {
+    _selection_list = list; _selection_len = len; }
+  void set_double_sided(bool flag) {double_sided = flag;}
+
  private:
  private:
   double _current_frame;
   double _current_frame;
+  ULONG *_selection_list; int _selection_len;
 
 
   bool convert_flip(double start_frame, double end_frame, 
   bool convert_flip(double start_frame, double end_frame, 
                     double frame_inc, double output_frame_rate);
                     double frame_inc, double output_frame_rate);
@@ -122,10 +135,9 @@ class MaxToEggConverter : public SomethingToEggConverter {
                                  const string &nurbs_name,
                                  const string &nurbs_name,
                                  EggGroupNode *egg_group,
                                  EggGroupNode *egg_group,
                                  int trim_curve_index);
                                  int trim_curve_index);
-  void make_nurbs_curve(const MDagPath &dag_path, 
-                        const MFnNurbsCurve &curve,
-                        EggGroup *group);
-  */
+ */
+  bool make_nurbs_curve(NURBSCVCurve *curve, const string &name,
+                        TimeValue time, EggGroup *egg_group);
   void make_polyset(INode *max_node,
   void make_polyset(INode *max_node,
                     Mesh *mesh,
                     Mesh *mesh,
                     EggGroup *egg_group,
                     EggGroup *egg_group,
@@ -162,6 +174,7 @@ class MaxToEggConverter : public SomethingToEggConverter {
   MaxNodeTree _tree;
   MaxNodeTree _tree;
   
   
   int _cur_tref;
   int _cur_tref;
+  bool double_sided;
 
 
  public:
  public:
   //MayaShaders _shaders;
   //MayaShaders _shaders;

+ 4 - 0
pandatool/src/maxegg/maxNodeDesc.h

@@ -29,12 +29,16 @@
 #include "namable.h"
 #include "namable.h"
 
 
 #ifdef MAX5
 #ifdef MAX5
+//Disable the "Too many actual parameters in istdplug.h" warning in Max5
+#pragma warning(push)
+#pragma warning(disable: 4002)
 #include "pre_max_include.h"
 #include "pre_max_include.h"
 #endif
 #endif
 #include <Max.h>
 #include <Max.h>
 #include "bipexp.h"
 #include "bipexp.h"
 #ifdef MAX5
 #ifdef MAX5
 #include "post_max_include.h"
 #include "post_max_include.h"
+#pragma warning(pop)
 #endif
 #endif
 
 
 class EggGroup;
 class EggGroup;

+ 35 - 6
pandatool/src/maxegg/maxNodeTree.cxx

@@ -23,11 +23,20 @@
 #include "eggXfmSAnim.h"
 #include "eggXfmSAnim.h"
 #include "eggData.h"
 #include "eggData.h"
 
 
+#ifdef MAX5
+//Disable the "Too many actual parameters in istdplug.h" warning in Max5
+#pragma warning(push)
+#pragma warning(disable: 4002)
 #include "pre_max_include.h"
 #include "pre_max_include.h"
+#endif
 #include "Max.h"
 #include "Max.h"
+#ifdef MAX5
 #include "post_max_include.h"
 #include "post_max_include.h"
+#pragma warning(pop)
+#endif
 #include "maxToEggConverter.h"
 #include "maxToEggConverter.h"
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MaxNodeTree::Constructor
 //     Function: MaxNodeTree::Constructor
 //       Access: Public
 //       Access: Public
@@ -73,13 +82,30 @@ build_joint(INode *max_node, MaxNodeDesc *node_joint) {
   return node_desc;
   return node_desc;
 }
 }
 
 
+bool MaxNodeTree::node_in_list(ULONG handle, ULONG *list, int len) {
+  if (!list) return true;
+  for (int i = 0; i < len; i++)
+    if (list[i] == handle) return true;
+  return false;
+}
+
+bool MaxNodeTree::is_joint(INode *node) {
+  Control *c = node->GetTMController();
+  return (node->GetBoneNodeOnOff() ||                    //joints
+         (c &&                                           //bipeds
+         ((c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) ||
+         (c->ClassID() == BIPBODY_CONTROL_CLASS_ID) ||
+         (c->ClassID() == FOOTPRINT_CLASS_ID))));
+}
+
 bool MaxNodeTree::
 bool MaxNodeTree::
-r_build_hierarchy(INode *root) {
-  build_node(root);
+r_build_hierarchy(INode *root, ULONG *selection_list, int len) {
+  if (node_in_list(root->GetHandle(), selection_list, len))
+    build_node(root);
   // Export children
   // Export children
   for ( int i = 0; i < root->NumberOfChildren(); i++ ) {
   for ( int i = 0; i < root->NumberOfChildren(); i++ ) {
     // *** Should probably be checking the return value of the following line
     // *** Should probably be checking the return value of the following line
-    r_build_hierarchy(root->GetChildNode(i));
+    r_build_hierarchy(root->GetChildNode(i), selection_list, len);
   }
   }
   return true;
   return true;
 }
 }
@@ -90,7 +116,7 @@ r_build_hierarchy(INode *root) {
 //               up the corresponding tree.
 //               up the corresponding tree.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MaxNodeTree::
 bool MaxNodeTree::
-build_complete_hierarchy(INode *root) {
+build_complete_hierarchy(INode *root, ULONG *selection_list, int len) {
 
 
   // Get the entire Max scene.
   // Get the entire Max scene.
   if (root == NULL) {
   if (root == NULL) {
@@ -99,7 +125,7 @@ build_complete_hierarchy(INode *root) {
   }
   }
     
     
   bool all_ok = true;
   bool all_ok = true;
-  r_build_hierarchy(root);
+  r_build_hierarchy(root, selection_list, len);
 
 
   if (all_ok) {
   if (all_ok) {
     _root->check_pseudo_joints(false);
     _root->check_pseudo_joints(false);
@@ -462,5 +488,8 @@ find_node(INode* max_node)
 MaxNodeDesc *MaxNodeTree::
 MaxNodeDesc *MaxNodeTree::
 find_joint(INode* max_node) 
 find_joint(INode* max_node) 
 {
 {
-  return find_node(max_node)->_joint_entry;
+  MaxNodeDesc *node = find_node(max_node);
+  if (!node || (is_joint(max_node) && !node->is_node_joint()))
+    node = build_node(max_node);
+  return node->_joint_entry;
 }
 }

+ 4 - 2
pandatool/src/maxegg/maxNodeTree.h

@@ -37,7 +37,7 @@ public:
   MaxNodeTree();
   MaxNodeTree();
   MaxNodeDesc *build_node(INode *max_node);
   MaxNodeDesc *build_node(INode *max_node);
   MaxNodeDesc *build_joint(INode *max_node, MaxNodeDesc *node_joint);
   MaxNodeDesc *build_joint(INode *max_node, MaxNodeDesc *node_joint);
-  bool build_complete_hierarchy(INode *root);
+  bool build_complete_hierarchy(INode *root, ULONG *selection_list, int len);
   bool build_selected_hierarchy(INode *root);
   bool build_selected_hierarchy(INode *root);
   MaxNodeDesc *find_node(INode *max_node);
   MaxNodeDesc *find_node(INode *max_node);
   MaxNodeDesc *find_joint(INode *max_node);
   MaxNodeDesc *find_joint(INode *max_node);
@@ -61,7 +61,9 @@ private:
 
 
   MaxNodeDesc *r_build_node(INode *max_node);
   MaxNodeDesc *r_build_node(INode *max_node);
   MaxNodeDesc *r_build_joint(MaxNodeDesc *node_desc, INode *max_node);
   MaxNodeDesc *r_build_joint(MaxNodeDesc *node_desc, INode *max_node);
-  bool r_build_hierarchy(INode *root);
+  bool node_in_list(ULONG handle, ULONG *list, int len);
+  bool r_build_hierarchy(INode *root, ULONG *selection_list, int len);
+  bool is_joint(INode *node);
 
 
   typedef pmap<ULONG, MaxNodeDesc *> NodesByPath;
   typedef pmap<ULONG, MaxNodeDesc *> NodesByPath;
   NodesByPath _nodes_by_path;
   NodesByPath _nodes_by_path;

+ 83 - 52
pandatool/src/maxegg/maxToEggConverter.cxx

@@ -39,6 +39,8 @@ MaxToEggConverter(const string &program_name) :
   _transform_type = TT_model;
   _transform_type = TT_model;
   _cur_tref = 0;
   _cur_tref = 0;
   _current_frame = 0;
   _current_frame = 0;
+  _selection_list = NULL;
+  _selection_len = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -195,7 +197,7 @@ convert_max(bool from_selection) {
   if (_from_selection) {
   if (_from_selection) {
     all_ok = _tree.build_selected_hierarchy(maxInterface->GetRootNode());
     all_ok = _tree.build_selected_hierarchy(maxInterface->GetRootNode());
   } else {
   } else {
-    all_ok = _tree.build_complete_hierarchy(maxInterface->GetRootNode());
+    all_ok = _tree.build_complete_hierarchy(maxInterface->GetRootNode(), _selection_list, _selection_len);
   }
   }
 
 
   if (all_ok) {
   if (all_ok) {
@@ -398,22 +400,23 @@ convert_char_chan(double start_frame, double end_frame, double frame_inc,
       // Find all joints in the hierarchy
       // Find all joints in the hierarchy
       MaxNodeDesc *node_desc = _tree.get_node(i);
       MaxNodeDesc *node_desc = _tree.get_node(i);
       if (node_desc->is_joint()) {
       if (node_desc->is_joint()) {
-	tgroup = new EggGroup();
-	INode *max_node = node_desc->get_max_node();
-
-	if (node_desc->_parent && node_desc->_parent->is_joint()) {
-	  // If this joint also has a joint as a parent, the parent's 
-	  // transformation has to be divided out of this joint's TM
-	  get_joint_transform(max_node, node_desc->_parent->get_max_node(), 
-			      tgroup);
-	} else {
-	  get_joint_transform(max_node, NULL, tgroup);
-	}
+	      tgroup = new EggGroup();
+	      INode *max_node = node_desc->get_max_node();
+
+	      if (node_desc->_parent && node_desc->_parent->is_joint()) {
+	      // If this joint also has a joint as a parent, the parent's 
+	      // transformation has to be divided out of this joint's TM
+	      get_joint_transform(max_node, node_desc->_parent->get_max_node(), 
+			                      tgroup);
+	      } else {
+	        get_joint_transform(max_node, NULL, tgroup);
+	      }
+        
         EggXfmSAnim *anim = _tree.get_egg_anim(node_desc);
         EggXfmSAnim *anim = _tree.get_egg_anim(node_desc);
         if (!anim->add_data(tgroup->get_transform())) {
         if (!anim->add_data(tgroup->get_transform())) {
-	  // *** log an error
-	}
-	delete tgroup;
+	        // *** log an error
+	      }
+	      delete tgroup;
       }
       }
     }
     }
     
     
@@ -441,10 +444,10 @@ convert_char_chan(double start_frame, double end_frame, double frame_inc,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MaxToEggConverter::
 bool MaxToEggConverter::
 convert_hierarchy(EggGroupNode *egg_root) {
 convert_hierarchy(EggGroupNode *egg_root) {
-  int num_nodes = _tree.get_num_nodes();
+  //int num_nodes = _tree.get_num_nodes();
 
 
   _tree.clear_egg(&get_egg_data(), egg_root, NULL);
   _tree.clear_egg(&get_egg_data(), egg_root, NULL);
-  for (int i = 0; i < num_nodes; i++) {
+  for (int i = 0; i < _tree.get_num_nodes(); i++) {
     if (!process_model_node(_tree.get_node(i))) {
     if (!process_model_node(_tree.get_node(i))) {
       return false;
       return false;
     }
     }
@@ -480,15 +483,15 @@ process_model_node(MaxNodeDesc *node_desc) {
   state = max_node->EvalWorldState(_current_frame * GetTicksPerFrame());
   state = max_node->EvalWorldState(_current_frame * GetTicksPerFrame());
 
 
   if (node_desc->is_joint()) {
   if (node_desc->is_joint()) {
+    EggGroup *egg_group = _tree.get_egg_group(node_desc);
     // Don't bother with joints unless we're getting an animatable
     // Don't bother with joints unless we're getting an animatable
     // model.
     // model.
     if (_animation_convert == AC_model) { 
     if (_animation_convert == AC_model) { 
-      EggGroup *egg_group = _tree.get_egg_group(node_desc);
       get_joint_transform(max_node, egg_group);
       get_joint_transform(max_node, egg_group);
     }
     }
   } else {
   } else {
     if (state.obj) {
     if (state.obj) {
-      EggGroup *egg_group;
+      EggGroup *egg_group = NULL;
       TriObject *myMaxTriObject;
       TriObject *myMaxTriObject;
       Mesh max_mesh;
       Mesh max_mesh;
       //Call the correct exporter based on what type of object this is.
       //Call the correct exporter based on what type of object this is.
@@ -497,10 +500,10 @@ process_model_node(MaxNodeDesc *node_desc) {
         case GEOMOBJECT_CLASS_ID:
         case GEOMOBJECT_CLASS_ID:
 	  Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, 
 	  Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, 
 		       "Found a geometric object in the hierarchy!" );
 		       "Found a geometric object in the hierarchy!" );
-	  egg_group = _tree.get_egg_group(node_desc);
+    egg_group = _tree.get_egg_group(node_desc);
 	  get_transform(max_node, egg_group);
 	  get_transform(max_node, egg_group);
-	  
-	  //Try converting this geometric object to a mesh we can use.
+    
+    //Try converting this geometric object to a mesh we can use.
 	  if (!state.obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) {
 	  if (!state.obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) {
 	    Logger::Log(MTEC, Logger::SAT_OTHER_ERROR, 
 	    Logger::Log(MTEC, Logger::SAT_OTHER_ERROR, 
 			"Cannot create geometry from state.obj!");
 			"Cannot create geometry from state.obj!");
@@ -523,7 +526,25 @@ process_model_node(MaxNodeDesc *node_desc) {
 	  if (myMaxTriObject != state.obj)
 	  if (myMaxTriObject != state.obj)
 	    delete myMaxTriObject;
 	    delete myMaxTriObject;
 	  break;
 	  break;
-	  
+
+        case SHAPE_CLASS_ID:
+    if (state.obj->ClassID() == EDITABLE_SURF_CLASS_ID) {
+	    Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, 
+		         "Found a NURB object in the hierarchy!" );
+      NURBSSet getSet;
+      if (GetNURBSSet(state.obj, time, getSet, TRUE)) {
+        NURBSObject *nObj = getSet.GetNURBSObject(0);
+        if (nObj->GetType() == kNCVCurve) {
+          //It's a CV Curve, process it
+          egg_group = _tree.get_egg_group(node_desc);
+      	  get_transform(max_node, egg_group);
+          make_nurbs_curve((NURBSCVCurve *)nObj, string(max_node->GetName()),
+                           time, egg_group);
+        }
+      }
+    }
+    break;
+
         case CAMERA_CLASS_ID:
         case CAMERA_CLASS_ID:
 	  Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, 
 	  Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, 
 		       "Found a camera object in the hierarchy!" );
 		       "Found a camera object in the hierarchy!" );
@@ -538,6 +559,13 @@ process_model_node(MaxNodeDesc *node_desc) {
 	  Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, 
 	  Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, 
 		       "Found a helper object in the hierarchy!" );
 		       "Found a helper object in the hierarchy!" );
 	  break;
 	  break;
+/*        default:
+          char buf[1024];
+          sprintf(buf, "Unknown Superclass ID: %x, ClassID: %x,%x", state.obj->SuperClassID(),
+            state.obj->ClassID().PartA(), state.obj->ClassID().PartB());
+	  Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, 
+		       buf ); */
+
       }
       }
     }
     }
   }
   }
@@ -1246,7 +1274,7 @@ make_trim_curve(const MFnNurbsCurve &curve, const string &nurbs_name,
   }
   }
 
 
   return egg_curve;
   return egg_curve;
-}
+} */
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MaxToEggConverter::make_nurbs_curve
 //     Function: MaxToEggConverter::make_nurbs_curve
@@ -1255,10 +1283,12 @@ make_trim_curve(const MFnNurbsCurve &curve, const string &nurbs_name,
 //               curve, not a trim curve) to a corresponding egg
 //               curve, not a trim curve) to a corresponding egg
 //               structure and attaches it to the indicated egg group.
 //               structure and attaches it to the indicated egg group.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void MaxToEggConverter::
-make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve,
-                 EggGroup *egg_group) {
-  MStatus status;
+bool MaxToEggConverter::
+make_nurbs_curve(NURBSCVCurve *curve, const string &name,
+                 TimeValue time, EggGroup *egg_group) 
+{
+                   
+/*  MStatus status;
   string name = curve.name().asChar();
   string name = curve.name().asChar();
 
 
   if (mayaegg_cat.is_spam()) {
   if (mayaegg_cat.is_spam()) {
@@ -1290,12 +1320,19 @@ make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve,
   }
   }
 
 
   //  MFnNurbsCurve::Form form = curve.form();
   //  MFnNurbsCurve::Form form = curve.form();
+*/
 
 
-  int degree = curve.degree();
-  int cvs = curve.numCVs();
-  int knots = curve.numKnots();
+  int degree = curve->GetOrder();
+  int cvs = curve->GetNumCVs();
+  int knots = curve->GetNumKnots();
+  int i;
 
 
-  assert(knots == cvs + degree - 1);
+  if (knots != cvs + degree) {
+    char buf[1024];
+    sprintf(buf, "NURBS knots count incorrect. Order %d, CVs %d, knots %d", degree, cvs, knots);
+    Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, buf);
+    return false;
+  }
 
 
   string vpool_name = name + ".cvs";
   string vpool_name = name + ".cvs";
   EggVertexPool *vpool = new EggVertexPool(vpool_name);
   EggVertexPool *vpool = new EggVertexPool(vpool_name);
@@ -1303,38 +1340,32 @@ make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve,
 
 
   EggNurbsCurve *egg_curve = new EggNurbsCurve(name);
   EggNurbsCurve *egg_curve = new EggNurbsCurve(name);
   egg_group->add_child(egg_curve);
   egg_group->add_child(egg_curve);
-  egg_curve->setup(degree + 1, knots + 2);
+  egg_curve->setup(degree, knots);
 
 
-  int i;
-
-  egg_curve->set_knot(0, knot_array[0]);
-  for (i = 0; i < knots; i++) {
-    egg_curve->set_knot(i + 1, knot_array[i]);
-  }
-  egg_curve->set_knot(knots + 1, knot_array[knots - 1]);
+  for (i = 0; i < knots; i++)
+    egg_curve->set_knot(i, curve->GetKnot(i));
 
 
   LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
   LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
 
 
-  for (i = 0; i < egg_curve->get_num_cvs(); i++) {
-    double v[4];
-    MStatus status = cv_array[i].get(v);
-    if (!status) {
-      status.perror("MPoint::get");
+  for (i = 0; i < cvs; i++) {
+    NURBSControlVertex *cv = curve->GetCV(i);
+    if (!cv) {
+      char buf[1024];
+      sprintf(buf, "Error getting CV %d", i);
+      Logger::Log( MTEC, Logger::SAT_HIGH_LEVEL, buf);
+      return false;
     } else {
     } else {
       EggVertex vert;
       EggVertex vert;
-      LPoint4d p4d(v[0], v[1], v[2], v[3]);
+      LPoint4d p4d(0, 0, 0, 1.0);
+      cv->GetPosition(time, p4d[0], p4d[1], p4d[2]);
       p4d = p4d * vertex_frame_inv;
       p4d = p4d * vertex_frame_inv;
       vert.set_pos(p4d);
       vert.set_pos(p4d);
       egg_curve->add_vertex(vpool->create_unique_vertex(vert));
       egg_curve->add_vertex(vpool->create_unique_vertex(vert));
     }
     }
   }
   }
 
 
-  MayaShader *shader = _shaders.find_shader_for_node(curve.object());
-  if (shader != (MayaShader *)NULL) {
-    set_shader_attributes(*egg_curve, *shader);
-  }
+  return true;
 }
 }
-*/
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MaxToEggConverter::make_polyset
 //     Function: MaxToEggConverter::make_polyset
@@ -1348,7 +1379,7 @@ make_polyset(INode *max_node, Mesh *mesh,
              EggGroup *egg_group, Shader *default_shader) {
              EggGroup *egg_group, Shader *default_shader) {
 	Logger::Log( MTEC, Logger::SAT_MEDIUM_LEVEL, "Entered make_poly_set." );
 	Logger::Log( MTEC, Logger::SAT_MEDIUM_LEVEL, "Entered make_poly_set." );
 
 
-  bool double_sided = false;
+  //bool double_sided = false;
 
 
   // *** I think this needs to have a plugin written to support
   // *** I think this needs to have a plugin written to support
   /*
   /*

+ 1 - 0
pandatool/src/maxegg/maxegg_composite1.cxx

@@ -1,6 +1,7 @@
 #include "DllEntry.cpp"
 #include "DllEntry.cpp"
 #include "Logger.cpp"
 #include "Logger.cpp"
 #include "MaxEgg.cpp"
 #include "MaxEgg.cpp"
+#include "MaxEggExpOptions.cpp"
 #include "maxNodeDesc.cxx"
 #include "maxNodeDesc.cxx"
 #include "MaxNodeTree.cxx"
 #include "MaxNodeTree.cxx"
 #include "MaxToEgg.cpp"
 #include "MaxToEgg.cpp"

+ 53 - 48
pandatool/src/maxegg/resource.h

@@ -1,48 +1,53 @@
-//{{NO_DEPENDENCIES}}
-// Microsoft Visual C++ generated include file.
-// Used by MaxEgg.rc
-//
-#define IDS_LIBDESCRIPTION              1
-#define IDS_CATEGORY                    2
-#define IDS_CLASS_NAME                  3
-#define IDS_PARAMS                      4
-#define IDS_SPIN                        5
-#define IDD_PANEL                       101
-#define IDC_CLOSEBUTTON                 1000
-#define IDC_DOSTUFF                     1000
-#define IDC_CHECK1                      1001
-#define IDC_MAKE_BAM                    1001
-#define IDC_DONE                        1002
-#define IDC_ANIMATION                   1003
-#define IDC_MODEL                       1004
-#define IDC_CHAN                        1005
-#define IDC_POSE                        1006
-#define IDC_STROBE                      1007
-#define IDC_BOTH                        1008
-#define IDC_EDIT1                       1009
-#define IDC_CN                          1009
-#define IDC_SF                          1010
-#define IDC_IF                          1011
-#define IDC_EF                          1012
-#define IDC_SF_LABEL                    1013
-#define IDC_EF_LABEL                    1014
-#define IDC_IF_LABEL                    1015
-#define IDC_CN_LABEL                    1016
-#define IDC_RADIO1                      1017
-#define IDC_DONE2                       1017
-#define IDC_CANCEL                      1017
-#define IDC_RADIO2                      1018
-#define IDC_COLOR                       1456
-#define IDC_EDIT                        1490
-#define IDC_SPIN                        1496
-
-// Next default values for new objects
-// 
-#ifdef APSTUDIO_INVOKED
-#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        101
-#define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1019
-#define _APS_NEXT_SYMED_VALUE           101
-#endif
-#endif
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by MaxEgg.rc
+//
+#define IDS_LIBDESCRIPTION              1
+#define IDS_CATEGORY                    2
+#define IDS_CLASS_NAME                  3
+#define IDS_PARAMS                      4
+#define IDS_SPIN                        5
+#define IDD_PANEL                       101
+#define IDD_EGG_DETAILS                 102
+#define IDC_EGGS_LABEL                  1019
+#define IDC_ADD_EGG                     1021
+#define IDC_EDIT_EGG                    1022
+#define IDC_REMOVE_EGG                  1023
+#define IDC_LIST_EGGS                   1024
+#define IDC_OVERWRITE_CHECK             1027
+#define IDC_EXPORT                      1028
+#define IDC_PANEL_TITLE                 1029
+#define IDC_OVERWRITE_CHECK2            1030
+#define IDC_PVIEW_CHECK                 1030
+#define IDC_MODEL                       1033
+#define IDC_ANIMATION                   1034
+#define IDC_BOTH                        1035
+#define IDC_POSE                        1036
+#define IDC_FILENAME                    1038
+#define IDC_BROWSE                      1039
+#define IDC_EXPORT_ALL                  1040
+#define IDC_EXPORT_SELECTED             1041
+#define IDC_ADD_EXPORT                  1043
+#define IDC_LIST_EXPORT                 1044
+#define IDC_REMOVE_EXPORT               1046
+#define IDC_EXP_ALL_FRAMES              1049
+#define IDC_EXP_SEL_FRAMES              1050
+#define IDC_SF                          1051
+#define IDC_SF_LABEL                    1052
+#define IDC_EF                          1053
+#define IDC_CHECK1                      1054
+#define IDC_EF_LABEL                    1057
+#define IDC_OK                          1058
+#define IDC_CANCEL                      1059
+#define IDC_LOGGING                     1060
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        103
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1061
+#define _APS_NEXT_SYMED_VALUE           102
+#endif
+#endif