The Maxon SDK Team is currently short staffed due to the winter holidays. No forum support is being provided between 15/12/2025 and 5/1/2026. For details see Maxon SDK 2025 Winter Holidays.
  • Cinema S26 and newer is not longer threadsafe for ObjectData?

    s26 windows c++
    2
    0 Votes
    2 Posts
    529 Views
    ferdinandF
    Hello @victor, Thank you for reaching out to us. While I am fairly confident that I understand your question, I am going to be very literal in my answer to avoid some ambiguities. Some Facts ObjectData was never 'thread-safe'. Or to be more precise, the classic API scene graph of Cinema 4D was never thread-safe (an ObjectData instance does not store any public data itself and therefore does not have to be accessed, i.e., falls outside of the notion of thread-safe or not). Some methods, as for example ObjectData::GetVirtualObjects, are never being called from the main thread and it is therefore forbidden for example to modify the scene graph or invoke EventAdd() or drawing calls from there. Some methods, e.g., ::Message(), are being called relatively often from the main thread and can therefore be used to execute things on the main thread. However, there is and never was any guarantee that ::Message() will always be on the main thread. You must still always check yourself with GeIsMainThread() or GeIsMainThreadAndNoDrawThread(). Some code could always send a message from any thread to your object. The fact that MSG_DOCUMENTINFO can now be broadcasted outside of the main thread is likely tied to async scene loading introduced with S26. The Problem with your Question You state that '[your] plugin requires third-party communications' and therefore assert that 'executing some functions on the main thread is necessary' for you. I do not understand what you mean by that. You should describe more precisely what you want to do in that message function. Do you want to modify scene graph data; add/remove objects, tags, shaders, etc. ? Do you want to modify your own global data? Do you want to communicate with a server or something like this? You should really clarify what your goals are and ideally share code with us either here or via sdk_suppport(at)maxon(dot).net. Otherwise it will be very hard to help you. Possible Solutions What the right solution is for you depends heavily on what you want to do. What however is not possible, is to force your ::Message method only being called from the main thread. Share Write Access to your own Global Data To do that, you should use a lock/semaphore, so that only one entity/thread at a time can access your data, e.g., write to a global log file or send data to a server. There are classic API and maxon API types which can help you with that. It is strongly recommend to use them over similar functions of the std library. Modify the Scene Graph You must defer the execution of your code to the main thread here. There are in principle two ways to achieve that. Defer by waiting: The simplest pattern is to simply store the notion that you want to do X on the main thread when you encounter the the state Y in a non-main thread. In the simplest form this could be a private field Bool _doX; on your object hook which you then set to true. The next time ::Message is being called and you are on the main thread, you then simply carry out doing X and then set the field back to false. The disadvantage of this approach is that you have no control over when X is actually carried out, as you must wait for something else calling your object hook on the main thread. The advantage is that you do not hold up everything else as with the second method. Defer with ExecuteOnMainThread: With this function you defer the execution of a lambda/delegate to the main thread. The advantage of this approach is that the changes are carried out immediately (at least relatively) and you can directly 'carry on' in your code. The disadvantage is that that the function is based on maxon::JobInterface, i.e., there is a queue of jobs on the main thread which are solved sequentially, and you might not be first in line. Also, this is by definition blocking. So, when you are inside a thread Y which has been optimized for speed, and you then defer the computationally heavy task X to the main thread, first wait for other things to be done, and then do your task X, the thread Y is waiting all that time for you and with it everything that relies on that thread. This does not mean that you should not use the function, but you should be careful. It could look something like this (untested pseudo-code): MyObjectData::Message(GeListNode* node, Int32 type, void* data) { // The #something event has happened, and we are not on the main thread. if ((type == ID_SOMETHING) && !GeIsMainThreadAndNoDrawThread()) { // We add a cube to the document of #node from the main thread. maxon::ExecuteOnMainThread([&node]() { iferr_scope_handler { err.CritStop(); return; }; BaseDocument* const doc = node.GetDocument(); BaseObject* const cube = BaseObject::Alloc(Ocube); if (!doc || !cube) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION); doc->InsertObject(cube, nullptr, nullptr); }, maxon::WAITMODE::DONT_WAIT); } // Other code ... return SUPER::Message(node, type, data); } You could also use other more complex approaches here, but they are always just a variation of these two (e.g., use a MessageData or SceneHookData plugin). Cheers, Ferdinand
  • Can I get active things order with python?

    windows python s26
    8
    1
    0 Votes
    8 Posts
    2k Views
    DunhouD
    @ferdinand Thanks for the detailed explain I think DescriptionCustomGui is the best way to solve this for now . With learning furthur , maybe I will try a C++ version , but for me it's not time . Anyway , It is helpful for this techniclly explain and the example
  • Why does "Symbols Parser" not work?

    python
    3
    3
    0 Votes
    3 Posts
    615 Views
    L
    Great!!! It runs. You are right. Thank you so much! I think your answer is worth being written in the API document to make the workflow more clear.
  • SendModelingCommand(MCOMMAND_SUBDIVIDE) broken in C4D 2023?

    c++
    4
    1 Votes
    4 Posts
    607 Views
    fwilleke80F
    Thank you, that fixed it.
  • 0 Votes
    5 Posts
    1k Views
    jochemdkJ
    Done, didn't know I could do it myself :}
  • Check the user switch documents in CommandData plugins

    python s22
    3
    0 Votes
    3 Posts
    693 Views
    chuanzhenC
    @ferdinand Thanks for your help!
  • Get ID of Undo-ed parameter...?

    c++ s26
    5
    0 Votes
    5 Posts
    1k Views
    Y
    Thank you for your explanation. I suspected that most likely it would not be possible to catch the moment of the beginning of Undo/Redo. Sad... So if to consider your advice:... @manuel said in Get ID of Undo-ed parameter...?: You can either update all your objects based on the new data or check all your structure and check if it needs any updates. ...if I want to call update function in MSG_DOCUMENTINFO_TYPE_UNDO, so how to retrieve the correct object which have been updated here? (it should be my node). As I think I do not need make update in situations when Undo was made for another objects in the scene. And also I did not understand the second part in the quote. What the structure should I check? Structure of parameters of my node? Or structure of objects? And how to check that they need any updates?
  • Setting render engine to Redshift

    s26 c++
    3
    0 Votes
    3 Posts
    577 Views
    S
    Hi Ferdinand, That's great, I'll take a look at that file and see what happens with that. Many thanks once more for your help. (Later) Got, it, works perfectly, thank you very much! Cheers, Steve
  • Cinema 4D 2023 None bug

    sdk python windows
    3
    2
    0 Votes
    3 Posts
    611 Views
    DunhouD
    @ferdinand I Check with my home PC in R 2023, It works fine , And for some reason , It report a none last time I am pretty sure I have not insert a layer field in fieldlist ( I just miniest the scene for test code ) , But when I resart C4D today this none warning just gone Maybe It is just a oolong events I make some thing I don't know . Sorry for that . And Thanks for the TIPS , next post I will take a more spesific report cheers~
  • Remove node from Redshift node graph

    s26 c++
    3
    0 Votes
    3 Posts
    493 Views
    S
    Ah...so simple in the end! Navigating this maze of interdependent classes used in the node system is not easy, that's my excuse Many thanks for this, it solves a very tricky problem I was having. Cheers, Steve
  • Render Settings change Save state

    python s22
    8
    1
    0 Votes
    8 Posts
    1k Views
    chuanzhenC
    @ferdinand Thanks you for your help!
  • how to Scroll UserArea

    s22 python
    7
    1
    0 Votes
    7 Posts
    2k Views
    chuanzhenC
    @m_adam Thanks for your help,great work!
  • Removing an object from a PointerArray without deleting it

    c++
    5
    0 Votes
    5 Posts
    886 Views
    fwilleke80F
    Hi Ferdinand, thank you! In deed, that looks pretty simple. Wonder why I couldn't get it to compile... I'll test this and report back in case it doesn't work. Since I guess you found it in active code, I'll mark this thread as solved for now. thanks again! Cheers, Frank
  • 0 Votes
    7 Posts
    1k Views
    DunhouD
    @ferdinand Thnaks for your help, I think it is enough for this specific toppic . It work as espected
  • Sometimes, IRR stops working when my plugin is placed on a scene.

    python
    11
    0 Votes
    11 Posts
    2k Views
    R
    Since I know exactly what methods access/check/change the self.xxxx variables, it is a risk I'm willing to take As for the solution, I removed the c4d.EventAdd() and at the GetContour method I was simply calling the function that was creating the spline. Now I perform a few checks for "dirtyness" and only after they show that something is dirty, I call the function to produce the spline.
  • 0 Votes
    3 Posts
    587 Views
    ferdinandF
    Hello @lingza, Thank you for reaching out to us. There are in principle two ways how you can do this: Do not use an InExcludeData and make the dependencies of your deformer direct children of the deformer. A deformer will automatically be invoked when either the direct parent or children are modified. This is IMHO a much simpler solution, but you are of course restricted in where you can put these dependencies in a scene, as they must be children of the deformer. When you want to go with InExcludeData, you must overwrite ObjectData.CheckDirty to manually flag the deformer as dirty when necessary. You iterate over all objects in the InExcludeData and based on their dirty count decide if the deformer is dirty or not. Find a draft for the method at the end of my posting. Cheers, Ferdinand The result: [image: 1662476641343-modifierdirty.gif] The code: def CheckDirty(self, op: c4d.BaseObject, doc: c4d.documents.BaseDocument) -> None: """Called by Cinema 4D to let a modifier flag itself as dirty. Args: op: The object representing the modifier hook. doc: The document containing the modifier. """ # When we want to track dependencies we must keep a cache for what we consider to be the # last no-dirty state of things. This could be done in many ways, I have chosen here a # dict of (bytes, int) tuples. # # The general idea is to store the last known dirty state data for each item in the # InExcludeData as such: # # { Object_A: 193, Object_B: 176, Object_C: 25, ...} # # But BaseList2d instances itself are not hashable, i.e., cannot be keys in a dict, and # doing something like `myDict[id(someObject)] = someValue` would be a really bad idea, # as objects are managed in the backend by Cinema 4D and can be reallocated all the time, # so id(myBaseList2d) is not a safe way to identify objects. Instead we are using # C4DAtom.FindUniqueID to get the unique MAXON_CREATOR_ID marker of each item and then # use that marker to store the dirty flags. # The dirty cache data structure attached to the plugin class and the InExcludeData parameter # in question. self._linklistDirtyCache: dict[bytes: int] data: c4d.InExcludeData = op[c4d.ID_LINKLIST] if not isinstance(data, c4d.InExcludeData): return # Now we iterate over all items in the InExcludeData to see if their dirty state has changed. isDirty: bool = False for i in range(data.GetObjectCount()): node: c4d.BaseList2D = data.ObjectFromIndex(doc, i) if not isinstance(node, c4d.BaseList2D): continue # Get the MAXON_CREATOR_ID unique ID of this node. mem: memoryview = node.FindUniqueID(c4d.MAXON_CREATOR_ID) if not isinstance(mem, memoryview): continue uuid: bytes = bytes(mem) # Get the current and cached dirty state of that item. currentDirtyCount: int = node.GetDirty(c4d.DIRTYFLAGS_DATA | c4d.DIRTYFLAGS_MATRIX) cachedDirtyCount: typing.Optional[int] = self._linklistDirtyCache.get(uuid, None) # When there is either no cache or the cache differs from the current value, we update # our cache and set isDirty to True. if (currentDirtyCount is None) or (cachedDirtyCount != currentDirtyCount): isDirty = True self._linklistDirtyCache[uuid] = currentDirtyCount # When isDirty is True, we flag ourselves as dirty, which will cause ModifyObject to be called. if isDirty: op.SetDirty(c4d.DIRTYFLAGS_DATA) print (f"Modifier has been set dependency dirty: {self._linklistDirtyCache.values()}")
  • Setting bitmap in Redshift dome light

    s26 c++ windows
    10
    0 Votes
    10 Posts
    2k Views
    ferdinandF
    Hey @spedler, I am glad that this solved your problem. Regarding putting this in the docs or SDK: I have created a task for doing this, but there is other stuff in front of the queue, so it might take some time before I find the time to do so. Cheers, Ferdinand
  • How do I get the status of redshift AOV mode?

    python
    2
    0 Votes
    2 Posts
    328 Views
    ManuelM
    Hi, Please mark your thread as a question using the forum tools. Redshift is a VideoPostData plugin. You cannot access the parameter directly in the renderdata. This is just a guess of what you are trying to do. Instead, you must find the VideoPostData that contain all Redshift parameters. from typing import Optional import c4d import redshift doc: c4d.documents.BaseDocument # The active document op: Optional[c4d.BaseObject] # The active object, None if unselected def main() -> None: renderdata = doc.GetActiveRenderData() vprs = redshift.FindAddVideoPost(renderdata, redshift.VPrsrenderer) if vprs is None: raise ValueError("Cannot find the redshift VideoPostData") print (vprs[c4d.REDSHIFT_RENDERER_AOV_GLOBAL_MODE]) if __name__ == '__main__': main() There is this thread where you will find a script to print all the AOVs Cheers, Manuel
  • some problem with CUSTOMGUI_BITMAPBUTTON

    s26 python sdk
    3
    2
    0 Votes
    3 Posts
    580 Views
    M
    thank you very much! It's help me a lot! sorry about the multiple topics, I'll take them apart next time
  • Attach Export Dialog

    python
    5
    0 Votes
    5 Posts
    822 Views
    J
    @m_adam Thank you very much! This is what I was looking for.