Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Recent
    • Tags
    • Users
    • Register
    • Login
    1. Maxon Developers Forum
    2. ferdinand
    3. Posts
    Offline
    • Profile
    • Following 0
    • Followers 16
    • Topics 56
    • Posts 3,219
    • Groups 2

    Posts

    Recent Best Controversial
    • RE: V-Ray Materials Not Detected by Project Asset Inspector

      Please also share which version of Cinema 4D you are using, I'll try to have a look this or next week.

      edit: I briefly checked with scripts/05_modules/node/create_redshift_nodematerial_2024.py and there, with Redshift, it works and the assets show up in the inspector right away. Which hints at this being an exlusive VRay issue which only can be solved by Chaos.

      c613fbf4-4621-46d8-af96-1253e7df7c6a-image.png

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: Shader bitmap rendering with MemoryFileStruct

      Hey @WickedP,

      Thank you for reaching out to us. This is rather hard to answer as is. It is not that your question would be inadequate or the code example unclear, but this is also a subject I would have to try out myself to know for sure. The problem with pseudo code is that it is not executable, when you want a definitive answer, I must ask you to provide compileable code which demonstrates the issue.

      About your Issue

      Since you say that sending such image file to the Picture Viewer works, we can rule out that you made any mistakes in setting up the Filename in memory mode or properly encoding your image data.

      What is noteworthy is that Filename is a Cinema API type from the 'old' days which is today just a hollowed out shell that wraps UrlInterface. When using the memory feature of Filename you are actually using maxon::URLSCHEME_MEMORYFILESTRUCT (not to be confused with URLSCHEME_MEMORY). Internally we tend to use yet another file virtualization scheme implemented for URLs when embedding virtual files in scenes or renderings, maxon::URLSCHEME_RAMDISK.

      There could be a threading issue with your Filename (check if the issue also happens when you pass RENDERFLAGS::NODOCUMENTCLONE to your RenderDocument call).

      The most forward fix would be however to use a ram disk, as that is what is proven to work with render engines. The problem is that there are currently no public examples for creating URLSCHEME_RAMDISK urls in the public SDK (as it is sort of a semi-official thing). The only place where I used it was in the Asset API docs, but only to ensure ram disk assets are being cached, not to create new ones. But ram disk scheme urls have a convenience interface, RamDiskInterface which makes it quite straight forward to use. Find below some mock code for your use case (did not compile it due to the lack of a project for this topic).

      Cheers,
      Ferdinand

      using namespace maxon;
      
      Result<void> DoRamDiskStuff()
      {
        iferr_scope; // Error handler for this scope because we are using the Maxon API. When you want to merge
                     // this with Cinema API code, i.e., use it in a function that is not of type Result<T>,
                     // you would have to use here iferr_scope_handler to terminate errors into the return
                     // type of your function.
      
        // Create a new mounting point for ram disk files. Use a unique ID in reversed domain name notation 
        // to avoid conflicts with other mounting points or use the other constructor to create an automatic 
        // hashed mounting point (which will not be persistent over reruns).
        RamDiskRef mountingPoint = RamDiskInterface::Create("net.mycompany.ramdisk.foo"_s) iferr_return;
        Url root = mountingPoint.GetRoot() iferr_return;
      
        // There are now basically two ways to create ram disk files: 
        //   (1) direct creation where we effectively treat the Url just like a file.
        //   (2) lazy creation where we provide a callback that will be called when the file is first accessed.
      
        // Our payload.
        const Char payload[] = "Hello World!";
      
        // Direct creation:
      
        // We create a file under our ram disk mounting point, it would have an URL such as 
        // "ramdisk://.../file.txt". You can directly use this URL in all parameters and file 
        // operations. We can also turn an Url back into a Filename using cinema::MaxonConvert.
        Url directFile = (root + "direct_file.txt"_s) iferr_return;
      
        // Now we write to the file, this is just standard Maxon API file I/O.
        InOutputStreamRef inout = directFile.OpenInOutputStream() iferr_return;
        inout.Write(payload) iferr_return;
      
        // And this would be the way to read back.
        Char buffer[SIZEOF(payload)];
        inout.Seek(0) iferr_return;
        inout.Read(buffer) iferr_return;
      
        // Deferred/Lazy creation:
      
        // This way is in a certain sense a bit easier (as stream handlers can be a bit scary when new
        // to the Maxon API). The advantage is of course that we only pay the costs when something is
        // actually needed. The downside is that is this can happen at an inconvenient time. Which is
        // also why I showed in the Asset API examples for how to build caches in advance. But in your 
        // case where you create the URLs yourself, this makes no sense.
      
        // This is just this one call which we invoke on our ram disk mounting point. We provide a callback 
        // that will be called when the file is first accessed, and in this callback we create an IoMemoryRef 
        // with our payload and return it.
        Url lazyFile = mountingPoint.CreateLazyFile(ToSingletonBlock("lazy_file.txt"_s),
          [payload] () -> Result<IoMemoryRef>
          {
            iferr_scope;
            DiagnosticOutput("Lazy content creation triggered.");
            IoMemoryRef mem = IoMemoryRef::Create() iferr_return;
            mem.WriteBytes(0, payload) iferr_return;
            return mem;
          }) iferr_return;
      
        // Reading back would work the same here.
      
        return OK;
      }
      

      In your specific case, I do not know if BaseBitmap::Save will work directly on a Filename that wraps a ram disk url (via Filename file = cinema::MaxonConvert(myRamDiskUrl, MAXONCONVERTMODE::WRITE);), but you can try and it should work. When push comes to shove you can always write data directly as you showed yourself.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: V-Ray Materials Not Detected by Project Asset Inspector

      Hey @Ross,

      Thank you for reaching out to us. Assets in the sense of the Asset Inspector are realized via MSG_GETALLASSETS in general. For material nodes you have to set IMAGENODEASSETID and IMAGENODEPORTS on the node space, or directly implement MATERIALMESSAGEHANDLERFUNC (all attributes are in the same namespace). Under the hood, all these Nodes API systems pipe back into MSG_GETALLASSETS and it is the implementer who must realize them, i.e., Chaos in this case. You as a user cannot do anything about, the asset data is either there or not.

      I am currently on my Mac where I do not have VRay installed. The main question would be: Does VRay show its assets when you wire up the same materials manually? I think it does, right? You can try sending a MSG_UPDATE to the material holding the graph(s) but that should not be necessary by default(myMaterial.Message(c4d.MSG_UPDATE)). It could be that VRay is doing some black magic with its assets and MATERIALMESSAGEHANDLERFUNC which is not correctly setup when you programmatically create graphs. But that (a) strikes me as a bit unlikely and (b) only Chaos can help you with that.

      VRay is as a third party plugin generally out of scope of support, so their support is your best shot. But when you share your code and a scene, I can have an informal look as what is going wrong there (but it might take a week or so before I get to it).

      Cheers,
      Ferdinand

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: How to start a new line in plugin help with localization?

      It depends a bit on what you are doing. When you look at these screenshot I already posted above


      then you can see, Cinema 4D is already doing this automatically. The tooltip for a command¹ is always split into a name, description and shortcut. As a plugin author, you define the name and description and Cinema 4D then composes them for the tooltip. The shortcut will only be shown when this command has a shortcut set, either because your user set it up, or because you programmatically set it in some sort of 'installer' routine for your plugin. That the Py - Resource Gui example does not have a shortcut shown, is simply due to the fact that I did not define one.

      But your question implies a different type of 'shortcut', namely modifier keys. I.e., you do not want to display a key combination with which something can be activated (commands already fully handle this for you), but rather keys that change the behaviour of something while it is running. The canonical place to display such information would be the status bar. The Move tool shows for example this in the status bar once it has been activated (and a few other criteria are met):

      91530485-629f-4d43-8376-70ce83a1fb3a-image.png

      Other than the command tooltips, this is not automated. And each plugin has to set this on its own via c4d.gui.StatusSetText. The problem is a bit that these kind of status messages make mostly sense for tools, i.e., things where you have clearly defined states of a thing being active or not, and when the user does send mouse- or keyboard interactions. These states help managing the status bar because you not only have to set these messages, you also have to clear the status bar once you are done.

      So, it depends on what kind of plugin you implement how to do this concretely. When you implement just a command with a dialog, there is no predefined way how to do this, as dialogs are not meant to have such modifier keys. You could maybe show or hide such message based on if your dialog got or lost focus.

      Cheers,
      Ferdinand

      ¹ - Everything in this context is a command, even non CommandData plugins such as objects or tools, because Cinema 4D will always wrap them with a CommandData plugin for you, so that they can show up in palettes and menus. So, in menus and palettes are only commands, even when you did not implement one yourself.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to start a new line in plugin help with localization?

      Hey @Dunhou,

      our c4d_string.str files use standard ASCII escaping (of unicode). There is the relatively new py-cmd_gui_resources_2024 example which documents this (of particular interest would be unicode_encode_strings.py for you). But when I wrote unicode_encode_strings.py, I think it was a deliberate decision of mine to exclude the so called control characters in ASCII/Unicode (i.e., \x00 to \x1F as Python Unicode escape sequences). Which includes the whitespace characters like \t, \n, \r, etc. So, the script will not encode them for you.

      If you take the code example from above and wanted to add a line break in its help string, you cannot use \n in the string, because it would be escaped as \\n in the output, which is not what you want. Instead, you would have to use \x0A (i.e., the value 10 which is the code point for a line break in ASCII). But since this is not Python, but our C++ code which follows the Unicode standard to the letter, we do not use the \x prefix but \u and also pad it to 4 digits, so it would actually be \u000A for a line break.

      STRINGTABLE
      {
      	IDS_PLUGIN_NAME "Py - Resource Gui"; 
      	IDS_PLUGIN_HELP "Opens a dialog window that is populated\u000a using resource files.";
      }
      

      d48918e5-eca2-4b9d-90f5-36101de3f0bc-image.png

      If you wanted a more generic solution than writing such strings by hand, you would have to update EscapeUnicode in unicode_encode_strings.py to something like this:

      def EscapeUnicode(item: str) -> str:
          """Escapes both non-ASCII and control characters in a string to their unicode code point representation,
             e.g., \u000A for a line break.
          """
          return re.sub(r"([^\x00-\x7f]|\x00-\x1F)",
                          lambda m: "\\u{:04x}".format(ord(m.group(1))), item
          )
      

      You could also just escape everything, but that would make the strings less readable in the resource files. I am personally also not sure it is a good idea to allow users putting control characters into strings, primarily due to whitespace. I would for example say that it is not wanted that strings in general and tooltips in particular contain line breaks, and that it is up to Cinema 4D to handle wrapping strings. When I look at our Move tool, I see no line breaks in the help string? The help string is just 'Move Tool', the other things (the name and the shortcut) are added by Cinema 4D dynamically (and therefore put onto a new line).

      2d99ee72-e853-415c-a4a6-448796c526b7-image.png

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How do I retrieve a command call from it's index number ?

      The second ID is the sub ID. You can have a command (FOO = 100) which supports the sub-commands (BAR=1, and BAZ=2). So, you can then call CallCommand(100, 1) or CallCommand(100, 2) to invoke the two different things FOO can do. But as you said yourself, this is a rather unusual thing, and it is even more unusual that I manged to randomly click five things in a row that all have sub IDs 😄

      What should also be said is that my little "extrude" log there does not really reproduce the modelling operations I carried out. It just runs the tools in the order they are invoked. And because when you run this right after your created the log, the extrude tool will for example still have set the same extrude depth from the last operation. But when you manually extrude something, and then run the script again, it will use these new current extrude tool settings, i.e., and not the ones from when your 'recorded' the log.

      The script log can be useful, but it is not the auto-script-recording feature users often whish it was.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How do I retrieve a command call from it's index number ?

      Hey @Dimitris_Derm. ,

      I still do not 100% follow what you are saying and asking, which likely means that you or I are misunderstanding something.

      What are Commands?

      Cinema 4D is built itself on the same plugin architecture offered to third parties. There are many plugin types in Cinema 4D, but a central one is CommandData. A command is an operation that can be invoked without any arguments except the plugin ID of the command to invoke (and optionally a sub ID). CallComand is the function to call commands by their command/plugin ID. I.e., when you implement a command plugin as a user, everyone can call it via its command ID. Commands in this sense are not to be confused with modelling or uv commands, which are a different thing.

      Almost all buttons and menu entries in Cinema 4D are commands. So, when you for example implement an ObjectData 'Plane' plugin and register it, Cinema 4D will create a hidden CommandData plugin for you, so that this object can be created from a menu. Here shown with the builtin 'Plane' object:

      40b30de8-d931-4b34-9fda-d8f9b2cd0a51-image.png

      What are Symbols?

      The raw integer values for plugin IDs are usually (but not always) aliased with a 'symbol'. Symbol is just programmer slang for an alphanumeric label under which some data is accessible. E.g., foo, bar, and baz below are symbols:

      foo: int = 123456
      bar: dict[str, float] = {"Hello": 1.0, "World": 2.0}
      baz: callable = lambda x: x * 2
      

      In C(++), the langauge Cinema 4D is written in, the word symbol is very common. In the Cinema API it always refers to some alpha-numeric label for some integer value. There is code which is shipped to customers (the public APIs) and there is code that is internal. In order not to have users use raw integer values, we have to define such symbol, i.e., alias in the public API. This does not always happen.

      What is the Script Log doing?

      The script log is just a series of the the commands being called. There are no classes, functions, or methods. In some cases the script log creates more complex code (for tools), but the script log is never a place to learn the API, as it only operates via command IDs. And the script log uses the raw integer values because for once there is not always a symbol, and it would also not be trivial to access the symbol for each command from the C++ code that generates the log.

      3a8deeef-11fb-44c5-8f55-954e4e691b4d-image.png

      Cheers,
      Ferdinand

      PS:

      By "method" I meant the programmatic term which some times coincides with the term "function".

      I understood that, but just as a clarification, method and function are not synonymous. A method is a function that is associated with an object (i.e., it is a member of a class). A function is a more general term that can refer to any callable piece of code, including methods, but also standalone functions that are not associated with any object.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Executing a Redshift texture bake from Python

      Hello @mplec1,

      Welcome to the Maxon developers forum and its community, it is great to have you with us!

      Getting Started

      Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.

      • Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
      • Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
      • Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.

      It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: How to Ask Questions.

      About your First Question

      The bake tag and by extension cinema::BakeTexture (and its Python equivalent c4d.utils.BakeTexture) unfortunately do not support Redshift, even for the older Xpresso based materials.

      Redshift uses the tools found in Redshift/Tools/Texture Baking for baking. And while you can programmatically create a bake set and then programmatically click the 'Bake' button in it (which both would also work in a headless version of Cinema 4D, such as c4dpy), the following dialog which opens to set baking details and actually start the rendering is sealed, i.e., you cannot interact with it from the public API. And opening such dialog would also fail in a headless Cinema 4D instance.

      So, I am afraid there is currently no solution for your problem. You can technically export the whole scene to a format such FBX or USD, and use the builtin baking output (which would also work in a headless environment, as long as you do NOT pass SAVEDOCUMENTFLAGS_DIALOGSALLOWED to the save/export operation). But the output of that automated baking is often of poor quality compared to manually baking object(s) via bake sets.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How do I retrieve a command call from it's index number ?

      Hey @Dimitris_Derm. ,

      what do you mean by 'method'? CallCommand just invokes the CommandData plugin which has been registered under the plugin ID you passed to the call.

      What you can do, is resolve the numeric plugin ID integer value to a symbol. But unlike for message IDs, the docs currently do not resolve symbols to their integer values. You can do two things:

      1. Just search in the main symbol definition file with a text editor, i.e., {c4d}/resource/modules/python/libs/python311/c4d/__init__.py
      2. Or use mxutils.SymbolTranslationCache which exists for the very purpose of resolving such integer values to symbols.

      But in your case, 14046 , you will draw a blank in both cases, which simply means developers never defined a public symbol for that command.
      1b4e6537-4fad-48a1-b4ec-c6edd4103083-image.png

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Using the Bridge Tool

      Hello @Dimitris_Derm.

      No, with islands I do not mean different objects. With islands are polygon (selection) islands meant. And that is just the term that is used for these things. The "Plane" object below has two polygon islands, the left and right rectangle shown in the viewport, each composed out of 4 * 5 polygons. They are islands because they are topological disjunct from each other - you cannot 'get' from one island to another without jumping over a gap.

      a88ac530-c105-430c-bd11-f922f5688881-image.png

      The same can be applied to selections, as shown below. Now the left polygon island in the mesh has two polygonal selection islands. You cannot get from one selection island to another without jumping over a gap (they are topologically disjunct).

      bf41be6a-3993-4eb1-b603-438df9aa1bb3-image.png

      This is a requirement because as my code example demonstrates, when bridging in island mode, you just specify a polygon and a point (and set the flags), and the tool then 'grows out' the to be bridged patch from the given polygon, based on the active polygon selection. And this growing will stop at topological boundaries. So, when you would bridge from the selection in the lower left corner of the left rectangle (to some unspecified target in another mesh), it would only grow into these four polygons. The selection in the top right corner would never be part of the operation.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Using the Bridge Tool

      Hey @Dimitris_Derm. ,

      you should be able to upload things, it is at least working for me and other users. But we have a file size limit of 32 megabytes and only allow the file types: png jpg bmp c4d gif txt py pyp vdb zip mp4 webm cpp h pdf mov.

      NodeBB sometimes also rejects GIFs and JPEGs when it thinks they contain a malicious payload but that should not happen for images sourced from an OS or reputable apps such as Photoshop, Gimp, InfraView, XnView, etc.

      Your code contains some mistakes, and the question what you want to bridge (islands or elements) is still not clear to me, as you yourself do not seem to be quite sure. Since the case of bridging islands is not entirely trivial, and because SMC in general tends to be a hurdle for users, I decided to add a dedicated bridge tool SMC example to the SDK.

      You can find the example here, it should cover you use case, since I go over both bridging distinct elements and bridging selection islands.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Using the Bridge Tool

      Hey @Dimitris_Derm. ,

      please share complete code and a scene of what you are doing, otherwise I am guessing what you are trying to do. I assume you are trying to bridge to polygon islands, i.e., instead of distinct mesh elements in the destination and target, you have a group of polygons selected in both and then want to bridge by specifying a point index in the destination and target (which are an element of said polygon selections)?

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Using the Bridge Tool

      Hey @Dimitris_Derm. ,

      Thank you for reaching out to us. The bridge tool in Cinema supports (in point mode) up to four objects as inputs. MDATA_BRIDGE_OBJINDEX1 to MDATA_BRIDGE_OBJINDEX4 are BaseLink parameters where you must link the PolygonObject instances you want to bridge. When you want the common case of bridging within one object, you set the same object for all four slots.

      MDATA_BRIDGE_ELEMENT1 to MDATA_BRIDGE_ELEMENT4 are then the element (e.g. point) indices to bridge (which can be scattered over multiple objects). They will be evaluated in relation to what you have set for the object indices.

      MDATA_BRIDGE_DELETE is "Delete Original Polygons" when you are bridging polygons in polygon mode. MDATA_BRIDGE_ISO seems to be related to SDS isoparms in the viewport, you can probably ignore it.

      // Only use case of MDATA_BRIDGE_ISO in our code base, very likely a dormant parameter.
      Bool BridgeTool::MouseInput(...)
      {
        // ...
        if (bd->GetEditState()& DISPLAYEDITSTATE::SDS)
          data.SetBool(MDATA_BRIDGE_ISO, true);
        else
          data.SetBool(MDATA_BRIDGE_ISO, false);
        // ...
      

      When bridging in edge mode, you likely will have to operate with half-edge indices, as for example described under GetEdgeS. I would personally either go for point or polygon mode bridging, as edges are a virtual construct which can be a pain in the *** to deal with in code.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • Maxon One 2026.2 SDK Release

      Dear development community,

      On April the 15th, 2026, Maxon Computer released Maxon One 2026.2. For an overview of the new features please refer to the end user release notes.

      Alongside this release, existing APIs have been updated. For a detailed overview, please see the Cinema 4D C++ SDK and the Cinema 4D Python SDK change notes.

      Cinema 4D

      C++ API

      • Updated RenderDocument workflow to offer a more streamlined experience when rendering OCIO documents.
      • Also added two new rendering related code examples and updated older examples.

      Python API

      • Updated c4d.documents.RenderDocument workflow to offer a more streamlined experience when rendering modern OCIO documents which is explained in a suite of new rendering examples.
      • Added the ability to read and write SDS weighting data through the new c4d.HNWeightTag class.
      • Added the ability to do efficient nearest neighbor searches through the new c4d.utils.KDTree and c4d.utils.KDTreeQuery classes.
      • Added multiple new code examples, mostly revolving around rendering.

      ZBrush

      The ZBrush Python SDK has not been updated for this release.

      Head to our download section to grab the newest SDK downloads.

      Happy rendering and coding,
      the Maxon SDK Team

      ℹ We are aware that the notifications are currently malfunctioning on the forum.
      ℹ Cloudflare unfortunately still does interfere with our server cache. You might have to refresh your cache manually to see new data when you read this posting within 24 hours of its release.

      posted in News & Information cinema 4d zbrush c++ python sdk information
      ferdinandF
      ferdinand
    • RE: Tile rendering with Cinema 4D

      Hey,

      just as an FYI, I added this as an example to the 2026.2 rendering examples. You will be able to find it under \scripts\04_3d_concepts\rendering\render_document_tiles_2026_2.py. Since the script will use some 2026.2 features, it does not make much sense to post a preview here, as you will not be able to run it right now. The example also does the kernel border thing we discussed here.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to select multiple files using "c4d.storage.LoadDialog()"?

      Your approach is not necessarily worse, one could even argue that it is better. I personally would always avoid manually binding to an OS DLL via ctypes, but that is more a personal preference.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Is it okay to release a free plugin that overlaps with paid alternatives?

      Hey @Anlv,

      to make it short: You can release whatever you want, there is no etiquette that prevents you from releasing your software because it rains on the parade of someone else. That is just part of software development that you always outshine someone else in some shape or form.

      But the Cinema community is very polite, I would be surprised if someone would take offsense in any shape or form.

      Cheers,
      Ferdinand

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: How to select multiple files using "c4d.storage.LoadDialog()"?

      Yes, that is a very low level way to do it, I assume you had help from an AI in writing it?

      I personally meant more to use a GUI kit such as tkinter or QT. We deliberately do not ship our Python with tkinter (the builtin GUI lib of Python), but you could just install it yourself. The advantage of tkinter or a similar lib is that things like filedialog.askopenfilenames will work on multiple OS's and versions of it, the disadvantge is that you have to install the dependency. Doing it manually yourself means that you have to support each OS yourself and also have to track versions of the API. But at least GetOpenFileNameW is probably so stable that you never have to care about the latter on WIndows.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Effector Objects written in Python

      Hey @Dimitris_Derm. ,

      no there is currently no dedicated plugin class for MoGraph effectors and fields in Python, only the scripting objects exist at the moment.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Reading Immutable Selection Tags

      Hello @Dimitris_Derm. ,

      Thank you for reaching out to us. Please follow our Support Procedures, especially regarding providing code and scene data alongside your questions. Because otherwise things can become a guessing game for us. It also often the case that things are more complicated than it seems, as the case here.

      About your Question

      When you talk about 'selection tags', I assume from the context that you actually mean selection proxy tags (denoted by the little link overlay icon). E.g., as here generated by the extrude object.
      13749476-8f97-400c-af06-411186aaad78-image.png

      Proxy selection tags exist because selection tags need a point object to work, when a selection tag sits on a generator, it becomes ambiguous to which point object inside the cache of the generator they belong. Proxies solve this by pointing to one or many actual selection tags in a cache (it is often the case that a proxy tag does not resolve to a singular discrete tag in the cache but to multiple tags).

      The proxy tag type class/interface is private as declared in the C++ API, i.e., one cannot interact directly with them (as a third party). But one can send MSG_GETREALTAGDATA to find the discrete tag(s) it points to.

      ⚠ The data returned by MSG_GETREALTAGDATA is by design data in caches. You can read data in caches, but you should never attempt to write data in caches, as caches are static by design, and violating this rule can lead to crashes.

      ⚠ What makes this more complicated in this case is that you use Neutron (Scene Nodes) which adds another layer of complexity. We would need here your existing code and a concrete scene to give an answer, as the approach shown below will likely not work there.

      Cheers,
      Ferdinand

      Result

      An extrude object which holds a proxy selection tag. In the console we can see the partial scene graph for the object. I have highlighted the line which is the actual selection tag inside the cache of the extrude object the proxy points to.

      ec1e1feb-d525-49c5-a800-4668aeb9abd4-image.png

      My scene: extrude.c4d

      Code

      """Demonstrates how to find the discrete selection tags which are pointed to by a selection proxy tag 
      on a generator.
      """
      
      import c4d
      import mxutils
      
      doc: c4d.documents.BaseDocument  # The currently active document.
      op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
      
      # The types of proxy tags which we want to find on the selected object.
      PROXY_TAG_TYPES: tuple[int, ...] = (
          c4d.Tcacheproxytagpointselection,
          c4d.Tcacheproxytagedgeselection,
          c4d.Tcacheproxytagpolyselection
      )
      
      def main() -> None:
          """Called by Cinema 4D when the script is being executed.
          """
          if op is None:
              return c4d.gui.MessageDialog('Please select an object.')
          
          # The proxy tags of #op. Proxy tags exist because selections (and some caches) only work directly
          # on a point object. A selection tag on a generator can be ambiguous, because it might not be 
          # clear which of the multiple point objects in the cache of the generator it should be applied 
          # to. Proxy tags solve this problem, as they point to one or multiple discrete selection tags 
          # in the cache of a generator, which should be used in place of the proxy.
          proxyTags: list[c4d.BaseTag] = [t for t in op.GetTags() if t.GetType() in PROXY_TAG_TYPES]
          if not proxyTags:
              return c4d.gui.MessageDialog('The selected object has no proxy tags.')
          
          # Print the partial scene graph of #op, so that we can see what is going on in the cache tree 
          # of it.We should see there the discrete selection tag(s) which are pointed to by the proxy tags 
          # of #op.
          print(mxutils.GetSceneGraphString(op))
          print("\n", "-" * 80, "\n")
      
          # Iterate over all proxy tags and find the discrete selection tags they point to, using the 
          # message c4d.MSG_GETREALTAGDATA. The result of this message is a dictionary, which contains 
          # references to the discrete selection tags in the cache of the generator, which are pointed 
          # to by the proxy tag.
          for proxyTag in proxyTags:
              res: dict = {}
              proxyTag.Message(c4d.MSG_GETREALTAGDATA, res)
              tagSymbol: str = mxutils.g_c4d_symbol_translation_cache.Get(proxyTag.GetType())[0]
              print(f"{proxyTag.GetName()}({tagSymbol}) points to: {res}")
      
          # NEVER modify the returned tags, as caches are by design static (but that is not physically 
          # enforced by Cinema 4D). Changing the content of caches can very easily lead to crashes, unless
          # you know exactly what you are doing. Reading data from caches is perfectly fine, though.
      
      if __name__ == '__main__':
          main()
      
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand