• 1 Votes
    4 Posts
    1k Views
    ferdinandF
    Hey Jacob, Thank you for the added data. First of all, I have invited you to the forum to end this middle man communication, which is a bit odd. The pyz file is part of python ... I am aware of what pyz is, I just pointed this out because I of course looked inside your package and found all the py_armor obfuscated code and the injected binaries in there. So, I pointed out that this is bit more than just "packaged in a pyz file for ease of distribution [...]" as Lasse/you put it, the goal is here clearly obfuscation. Which is also relevant for support, as it limits what you and I can see (without getting hacky). My finding with the 10mb file freeze comes from my trial and error ... mean[t] when you run a script from Extensions -> User Scripts. Your code also freezes when you load it as a Script Manager script. That is what I did with the last package from Lasse, and now also yours. The code in your script is again wrong, which is why it won't freeze until you fix it. This is the code I found: [image: 1760086817339-2bd4290e-78b2-43d4-936d-1e2a7eaf366b-image.png] And I fixed it then to this. When I ran it then, Cinema 4D froze for two minutes or so, after that it opened a myriad of dialogs to then terminate into two crash dialogs (it was pure luck that I let it run for so long, Lasses previous version might have acted similar, but there I killed the C4D process, as soon as I saw the 'beach ball of death' cursor on MacOS). [image: 1760086755748-69fcb5da-ac49-477e-8f70-9daeb1daa1aa-image.png] Please read my answer below carefully, as I already pointed out most of this in my previous posting. I would STRONGLY suggest debugging this without obfuscation. Maxon also cannot debug larger sections of code or test further packages for you. I understand that obfuscation might not be your choice, but it will make your life harder in debugging this, as you always fly blind. We of course still will provide support, but you have to provide more than "it does not work/crashes/freezes, please help us", especially when this is not code tied to our APIs. Attach a debugger from the Script Manager and see why your code crashes/freezes (see link in last posting when unsure how to do this). But you need an un-obfuscated code base for this to make any sense. Defer your loading to a later point, e.g., C4DPL_PROGRAM_STARTED, when you have issues in the direct plugin registration context. In that case you would always register your plugin, but then only execute it when the your own license check succeeded. But you absolutely cannot ship a plugin which freezes Cinema 4D for multiple minutes on startup or when invoking your plugin because your licensing takes so long. When we see this in the wild, we will have to blacklist your plugin IDs, as this damages the brand Cinema 4D. Please use threading then to not block the main thread with your long running code. What I did not notice before is that you apparently try to open multiple dialogs (for me it opened multiple dialogs when I ran the script). The GUI and many other systems are not yet available when Cinema 4D is still booting, e.g., in the splash screen. You can expect all systems to be up and running when C4DPL_STARTACTIVITY is emitted, but it is better to wait for C4DPL_PROGRAM_STARTED for long running tasks (i.e., the two events I tested in my previous posting). Please also keep in mind that Cinema 4D has its own anti-piracy measures. Python plugins are sort of special in that they work slightly different than native C++ plugin modules (the Python C++ module shipped by Maxon sort of acts as a surrogate for Python plugins in the module registration phase). But Cinema 4D won't allow plugin threads to start their own processes at this point (which you might be trying to do with your injected binaries), and threading should also be avoided at this point, as the job system of Cinema 4D might be still booting. What you are meant to do in PluginStart (or the __main__ context of a pyp file), is register your plugins. You can run some quick logic there, but you are certainly not meant to start communicating with servers and opening GUIs there. You can read here a bit more about this from a C++ system perspective. I would recommend to do your license check in the background in its own thread once C4DPL_PROGRAM_STARTED has been emitted (so that you can also open dialogs to signal errors). An alternative would be to do it when the user clicks the button of your command. But you should also here put it into its own thread, so that it does not block everything else. Cheers, Ferdinand
  • 0 Votes
    3 Posts
    948 Views
    gheyretG
    Thank you for the C++ code—it's been very helpful! Cheers!
  • 0 Votes
    4 Posts
    992 Views
    M
    I'm not sure I understand you correctly, you do not have to use QuickTabRadio bar with resource, you just retrieve the int value of the parameter, in the case of the previous example I share with a GetInt32(c4d.MGCLONER_VOLUMEINSTANCES_MODE). If you can provide a code example of what is blocking you that would be nice. You can find a non-exhaustive list of type of control available in *res file within the C++ documentation in Description Resource. Cheers, Maxime.
  • 0 Votes
    2 Posts
    497 Views
    M
    Hey you need to react to MSG_GETCUSTOM_NAME_ADDITION and return a dict with the expected string with the "res" key. You need to register your object with c4d.OBJECT_CUSTOM_NAME_ADDITION to have the additional name displayed in the Object Manager. Cheers, Maxime.
  • exporting usd with python

    Cinema 4D SDK 2025 python windows
    2
    0 Votes
    2 Posts
    623 Views
    ferdinandF
    Hey @lionlion44, Thank you for reaching out to us. I doubt that this will not work in Python, it is just that we have not documented the symbols there yet. But you can find them in the C++ docs: [image: 1757607809636-6e23eada-2198-4c34-84bf-a576bbdd0ef9-image.png] I.e., it will be c4d.FORMAT_USDIMPORT and c4d.FORMAT_USDEXPORT. Cheers, Ferdinand
  • GetRad() / GetMp() update issue

    Cinema 4D SDK windows python 2025
    2
    0 Votes
    2 Posts
    433 Views
    ferdinandF
    Hey @datamilch, Thank you for reaching out to us. GetRad and GetMp express the locally aligned bounding box of an object. I.e., the bounding box in the coordinate system of the object. For a world space aligned bounding box we once had this thread, the Cinema API does not offer this out of the box. So, transforming an object will never change its bounding box. What will change the bounding box of an object, is changing its size or changing the parameters of a generator object. When you change the parameters of a generator or when your instantiate a new object, you will have to execute the passes (BaseDocument.ExecutePasses) on a document that contains the object to see the correct bounding box - or wait for the next scene update. PS: Your script is also running illegal code. You cannot modify the active document off-main-thread. I assume the is a script for a Python generator object. Its main function runs off-main-thread and you call there your csto which in turn calls SendModelingCommand on the active document. This will all sooner or later crash. https://developers.maxon.net/docs/py/2025_3_1/manuals/manual_threading.html#threading-information Cheers, Ferdinand
  • 0 Votes
    11 Posts
    2k Views
    A
    @ferdinand Thank you for your in-depth analysis. It would have taken me way too long to figure this out lol, especially finding "CUSTOMDATA_BLEND_LIST". The first example seems to work for me, but like you said, it may not be reliable. The Python node example also works great. Again, thank you for your time.
  • Can I get keyboard input mode?

    Cinema 4D SDK windows python c++ 2025
    4
    1
    0 Votes
    4 Posts
    705 Views
    ferdinandF
    Hey @Dunhou, I have edited your posting. It is fine to mention the beta forum, but we would rather not see its name written here. When you are talking about 'C++', I assume you are talking here about the Windows SDK/API, as for example GetGUIThreadInfo. I would doubt that you get very far with that here. Cinema 4D is very OS agnostic and we have our whole GUI decoupled from the OS. The little rename window in our tree view control is not an OS rename dialog. I am not even sure if this is something with an HWND handle which you could address so that you could intercept keyboard events to it, or if this is just a virtual window within our API. For this IMM thing to work, we would have to use the IME API in Cinema 4D, so that our text edit gadgets support it. I also do not quite understand what you are trying to do. The missing piece is here probably that our text edit gadget does not send its current content to the IME API (at a glance, via ImmSetCompositionStringW as shown here). And as a third party, you cannot really fix that. Because you (a) do not own the implementation and you (b) are not informed about an edit event, so you cannot just write a plugin which sends the object name to the IME API. Cheers, Ferdinand
  • 0 Votes
    4 Posts
    782 Views
    ferdinandF
    Hey, thanks for the extended code, but I still cannot run this, as it is only a fragment So, I only have a very rough understanding of what is going wrong. A likely issue apart from you just having a bug in your hit logic, is that you use the wrong coordinate system. if msg[c4d.BFM_INPUT_DEVICE] == c4d.BFM_INPUT_MOUSE \ and msg[c4d.BFM_INPUT_CHANNEL] == c4d.BFM_INPUT_MOUSELEFT \ and msg[c4d.BFM_INPUT_VALUE]: local_x = msg[c4d.BFM_INPUT_X] local_y = msg[c4d.BFM_INPUT_Y] scroll_y = self.dialog.get_scroll_offset() I would have to check myself, but there is some inconsistency with which dialogs send mouse input messages regarding the used coordinate system. Sometimes they send messages in the coordinate system of the dialog and sometimes messages in the coordinate system of the gadget. Just print out your (local_x and local_y) and check if the values make sense as local coordinates as you seem to treat them. On GeUserArea are multiple coordinate conversion methods. I think BFM_INPUT in this context is in local user area coordinates but I am not sure, it could also be that you have to convert them. The other thing is of course that you add this self.dialog.get_scroll_offset() on top of things. I assume this is sourced by something like GeDialog.GetVisibleArea? There you could also unintentionally mix coordinate systems. Cheers, Ferdinand
  • 0 Votes
    3 Posts
    778 Views
    d_keithD
    Thanks for the response! I appreciate the ASSETDATA_FLAG_MULTIPLEUSE tip. My hope was that there was some sort of existing RenameAsset SDK method that would also update all references to that asset without implementing custom handling for various element types (Redshift Nodal Material, Cinema 4D Classic Material, Arnold Dome Light, XRef, etc). Evidently such a public python SDK method doesn't exist. My example code was texture specific as that's the part of the problem that's most pressing to me and I was able to implement with custom per-type handling - if I manage to get other types working, I'll attempt to update over time.
  • Motion Blur, ExecutePasses in VideoPostData

    Cinema 4D SDK c++ 2025
    2
    0 Votes
    2 Posts
    447 Views
    ferdinandF
    Hello @Aaron, Thank you for reaching out to us. Please follow our support procedures. You should consolidate your postings until we answer, i.e., update your last posting instead of creating new ones. I have consolidated your postings for you. You should also spend more time on making your problems reproducible for us. I know it can be easy to overlook in the heat of the battle but your question(s) are not as obvious to me as you probably think they are. As lined out in our support procedures, example code and/or scene data is usually required. I cannot answer every possible angle and interpretation this topic of yours could take, but I can state three things which come to mind when reading your two postings. What's the way to get several steps of motion blur transformation matrices for BaseObjects of the BaseDocument. That is simply not possible. A document has no notion of motion blur as it is a render effects implemented by a render engine, not something documents implement. Cinema's time system BaseTime allows for sub-frame accuracy, e.g., you could set a 10fps document to BaseTime(i, 30) and with that step with three sub-frames through the document. Simple things like position animations or other interpolated values will also work doing this. But simulations might yield different results than stepping with the natural frame rate through the document. And the most important thing is that there is absolutely no guarantee that this approach will result in 'matrices', i.e., transforms, that will match with what a render engine produces when you add some kind of sub-frame-based motion blur. Using this BaseTime approach is one way how the render engine could do it, but since render engines always have their own scene format, they could also do the calculation there, yielding different results than what Cinema's animation system would yield. When we want to add Motion Blur and call document->ExecutePasses(...) for some time then this POC is flushed and has no polygons or vertices, so it's not collected and can't be rendered It is too opaque for me what you are doing there to give a real answer, but: There is no guarantee that the caches of an object are built at any point except for when a render engine gets a scene state to render or inside GetVirtualsObjects when you manually build your dependent objcts. Usually, caches are built but depending on where you are, the cache of another object might not have been built or updated yet, resulting in either an empty cache or you looking at the cache output from the previous scene update. ExecutePasses has become a bit too popular tool recently. I guess that is partially my fault, because I have shown its usage in some examples. But executing the passes on a document is a very expensive operation and should only be carried out when absolutely necessary. I would absolutely not be surprised if something like liquids do not support stepping through a document with a frame rate lower than the documents frame rate. But without concrete data and example code I am just guessing. Cheers, Ferdinand
  • 0 Votes
    3 Posts
    560 Views
    A
    okay the docs you gave were great for insight working code to change parameter of roughness for x given mat selections import c4d import maxon from maxon import GraphDescription doc = c4d.documents.GetActiveDocument() # Get selected materials directly selected_materials = doc.GetActiveMaterials() # Modify roughness for each selected material for material in selected_materials: graph = GraphDescription.GetGraph(material, nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial) GraphDescription.ApplyDescription(graph, { "$query": { "$type": "Standard Material" }, "Reflection/Roughness": 1 }) c4d.EventAdd()
  • How to get explicit import node space?

    Cinema 4D SDK windows python 2025
    3
    1
    0 Votes
    3 Posts
    442 Views
    DunhouD
    Thanks @ferdinand for your great solution! Cheers~ DunHou
  • 0 Votes
    5 Posts
    751 Views
    DunhouD
    Hey @ferdinand , thanks for details. Cheers~ DunHou About forum, I can get notification about my own topic, And I clear all the cache, but nothing changed. After I tried to reset my account settings manually, then it worked as expected.
  • Knowing if a font exists

    Cinema 4D SDK windows python 2025
    4
    0 Votes
    4 Posts
    574 Views
    D
    for my use case i will probably have a fallback solution with web-save or system fonts like this: import platform system_name = platform.system() if system_name == "Windows": font_name = "Consolas" elif system_name == "Darwin": font_name = "Menlo" elif system_name == "Linux": font_name = "DejaVu Sans Mono"
  • 0 Votes
    8 Posts
    959 Views
    A
    @ferdinand said in BaseBitmap.Init("somefile.exr") crashes with multiple std::thread: omp #parallel Sorry for late reply, I am not advocating for std::threads, I understand your design explanation. The omp #parallel thing is OpenMP directive to launch parallel threads. It's an old but simple method. Actually I have made a solution by exploiting Cinema way of launching threads and it works fine. However, I hope it would be good to make BaseBitmap->Init a thread safe and independent call from other C4D threads and resources. I will collect an example for you with exr images soon. Cheers, Aaron
  • Bundle fonts with Cinema 4D submissions

    Cinema 4D SDK python 2025 2024
    7
    0 Votes
    7 Posts
    2k Views
    K
    Hey @ferdinand , Sorry for the late replies. You can only answer these with end user support and the beta community. I will do that. Although I haven't used the beta community yet, I'll post the same question there to better understand what the recommended workflows are right now. you would either have to limit the feature to 2024+ clients That's good enough for us. Our plugin officially supports only the latest versions of 2024 and 2025.
  • How to Encrypt Python Plugins without a License?

    Cinema 4D SDK python 2025
    2
    0 Votes
    2 Posts
    509 Views
    ferdinandF
    Hey @kbar, thank you for reaching out to us. Please remember creating new topics instead of posting in older one's, so that our forum remains a searchable database. I have forked your question. I understand the necessity and use case for this, but in the proposed form, this is not possible. At least not without more work than it makes sense to invest here. Because the license validation is one of the first steps in the boot sequence of Cinema 4D, and trying to evaluate a command line argument and use all the components of our API necessary to encrypt a file could easily lead to problems (as these modules might not have been loaded yet). What we could think about is offering this as a standalone tool. The issue is here that we cannot just ship the source for that tool (for third parties to compile it themself) as the source would then also be known to bad actors. And shipping it as a binary would be extra work. An alternative could be a REST service, but that is also extra work (but less than the former route). I currently have no time for either of this. At the end of the year I will probably do some website work, then we could think about the REST route. But it is likely that I then also do not have time for this. As an MRD, you also have the option to ask in our beta forums or make a source code sharing request. In the end, this is very simple code and could probably easily emulated with a couple of lines of Python code. That is probably the most sensible route, but we would have to involve PMs etc. here, if we are willing to do that. I myself am even not sure if I would greenlight such request due to the sensitive nature of the topic. So, unfortunately, there is no clear cut route here, although I definitely can see the use case. Cheers, Ferdinand
  • 0 Votes
    2 Posts
    570 Views
    ferdinandF
    Hey @Amazing_iKe, Thank you for reaching out to us. That RenderDocument does not support animations is only partially true. Effectively it does not for users, but that is more a regression than the lack of an actual feature. When you look into its documentation, you will see that many symbols and arguments are animation related. And when you send it a document which holds an animation, you are paying the price for rendering the full animation. The regressed state is however that you effectively only have access to the bitmap of the last frame of that animation. There is also the issue that the OCIO workflow with RenderDocument is a bit clunky at the moment. We have these issues in our backlog by I unfortunately cannot make any guarantees as to when we will fix them. You have two options: Render the document frame by frame yourself with RenderDocument and then assemble the animation yourself. Use the BatchRender, i.e., what is shown to the user as the render queue. You can use this in a fully programmatic manner, but you are restricted to rendering documents that have been saved to disk, and the output will also be put to disk. I.e., you do not have the 'memory only'-aspect of RenderDocument. There are many topics about this subject on this forum, for example here where I showed how you abstract some of the disc-only limitations aways. Maxime also recently added some features to the batch render like switching cameras and takes, which makes it less likely that you have to modify the file. I cannot write examples for all possible routes here. You have to decide yourself which route you want to go, and when you are then stuck there, I will help you. What to pick depends on your goals. When you expect the renderings to be quick, doing it manually with RenderDocument could be a solution. For more complex animations, the batch renderer is probably the better route. Cheers, Ferdinand
  • 0 Votes
    3 Posts
    644 Views
    ferdinandF
    Hey @Amazing_iKe, Thank you for reaching out to us. You do the same thing you did incorrectly in your other thread. You create an async dialog and you do not keep it alive. We would prefer it to keep things G-rated here. I.e., no nudity, no violence, and no weapons. I removed the image in your posting. With that being said, and when I try out your script (with the dlg thing fixed). I end up with this: [image: 1752854682502-fc83d223-abc0-4568-98fc-deb82596b059-image.png] I.e., it just works. But you docked your dialog (and so did I imitating what you did). I now cannot reproduce this anymore, but when I did something in my layout, I think I dragged a palette, I got a similar result as yours. That is not too surprising though as you implement here an async dialog in a hacky manner. You have no CommandData.RestoreLayout which would handle layout events for your dialog. Something getting out of whack with scroll areas is not out of question for such irregular cases. Please follow the patterns shown in our GUI examples I would recommend to follow py-cmd_gui_simple_2024, it also contains code for handling layout events. I.e., this: def RestoreLayout(self, secret: any) -> bool: """Restores the dialog on layout changes. Implementing this is absolutely necessary, as otherwise the dialog will not be restored when the user changes the layout of Cinema 4D. """ return self.Dialog.Restore(self.ID_PLUGIN, secret) And to be clear, it is absolutely fine to share plugins with us here. It does not have to be a script manager script. Things should just not get too long. Feel free to anonymize your plugin IDs when you feel skittish about sharing them in public. Cheers, Ferdinand edit: Okay, there it is again. Not exactly the same as yours but close. But I would still have to ask you to provide an example where this happens inside a valid async dialog implementation (with a command or a similar owner). It is not out of question that there is a bug but we still need a non-hacky example. [image: 1752856082257-df89038f-124d-434f-8cde-3442bd9aebba-image.png]