Explode Scene (Coffee-Plugin)

Aus CINEMA 4D Wiki

Wechseln zu: Navigation, Suche

In diesem Artikel wird ein Coffee-Plugin vorgestellt, und samt Quellcode dokumentiert. Es durchläuft horizontal eine Objekthierarchie, kopiert Objekte in neue Szenen und speichert die Szenen ab. Das Plugin ist sehr klein und einfach, daher gut für Coffee-Einsteiger geeignet.


Inhaltsverzeichnis


Aufgabe

Aufgabe
Eine Szene mit einer unbestimmten Anzahl Objekte soll zur Bearbeitung durch unterschiedliche 3D Artists in einzelne Dateien aufgesplittet werden. Jedes Objekt auf oberster Hierarchie-Ebene soll in eine eigene Datei ausgelagert werden, Unterobjekte sollen erhalten bleiben. Die Benennung der Dateien soll sich aus der Benennung der Objekte ableiten.
Lösung
In Coffee wird ein kleines Plugin geschrieben, welches quasi auf Mausklick alle Objekte auf oberster Hierarchie-Ebene in separate Dateien speichert.


Konzept

Das Plugin muss in einer Schleife alle Objekte auf oberster Hierarchie-Ebene durchlaufen. Jedes Objekt samt Unterobjekte wird kopiert, es wird eine neue Szene erzeugt, und das kopierte Objekt dort eingefügt. Die Szene wird gespeichert, wobei der Name dem soeben kopierten Objekt entnommen wird. Dann geht es weiter mit dem nächsten Objekt. Sind alle Objekte abgearbeitet, wird eine "Fertig"-Meldung ausgegeben, und zur Information die Anzahl der gespeicherten Objekte/Szenen gezeigt.


Dateien

Quellcode

Hier ist nun der Quellcode des Plugins. Die Datei sollte als "explodescene.cof" gespeichert werden.

//##############################################################################
//##                                                                          ##
//##    Explode Scene 1.0                                                     ##
//##    =================                                                     ##
//##                                                                          ##
//##    Stores all objects on first hierarchy level to separate .c4d files    ##
//##    Resulting files are named like the object they contain.               ##
//##                                                                          ##
//##    2008 by c4d-Jack; www.c4d-jack.de                                     ##
//##    OpenSource. No warranty, no support.                                  ##
//##                                                                          ##
//##############################################################################

/* Declaration of some basic values */
var PLUGIN_ID = 1022355;							// ID is registered at plugincafe.com
var MENU_NAME = "Explode Scene";						// Title of the plugin
var HELP_STR  = "Saves all objects in 1st hierarchy level to separate files";	// Help text to appear in the status bar

/* Declaration of the plugin class */
class ExplodeSceneMenu : MenuPlugin
{
  public:
	 ExplodeSceneMenu();		// Constructor
	 GetHelp();			// Return help text
	 GetID();			// Return plugin ID
	 GetName();			// Return plugin title
	 GetIcon();			// Return icon
	 Execute(doc);			// Execute plugin
}

/* Plugin class constructor */
ExplodeSceneMenu::ExplodeSceneMenu()
{
        super();
}

/* Return plugin ID */
ExplodeSceneMenu::GetID()
{ 
        return PLUGIN_ID;
}  	

/* Return plugin title */
ExplodeSceneMenu::GetName()
{
        return MENU_NAME;
}

/* Return help text */
ExplodeSceneMenu::GetHelp()
{
        return HELP_STR;
}

/* Return icon (used for menu and toolbar display) */
ExplodeSceneMenu::GetIcon()
{
  var fn = GeGetRootFilename(); 
  fn->RemoveLast();             
  fn->AddLast("explodescene_icon.tif");  
  var bm = new(BaseBitmap, 1, 1);
  bm->Load(fn); 
  return bm;
}

