Cinema 4D - my C++ plugins - object converter

From NoskeWiki
Jump to navigation Jump to search
Object converter plugin dialog

About

NOTE: This page is a daughter page of: Cinema 4D


This page contains code for a C++ plugin I've written for Cinema 4D. To understand how it compiles see Cinema 4D - my C++ plugins.

objectconverter.cpp - for reducing polygon count of objects and then flattening everything into meshes

//------------------
//-- FILE:    objectconverter.cpp
//-- AUTHORS: Andrew Noske
//--
//-- This Cinema 4D menu plugin provides a way to prepare a model
//-- for importing into Unity ( see: http://unity3d.com/ ) by deleting,
//-- flattening and normal reversing certain objects in the scene
//-- which have been marked in the object name.
//-- 
//-- 
//-- For more information see: http://www.andrewnoske.com/wiki/index.php?title=Unity

//########################################
//-- Includes and Globals Variables:

#include "c4d.h"

#define ID_OBJECTCONVERTERDIALOGTEST 1026966
                                    // My unique registered plugin ID "Object Converter".

enum 
{
  DLG_CHK_DEL1          = 1000,
  DLG_CHK_TURNON        = 1001,
  DLG_CHK_MAKEEDIT,
  DLG_CHK_FLATTEN,
  DLG_CHK_REVERSE,
  DLG_CHK_COPY,
  DLG_CHK_DEL2,
  
  DLG_EDIT_DEL1STR,
  DLG_EDIT_TURNONSTR,
  DLG_EDIT_MAKEEDITSTR,
  DLG_EDIT_FLATTENSTR,
  DLG_EDIT_REVERSESTR,
  DLG_EDIT_COPYSTR,
  DLG_EDIT_DEL2STR,
  
  DLG_CHK_CHANGE_DELOFF_D,
  DLG_CHK_CHANGE_TEXTSPLINES,
  DLG_CHK_CHANGE_SPLINES,
  DLG_CHK_CHANGE_CYLINDERS,
  DLG_CHK_FLATTEN_EXTRUDED,
  DLG_CHK_FLATTEN_INSTANCES,
  
  DLG_SPN_TEXTSPLINE_ANGLE,
  DLG_SPN_SPLINE_ANGLE,
  DLG_SPN_CYLINDER_SEGS,
  
  DLG_CHK_CURROBJONLY,
  DLG_CHK_ALLOWANGLEDEC,
  DLG_CHK_OPTIMIZEONFLATTEN,
  DLG_CHK_TURNONEVERYTHING,
  
  DLG_BTN_HELP,
  DLG_BTN_CONVERT,
  DLG_LBL1,
  DLG_LBL2,
  DLG_LBL3,
  DLG_LBL4,
  DLG_LBL5,
  DLG_LBL6,
  DLG_LBL7,
  DLG_GRP1,
  DLG_GRP2,
  DLG_GRP3
};

//########################################
//-- Functions:

//------------------
//-- Returns the value of the matching element ID within "dialog" as a String.
//-- NOTE: Designed for EditText() or StaticText().

inline String GetStringFromElement( GeDialog *dialog, int id )
{
  String str;
  dialog->GetString(id,str);
  return str;
}

//------------------
//-- Returns the value of the matching element ID within "dialog" as a Bool.
//-- NOTE: Designed for Checkbox().

inline bool GetBoolFromElement( GeDialog *dialog, int id )
{
  Bool boolVal;
  dialog->GetBool(id,boolVal);
  return boolVal;
}

//------------------
//-- Returns the value of the matching element ID within "dialog" as a String.
//-- NOTE: Designed for EditText() or StaticText().

inline Real GetRealFromElement( GeDialog *dialog, int id )
{
  Real returnVal;
  dialog->GetReal(id,returnVal);
  return returnVal;
}

//------------------
//-- Returns 1 if the object name contains the string "find", else returns 0.

int ObjectNameContainsString( BaseObject *obj, String *find )
{
  String objName = obj->GetName();
  LONG pos;
  Bool stringFound = objName.FindFirst( *find, &pos, 0);
  return ( stringFound ? 1 : 0 );
}

//------------------
//-- Inputs a string "str" and replaces the first occurance of "find" with "replace".
//-- Returns 1 if a replacement was made, or 0 if "find" was not found.

int StringReplaceFirstOccurance( String *str, String find, String replace )
{
  LONG pos;
  Bool stringFound = str->FindFirst( find, &pos, 0);
  if( stringFound )
  {
    str->Delete( pos, find.GetLength() );
    str->Insert( pos, replace );
    return 1;
  }
  return 0;
}

//------------------
//-- Takes an object, selects it and then runs "MAKE EDITABLE", "SELECT CHILDREN"
//-- and finally "CONNECT+DELETE".
//-- Returns 1 if successful, else returns 0.

