Open Search
    C4DThread Manual

    About

    C4DThread is the base class for custom Cinema API threads in Cinema 4D. It can be used to perform operations on multiple cores or in the background.

    Warning
    Threads must not use any OS functions. This is strictly forbidden. The only system calls allowed are memory (de-)allocations.
    For custom Maxon API threads see Threads Manual and Jobs Manual.
    // This example contains a custom thread and a MessageData plugin.
    // The thread renders the given BaseDocument and sends a core message when it has finished.
    // This core message is caught by the MessageData plugin that will display the render result
    // in the Picture Viewer.
    // the render thread
    class RenderThread : public C4DThread
    {
    private:
    BaseDocument* _doc;
    BaseBitmap* _bitmap;
    public:
    RenderThread() { _doc = nullptr; }
    ~RenderThread() { this->DeleteDoc(); }
    void DeleteDoc() { BaseDocument::Free(_doc); }
    // Sets the document and bitmap.
    // The thread takes the ownership of the given BaseDocument.
    void SetData(BaseDocument* const doc, BaseBitmap* const bitmap)
    {
    this->DeleteDoc();
    _doc = doc;
    _bitmap = bitmap;
    }
    void Main()
    {
    if (_doc == nullptr)
    return;
    if (_bitmap)
    {
    RenderData* const rdata = _doc->GetActiveRenderData();
    const BaseContainer renderSettings = rdata->GetDataInstanceRef();
    BaseThread* const thread = this->Get();
    RenderDocument(_doc, renderSettings, nullptr, nullptr, _bitmap, flags, thread);
    }
    // send core message that the thread finished
    SpecialEventAdd(CUSTOM_ID_RENDER_FINISH);
    this->DeleteDoc();
    }
    const Char* GetThreadName() { return "RenderThread"; }
    };
    // the MessageData plugin
    class RenderThreadMessages : public MessageData
    {
    Bool CoreMessage(Int32 id, const BaseContainer& bc)
    {
    if (id == CUSTOM_ID_RENDER_FINISH)
    {
    if (g_displayBitmap)
    {
    ShowBitmap(g_displayBitmap);
    }
    }
    return true;
    }
    };
    PyCompilerFlags * flags
    Definition: ast.h:14
    NODOCUMENTCLONE
    Set to avoid an automatic clone of the scene sent to RenderDocument().
    Definition: ge_prepass.h:2
    RENDERFLAGS
    Definition: ge_prepass.h:4725
    maxon::Char Char
    Definition: ge_sys_math.h:47
    maxon::Bool Bool
    Definition: ge_sys_math.h:46
    Bool ShowBitmap(const Filename &fn)
    void SpecialEventAdd(Int32 messageid, UInt p1=0, UInt p2=0)
    maxon::Int32 Int32
    Definition: ge_sys_math.h:51
    RENDERRESULT RenderDocument(BaseDocument *doc, const BaseContainer &rdata, ProgressHook *prog, void *private_data, BaseBitmap *bmp, RENDERFLAGS renderflags, BaseThread *th, WriteProgressHook *wprog=nullptr, void *data=nullptr)
    const Class< R > & Get(const Id &cls)
    Definition: objectbase.h:2090
    const char * doc
    Definition: pyerrors.h:226
    // This example shows how to create and start a new custom thread instance.
    // allocate thread if needed
    if (g_renderThread == nullptr)
    {
    ifnoerr (g_renderThread = NewObj(RenderThread))
    {
    if (g_renderThread == nullptr)
    return false;
    }
    }
    // check if the thread is running
    if (g_renderThread->IsRunning())
    {
    // stop the thread
    g_renderThread->End(true);
    }
    // thread takes ownership of the document clone but not of the bitmap!
    g_renderThread->SetData(documentClone, g_displayBitmap);
    const Bool started = g_renderThread->Start();
    #define ifnoerr(...)
    The opposite of iferr.
    Definition: errorbase.h:393
    #define NewObj(T,...)
    Definition: newobj.h:108

    Creation

    A custom thread class is created by implementing a class based on C4DThread:

    // This example shows the most simple C4DThread based custom thread.
    class ExampleThread : public C4DThread
    {
    public:
    ExampleThread() { }
    virtual void Main() { }
    virtual const Char* GetThreadName()
    {
    return "ExampleThread";
    }
    };

    Now an instance of this custom thread class can be created and used:

    // thread stored in a global variable
    ExampleThread* g_exampleThread = nullptr;
    static maxon::Result<void> CreateExampleThread()
    {
    // create thread instance on demand
    if (g_exampleThread == nullptr)
    {
    g_exampleThread = NewObj(ExampleThread) iferr_return;
    }
    return maxon::OK;
    }
    // delete thread stored in the global variable
    MAXON_INITIALIZATION(nullptr, []()
    {
    DeleteObj(g_exampleThread);
    });
    return OK
    Definition: apibase.h:2771
    #define MAXON_INITIALIZATION(...)
    Definition: module.h:877
    #define DeleteObj(obj)
    Definition: newobj.h:159
    #define iferr_scope
    Definition: resultbase.h:1396
    #define iferr_return
    Definition: resultbase.h:1531

    Custom Threads

    A custom thread class has to implement these virtual functions:

    • C4DThread::Main(): The main function of the thread that is executed when the thread is started.
    • C4DThread::GetThreadName(): Returns the thread name.
    • C4DThread::TestDBreak(): The implementation of BaseThread::TestBreak() has to return false if the thread should stop.
    // This example just prints some text to the console for 60 seconds.
    // (TestDBreak() will stop it earlier)
    void Main()
    {
    // save start time for TestDBreak()
    _startTime = GeGetTimer();
    // activity loop
    for (Int32 i = 0; i < 60; ++i)
    {
    ApplicationOutput("perform background action " + String::IntToString(i));
    // check if the thread should be stopped
    if (this->TestBreak())
    return;
    GeSleep(1000);
    }
    }
    // This example just returns the thread name
    const Char* GetThreadName()
    {
    return "ExampleCustomThread";
    }
    // This example checks how long the thread is running.
    // If the thread is running for longer than 20 seconds it should stop.
    Bool TestDBreak()
    {
    if (_startTime == -1)
    return true;
    const Int32 time = GeGetTimer();
    // check if 20000 milliseconds have passed
    if ((time - _startTime) > 20000)
    return true;
    return false;
    }
    Py_ssize_t i
    Definition: abstract.h:645
    #define ApplicationOutput(formatString,...)
    Definition: debugdiagnostics.h:204
    void GeSleep(Int32 milliseconds)
    Int32 GeGetTimer()
    Definition: c4d_general.h:453
    Note
    To send a message from a thread to the main thread use SpecialEventAdd(). See Core Messages Manual.

    Use

    The custom thread can be started and stopped with these functions:

    • C4DThread::Start(): Starts the custom thread.
    • C4DThread::End(): Stops the custom thread.
    // This example starts the given thread object.
    const Bool started = g_exampleThread->Start();
    if (started == false)
    return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
    #define MAXON_SOURCE_LOCATION
    Definition: memoryallocationbase.h:69
    Note
    The THREADPRIORITY should typically not be changed.

    Read

    Further thread properties are accessed with:

    • C4DThread::Get(): Returns the corresponding BaseThread for the custom thread.
    • C4DThread::IsRunning(): Returns true if the thread is running.
    • C4DThread::TestBreak(): Returns true if the thread encountered a break condition.
    • C4DThread::Wait(): Waits until the thread finished.
    // check if the thread is running
    if (g_exampleThread->IsRunning())
    {
    // stop the thread
    g_exampleThread->End(true);
    }

    Further Reading