/* Execute the plugin */
ExplodeSceneMenu::Execute(doc) 
{
  var cObj = doc->GetFirstObject();	// Get the first object in the scene
  var fName = new(Filename);		// Filename object (used to assembly the paths & names of the files)
  var result;				// Store the result of user choices here
  var bPath;				// Basic path (directory where resulting files will be saved)
  var oCount = 0;			// Counter

  // Check for objects in the scene
  if (!cObj)
  {
    // No objects in the scene -> Output a message and abort
    println(MENU_NAME + ": No objects in the scene.");
    result = TextDialog("No objects found in the scene.\n" + MENU_NAME + " aborted.",DLG_OK+DLG_ICONSTOP);
  }
  else
  {
    // There are objects in the scene. Here we go...

    fName->PathSelect("Target directory for files:\n(Existing files will be overwritten)");		// Get destination path from user
    bPath = fName->GetFullString();									// Store destination path
    if (bPath == "")
    {
      // User has clicked the CANCEL button -> Ask what should be done
      result = TextDialog("You clicked 'Cancel'. Do you want to continue and\nspecify all file names manually?", DLG_OKCANCEL+DLG_ICONQUESTION);
      if (result == DLG_R_CANCEL)
      {
	// User wants to abort the whole process -> Output message & abort
        result = TextDialog(MENU_NAME + " aborted.",DLG_OK+DLG_ICONSTOP);
        println(MENU_NAME + " aborted by user.");
        return FALSE;
      }
    }
    
    // User wants to continue
    println(MENU_NAME + ": Exploding started\n");		// Output starting message to console
    println(MENU_NAME + ": Storing fragments in " + bPath);	// Output destination path to console
    
    while (cObj)
    {
      // LOOP: Iterate all objects on 1st hierarchy level (select next object until no more objects are left)

      // Select object
      println("Selecting " + cObj->GetName());	// Output name of current object to console
      doc->SetActiveObject(cObj);		// Select current object in the object manager
      
      var SaveScene = new(BaseDocument);	// Create an empty new scene
      if (SaveScene)				// Check if scene creation was successfull
      {
	// Everything OK -> Output success message to console
        println("   Creating new scene... OK");
      }
      else
      {
	// Error happened -> Output error message to console
        println("   Creating new scene... ERROR");
      }
      
      var nObj = cObj->GetClone(CL_ONLY_VISIBLETAGS);		// Get a clone of the object
      if (SaveScene->InsertObject(nObj, NULL, NULL))		// Insert cloned object into new scene, check if successfull
      {
	// Object successfully inserted -> Output message to console
        println("   Insert " + nObj->GetName() + "... OK");
      }
      else
      {
	// Object could not be inserted -> Output error message to console
        println("   Insert " + nObj->GetName() + "... ERROR");
      }
      
      // Assemble Filename
      fName->SetFullString(bPath);			// Set basic path (stored at the beginning)
      fName->AddLast(cObj->GetName() + ".c4d");		// Get current object's name, set as filename
      
      // Save Scene
      if (SaveScene->Save(fName))					// Save scene, check if saving was successfull
      {
	// Scene successfully saved -> Output message to console and increase counter
        println("   Save as " + fName->GetFullString() + "... OK");
        oCount++;
      }
      else
      {
	// Error while saving scene -> Output error message to console
        println("   Save as " + fName->GetFullString() + "... ERROR");
      }
      
      cObj = cObj->GetNext();			// Select next object in the scene
    }					// End of Loop

    gc();				// Call garbage collection (free unused resources)

    println(MENU_NAME + ": Process finished (" + tostring(oCount) + " files saved)");			// Output message to console
    TextDialog(MENU_NAME + " finished.\n" + tostring(oCount) + " files saved",DLG_ICONASTERISK);	// Show "finished" box.
  }
  return TRUE;
}

/* This function is called when CINEMA 4D is started */
main ()
{
    if (Register(ExplodeSceneMenu))		// Try to register the plugin
    {
      // Plugin successfully registered -> Output version and copyright to console
      println(MENU_NAME + " 1.0 successfully registered | OpenSource | 2008 by c4d-Jack | www.c4d-jack.de");
      return TRUE;
    }
    else
    {
      // Plugin could not be registered -> Output error message to console
      println(MENU_NAME + " could not be registered!");
      return FALSE;
    }
}


Besonderheiten

PLUGIN_ID
var PLUGIN_ID = 1022355

Jedes Plugin in CINEMA 4D (egal, ob in C++ oder in Coffee geschrieben) muss eine einzigartige ID haben. Die ID, die im Quellcode von "Explode Scene" eingetragen ist, ist bei www.plugincafe.comfür dieses Plugin registriert, und sollte nicht geändert werden.

class ExplodeSceneMenu : MenuPlugin
class ExplodeSceneMenu : MenuPlugin
{
  public:
  ...

  private:
  ...
}