int FlattenObject( BaseDocument *doc, BaseObject *obj, bool optimize )
{
  if(obj==NULL)
    return 0;
  doc->SetActiveObject( obj );
  doc->AddUndo( UNDOTYPE_CHANGE, obj );
  CallCommand( 12236 );      // "MAKE EDITABLE".
  CallCommand( 16388 );      // "SELECT CHILDREN".
  CallCommand( 16768 );      // "CONNECT + DELETE".
  obj = doc->GetActiveObject();    // Must reselect because we've just changed it.
  if(obj!=NULL && optimize)
    CallCommand( 14039 );      // "OPTIMIZE".
  return 1;
}

//------------------
//-- Takes an object, selects it and then runs "MAKE EDITABLE".
//-- Returns 1 if successful, else returns 0.

int MakeObjectEditable( BaseDocument *doc, BaseObject *obj )
{
  if(obj==NULL)
    return 0;
  doc->SetActiveObject( obj );
  doc->AddUndo( UNDOTYPE_CHANGE, obj );
  CallCommand( 12236 );      // "MAKE EDITABLE"
  return 1;
}


//------------------
//-- Recursive function which inputs an object and traverses down the heirarchy
//-- (depth first search) and applies the command "commandID" to any objects
//-- containing the string "find".
//-- The number of objects flattened is added to "numObjsChanged".

void ChangeObjectsContainingString( BaseDocument *doc, BaseObject *obj, String *find,
                                    int commandID, int *numObjsChanged )
{
  for(; (obj); obj=obj->GetNext() )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      ChangeObjectsContainingString( doc, child, find, commandID, numObjsChanged );    // Recursive call.
    
    if( ObjectNameContainsString( obj, find) )    // If match found:
    {
      doc->SetActiveObject( obj );                  // Select this object.
      doc->AddUndo( UNDOTYPE_CHANGE, obj );          // Register undo (assumes a change).
      CallCommand( commandID );                      // EXECUTE COMMAND.
      (*numObjsChanged)++;                          // Tally number of objects changed.
    }
  }
}


//------------------
//-- Recursive function which deleting any objects containing the string "find".
//-- The number of objects deleted is added to "numObjsDeleted".

void DeleteObjectsContainingString( BaseDocument *doc, BaseObject *obj, String *find,
                                    int *numObjsDeleted )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      DeleteObjectsContainingString( doc, child, find, numObjsDeleted );    // Recursive call.
    
    if( ObjectNameContainsString( obj, find ) )
    {
      BaseObject *objToRemove = obj;
      obj = obj->GetNext();
      doc->AddUndo( UNDOTYPE_DELETE, objToRemove );
      objToRemove->Remove();
      *(numObjsDeleted)++;
      continue;
    }
    obj = obj->GetNext();
  }
}


//------------------
//-- Recursive function which makes a copy of any objects containing the string "find"
//-- and gives this the copy a similar name but replacing the string "find" with
//-- the string "find". By default this replace still is set to"{COPY}(r}{d}" -
//-- helping prevent multiple copies of the same object and using the "{r}"
//-- so that its normals will flagged for reversing.
//-- The number of objects deleted is added to "numObjsDeleted".

void CopyObjectsContainingString( BaseDocument *doc, BaseObject *obj, String *find,
                                  int *numObjsCopied, bool reverse,
                                  String replace="{COPY}(r}{d}" )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      CopyObjectsContainingString( doc, child, find, numObjsCopied, reverse, replace );    // Recursive call.
    
    if( ObjectNameContainsString( obj, find ) )
    {
      doc->InsertObject( (BaseObject*)obj->GetClone( COPYFLAGS_0, NULL ), NULL, obj );
      BaseObject *newObj = obj->GetNext();
      doc->AddUndo( UNDOTYPE_NEW, newObj );         // Register undo (called after adding).
      doc->AddUndo( UNDOTYPE_CHANGE, newObj );      // Register undo (called after adding).
      String objName = newObj->GetName();
      StringReplaceFirstOccurance( &objName, *find, replace );
      newObj->SetName( objName );
      if(reverse)
      {
        doc->SetActiveObject( newObj );            // Select this new object.
        doc->AddUndo( UNDOTYPE_CHANGE, newObj );   // Register undo.
        CallCommand( 14041 );                      // REVERSE NORMALS.
      }
      (*numObjsCopied)++;
    }
    obj = obj->GetNext();
  }
}


//------------------
//-- Recursive function which flattens any objects containing the string "find".
//-- The number of objects flattened is added to "numObjsFlattened".

void FlattenObjectsContainingString( BaseDocument *doc, BaseObject *obj, String *find,
                                     bool optimize, int *numObjsChanged )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      FlattenObjectsContainingString( doc, child, find, optimize, numObjsChanged );    // Recursive call.
    
    if( ObjectNameContainsString( obj, find) )
    {
      BaseObject *objToFlatten = obj;
      obj = obj->GetNext();
      (*numObjsChanged) += FlattenObject( doc, objToFlatten, optimize );
      continue;
    }
    obj = obj->GetNext();
  }
}

