Open Search
    Getting Started: Foundations

    Overview

    • The Source Processor is a tool that automatically analyses the plugin source code and creates utility code.
    • Debug messages can be written with ApplicationOutput() or DiagnosticOutput().
    • The plugin code must handle error objects that are returned by sub-functions.
    • It is also possible to return error object to report errors.
    • Plugins using the "classic" API must implement PluginStart(), PluginMessage() and PluginEnd().
    • The "classic" API allows to create new objects and tools by implementing plugin hooks like ObjectData etc.
    • Most elements of the "classic" API are based on C4DAtom, GeListNode and BaseList2D.
    • Parameters of Cinema 4D elements are accessed with C4DAtom::GetParameter() and C4DAtom::SetParameter(). The parameter IDs are represented with DescID objects.
    • The MAXON API allows to create new components by implementing interfaces. These implementation are shared with registries or as published object.

    Source Processor

    The Source Processor is a Python script that is executed every time a MAXON API framework or plugin is build. The script checks the code for certain programming errors and creates additional code.

    If a project file is created with the Project Tool, the script is automatically added to the build process. It is not needed to run it manually. The output console of the IDE will display which file or files are parsed and what errors the script has detected.

    Printing Messages

    To debug a program and to follow the program flow it is useful to print a statement to the IDE's console window. This can easily be done with DiagnosticOutput().

    For more debug functions see Debug and Output Functions and Debugging.

    // This example simply prints "Hello World!" to the debug console.
    DiagnosticOutput("Hello World!");
    #define DiagnosticOutput(formatString,...)
    Definition: debugdiagnostics.h:176
    // This example defines a function that prints "Hello World!" to the debug console.
    // MAXON API includes
    // include header files from core.framwork
    #include "maxon/apibase.h"
    #include "maxon/string.h"
    // ------------------------------------------------------------------------
    // Prints "Hello World!" to the console.
    // ------------------------------------------------------------------------
    static void PrintHelloWorld()
    {
    const maxon::String world { "World!" };
    DiagnosticOutput("Hello @", world);
    }
    Definition: string.h:1235

    Error System

    The MAXON API includes a sophisticated error system. This system allows functions not only to return arbitrary values but also dedicated error objects. Such an error object contains information on what exactly went wrong within the function. For an overview see Error Handling.

    This simple code snippet accesses the name of the host machine:

    // get machine data
    const maxon::DataDictionary data = maxon::Application::GetMachineInfo();
    // get machine name
    const maxon::Result<maxon::String> result = data.Get(maxon::MACHINEINFO::COMPUTERNAME);
    static MAXON_METHOD DataDictionary GetMachineInfo()
    Definition: resultbase.h:766
    PyObject PyObject * result
    Definition: abstract.h:43

    maxon::Application::GetMachineInfo() returns a DataDictionary that includes various system data. To access the data one can use maxon::DataDictionaryInterface::Get(). This function does not return a primitive value but a maxon::Result object. Such a maxon::Result object contains either the return value or an error object.

    It is possible to manually handle the maxon::Result object. See also Error Result.

    // get machine data
    const maxon::DataDictionary data = maxon::Application::GetMachineInfo();
    // get machine name
    const maxon::Result<maxon::String> result = data.Get(maxon::MACHINEINFO::COMPUTERNAME);
    // check if the Result object contains an error or not
    if (result == maxon::OK)
    {
    // get the value stored in the Result object
    const maxon::String name = result.GetValue();
    DiagnosticOutput("Machine Name: @", name);
    }
    else
    {
    // get the error stored in the Result object
    const maxon::Error& err = result.GetError();
    DiagnosticOutput("Error: @", err);
    }
    const char const char * name
    Definition: abstract.h:195
    return OK
    Definition: apibase.h:2746

    The preferred way of handling errors is to use various attributes. These attributes can be used to handle the program flow in the case of an error. E.g. ifnoerr() executes the given code only if no error occurred.

    // get machine data
    maxon::DataDictionary data = maxon::Application::GetMachineInfo();
    // get the machine name
    ifnoerr (const maxon::String name = data.Get(maxon::MACHINEINFO::COMPUTERNAME))
    {
    // print the machine name
    DiagnosticOutput("Machine Name: @", name);
    }
    #define ifnoerr(...)
    The opposite of iferr.
    Definition: errorbase.h:393

    Within a function that returns a maxon::Result one can return either a valid return value or an error object. Such an error object can be one of many build-in error types. See Error Types.

    Within such a function one can also return the error returned by a sub-function. This is automatically done by using the attribute iferr_return. To use this macro one must prepare the scope using iferr_scope.

    The attribute iferr_scope_handler allows to define some code that is invoked when an error is detected with iferr_return.

    // This example defines a function that uses iferr_scope_handler to handle any internal error.
    // ------------------------------------------------------------------------
    // Prints the machine name to the console.
    // If an error occurs, the error is printed instead.
    // ------------------------------------------------------------------------
    static void PrintMachineName()
    {
    // handle errors
    {
    // print error
    // "err" is defined within iferr_scope_handler
    DiagnosticOutput("Error: @", err);
    };
    // get application data
    const maxon::DataDictionary data = maxon::Application::GetMachineInfo();
    // get machine name
    const maxon::String machineName = data.Get(maxon::MACHINEINFO::COMPUTERNAME) iferr_return;
    // print machine name
    DiagnosticOutput("Machine Name: @", machineName);
    }
    #define iferr_scope_handler
    Definition: resultbase.h:1407
    #define iferr_return
    Definition: resultbase.h:1524
    // This example defines a function that uses iferr_scope in order to handle errors using iferr_return.
    // core.framework
    // ------------------------------------------------------------------------
    // Compares the machine name with the given name.
    // @param[in] name A maxon::String to be compared with the machine name.
    // @return True if the name is equal to the machine name; false if the name is not equal to the machine name.
    // ------------------------------------------------------------------------
    static maxon::Result<maxon::Bool> CompareMachineName(const maxon::String& name)
    {
    // needed for "iferr_return"
    // check if the given string argument is set
    // return an error if the string is empty
    if (name.IsEmpty())
    return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION, "\"name\" argument is empty."_s);
    // get the machine name
    // return an error if the machine name could not be obtained
    const maxon::DataDictionary data = maxon::Application::GetMachineInfo();
    const maxon::String machineName = data.Get(maxon::MACHINEINFO::COMPUTERNAME) iferr_return;
    // return true if the strings are the same
    if (name.Compare(machineName) == maxon::COMPARERESULT::EQUAL)
    return true;
    return false;
    }
    @ EQUAL
    result is equal
    #define MAXON_SOURCE_LOCATION
    Definition: memoryallocationbase.h:67
    #define iferr_scope
    Definition: resultbase.h:1389

    Classic API Foundations

    Basic Plugin Functions

    Every plugin that uses the classic API (linking to cinema.framework) must implement the global functions PluginStart(), PluginMessage() and PluginEnd(). This is typically done in a main.cpp file.

    PluginStart() is used to register classic API plugins. PluginMessage() can receive various global messages. PluginEnd() is used to free data before the plugin in unloaded when Cinema 4D ends. See Plugin Functions Manual.

    Classic plugins are created by implementing a plugin class e.g. ObjectData or TagData. Such an implementation must be registered in PluginStart() using a specific function like RegisterObjectPlugin(). See General Plugin Information Manual.

    C4DAtom

    C4DAtom is the base class for many elements of the classic API. It gives access to the element's type, its parameter Description, parameter values and dirty state. The class is also used to copy the element and send messages to it. See C4DAtom Manual.

    // "cubeObject" is a BaseObject which is based on C4DAtom.
    // This example checks the object's type, reads a parameter
    // and copies the object.
    // get type
    const Int32 type = cubeObject->GetType();
    DiagnosticOutput("Type: @", type);
    // get a parameter value
    GeData data;
    cubeObject->GetParameter(ConstDescID(DescLevel(PRIM_CUBE_LEN)), data, DESCFLAGS_GET::NONE);
    DiagnosticOutput("Size: @", size);
    // create copy
    C4DAtom* const clone = cubeObject->GetClone(COPYFLAGS::NONE, nullptr);
    if (clone == nullptr)
    return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
    // insert object into the BaseDocument
    BaseObject* const objectClone = static_cast<BaseObject*>(clone);
    doc->InsertObject(objectClone, nullptr, nullptr);
    Definition: c4d_baseobject.h:248
    Definition: c4d_baselist.h:1522
    C4DAtom * GetClone(COPYFLAGS flags, AliasTrans *trn) const
    Definition: c4d_baselist.h:1607
    Definition: c4d_gedata.h:83
    const Vector & GetVector() const
    Definition: c4d_gedata.h:486
    Py_ssize_t size
    Definition: bytesobject.h:86
    maxon::Vec3< maxon::Float64, 1 > Vector
    Definition: ge_math.h:145
    maxon::Int32 Int32
    Definition: ge_sys_math.h:60
    @ NONE
    None.
    #define ConstDescID(...)
    Definition: lib_description.h:594
    @ PRIM_CUBE_LEN
    Definition: ocube.h:6
    PyObject ** type
    Definition: pycore_pyerrors.h:34
    const char * doc
    Definition: pyerrors.h:226
    Represents a level within a DescID.
    Definition: lib_description.h:298

    GeListNode

    GeListNode is another frequently used base class. It allows to organize elements in lists and trees. It also gives access to the element's document and registration information. See GeListNode Manual.

    // "sceneObject" is a BaseObject which is based on GeListNode.
    // This example accesses the child and next object, the
    // BaseDocument and registration information on the object.
    // get child object
    BaseObject* const childObject = sceneObject->GetDown();
    // get next object
    BaseObject* const nextObject = sceneObject->GetNext();
    // get the document
    BaseDocument* const document = sceneObject->GetDocument();
    // get info
    const Int32 info = sceneObject->GetInfo();
    {
    DiagnosticOutput("Object is hidden in the Plugins menu");
    }
    Definition: c4d_basedocument.h:497
    BaseObject * GetNext()
    Definition: c4d_baseobject.h:279
    BaseObject * GetDown()
    Definition: c4d_baseobject.h:300
    const BaseDocument * GetDocument() const
    Definition: c4d_baselist.h:2134
    #define PLUGINFLAG_HIDEPLUGINMENU
    Hide the plugin's menu entry.
    Definition: c4d_baseplugin.h:31
    _Py_clock_info_t * info
    Definition: pytime.h:197

    BaseList2D

    The base class BaseList2D provides functions to handle the element's name, animation tracks, internal data, bits, shaders, layers and IDs. See BaseList2D Manual.

    // This example creates a new Material.
    // The "Material class is based on "BaseList2D",
    // so it is possible to insert shaders and assign layers.
    // create a new material
    Material* const newMaterial = Material::Alloc();
    if (newMaterial == nullptr)
    return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
    // insert material
    doc->InsertMaterial(newMaterial);
    // set name
    newMaterial->SetName("The new Material"_s);
    // make a new shader
    BaseShader* const noiseShader = BaseShader::Alloc(Xnoise);
    if (noiseShader == nullptr)
    return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
    // insert shader
    newMaterial->InsertShader(noiseShader);
    // make layer
    GeListHead* const root = doc->GetLayerObjectRoot();
    if (root == nullptr)
    return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
    LayerObject* const newLayer = LayerObject::Alloc();
    if (newLayer == nullptr)
    return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
    newLayer->SetName("New Layer"_s);
    // insert layer
    root->InsertLast(newLayer);
    // set layer
    newMaterial->SetLayerObject(newLayer);
    Bool SetLayerObject(const LayerObject *layer)
    void InsertShader(BaseShader *shader, BaseShader *pred=nullptr)
    Definition: c4d_baselist.h:2753
    void SetName(const maxon::String &name, Bool setDirty=true)
    Definition: c4d_baselist.h:2544
    Definition: c4d_basechannel.h:36
    static BaseShader * Alloc(Int32 type)
    Bool SetParameter(const DescID &id, const GeData &t_data, DESCFLAGS_SET flags)
    Definition: c4d_baselist.h:2203
    void InsertLast(GeListNode *bn)
    Definition: c4d_baselist.h:2290
    Definition: c4d_basedocument.h:252
    static LayerObject * Alloc()
    Definition: c4d_basematerial.h:241
    static Material * Alloc()
    #define Xnoise
    Noise.
    Definition: ge_prepass.h:1352
    @ MATERIAL_COLOR_SHADER
    Definition: mmaterial.h:294

    Parameter IDs

    The functions C4DAtom::GetParameter() and C4DAtom::SetParameter() allow to access the parameter values of objects, materials, tags etc. The ID of such a parameter is defined using a DescID object. Such a DescID object is composed of multiple DescLevel elements. The complete DescID is also needed when handling an animation track of a given parameter. See DescID Manual.

    // This example reads a parameter of the given cube object
    // and creates an animation track for that parameter.
    // construct DescID
    DescID parameterID;
    parameterID.PushId(DescLevel(VECTOR_X, DTYPE_REAL, 0));
    // get value
    GeData data;
    cubeObject->GetParameter(parameterID, data, DESCFLAGS_GET::NONE);
    const Float value = data.GetFloat();
    DiagnosticOutput("Value: @", value);
    // make animation track
    // search for the track
    CTrack* track = cubeObject->FindCTrack(parameterID);
    if (track == nullptr)
    {
    // track not found, create new track
    track = CTrack::Alloc(cubeObject, parameterID);
    if (track == nullptr)
    return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
    // add track to stage object
    cubeObject->InsertTrackSorted(track);
    }
    PyObject * value
    Definition: abstract.h:715
    CTrack * FindCTrack(const DescID &id)
    Definition: c4d_baselist.h:2878
    Definition: c4d_canimation.h:671
    static CTrack * Alloc(BaseList2D *bl, const DescID &id)
    Definition: lib_description.h:355
    void PushId(const DescLevel &subid)
    Float GetFloat() const
    Definition: c4d_gedata.h:468
    maxon::Float Float
    Definition: ge_sys_math.h:66
    @ DTYPE_VECTOR
    Vector
    Definition: lib_description.h:70
    @ DTYPE_REAL
    Float
    Definition: lib_description.h:68
    @ VECTOR_X
    X component.
    Definition: lib_description.h:279

    MAXON API Foundations

    Interfaces

    An interface is a public abstract declaration of a class. Such an interface can be implemented one or multiple times; such implementations are separated from the public interface declaration. This interface system is the base for most MAXON API classes. See MAXON API Interfaces.

    // This example shows the declaration of a simple interface.
    // ---------------------------------------------------------------------
    // Simple class that stores a maxon::Int number.
    // ---------------------------------------------------------------------
    class SimpleClassInterface : MAXON_INTERFACE_BASES(maxon::ObjectInterface)
    {
    MAXON_INTERFACE(SimpleClassInterface, MAXON_REFERENCE_NORMAL, "net.maxonexample.interfaces.simpleclass");
    public:
    // ---------------------------------------------------------------------
    // Sets the number to store.
    // ---------------------------------------------------------------------
    // ---------------------------------------------------------------------
    // Returns the stored number.
    // ---------------------------------------------------------------------
    };
    // This interface is declared in a file named "simpleclass.h". The automatically
    // generated files are therefore named "simpleclass1.hxx" and "simpleclass2.hxx"
    // The .hxx header files define the reference class "SimpleClassRef".
    #include "simpleclass1.hxx"
    // declare the published objects "SomeSimpleClass" and "OtherSimpleClass"
    // that give access to implementations of SimpleClassInterface
    // the declaration must be placed between the two hxx files since "SimpleClassRef" is defined in the first hxx file
    MAXON_DECLARATION(maxon::Class<SimpleClassRef>, SomeSimpleClass, "net.maxonexample.somesimpleclass");
    MAXON_DECLARATION(SimpleClassRef, OtherSimpleClass, "net.maxonexample.othersimpleclass");
    #include "simpleclass2.hxx"
    [interfaces_basic_virtual_interface]
    Definition: simpleclass.h:15
    MAXON_METHOD void SetNumber(maxon::Int number)
    MAXON_INTERFACE(SimpleClassInterface, MAXON_REFERENCE_NORMAL, "net.maxonexample.interfaces.simpleclass")
    MAXON_METHOD maxon::Int GetNumber() const
    Definition: objectbase.h:696
    Int64 Int
    signed 32/64 bit int, size depends on the platform
    Definition: apibase.h:213
    #define MAXON_REFERENCE_NORMAL(FREEIMPL)
    Definition: interfacebase.h:1184
    #define MAXON_DECLARATION(T, Name, id,...)
    Definition: module.h:945
    #define MAXON_METHOD
    Definition: interfacebase.h:1012
    #define MAXON_INTERFACE_BASES(...)
    Definition: objectbase.h:1049
    // This example shows the implementation of a simple interface.
    // This class implements SimpleClassInterface.
    class SimpleClassImplementation : public maxon::Component<SimpleClassImplementation, SimpleClassInterface>
    {
    public:
    // implementation of interface methods
    MAXON_METHOD void SetNumber(maxon::Int number)
    {
    _value = number;
    }
    MAXON_METHOD maxon::Int GetNumber() const
    {
    return _value;
    }
    // private data only available inside the implementation
    private:
    maxon::Int _value = 1;
    };
    Definition: objectbase.h:2641
    #define MAXON_COMPONENT(KIND,...)
    Definition: objectbase.h:2199
    // This example creates an instance of an interface
    // and uses the created instance.
    // define the ID of the component to use
    const maxon::Id id { "net.maxonexample.class.somesimpleclass" };
    // get component class of the given ID from the global maxon::Classes registry
    const maxon::Class<SimpleClassRef>& componentClass = maxon::Classes::Get<SimpleClassRef>(id);
    // create reference
    const SimpleClassRef simpleClass = componentClass.Create() iferr_return;
    // use reference
    simpleClass.SetNumber(123);
    const maxon::Int number = simpleClass.GetNumber();
    DiagnosticOutput("Number: @", number);
    Definition: apibaseid.h:237

    References

    The MAXON API makes heavy use of reference counting. New instances of a given class are typically handled as reference counted objects. The instance will be freed when all references using that object are deleted. See References.

    // This example allocates a new Vector and handles the ownership using a StrongRef.
    // When the StrongRef object is deleted; the Vector instance gets freed.
    {
    // allocate an object
    // typically one should not allocate objects this way; this is only an example
    // strong reference takes ownership
    const maxon::StrongRef<maxon::Vector> vectorRef { vec };
    // edit referenced object
    vectorRef->x = 100.0f;
    vectorRef->y = 200.0f;
    vectorRef->z = 300.0f;
    DiagnosticOutput("Vector: @", vectorRef);
    // when the scope is left, the referenced object is deleted
    }
    Definition: baseref.h:62
    #define MAXON_SCOPE
    Definition: apibase.h:2897
    #define NewObj(T,...)
    Definition: newobj.h:108
    T x
    Definition: vec.h:39

    Registries

    A registry is used to store implementations of a given interface. Using such a registry it is possible to access all implementations of that interface. See Registries.

    // This example prints the IDs of all stream conversion implementations.
    // These implementations are registered at maxon::StreamConversions.
    // check all stream conversions
    for (const auto& it : maxon::StreamConversions::GetEntriesWithId())
    {
    // get ID
    const maxon::Id& eid = it.GetKey();
    DiagnosticOutput("Stream Conversion: @", eid);
    }

    Published Objects

    A published object gives access to a certain object. This can be any kind of object in memory; typically it is a class representing an implementation of an interface. See Published Objects.

    // This example uses a specific stream conversion implementation.
    // An instance of that implementation is created by obtaining
    // the reference class stored at the published object "HexEncoder".
    // get encoder factory from published object StreamConversions::HexEncoder
    // and use it to create a new instance
    const maxon::StreamConversionRef hexEncoder = maxon::StreamConversions::HexEncoder().Create() iferr_return;
    // the original number
    const maxon::Int number = 123;
    source.Append((maxon::Char)number) iferr_return;
    // convert to HEX
    hexEncoder.ConvertAll(source, destination) iferr_return;
    // print result
    const maxon::String hexString { destination };
    DiagnosticOutput("Hex: @", hexString);
    Definition: basearray.h:415
    const Py_UNICODE * source
    Definition: unicodeobject.h:54
    char Char
    signed 8 bit character
    Definition: apibase.h:209