Open Search
    Migrating Plugins to the 2025.0 API

    Provides an overview of the major changes introduced with Cinema 4D 2025.0 API.

    Note
    This manual does not include the changes that must be made for a pre 2024.0 API plugin to compile and run under the 2025.0 API. See the Migrating Plugins to the 2024.0 API for a detailed overview of how to convert such legacy plugins.

    Overview

    The major change of the 2025 API is the introduction of the cinema namespace. The introduction was necessary to better separate product namespaces. The cinema namespace contains all Cinema 4D specific concepts, while the already existing maxon namespace is meant to contain concepts that in principle could be used in other products as well. The cinema namespace is the new home of the Cinema 4D API and replaces what was formerly known as the Classic API and encompassed all entities in the formerly global anonymous namespace. E.g., the Classic API type BaseContainer became cinema::BaseContainer, and the classic API BaseList2D became cinema::BaseList2D, and so on.

    opposed to the maxon API and namespace which contains general purpose concepts.

    The cinema namespace is the new home of the Cinema 4D API and replaces what was formerly known as the Classic API and encompassed all entities in the global anonymous namespace. E.g., the Classic API type BaseContainer became cinema::BaseContainer, and the classic API BaseList2D became cinema::BaseList2D, and so on. See Managing Namespaces for details on how to update your existing code to the new namespace.

    Managing Namespaces

    With now two top level namespaces, cinema and maxon, it is important to understand how to manage them in your code. There are in principle three ways to do this:

    • Invoking using directives to make types from a namespace available in the global namespace.
    • Wrapping code in a namespace to make types from a namespace available in a specific scope.
    • Adding qualifiers to all types to make them available in a specific scope.

    Using Directives

    This is in most cases the best option to update your code as it is the fastest way to update files and a project. The following pre-2025 code shown below,

    // Written for 2024.5.1 or earlier ...
    #include "c4d.h"
    class MyPlugin : public ObjectData
    {
    INSTANCEOF(MyPlugin, ObjectData)
    public:
    virtual Bool Init(GeListNode* node) override { return true; }
    maxon::Result<void> MyMethod() { iferr_scope; return maxon::OK; }
    };
    #define INSTANCEOF(X, Y)
    Definition: c4d_baselist.h:49
    return OK
    Definition: apibase.h:2740
    maxon::Bool Bool
    Definition: ge_sys_math.h:46
    #define iferr_scope
    Definition: resultbase.h:1396
    Definition: node.h:10

    can be updated by adding a using namespace cinema; directive at the top of the file. This will make all Cinema 4D API types available in the global namespace; given that they are being included. The updated code would look like this:

    // Written for 2025.0.0 or later ...
    #include "c4d.h"
    using namespace cinema;
    class MyPlugin : public ObjectData
    {
    INSTANCEOF(MyPlugin, ObjectData)
    public:
    virtual Bool Init(GeListNode* node) override { return true; }
    maxon::Result<void> MyMethod() { iferr_scope; return maxon::OK; }
    };
    Definition: autoweight.h:11

    Note that we could also add a using namespace maxon; at the top to also make the maxon API types available in the global namespace. As long as there are no naming conflicts, the compiler will be able to resolve the types correctly.

    #include "c4d.h"
    using namespace cinema;
    using namespace maxon;
    class MyPlugin : public ObjectData
    {
    INSTANCEOF(MyPlugin, ObjectData)
    public:
    virtual Bool Init(GeListNode* node, Bool isCloneInit) { return true; }
    Result<void> MyMethod() { iferr_scope; return OK; }
    };
    OK
    User has selected a font.
    Definition: customgui_fontchooser.h:0
    The maxon namespace contains all declarations of the Maxon API.
    Definition: autoweight.h:21

    But when we use multiple namespace in this manner and at the same time include header files containing colliding identifiers, the compiler will not be able to resolve the types correctly. This is because the using directive makes all types from the namespace available in the global namespace, and the compiler will not be able to resolve ambiguous definitions when an identifier is defined in both namespaces. An example for this is the type Asset which can refer both to cinema::Asset and maxon::Asset. As long as we include only one of the types in the file, the compiler will be able to resolve the type correctly, as shown below.

    #include "c4d.h"
    #include "maxon/assets.h" // Contains a type called Asset in the maxon namespace
    using namespace cinema;
    using namespace maxon;
    class MyPlugin : public ObjectData
    {
    INSTANCEOF(MyPlugin, ObjectData)
    public:
    virtual Bool Init(GeListNode* node, Bool isCloneInit) { return true; }
    Result<void> MyMethod() { iferr_scope; return OK; }
    // The functions expects a maxon::Asset, the compiler will resolve the type correctly.
    Result<void> Foo(const Asset& asset) { iferr_scope; return OK; }
    };

    But as soon as we also include the header file for the classic API type, Asset, the compiler will not be able to resolve the type correctly and throw an error.

    #include "c4d.h"
    #include "lib_net.h" // Contains a type called Asset in the cinema namespace
    #include "maxon/assets.h" // Contains a type called Asset in the maxon namespace
    using namespace cinema;
    using namespace maxon;
    class MyPlugin : public ObjectData
    {
    INSTANCEOF(MyPlugin, ObjectData)
    public:
    virtual Bool Init(GeListNode* node, Bool isCloneInit) { return true; }
    Result<void> MyMethod() { iferr_scope; return OK; }
    // Is now an ambiguous definition and the compiler will throw an error.
    Result<void> Foo(const Asset& asset) { iferr_scope; return OK; }
    };

    While it is probably rare that we include both types in the same file, indirectly including both types through umbrella includes can happen. In such cases, it is best to qualify the types with the respective namespace to resolve the ambiguity.

    #include "c4d.h"
    #include "lib_net.h" // Contains a type called Asset in the cinema namespace
    #include "maxon/assets.h" // Contains a type called Asset in the maxon namespace
    using namespace cinema;
    using namespace maxon;
    class MyPlugin : public ObjectData
    {
    INSTANCEOF(MyPlugin, ObjectData)
    public:
    virtual Bool Init(GeListNode* node, Bool isCloneInit) { return true; }
    Result<void> MyMethod() { iferr_scope; return OK; }
    // Is now an unambiguous definition and the compiler will not throw an error.
    Result<void> Foo(const maxon::Asset& asset) { iferr_scope; return OK; }
    };

    An exception from this are the atomic types such as cinema::Int32, maxon::Int32, cinema::Bool, maxon::Bool, cinema::String, or maxon::String which are defined in both namespaces and can be used interchangeably; the types in the cinema namespace are just an alias for the types in the maxon namespace. This is also why the Bool in Init function of MyPlugin does not have to be qualified with cinema:: or maxon::.

    To summarize:

    • Using directives are the fastest way to update files,
    • and can be used to make types from multiple namespaces available in the global namespace,
    • where the compiler in most cases can resolve the types correctly.
    • But this can lead to ambiguous definitions when including header files with colliding identifiers from different namespaces.
    • Be careful with registration functions such as RegisterMyHook() which then call internally one or many plugin hook registration functions such as RegisterObjectPlugin(). These functions are usually meant to live in the global namespace as they tend to be called from the main file with a forward declaration. A using directive in either the main.cpp/.h or the file containing the custom registration function RegisterMyHook() definition can lead to problems.

    Namespace Scopes and Qualifiers

    A more controlled way to manage namespaces is to wrap code in namespaces or to add qualifiers to all types. This gives you more control over which parts of your code are affected by the namespace. The following example shows how to wrap code in a namespace.

    #include "c4d.h"
    #include "maxon/assets.h"
    using namespace cinema;
    using namespace maxon;
    namespace cinema
    {
    class MyPlugin : public ObjectData
    {
    INSTANCEOF(MyPlugin, ObjectData)
    public:
    virtual Bool Init(GeListNode* node, Bool isCloneInit) { return true; }
    Result<void> MyMethod() { iferr_scope; return OK; }
    Result<void> Foo(const Asset& asset) { iferr_scope; return OK; }
    };
    }
    • A namespace scope can still make use of another namespace exposed via a using directive. In the example shown above, we implicitly use the maxon namespace in the cinema namespace scope, Result, Asset, and OK are all members of the maxon namespace.
    • Duplication does not lead to errors, having both a using namespace cinema; directive and a namespace cinema scope in the same file is perfectly valid, the compiler will not try to interpret things as cinema::cinema:: in the namespace scope.
    • Namespace scopes bear less risk of ambiguous definitions compared to using directive in the global namespace.
    • You can also combine using directives and namespace scopes to limit the scope of the using directive to a specific namespace scope. E.g., namespace bar { using namespace foo; } will make all types from the foo namespace available in the bar namespace scope.
    • But just as for the using directive, the compiler will not be able to resolve ambiguous definitions when including header files with colliding identifiers from different namespaces. In such cases, it is best to qualify the types with the respective namespace to resolve the ambiguity.

    The most controlled way to manage namespaces is to add qualifiers to all types. This is the most time-consuming way but gives you the most control over which parts of your code are affected by the namespace. There are no drawbacks here except for the additional typing effort. The updated code would look like this:

    #include "c4d.h"
    #include "maxon/assets.h"
    class MyPlugin : public cinema::ObjectData
    {
    public:
    virtual cinema::Bool Init(cinema::GeListNode* node, cinema::Bool isCloneInit) { return true; }
    maxon::Result<void> MyMethod() { iferr_scope; return maxon::OK; }
    maxon::Result<void> Foo(const maxon::Asset& asset) { iferr_scope; return maxon::OK; }
    };
    Represents a C4DAtom that resides in a 4D list.
    Definition: c4d_baselist.h:1990
    Definition: c4d_objectdata.h:165

    Other Changes and Issues

    Other than that, there are only minor changes that need to be made to your existing code.

    • OCIO has been updated with 2025, leading to a changed behavior of how colors are being initialized. See OpenColorIO Manual for more information.
    • cinema.frameworks/source/description/ddoc.h is in most source code being pushed into the cinema namespace, although itself contains no namespace nor are other descriptions being placed in the cinema namespace. This happens due to operatingsystem.h which includes ddoc.h inside a cinema namespace. Most projects will indirectly include operatingsystem.h before they had a chance to include ddoc.h, causing for example the identifier for the document frame rate to be cinema::DOCUMENT_FPS and not DOCUMENT_FPS as it was before.
      // This is how most projects will include ddoc.h, pushing its members into the cinema namespace. They either
      // include operatingsystem.h directly or indirectly with an umbrella include such as c4d.h before they include
      // ddoc.h. This will then cause the content of ddoc.h being placed in the cinema namespace.
      #include "c4d.h" // Umbrella include which includes operatingsystem.h
      #include "description/ddoc.h" // Meaningless now, ddoc.h is already defined.
      #include "description/xnoise."
      // This will then result in all ddoc members being in the cinema namespace, while other descriptions such as the
      // xnoise, the noise shader description, remain in the anonymous global namespace.
      // --- snip ----------------------------------------------------------------------------------------------------
      // This behavior can be bypassed by including ddoc.h before operatingsystem.h.
      // Now the members of ddoc.h are in the global namespace and not in the cinema namespace.
      @ DOCUMENT_FPS
      Definition: ddoc.h:15
      maxon::Int32 Int32
      Definition: ge_sys_math.h:51
      @ NOISESHADER_COLOR
      Definition: xnoise.h:8
    • The macro ConstDescID behaves slightly irregularly when used inside the global anonymous namespace.
      // This is the correct way to define a description level inside the global anonymous namespace.
      // But when we want to define a compile time constant DescID, this happens, we must write DescLevel
      // and not cinema::DescLevel. This is because the macro tries to wrangle the type name of DescLevel
      // which will not work if there is a cinema:: in front of it.
      // Will raise a compiler error.
      // The dynamic counter part of ConstDescID for creating DescID's at runtime does not exhibit this exception,
      // here we can write cinema::DescLevel.
      for (cinema::Int32 i = 0; i < 10; i++)
      // --- snip ----------------------------------------------------------------------------------------------------
      // When we are inside a cinema namespace, or are invoking a 'using namespace cinema directive', the issue
      // does not apply, or is at least not so visible.
      using namespace cinema;
      const DescLevel level = DescLevel(NOISESHADER_COLOR);
      const DescID did = ConstDescID(DescLevel(NOISESHADER_COLOR));
      for (cinema::Int32 i = 0; i < 10; i++)
      const DescID otherId = CreateDescID(DescLevel(NOISESHADER_COLOR), DescLevel(i));
      Py_ssize_t i
      Definition: abstract.h:645
      Definition: lib_description.h:353
      #define ConstDescID(...)
      Definition: lib_description.h:592
      #define CreateDescID(...)
      Definition: lib_description.h:593
      PyObject PyObject PyObject int level
      Definition: import.h:58
      Represents a level within a DescID.
      Definition: lib_description.h:296
    • maxon.Opt.GetValueOrDefault() has been renamed to maxon.Opt.GetOrDefault().
      const maxon::Id myId = entity.Get<Id>().GetValueOrDefault() iferr_return; // 2024 and earlier
      const maxon::Id myId = entity.Get<Id>().GetOrDefault() iferr_return; // 2025 and later
      Definition: apibaseid.h:243
      const Id & Get() const
      Definition: apibaseid.h:159
      const Class< R > & Get(const Id &cls)
      Definition: objectbase.h:2090
      #define iferr_return
      Definition: resultbase.h:1531