//------------------
//-- Recursive function which flattens any objects containing the string "find".
//-- The number of objects flattened is added to "numObjsFlattened".

void MakeEditableObjectsContainingString( BaseDocument *doc, BaseObject *obj, String *find,
                                          int *numObjsChanged )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      MakeEditableObjectsContainingString( doc, child, find, numObjsChanged );    // Recursive call.
    
    if( ObjectNameContainsString( obj, find) )
    {
      BaseObject *objToFlatten = obj;
      obj = obj->GetNext();
      (*numObjsChanged) += MakeObjectEditable( doc, objToFlatten );
      continue;
    }
    obj = obj->GetNext();
  }
}

//------------------
//-- Recursive function which finds any objects which match the specified
//-- type "otype" and flattens them.
//-- The number of objects flattened is added to "numObjsFlattened".

void FlattenObjectsMatchingType( BaseDocument *doc, BaseObject *obj, int otype,
                                 bool optimize, int *numObjsFlattened )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      FlattenObjectsMatchingType( doc, child, otype, optimize, numObjsFlattened );    // Recursive call.
    
    if( obj->GetType() == otype )
    {
      BaseObject *objToFlatten = obj;
      obj = obj->GetNext();
      (*numObjsFlattened) += FlattenObject( doc, objToFlatten, optimize );
      continue;
    }
    obj = obj->GetNext();
  }
}


//------------------
//-- Recursive function which finds any text spline objects and changes its
//-- angle to match the specified angle "angleInDegs" (specified in degrees).
//-- The number of objects flattened is added to "numSplinesChanged".

void ChangeAngleTextSplines( BaseDocument *doc, BaseObject *obj, Real angleInDegs,
                             bool allowAngleDec, int *numSplinesChanged )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      ChangeAngleTextSplines( doc, child, angleInDegs, allowAngleDec, numSplinesChanged );    // Recursive call.
    
    int otype = obj->GetType();
    if( otype == Osplinetext )
    {
      BaseContainer *bc = obj->GetDataInstance(); 
      Real currAngle = bc->GetReal(SPLINEOBJECT_ANGLE) / (pi/180);
      if( currAngle < angleInDegs || (allowAngleDec && currAngle > angleInDegs) )
      {
        doc->AddUndo( UNDOTYPE_CHANGE_SMALL, obj );
        Real angleInRads = angleInDegs * (pi/180);
        obj->SetParameter(SPLINEOBJECT_ANGLE, GeData(angleInRads), DESCFLAGS_SET_0);
        (*numSplinesChanged)++;
      }
    }
    obj = obj->GetNext();
  }
}

//------------------
//-- Recursive function which finds any spline objects - including pretty
//-- much every type of spline object except text splines -
//-- and changes its angle to match the specified angle "angleInDegs"
//-- (specified in degrees).
//-- The number of objects flattened is added to "numSplinesChanged".

void ChangeAngleSplines( BaseDocument *doc, BaseObject *obj, Real angleInDegs,
                         bool allowAngleDec, int *numSplinesChanged )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      ChangeAngleSplines( doc, child, angleInDegs, allowAngleDec, numSplinesChanged );    // Recursive call.
    
    int otype = obj->GetType();
    if( otype == Osplineprimitive || otype == Osplineprofile || otype == Osplineflower
     || otype == Osplineformula || otype == Osplinenside || otype == Ospline4side
     || otype == Osplinecircle || otype == Osplinearc || otype == Osplinecissoid
     || otype == Osplinecycloid || otype == Osplinehelix || otype == Osplinerectangle
     || otype == Osplinestar || otype == Osplinecogwheel || otype == Osplinecontour )
    {
      BaseContainer *bc = obj->GetDataInstance(); 
      Real currAngle = bc->GetReal(SPLINEOBJECT_ANGLE) / (pi/180);
      //GePrint( "Spline Angle = " + RealToString(currAngle) );
      if( currAngle < angleInDegs || (allowAngleDec && currAngle > angleInDegs) )
      {
        doc->AddUndo( UNDOTYPE_CHANGE_SMALL, obj );
        Real angleInRads = angleInDegs * (pi/180);
        obj->SetParameter(SPLINEOBJECT_ANGLE, GeData(angleInRads), DESCFLAGS_SET_0);
        (*numSplinesChanged)++;
      }
    }
    obj = obj->GetNext();
  }
}

//------------------
//-- Recursive function which finds any cylinder spline objects and changes its
//-- number of segments to match "numSegs".
//-- The number of objects flattened is added to "numSplinesChanged".

