Cinema 4D - C++ plugins

From NoskeWiki
Jump to: navigation, search

About

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


This page is to help you get started creating your own C++ plugins for Cinema 4D (C4D). Unlike COFFEE (C4D's own interpreted scripting language), you can use the versatile C++ SDK to create new custom objects, animation tracks, mouse drawing tools and numerous other types of plugins (see at bottom). The downside: (1) there is a steep learning curve, (2) you must compile code yourself and (2) you must restart C4D each time you recompile! It's also worth noting that, as of Cinema R12, there were some major changes to Cinema 4D, including the addition of "Melange" - a C++ library that can be linked to your own application; allowing you to build, load and save C4D files - without the need for a C4D installation - although sadly there isn't great online support for this new feature yet.

NOTE: In the examples here I have used XCode on a Mac: if you use Windows I'd recommend using Microsoft Visual Studio.


Building the SDK Examples

I will assume you have downloaded a 42-day trial copy (here), or have a full version of C4D R11 or R12 (or later). This distribution comes with a directory full of C++ plugin examples, ready to be compiled. For C4D R11 on MacOS, it is required to be using MacOS 10.5 (or later) and Xcode 3.1 (or later). To compile it: go to /Applications/MAXON/CINEMA 4D R11/plugins/cinema4dsdk, then open cinema4dsdk.xcodeproj in XCode. Hit "Build". Hopefully it will compile without errors, and will create cinema4dsdk.dylib. Close and restart C4D. It will find this dynamic library and if you click the Plugins menu you see ~20 entries under a new "Cinema4dsdk" submenu - these plugin examples include custom dialogs (eg: Sub-Dialog), custom objects (eg: Rounded Tube), a very cool drawing tool (Liquid Drawing Tool) and so on.


WARNING: If using combinations of c4d 11.0x, 11.5, and Xcode 3.x, you may encounter difficulties with the plugins compiling in Xcode, but failing to show up in the C4D plugin menu. Cinema 4D R11 opens as 32 bit by default and 11.5 as 64 bit by default on a mac. You can change this by highlighting the actual C4D application in Applications> Maxon ... right click to get info and checking or unchecking the appropriate box. If your example plugins still fail to load, ensure that you force them to compile in the appropriate architecture as follows: Double click cinema4dsdk under Groups and Files. In the Project box, Build tab, change architecture to either 32 or 32/64/universal. Do the same for _api.xcodeproj. Go to Build, CleanAllTargets, say yes to defaults, then hit Build again. This should load, but beware that while you're developing, selecting 32/64 will take twice as long to compile, so its best to find out which is correct and then only create the true universal architecture version when compiling in Release mode.


The plugin examples (cinema4dsdk) directory is structured as follows:

  • source/ .............................. contains *almost* all the source/code files
    • main.cpp ............... main point of entry which registers all the examples
    • */ ........................... contains code for all the examples - most in a single .cpp file - but all have dependencies in the resource folder!
  • res/ .............................. contains a bunch of .tif files used as menu and/or object icons.
    • cd4_symbols.h .......... contains a big enum list giving each example unique IDs to identify dialog elements & strings.
    • description/ ................ contains more nasty enum lists and many .res "description resorce" files - a text file with it's own format which sets the size of certain objects.
    • dialog/ ..................... contains .res files to set the size of dialogs.
    • scene/ ........................ contains a .cd4 file with stairs.  :-/
    • string_us/ ........................ contains ".str" files
      • c4d_strings.str ........ a text file mapping a title string for each menu plugin.
        • description/ ........... more .str files.
        • dialog/ ........... more .str files for the dialog examples.

Unfortunately, this is quite a complex directory structure, with many file dependencies: making these plugin examples quite hard for beginners to understand. However, the good news is that you can create a C++ plugin for C4D using a single .cpp file.

If you want to isolate a single example and modify that to create your own plugin, there is a good tutorial here. I take the opposite approach in my tutorial, and start with the simplest possible plugin, and then build up.


IMPORTANT WARNING: The code below is for C4D R12 and later.... if you are using an earlier version of C4D follow the same instructions, but copy code from this page Cinema 4D R11 - C++ plugins. Significant changes were made in R12, including a completely different internal architecture (changing from single to double precision IEEE floats) and "undergone a lot of renaming so that structures and calls are more consistent and carry more logical names" (see: C++ Transition to R12). What this means is many constants/enums have been renamed (eg: "UNDO_NEW" was changed to "UNDOTYPE_NEW") and code for earlier versions will almost certainly give errors and not compile. It's a little frustrating they made these changes, but you'll have to deal with it.


Tutorial 1: Hello World

In this tutorial we are going to create a simple "Hello World" menu plugin:

  • In the main plugins folder: create a new folder called "MyCPlugins" (ie: /Applications/MAXON/CINEMA 4D R11/plugins/MyCPlugins)
  • Copy and rename cinema4dsdk/cinema4dsdk.xcodeproj to MyCPlugins/mycplugin.xcodeproj
  • Create a "source" folder and copy cinema4dsdk/source/main.cpp to MyCPlugins/source/main.cpp
  • Open mycplugin.xcodeproj and notice almost all the source files listed are red (since they are now broken links). Delete all these red files, so you are just left with "main.cpp" and the dependencies "_api.xcodeproj", "_api.debug.xcconfig" and "_api.release.xcconfig". NOTE: You can also expand "source" on the left and delete all the sub-folders but not essential.
  • Select Project > Edit Project Settings > Build scroll down, change the "product name" to "mycplugins".
  • Open main.cpp (double click) and enter this code (deleting whatever was there before):
#define ID_HELLOTEST 1000001
      // unique ID for this plugin
 
#include "c4d.h"
 
//########################################
 
class HelloWorldTest : public CommandData    // Data class for creating new commands (previously known as "menu plugins").
{
public:
  virtual Bool Execute(BaseDocument *doc)
  {
    MessageDialog("Hello World");            // Simple popup message.
    GePrint("This is my first plugin");      // Output to C4D's "Console window".
    return TRUE;
  }
};
 
//########################################
 
Bool PluginStart(void)                   // The main function C4D calls to begin your plugin (think of it as main).
{
  return RegisterCommandPlugin( ID_HELLOTEST,"Hello World",
                                0,NULL,String("Hello World"),
                                gNew HelloWorldTest );
  // registers a command plugin with C4D
  // (ie: the class to execute when user selects "Hello World" under the Plugin menu)
}
 
void PluginEnd(void)                     // Called when the plugin is unloaded from C4D.
{
}
 
Bool PluginMessage(LONG id, void *data)  // Allows you to receive plugin messages from C4D or other plugins.
{
  return TRUE;
}
  • Hit build
  • Restart C4D and click: Plugins > Hello World to see your first plugin work.
  • To see your GePrint() output you will need to open the Console [Shift+F10]. This Console is also useful because you can drag-and-drop labels from the Attributes manager into the Console's command line to see the label's ID.

Effective Debugging

If you're going to do a lot of coding I recommend you do the following:

  • Go Window > Console to open the Console.
  • Grab the little gray "position handler" (left of File) on the Console and drag it next to the Materials manager down the bottom. You will probably also want to close the Coordinates manager resize the CD4 window so it only occupies the right half of your screen.
  • Click Window > Layout > Save as Startout Layout... (since you will be restarting CD4 often).

NOTE: If your plugins crash C4D a bug report will be saved to: /Users/you/Library/Preferences/MAXON/CINEMA 4D R11_E81E98AF/_BugReport.txt. This file is hard to read however, so I'd recommend using GePrint() to debug instead. You may also want to turn off the error reporting by going Edit > Preferences > Common and turn off "Allow Bugreports".

Tutorial 2: Simple Custom Dialog

In this tutorial we will build on "Hello World" and create a nice little custom dialog. Since you will probably want to make multiple plugins - and maybe even register and share them - it makes sense to (1) have them appear under a submenu and (2) follow the SDK example more closely.

And so we will now modify main.cpp to read as follows:

#include "c4d.h"
 
//########################################
//-- Forward Declarations:
Bool RegisterMyDialog(void);                   // ADD ANY NEW PLUGINS HERE
 
 
//########################################
//-- CD4 Hooks:
 
Bool PluginStart(void)
{
  if (!RegisterMyDialog())  return FALSE;      // ADD ANY NEW PLUGINS HERE
  return TRUE;
}
 
void PluginEnd(void) {}
 
Bool PluginMessage(LONG id, void *data) {}

This represents a minimal version of the main.cpp in the cinema4dsdk directory. If you try compiling now, it won't do anything - we need to create another file implementing our RegsiterMyDialog() function and our custom dialog. In this dialog we will include an editable text box, a spin box ("edit number arrows"), a checkbox, a button, several labels ("static text") and a group box to lay out some our elements in a grid.

Create a new file and folder MyCPlugins/mydialog/mydialog.cpp, open this file in XCode and add the following code in XCode:

//########################################
//-- Includes and Globals Variables:
 
#define ID_MYDIALOGTEST 100002      // Unique ID for this dialog.
 
#include "c4d.h"
 
enum                                // Uniquely identify all your dialog elements here.
{
  DLG_EDIT_TEXT    = 1000,
  DLG_SPN_SIZE     = 1001,
  DLG_CHK_FLAT,
  DLG_BTN_ADD,
  DLG_LBL1,
  DLG_LBL2,
  DLG_GRP1,
};
 
 
//########################################
//-- MyDialog class: (the main dialog for this plugin)
 
class MyDialog : public GeDialog
{
public:
  MyDialog(void)            {}
  virtual ~MyDialog(void)   {}
 
  //----------
  //-- Set up your dialog elements here:
 
  virtual Bool CreateLayout(void)
  {
    this->SetTitle( "My Dialog" );
    GroupBorderSpace( 5, 5, 5, 5 );
    GroupSpace(10,10);
 
    GroupBegin(DLG_GRP1,BFH_SCALEFIT|BFV_SCALEFIT,2,0,"",BFV_GRIDGROUP_EQUALROWS);
    GroupSpace(5,5);
 
    AddStaticText       (DLG_LBL1, BFH_LEFT,0,0,"text:", BORDER_NONE);
    AddEditText         (DLG_EDIT_TEXT, BFH_SCALEFIT|BFV_SCALEFIT,300,0);
 
    AddStaticText       (DLG_LBL2, BFH_LEFT,0,0,"size:", BORDER_NONE);
    AddEditNumberArrows (DLG_SPN_SIZE,BFH_LEFT,50,0);
 
    GroupEnd();
 
    AddCheckbox         (DLG_CHK_FLAT,  BFH_SCALEFIT,0,0,    "flat");
    AddButton           (DLG_BTN_ADD,   BFH_LEFT,0,0, "Add Text");
 
    return TRUE;
  }
 
  //----------
  //-- Assign dialog elements their initial values here:
 
  virtual Bool InitValues(void)
  {
    this->SetString      (DLG_EDIT_TEXT, "Hello World");
    this->SetReal        (DLG_SPN_SIZE, 200, 1, 1000, 10 );
    this->SetBool        (DLG_CHK_FLAT, false);
    return TRUE;
  }
 
  //----------
  //-- Deal with any modification or "clicking" events here:
 
  virtual Bool Command(LONG id,const BaseContainer &msg)
  {
    switch (id)
    {
      case (DLG_BTN_ADD):
      {
        BaseDocument *doc = GetActiveDocument();
 
        //## GET USER ENTERED DATA:
 
        String text;   this->GetString (DLG_EDIT_TEXT, text);
        Real   size;   this->GetReal   (DLG_SPN_SIZE,  size);
        Bool   flat;   this->GetBool   (DLG_CHK_FLAT,  flat);
 
        doc->StartUndo();
 
        //## CREATE AND ADD NEW EXTRUDE NURBS OBJECT:
 
        BaseObject *objExtr = BaseObject::Alloc( Oextrude );
        if( flat )
          objExtr->SetParameter( EXTRUDEOBJECT_MOVE, GeData( Vector(0,0,0) ), DESCFLAGS_SET_0 );
        objExtr->SetName( text );
        doc->InsertObject( objExtr, NULL, NULL );
        doc->AddUndo( UNDOTYPE_NEW, objExtr );
 
        //## CREATE NEW TEXT SPLINE OBJECT AND ADD UNDER EXTRUDE:
 
        BaseObject *objText = BaseObject::Alloc( Osplinetext );
        objText->SetParameter( PRIM_TEXT_TEXT,   GeData(text), DESCFLAGS_SET_0 );
        objText->SetParameter( DescLevel(PRIM_TEXT_HEIGHT), GeData(size), DESCFLAGS_SET_0);
        objText->SetName( text );
 
        doc->InsertObject( objText, objExtr, NULL );
        doc->AddUndo( UNDOTYPE_NEW, objText );
 
        //## PRINT OUT VALUES AND REFRESH:
 
        StatusSetText("Text added");
        GePrint("Text size:" + RealToString(size) );
 
        doc->EndUndo();
        DrawViews( DRAWFLAGS_NO_THREAD );
      }
        break;
    }
    return TRUE;
  }
};
 
//########################################
 
class MyDialogTest : public CommandData
{
private:
  MyDialog dlg;
 
public:
  virtual Bool Execute(BaseDocument *doc)
  {
    return dlg.Open(DLG_TYPE_ASYNC, ID_MYDIALOGTEST,-1,-1);        // Opens the dialog window.
  }
};
 
//########################################
 
Bool RegisterMyDialog(void)
{
  return RegisterCommandPlugin(ID_MYDIALOGTEST, "My Dialog",
                               0,NULL,String("My Dialog"),gNew MyDialogTest );
}


Now return to your project and add this file, by selecting the "source" folder on the left column, then right click > Add > Add Existing Files and select mydialog.cpp. Now you click build again, and restart CD4. Open your new plugin and click "Add Text". This useful little plugin creates an "Extrude Nurbs" object and a "Text" object as its child. Text splines are not visible when you render [Cmd+r] unless extruded! Notice also, you can undo this operation thanks to the AddUndo() functions between the StartUndo() and EndUndo(). AddUndo(LONG type, void* data) always has to be called directly before a change is made (the most common are: UNDO_CHANGE, UNDO_CHANGE_NOCHILDS & UNDO_DELETE)... EXCEPT in the case of adding a new object/tag/material/track etc - where the call (using: UNDO_NEW) must be made directly after. Congratulations - you have now created a simple dialog plugin which you can play with and build on!

Download this complete example here.


More Plugin Examples

You should now be ready to explore some of the plugin examples in cplusfiles.dylib, and copy the code/classes you need into new files as you did with MyCPlugins/mydialog/mydialog.cpp. My personal advice is to only copy across small fragments at a time, and avoid dependencies on".res" description resource files, since you can code these flags directly into your .cpp file anyway.

I also have made available code for two of my own plugins here, which includes:

  • Find And Replace - works for all objects, tags and/or materials by making use of a recursive functions.
  • Fade Effect - allows you to instantly add a fade-in and/or fade-out effect by creating a display tag and visibility animation track to the selected (i.e. active) object, or changing the transparency brightness of the selected material.


Registering Your Plugins

You will notice at the top we have defined an ID for our plugins. This ID must be unique for each menu plugin including those written with COFFEE, or you will get conflicts. The sequence 100001-100010 is reserved for testing purposes, but if you want to distribute your plugins you must obtain a unique number here. This requires you to register with "Plugin Cafe" but it's quite easy.


Types of C++ Plugins

Numerous different types of plugins can be created, not just the command plugin shown. I've copied the full of these (from the SDK HTML) here:

  • Command plugins
    • CommandData - a hook to insert an item into the Plugins menu.
  • Tag plugins
    • TagData - a hook to create a custom tag, invisible or visible, that can be inserted into an object's tag list.
  • Object plugins
    • ObjectData - a hook to create a custom object type; generators, deformers, particle modifiers etc.
  • Tool plugins
    • ToolData - a hook to create a custom editor tool.
    • DescriptionToolData - a derived hook to create modeling tools.
  • Animation plugins
    • CTrackData - hooks to create custom tracks for the time-line.
  • Shaders
    • ShaderData - a hook to make 2D shaders.
    • MaterialData - a hook to make 3D shaders.
  • Export/import filters
    • BitmapLoaderData, BitmapSaverData - a hook to make import/export filters for image formats.
    • SceneLoaderData, SceneSaverData - a hook to make import/export filters for 3D formats.
  • Scene hook
    • SceneHookData - a hook that's executed during various stages of the redraw pipeline.
  • Video post effects
    • VideoPostData - a very versatile hook that's executed during rendering, for things like motion blur etc.
  • Message plugins
    • MessageData - a hook to receive plugin messages.
  • Custom GUI/datatypes
    • CustomDataTypeClass, ResourceDataTypeClass - creates a custom data type that can be used in containers and descriptions.
    • CustomGuiData - creates a custom gui element that can be used in dialogs and descriptions.
  • Node plugins
    • NodeData - used to create custom nodes.


Resources

As you program CD4 plugins your two most important resources will be:

  • The C4D C++ STK HTML - download it and bookmark index.html. I find the "Alphabetical Members Index" page the most useful.
  • Plugin Cafe - search CD4's official forum, or make a post when you run into real problems.

Unfortunately there is no online version of the STK (god know why not), there is no other help system, and the documentation itself is not very beginner friendly!

I hope this introductory lesson has been useful - all the best with your programming!


See Also

Add fade plugin dialog

Links