Mit diesem Code wird die Klasse des Plugins deklariert. Da Explode Scene vom Pluginmenu aus aufgerufen wird, ist es ein Plugin der Klasse MenuPlugin. Im Codeblock können öffentliche (public) und private (private) Methoden und Eigenschaften der Klasse deklariert werden. Öffentlich bedeutet, dass CINEMA 4D auf eine Eigenschaft oder Methode zufreifen kann; privat bedeutet, dass die Eigenschaft oder Methode nur intern im Plugin zur Verfügung ist. Deklarierte Methoden müssen im nachfolgenden Teil des Codes implementiert werden.

WHILE-Schleife
while (cObj)
{
  ...
  ...
  cObj = cObj->GetNext();
}

Mit diesem Code wird die While-Schleife definiert. Sie überprüft vor jedem Durchlauf, ob sich hinter cObj auch wirklich ein Objekt in der Szene verbirgt. Ist dies nicht der Fall, wird die Schleife nicht ausgeführt. Da noch vor Beginn der Schleife mit var cObj = doc->GetFirstObject() der erste (und hierarchisch oberste) Objekt der Szene ausgewählt wird, wird die Schleife bei einer leeren Szene (ohne Objekte) überhaupt nicht ausgeführt. Weil am Ende der Schleife mit cObj->GetNext() immer zum nachfolgenden Objekt in der Szene weitergeleitet wird, durchläuft sie somit sämtliche Objekte.

GetClone(CL_ONLY_VISIBLETAGS)
var nObj = cObj->GetClone(CL_ONLY_VISIBLETAGS);

Ein Objekt (in diesem Fall cObj) kann immer nur in einer Szene gleichzeitig sein. Versucht man, das selbe Objekt in zwei Szenen gleichzeitig zu verwenden, wird CINEMA 4D früher oder später abstürzen. Aus diesem Grund muss man eine Kopie des Objekts erzeugen, und diese in der neuen Szene einfügen. Das Argument CL_ONLY_VISIBLETAGS bewirkt, dass nur sichtbare Tags mit kopiert werden (sinnvoll, um keinen unnötigen Datenmüll in die neue Szene mitzuschleppen).

ExplodeSceneMenu()

Diese Funktion ist der sog. Konstruktor. Er muss einfach da sein.

GetName(), GetHelp(), GetID(), GetIcon()

Diese vier Funktionen werden von CINEMA 4D dazu benutzt, den Namen, den Hilfetext, die ID und das Icon des Plugins zu ermitteln. GetIcon() kann weggelassen werden, in dem Fall erscheint das Plugin ohne Symbol.


Dateiliste

Insgesamt werden nur 2 Dateien benötigt:

explodescene.cof
Die eigentliche Plugin-Datei, die den Code enthält.
explodescene_icon.tif
Ein 32x32 Pixel großes Bild im TIFF-Format. Es enthält das Icon (Symbol) des Plugins, welches im Plugin-Menü und ggf. in einer Werkzeugleiste angezeigt. Ein Alpha-Kanal ist nicht erforderlich, bewirkt aber eine schönere Darstellung des Icons, wenn z.B. der Menüpunkt markiert ist, oder wenn ein spezielles GUI-Layout verwendet wird.


Dateistruktur

Die beiden Dateien werden folgendermaßen installiert:

+ MAXON
  + CINEMA 4D Rx
    + plugins
      + ExplodeScene
        - explodescene.cof
        - explodescene_icon.tif


Testlauf

Sobald die .cof-Datei und die .tif-Datei im richtigen Verzeichnis liegen, kann CINEMA 4D gestartet werden. Das Plugin wird gleich beim Programmstart registriert. Ob das geklappt hat, sieht man sowohl daran, ob es im Plugin-Menü auftaucht, als auch an den Textausgaben in der Console.

Am besten, man erstellt sich eine einfache Szene, erzeugt darin einige verschiedene Grundobjekte, und ordnet ein paar der Objekte einander unter. Dann einfach den Menüpunkt "Explode Scene" im Plugin-Menü aufrufen, und schauen was passiert. Treten Laufzeitfehler auf, werden diese meistens durch entsprechende Textausgaben in der Console gekennzeichnet.


Download

Das fertige Plugin inklusive Icon kann hier heruntergeladen werden:


Lizenz

  • Explode Scene ist OpenSource.
  • Es wird keine Garantie auf fehlerfreie Funktion, oder Gewährleistung bei Schäden bzw. Datenverlust gegeben.
Persönliche Werkzeuge