void ChangeObjectsCylinderSegs( BaseDocument *doc, BaseObject *obj, LONG numSegs,
                                bool allowSegInc, int *numObjsChanged )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      ChangeObjectsCylinderSegs( doc, child, numSegs, allowSegInc, numObjsChanged );    // Recursive call.
    
    int otype = obj->GetType();
    if( otype == Ocylinder || otype == Ocone )
    {
      BaseContainer *bc = obj->GetDataInstance(); 
      LONG currSegs = bc->GetLong(PRIM_CYLINDER_SEG);
      //GePrint( "Spline Angle = " + LongToString(currAngle) );
      if( currSegs > numSegs || (allowSegInc && currSegs > numSegs) )
      {
        doc->AddUndo( UNDOTYPE_CHANGE_SMALL, obj );
        obj->SetParameter(PRIM_CYLINDER_SEG, GeData(numSegs), DESCFLAGS_SET_0);
        (*numObjsChanged)++;
      }
    }
    obj = obj->GetNext();
  }
}




//------------------
//-- Recursive function which deletes any objects, including all their children,
//-- which are turned off in the editor and/or renderer, depending on the values
//-- of "delIfEditorOff" && "delIfRendererOff" respectively.
//-- The number of objects deleted is added to "numObjsDeleted".

void DeleteObjectsTurnedOff( BaseDocument *doc, BaseObject *obj, bool delIfEditorOff,
                             bool delIfRendererOff, int *numObjsDeleted )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      DeleteObjectsTurnedOff( doc, child, delIfEditorOff, delIfRendererOff, numObjsDeleted );    // Recursive call.
    
    if(   (delIfEditorOff  ) && (obj->GetEditorMode() == MODE_OFF)
       || (delIfRendererOff) && (obj->GetRenderMode() == MODE_OFF) )
    {
      BaseObject *objToRemove = obj;
      obj = obj->GetNext();
      doc->AddUndo( UNDOTYPE_DELETE, objToRemove );
      objToRemove->Remove();
      *(numObjsDeleted)++;
      continue;
    }
    obj = obj->GetNext();
  }
}

//------------------
//-- Recursive function which turns on the visiblity of any objects
//-- which are turned off in the editor and/or renderer, depending on the values
//-- of "delIfEditorOff" && "delIfRendererOff" respectively.
//-- If the value if off it changes their visibility to "MODE_ON".
//-- The number of objects deleted is added to "numObjsDeleted".

void ChangeObjectsVisiblity( BaseDocument *doc, BaseObject *obj, bool turnOnInEditor,
                             bool turnOnInRenderer, int *numObjsChanged )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      ChangeObjectsVisiblity( doc, child, turnOnInEditor, turnOnInRenderer, numObjsChanged );    // Recursive call.
    
    int changed = 0;
    if( turnOnInEditor && obj->GetEditorMode() == MODE_OFF)
    {
      obj->SetEditorMode( MODE_ON );
      changed=1;
    }
    if( turnOnInRenderer && obj->GetRenderMode() == MODE_OFF)
    {
      obj->SetRenderMode( MODE_ON );
      changed=1;
    }
    numObjsChanged += changed;
    
    obj = obj->GetNext();
  }
}


//------------------
//-- Recursive function which changes the visibility of any objects containing the
//-- string find and which are turned off in the editor and/or renderer,
//-- depending on the values of "delIfEditorOff" && "delIfRendererOff" respectively.
//-- If the value if off it changes their visibility to "MODE_ON".
//-- The number of objects deleted is added to "numObjsDeleted".

void ChangeVisiblityObjectsContainingString( BaseDocument *doc, BaseObject *obj,
                                            String *find, bool turnOnInEditor,
                                            bool turnOnInRenderer, int *numObjsChanged )
{
  while( obj )
  {
    BaseObject *child = obj->GetDown();
    if( child )
      ChangeVisiblityObjectsContainingString( doc, child, find, turnOnInEditor, turnOnInRenderer, numObjsChanged );    // Recursive call.
    
    
    if( ObjectNameContainsString( obj, find ) )
    {
      int changed = 0;
      if( turnOnInEditor && obj->GetEditorMode() == MODE_OFF)
      {
        obj->SetEditorMode( MODE_ON );
        changed=1;
      }
      if( turnOnInRenderer && obj->GetRenderMode() == MODE_OFF)
      {
        obj->SetRenderMode( MODE_ON );
        changed=1;
      }
      numObjsChanged += changed;
    }
    
    obj = obj->GetNext();
  }
}


//########################################
//-- ObjectConverterDialog class: (the main dialog for this plugin)

class ObjectConverterDialog : public GeDialog
{
 public:
  ObjectConverterDialog(void)            {}
  virtual ~ObjectConverterDialog(void)   {}
  
