• Bundle fonts with Cinema 4D submissions

    Cinema 4D SDK python 2025 2024
    7
    0 Votes
    7 Posts
    839 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.
  • Knowing if a font exists

    Cinema 4D SDK windows python 2025
    4
    0 Votes
    4 Posts
    393 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"
  • String to Spline (cast)

    General Talk programming
    2
    0 Votes
    2 Posts
    398 Views
    ferdinandF
    Hey @Kantronin, Thank you for reaching out to us. Unfortunately, I do not understand your question. The print out you provided, <c4d.SplineObject object ..., already indicates that the object is a c4d.SplineObject. So, I do not really understand what you mean with 'get a spline-type variable'. To get the underlying editable spline object for a spline generator, e.g., get a spline with editable vertices and tangents for a Flower Spline generator, you should use BaseObject.GetRealSpline. But that is only necessary when you have a BaseObject which under the hood represents a spline. It does not apply when you already have a SplineObject as you do. Cheers, Ferdinand
  • 0 Votes
    2 Posts
    434 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
  • How to Encrypt Python Plugins without a License?

    Cinema 4D SDK python 2025
    2
    0 Votes
    2 Posts
    338 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
    3 Posts
    323 Views
    J
    Thank you so much
  • 0 Votes
    3 Posts
    308 Views
    J
    Thank you so much
  • Create Xpresso Node with input port Object

    Cinema 4D SDK python
    7
    2
    0 Votes
    7 Posts
    546 Views
    J
    Ok thanks and sorry
  • 0 Votes
    13 Posts
    2k Views
    ferdinandF
    Hey, your second code snippet never sets self._result. Regarding loading vs. cloning documents: That I did it how I did it was no accident, but that does not necessarily mean that cloning things is bad. C4DAtom::GetClone is implemented in the different scene elements independently; i.e., BaseObject::GetClone, BaseDocument::GetClone, BaseTag::GetClone, etc. and C4DAtom::GetClone is just the empty implementation. The different implementations then rely on C4DAtom::CopyTo to copy data from the reference into a new instance of that scene element type. On top of that comes that Cinema 4D is largely Copy-on-Write these days, with data actually only being copied when you try to modify it. This all explains why BaseDocument::GetClone is so inexplicably performant, it just copies the metadata and only copies the real payload of the scene when it has to. On top of that comes that cloning from the same document from multiple threads in parallel could entail read-access violations (although somewhat unlikely). On the other hand, our rendering pipeline does exactly the same, it clones documents for rendering (the cloning is however done on the main thread). I personally would say what you are doing is probably okay, but I would not write such code as example code. I would have to spend more time on this to figure out if this is absolutely safe. Doing the actual cloning off-main-thread seems risky though (but is probably also fine in 99,9% of the cases). Cheers, Ferdinand
  • 0 Votes
    3 Posts
    449 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]
  • 0 Votes
    3 Posts
    400 Views
    ferdinandF
    And just to be clear, using a modal dialog, e.g., DLG_TYPE_MODAL, is absolutely fine in a script manager script. Because then the GeDialog.Open call will only return when the dialog is closed (and you therefore do not have to keep the dialog alive). The hack I showed above is only needed when you need one of the async dialog types in a script manager script for testing purposes.
  • 0 Votes
    3 Posts
    518 Views
    Amazing_iKeA
    @ferdinand Thank you very much for your response and suggestions. I’ll make sure to follow best practices and provide a minimal, testable code sample when posting questions in the future. I’ll also give embedding a group a try. Thanks again for your support!
  • Forum rollback removes dormant users

    News & Information information forum
    1
    0 Votes
    1 Posts
    383 Views
    No one has replied
  • 0 Votes
    2 Posts
    367 Views
    ferdinandF
    Hey @Viktor-Velicko, Thank you for reaching out to us. Generally, our codebase supports Unicode, but in C++ source code and in *.str resource files, we only support Unicode escape sequences, not Unicode symbols directly. In Python, we do support Unicode symbols directly. In the 2025.3 SDK, I just added a code example for how to use Python to create a Unicode escaping pipeline around *.str files. So, the Unicode string const String slopeLabel = "Slope 90º"_s; would be written as const String slopeLabel = "Slope 90\\u00b0"_s; in C++. A bit more verbose variant would be const String slopeLabel ="Slope 90"_s + String("\\u00b0", STRINGENCODING::BIT7HEX));. In Python, you can use the string directly as slopeLabel: str = "Slope 90°". Cheers, Ferdinand
  • Unable to Sign In to Old Forum to Generate Plugin ID

    Cinema 4D SDK python
    3
    0 Votes
    3 Posts
    361 Views
    d_keithD
    Thanks Ferdinand - Up and running again!
  • 0 Votes
    2 Posts
    730 Views
    ferdinandF
    Hey, I now know how our API will look like in this regard in 2026.0.0. Its compile/runtime behaviour will be as for 2025.2.1 and before. And we will introduce a new flag NOHANDLEFOCUS which must be set when attaching the area when one explicitly wants to ignore focus events. So, you will not have to change your code when you were happy with your user area behaviour as it is has been. Cheers, Ferdinand
  • 0 Votes
    3 Posts
    446 Views
    ferdinandF
    Hey @lionlion44, Thank you for reaching out to us. We cannot provide support on third party libraries (Octane). But, yes, in general you are on the right track. We have this C++ example, which I loosely translated to Python. The thing to do which you are missing, is to check if such VP already exists, as you otherwise can land in a world of hurt. For everything else, you would have to talk with the Octane devs (of which some are here on this forum), if there are any special further steps to be taken for Octane. Cheers, Ferdinand """Provides an example for generically setting a render engine in Cinema 4D. Note that there is no guarantee that every render engine has a video post node, and when it has one, that it uses the same ID as the render engine. But it is highly conventional to implement a render engine like this. Derived from the C++ Example "Set Render Engine to Redshift": https://developers.maxon.net/docs/cpp/2023_2/page_manual_redshift_rendrer.html """ import c4d import mxutils doc: c4d.documents.BaseDocument # The active Cinema 4D document. def SetRenderEngine(doc: c4d.documents.BaseDocument, newEngineId: int, createsVideoPostNode: bool) -> bool: """Sets the render engine of the given document to the specified ID. """ # Make sure we are on the main thread, as we plan to modify the document and ensure that our # inputs are what we think they are. if not c4d.threading.GeIsMainThread(): raise RuntimeError("SetRenderEngine must be called from the main thread.") mxutils.CheckType(doc, c4d.documents.BaseDocument) mxutils.CheckType(newEngineId, int) mxutils.CheckType(createsVideoPostNode, bool) # Get the currently active render engine ID and get out if it matches the new one. renderData: c4d.documents.RenderData = doc.GetActiveRenderData() currentEngineId: int = renderData[c4d.RDATA_RENDERENGINE] if currentEngineId == newEngineId: print(f"Render engine {newEngineId} is already set, no changes made.") return True # Try to find a video post with the render engine ID. There is no absolute guarantee that every # render engine either has a video post node or that is gives it the same ID as the render # engine (but it is strongly conventional). if createsVideoPostNode: # Try to find an already existing video post node with the render engine ID. node: c4d.documents.BaseVideoPost | None = renderData.GetFirstVideoPost() while node: if node.GetType() == newEngineId: break node = node.GetNext() # There is no video post for the render engine, so we try to a new create one. if not node: try: node: c4d.documents.BaseVideoPost = c4d.documents.BaseVideoPost(newEngineId) renderData.InsertVideoPost(node) except Exception as e: raise RuntimeError(f"Failed to create video post node for render engine {newEngineId} ({e}).") # Finally, we set the render engine ID in the render data. renderData[c4d.RDATA_RENDERENGINE] = newEngineId return True def main() -> None: """Called by Cinema 4D to run the script. """ # Setting the standard render engine, here we do not have to create a video post node, since # the standard renderer is one of the rare cases that does not have a dedicated video post. SetRenderEngine(doc, newEngineId=c4d.RDATA_RENDERENGINE_STANDARD, createsVideoPostNode=False) # Set Redshift as the render engine, which does have a video post node. SetRenderEngine(doc, newEngineId=c4d.VPrsrenderer, createsVideoPostNode=True) # Push an update event. c4d.EventAdd() if __name__ == "__main__": main()
  • 0 Votes
    2 Posts
    419 Views
    ferdinandF
    Hey @shir, Thank you for reaching out to us. A Program Database (PDB) is a debug information format from Microsoft. It is comparable to the DWARF debug information format often used under Linux and macOS. However, unlike DWARF under Linux, where debug information is directly compiled into the binary, Microsoft chooses to store debug information in separate files, the pdb files. When you attach a debugger to a binary without any debug information, it will by default only see the machine code of the binary. So when you have an issue and the debugger puts out a stack trace, it will only show you the offsets in a library, e.g., something like this: #1 0x0000000000767576 in myBinary.dll #2 0x0000000000767df4 in otherBinary.dll #3 0x0000000000773aca in myBinary.dll #4 0x00000000004b893e in myBinary.dll You can see this happen in the call stack window in your screenshot. VS only provides information in the format someBinary.ext!someAddress(), e.g., c4d_base.xdl64!00007ffb200acfb7(), as it has no further information. With bin!address() VS means a function at that address is being called. In my opinion, VS has one of the most cryptic stack trace formats out there and can be a bit confusing for beginners. To see meaningful output, you need the debug information for that binary, which among other things contains the mapping of addresses to source code. If you have the pdb file for the binary, you can load it into your debugger, and it will then show you something like this instead: #1 0x0000000000767576 in MyClass::MyMethod() at myClass.cpp:42 #2 0x0000000000767df4 in OtherClass::OtherMethod() at otherClass.cpp:15 #3 0x0000000000773aca in MyClass::AnotherMethod() at myClass.cpp:78 #4 0x00000000004b893e in main() at main.cpp:10 When you compile the Cinema 4D SDK and your source code, it will automatically generate the pdb files for these binaries for you, so that you can debug them in a meaningful manner. But what we see here is Visual Studio asking you for the pdb for c4d_base.xdl64, one of the core library binaries located in the corelibs folder of the Cinema 4D application you are debugging with. You did not compile that binary, so you do not have the pdb file for it. And we do not ship our binaries with debug information, as that would not only be a very large download, but also would expose our source code to the public. You are hitting a debug stop there (VS tells you that in the info box by stating this is a __debugbreak). This is the less critical case of a debug event, which is covered by the very tutorial you are following (the other one being a critical stop). You can simply hit continue in your debugger and ignore this. The event seems to be raised from Redshift, judging by the stack trace we can see in the screenshot you provided. There is probably some minor hardware issue or so, and Redshift is trying to handle it gracefully by raising this debug event. It is, however, not normal when this happens permanently and usually it hints at a corrupted installation of Cinema 4D or a hardware issue when you are always greeted by debug events on startup (or even when just running and interacting with Cinema 4D). Sometimes debug stops can happen as a one-time thing when you are debugging for the first time against some Cinema 4D instance (and it has not yet built all its prefs, caches, and other things Cinema 4D builds in the background). When this persists and you are annoyed by having to press continue, I would recommend trying to either remove Redshift from your Cinema 4D installation or reinstall Cinema 4D altogether. You could also check inside of Cinema 4D if you can see any errors in the 'Redshift Feedback Display' window. For you as a third party, it is however not possible to find out what that issue in c4d_base.xdl64 at the offset 7ffb200acfb7 is. Cheers, Ferdinand PS: There is also g_enableDebugBreak=true|false which you can pass to your Cinema 4D instance as a commandline argument. With that you can permanently mute debug stops. But that is more of an expert feature and you probably do not want to enable that as a beginner.
  • How do I create a Plugin Identifier?

    Cinema 4D SDK windows python 2024
    14
    0 Votes
    14 Posts
    2k Views
    ferdinandF
    Hey @shir, good to hear that you solved the issue. Maybe NodeBB has an issue with the specific (top level) domain your mail handle was under? I just checked the logs and this is the event for the second registration mail that has been sent out (I edited your mail handle for privacy reasons). I.e., this is the one I manually invoked. There is another event for your actual registration. As far as NodeBB is concerned, it seems to be convinced that it successfully sent these mails. { "confirm_code": "dbcc0d6c-8646-4191-9975-badc1c7035f2", "email": "[email protected]", "subject": "Welcome to PluginCafé", "template": "welcome", "timestamp": 1751883962965 } NodeBB can be a bit buggy from time to time but that it fails to send a mail and then creates an event for successfully sending it, would be a bit odd. I will have an eye on this. Cheers, Ferdinand
  • 0 Votes
    6 Posts
    707 Views
    ferdinandF
    Hey @ECHekman, I sense there is some frustration, but I am not sure telling us how bad our API is will get us anywhere. Yes, the Nodes API ist not trivial, but you are only on the using part (which is not that hard to understand) not the implementation part (which is the trickly one). There are multiple render engine vendors who took that hurdle. I already answered your questions, and as always you will not see source code from us, unless you give us executable code in the first place upon which we can build, or we deem a subject new. I often bend these rules a bit where it makes sense to meet our customers and third parties halfway. But you cannot just throw a snippet at us and then expect us to invent everything around it and then fix that for you. Executable code makes a difference as lined out in our support procedures. My hunch would be that your getConnectedNode does not work because you do not check if your nodes are valid. You can get the value of a port with GetPortValue or GetEffectivePortValue. What you are doing with GetValue and EffectivePortValue is the old way but will still work. // Redshift expresses a lot of its enums as strings and not as ints (did not check if that is here the case). const String value = myPort.GetEffectivePortValue<String>().GetOrDefault() iferr_return; And as lined out before, what your function is trying to do, can likely be done via GraphModelHelper too, e.g., with GraphModelHelper::GetDirectPredecessors. An alternative and more manual approach would be using GraphNode.GetInnerNodes and GraphNode.GetConnections. And as always, I am not really looking for a discussion about what you or I would consider a good API. I am telling you that you will be in a world of hurt when you terminate your errors everywhere as you did in your code. Your code will then just silently fail without you knowing why. So, I gave you an example on how to use our error handling. Cheers, Ferdinand