  virtual Bool CreateLayout(void)
  {
    this->SetTitle( "Object Converter" );
    
    LONG SC_VH = BFH_SCALEFIT | BFV_SCALEFIT;
    
    GroupBorderSpace( 10, 10, 10, 10 );
    GroupSpace(5,5);
    
    AddStaticText   (DLG_LBL1, BFH_LEFT,0,0, "This plugin helps ready, clean and reduce models for export to programs ", BORDER_NONE);
    AddStaticText   (DLG_LBL2, BFH_LEFT,0,0, "like Unity. Click 'Help' for more info.", BORDER_NONE);
    AddStaticText   (DLG_LBL3, BFH_LEFT,0,0, "", BORDER_NONE);
    
    GroupBegin( DLG_GRP2,SC_VH,2,0,"actions based on object names:",BFV_GRIDGROUP_EQUALROWS );
      
      GroupBorder(BORDER_WITH_TITLE|BORDER_THIN_IN);
      GroupBorderSpace( 5, 5, 5, 5 );
      GroupSpace(3,3);
      
      //AddSeparatorH   (SC_VH);
      //AddSeparatorH   (SC_VH);
      
      AddStaticText   (DLG_LBL4, BFH_LEFT,100,0,"action:", BORDER_NONE);
      AddStaticText   (DLG_LBL5, BFH_LEFT,30,0,"text", BORDER_NONE);
      
      AddCheckbox     (DLG_CHK_DEL1,   SC_VH,100,0,"delete objects containing:");
      AddEditText     (DLG_EDIT_DEL1STR, SC_VH,30,0);
      
      AddCheckbox     (DLG_CHK_TURNON,  SC_VH,0,0,"make visible objects \" \" \" \" \" \" \" \":");
      AddEditText     (DLG_EDIT_TURNONSTR,SC_VH,0,0);
      
      AddCheckbox     (DLG_CHK_MAKEEDIT, SC_VH,0,0,"make editable objects \" \" \" \" \" \" \" \" \" \":");  
      AddEditText     (DLG_EDIT_MAKEEDITSTR,SC_VH,0,0);
      
      AddCheckbox     (DLG_CHK_FLATTEN, SC_VH,0,0,"flatten objects \" \" \" \" \" \" \" \" \" \":");  
      AddEditText     (DLG_EDIT_FLATTENSTR,SC_VH,0,0);
      
      AddCheckbox     (DLG_CHK_REVERSE,  SC_VH,0,0,"reverse normals \" \" \" \" \" \" \" \" \" \":");
      AddEditText     (DLG_EDIT_REVERSESTR,SC_VH,0,0);
      
      AddCheckbox     (DLG_CHK_COPY,  SC_VH,0,0,"copy & reverse objects \" \" \" \" \" \" \" \":");
      AddEditText     (DLG_EDIT_COPYSTR,SC_VH,0,0);
    
      AddCheckbox     (DLG_CHK_DEL2,      SC_VH,100,0,"post-delete objects  \" \" \" \" \" \" \" \":");
      AddEditText     (DLG_EDIT_DEL2STR, SC_VH,30,0);
      
    GroupEnd();
    
    
    GroupBegin(DLG_GRP1,SC_VH,2,0,"actions based on object types:",BFV_GRIDGROUP_EQUALROWS );
      GroupBorder(BORDER_WITH_TITLE|BORDER_THIN_IN);
      GroupBorderSpace( 5, 5, 5, 5 );
      GroupSpace(3,3);
      
      AddCheckbox     (DLG_CHK_CHANGE_TEXTSPLINES, SC_VH,80,0,"change text (min) spline angles to: ");
      AddEditNumberArrows (DLG_SPN_TEXTSPLINE_ANGLE, BFH_LEFT,40,0);
      
      AddCheckbox     (DLG_CHK_CHANGE_SPLINES, SC_VH,80,0,"change all (min) spline angles to: ");
      AddEditNumberArrows (DLG_SPN_SPLINE_ANGLE, BFH_LEFT,40,0);
      
      AddCheckbox     (DLG_CHK_CHANGE_CYLINDERS, SC_VH,80,0,"change cylinder (max) segs to: ");
      AddEditNumberArrows (DLG_SPN_CYLINDER_SEGS, BFH_LEFT,40,0);
      
      AddCheckbox     (DLG_CHK_FLATTEN_EXTRUDED, SC_VH,80,0,"flatten extruded objects");
      AddSeparatorH   (SC_VH,40);
      
      AddCheckbox     (DLG_CHK_FLATTEN_INSTANCES, SC_VH,80,0,"flatten instance objects");
      AddSeparatorH   (SC_VH,40);
      
      AddCheckbox     (DLG_CHK_CHANGE_DELOFF_D, SC_VH,80,0,"delete objects off in editor");
      AddSeparatorH   (SC_VH,40);
      
    GroupEnd();
    
    
    AddCheckbox     (DLG_CHK_CURROBJONLY,  SC_VH,0,0,"selected object only");
    AddCheckbox     (DLG_CHK_ALLOWANGLEDEC, SC_VH,0,0,"allow spline angles to decrease + cylinder seg increase");
    AddCheckbox     (DLG_CHK_OPTIMIZEONFLATTEN, SC_VH,0,0,"optimize each object flattened");
    AddCheckbox     (DLG_CHK_TURNONEVERYTHING, SC_VH,0,0,"show everything at end");
    
    
    GroupBegin(DLG_GRP3,SC_VH,2,0,"",BFV_GRIDGROUP_EQUALCOLS);
      GroupBorderSpace( 10, 10, 10, 10 );
      
      AddButton       (DLG_BTN_HELP,BFH_SCALEFIT,0,0,"Help");
      AddButton       (DLG_BTN_CONVERT,BFH_SCALEFIT,0,0,"Convert Objects");
    GroupEnd();
    
    return TRUE;
  }
  
  
  virtual Bool InitValues(void)
  {
    this->SetBool        (DLG_CHK_DEL1,    true);
    this->SetBool        (DLG_CHK_TURNON,  true);
    this->SetBool        (DLG_CHK_MAKEEDIT, true);
    this->SetBool        (DLG_CHK_FLATTEN, true);
    this->SetBool        (DLG_CHK_COPY,    true);
    this->SetBool        (DLG_CHK_REVERSE, true);
    this->SetBool        (DLG_CHK_DEL2,    true);
    
    this->SetString      (DLG_EDIT_DEL1STR,    "{ORIG}");
    this->SetString      (DLG_EDIT_TURNONSTR,  "{game}");
    this->SetString      (DLG_EDIT_MAKEEDITSTR,"{e}");
    this->SetString      (DLG_EDIT_FLATTENSTR, "{f}");
    this->SetString      (DLG_EDIT_COPYSTR,    "{c}");
    this->SetString      (DLG_EDIT_REVERSESTR, "{r}");
    this->SetString      (DLG_EDIT_DEL2STR,    "{d}");
    
    this->SetBool        (DLG_CHK_CHANGE_TEXTSPLINES, true);
    this->SetBool        (DLG_CHK_CHANGE_SPLINES,     true);
    this->SetBool        (DLG_CHK_CHANGE_CYLINDERS,   true);
    this->SetBool        (DLG_CHK_FLATTEN_EXTRUDED,   true);
    this->SetBool        (DLG_CHK_FLATTEN_INSTANCES,  true);
    this->SetBool        (DLG_CHK_CHANGE_DELOFF_D,    false);
    
    this->SetReal        (DLG_SPN_SPLINE_ANGLE,     (Real)15, 1, 90, 1 );
    this->SetReal        (DLG_SPN_TEXTSPLINE_ANGLE, (Real)45, 1, 90, 1 );
    this->SetReal        (DLG_SPN_CYLINDER_SEGS,    (Real)12, 1, 90, 1 );
    
    this->SetBool        (DLG_CHK_CURROBJONLY,        false);
    this->SetBool        (DLG_CHK_ALLOWANGLEDEC,      false);
    this->SetBool        (DLG_CHK_OPTIMIZEONFLATTEN,  false);
    this->SetBool        (DLG_CHK_TURNONEVERYTHING,   false);
    
    return TRUE;
  }
  
  
  virtual Bool Command(LONG id,const BaseContainer &msg)
  {
    switch (id)
    {
      case (DLG_BTN_CONVERT):
      {
        BaseDocument *doc = GetActiveDocument();
        
        //## GET USER ENTERED DATA:
        
        Bool delObjs1      = GetBoolFromElement(this,DLG_CHK_DEL1);
        Bool turnOnObjs    = GetBoolFromElement(this,DLG_CHK_TURNON);
        Bool makeEditObjs  = GetBoolFromElement(this,DLG_CHK_MAKEEDIT);
        Bool flattenObjs   = GetBoolFromElement(this,DLG_CHK_FLATTEN);
        Bool copyObjs      = GetBoolFromElement(this,DLG_CHK_COPY);
        Bool reverseObjs   = GetBoolFromElement(this,DLG_CHK_REVERSE);
        Bool delObjs2      = GetBoolFromElement(this,DLG_CHK_DEL2);
        
        String del1Str      = GetStringFromElement(this,DLG_EDIT_DEL1STR);
        String turnOnStr    = GetStringFromElement(this,DLG_EDIT_TURNONSTR);
        String makeEditStr  = GetStringFromElement(this,DLG_EDIT_MAKEEDITSTR);
        String flattenStr   = GetStringFromElement(this,DLG_EDIT_FLATTENSTR);
        String copyStr      = GetStringFromElement(this,DLG_EDIT_COPYSTR);
        String reverseStr   = GetStringFromElement(this,DLG_EDIT_REVERSESTR);
        String del2Str      = GetStringFromElement(this,DLG_EDIT_DEL2STR);
        
        Bool changeSplines     = GetBoolFromElement(this,DLG_CHK_CHANGE_SPLINES);
        Bool changeTextSplines = GetBoolFromElement(this,DLG_CHK_CHANGE_TEXTSPLINES);
        Bool changeCylSegs     = GetBoolFromElement(this,DLG_CHK_CHANGE_CYLINDERS);
        Bool flattenExtrude    = GetBoolFromElement(this,DLG_CHK_FLATTEN_EXTRUDED);
        Bool flattenInstances  = GetBoolFromElement(this,DLG_CHK_FLATTEN_INSTANCES);
        Bool delIfOffInEditor  = GetBoolFromElement(this,DLG_CHK_CHANGE_DELOFF_D);
        
        Real textSplineAngle = GetRealFromElement(this,DLG_SPN_TEXTSPLINE_ANGLE);
        Real splineAngle     = GetRealFromElement(this,DLG_SPN_SPLINE_ANGLE);
        Real cylinderSegs    = GetRealFromElement(this,DLG_SPN_CYLINDER_SEGS);
        
        Bool currObjOnly   = GetBoolFromElement(this,DLG_CHK_CURROBJONLY);
        Bool allowAngDec   = GetBoolFromElement(this,DLG_CHK_ALLOWANGLEDEC);
        Bool optimize      = GetBoolFromElement(this,DLG_CHK_OPTIMIZEONFLATTEN);
        Bool turnOnEverything = GetBoolFromElement(this,DLG_CHK_TURNONEVERYTHING);
        
        //## DETERMINE START OBJECT:
        
        BaseObject *startObj = doc->GetFirstObject();
        if( currObjOnly )
        {
          startObj = doc->GetActiveObject();
          if( startObj )
          {
            MessageDialog( "You have not selected a valid object!" );
            return FALSE;
          }
        }
        
        doc->StartUndo();
        
        //## APPLY ANY SELECTED "ACTIONS BASED ON OBJECT NAMES"
        //## BY RUNNING THE CORRESPONDING RECURSIVE FUNCTIONS:
        
        int numObjsDeleted1  = 0;
        int numObjsTurnedOn  = 0;
        int numObjsMadeEdit  = 0;
        int numObjsFlattened = 0;
        int numObjsReversed  = 0;
        int numObjsCopied    = 0;
        int numObjsDeleted2  = 0;
        
        if( delObjs1 && del1Str.GetLength() >= 1 )
        {
          GePrint( "Deleting objects containing '" + del1Str + "'" );
          DeleteObjectsContainingString( doc, startObj, &del1Str, &numObjsDeleted1 );
        }
        if( turnOnObjs && turnOnStr.GetLength() >= 1 )
        {
          GePrint( "Turning on objects containing '" + turnOnStr + "'" );
          ChangeVisiblityObjectsContainingString( doc, startObj, &turnOnStr, true, true, &numObjsTurnedOn );
        }
        if( makeEditObjs && makeEditStr.GetLength() >= 1 )
        {
          GePrint( "Making editable objects containing '" + makeEditStr + "'" );
          MakeEditableObjectsContainingString( doc, startObj, &makeEditStr, &numObjsMadeEdit );
        }
        if( flattenObjs && flattenStr.GetLength() >= 1 )
        {
          GePrint( "Flattening objects containing '" + flattenStr + "'" );
          FlattenObjectsContainingString( doc, startObj, &flattenStr, optimize, &numObjsFlattened );
        }
        if( reverseObjs && reverseStr.GetLength() >= 1 )
        {
          GePrint( "Reversing normals on objects containing '" + reverseStr + "'" );
          int REVERSE_COMMAND = 14041;
          ChangeObjectsContainingString( doc, startObj, &reverseStr, REVERSE_COMMAND, &numObjsReversed );
        }
        if( copyObjs && copyStr.GetLength() >= 1 )
        {
          GePrint( "Copy/reversing objects containing '" + copyStr + "'" );
          CopyObjectsContainingString( doc, startObj, &copyStr, &numObjsCopied, true );
        }
        if( delObjs2 && del2Str.GetLength() >= 1 )
        {
          GePrint( "Deleting objects containing '" + del2Str + "'" );
          DeleteObjectsContainingString( doc, startObj, &del2Str, &numObjsDeleted2 );
        }
        
        //## APPLY ANY SELECTED "ACTIONS BASED ON OBJECT TYPES"
        //## BY RUNNING THE CORRESPONDING RECURSIVE FUNCTIONS:
        
        int numChangedTextSplines = 0;
        int numChangedSplines     = 0;
        int numChangedCylinders   = 0;
        int numExtrudedObjsF      = 0;
        int numInstancesF         = 0;
        int numOffObjsDel         = 0;
        int numObjTurnedOn        = 0;
        
        if( changeTextSplines )
        {
          GePrint( "Change all text spline angles to '" + RealToString(textSplineAngle) + "'" );
          ChangeAngleTextSplines( doc, startObj, textSplineAngle, allowAngDec, &numChangedTextSplines );
        }
        if( changeSplines )
        {
          GePrint( "Change all spline angles to '" + RealToString(splineAngle) + "'" );
          ChangeAngleSplines( doc, startObj, splineAngle, allowAngDec, &numChangedSplines );
        }
        if( changeCylSegs )
        {
          GePrint( "Change all cylinder segs to '" + RealToString(cylinderSegs) + "'" );
          ChangeObjectsCylinderSegs( doc, startObj, cylinderSegs, allowAngDec, &numChangedCylinders );
        }
        if( flattenExtrude )
        {
          GePrint( "Flattening any extruded objects" );
          FlattenObjectsMatchingType( doc, startObj, Oextrude, optimize, &numExtrudedObjsF );
        }
        if( flattenInstances )
        {
          GePrint( "Flattening any object instances" );
          FlattenObjectsMatchingType( doc, startObj, Oinstance, optimize, &numInstancesF );
        }        
        if( delIfOffInEditor )
        {
          GePrint( "Deleting all objects off in editor" );
          DeleteObjectsTurnedOff( doc, startObj, true, false, &numOffObjsDel );
        }
        if( turnOnEverything )
        {
          ChangeObjectsVisiblity( doc, startObj, true, true, &numObjTurnedOn );
        }
        
        //## OUTPUT RESULTS AS MESSAGE BOX:
        
        int totObjsDeleted = numObjsDeleted1 + numObjsDeleted2 + numOffObjsDel;
        
        String msg = "Objects changed using names:  \n"
          " > " + RealToString(numObjsDeleted1)  + " objects pre-deleted \n"
          " > " + RealToString(numObjsTurnedOn)  + " objects turned on \n"
          " > " + RealToString(numObjsMadeEdit) + " objects made editable \n"
          " > " + RealToString(numObjsFlattened) + " objects flattened \n"
          " > " + RealToString(numObjsCopied)    + " objects copied \n"
          " > " + RealToString(numObjsReversed)  + " objects reversed \n"
          " > " + RealToString(numObjsDeleted2)  + " objects post-deleted \n"
          "---- \n"
          "Objects changed using object types: \n"
          " > " + RealToString(numChangedTextSplines) + " text spline angles changed \n"
           " > " + RealToString(numChangedSplines)     + " spline angles changed \n"
          " > " + RealToString(numChangedCylinders)   + " cylinder segs changed \n"
          " > " + RealToString(numExtrudedObjsF)  + " extruded objects flattened \n"
          " > " + RealToString(numInstancesF)     + " object instances flattened \n"
          " > " + RealToString(numOffObjsDel)     + " turned off objects deleted \n"
          "---- \n"
          "Total objects deleted: " + RealToString(totObjsDeleted) + "\n";
        
        if(turnOnEverything)
          msg += "Total objects turned on: " + RealToString(numObjTurnedOn) + "\n";
        
        
        MessageDialog( msg );
        GePrint( msg );
        
        doc->EndUndo();
        DrawViews( DRAWFLAGS_NO_THREAD );
      }
      break;
        
      case (DLG_BTN_HELP):
      {
        MessageDialog("Help has not been written yet");
        DrawViews( DRAWFLAGS_NO_THREAD );
      }
       break;
    }
    return TRUE;
  }
};

//########################################

class ObjectConverterDialogTest : public CommandData
{
 private:
  ObjectConverterDialog dlg;

 public:
  virtual Bool Execute(BaseDocument *doc)
  {
    return dlg.Open(DLG_TYPE_ASYNC, ID_OBJECTCONVERTERDIALOGTEST,-1,-1);
  }
};

//########################################

Bool RegisterObjectConverterDialog(void)
{
  return RegisterCommandPlugin(ID_OBJECTCONVERTERDIALOGTEST, "Object Converter",
                               0,NULL,String("Object Converter"),gNew ObjectConverterDialogTest );
}


See Also


Code license
For all of the code on my site... if there are specific instruction or licence comments please leave them in. If you copy my code with minimum modifications to another webpage, or into any code other people will see I would love an acknowledgment to my site.... otherwise, the license for this code is more-or-less WTFPL (do what you want)! If only copying <20 lines, then don't bother. That said - if you'd like to add a web-link to my site www.andrewnoske.com or (better yet) the specific page with code, that's a really sweet gestures! Links to the page may be useful to yourself or your users and helps increase traffic to my site. Hope my code is useful! :)