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 17
    • Topics 56
    • Posts 3,237
    • Groups 2

    Posts

    Recent Best Controversial
    • RE: How to export icons of asset

      Hm, okay. So, the situation is, you have a Cinema app/plugin, but since you use a non-native GUI, you must bridge the gap to your GUI and Cinema native image data.

      Please understand that using external UI kits is out of scope of support. Not only because it is a third party library, and because we refuse support for them, but also because we generally do not like it when plugins use other UI tool kits than the native one.

      With that being said:

      The script I have shown you above would be at least a way to cache command icons. You would have to add a mechanism to avoid having to write all bitmaps to disk every time, and to delete unused ones, because as I explained above, not all commands are static.

      For assets I would have to check myself or see some actual code. My base assumption would be that at least bitmap icons of assets can be loaded via BaseBitmap::Init (or InitWith in Python), even when the asset uses an exotic URL such as c4d://[relative:///icon]/5159. Because Python binds to BaseBitmap::Init which expects a cinema::Filename as the first argument, which will be internally converted to a maxon::Url and the Maxon API should then just figure out what you mean with c4d://[relative:///icon]/5159 and load the correct bitmap for you. In asset icons you might also encounter other exotic URL types such as ramdisk urls, zipped urls and more. But in all cases, at least in theory, the unpacking into a BaseBitmap should work from the Python API. For vector icons you will have to wait a bit.

      Cheers,
      Ferdinand

      PS: I am also not sure if serializing to disk is the best way. In C++ it would be for sure not be faster to write everything to disk and then read it back, instead of just converting data in memory. In slow, slow, slow Python, it might actually be faster to write stuff to disk and load it back using the C++ backend, than copying data in Python in memory.

      I.e., I mean something like this. But I did it there in the other direction, our GUI wraps alien image data. And not an alien GUI wraps our image data.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to export icons of asset

      Hm,

      I still do not really understand what you want to do. I suppose you want to support assets dragged into palettes as I have done below with a texture? Because that is the only way how I can bring the terms "asset", "command", and "icon" into a meaningful context.

      40b5df15-7d82-49a6-bb74-bdae7cf90549-image.png

      Most commands, as for example an explicitly implemented CommandData plugin or the CommandData wrapper created by Cinema 4D for a NodeData plugin such as ObjectData or TagData are static, i.e., their ID will never change.

      But Cinema 4D also creates commands dynamically. The Script Manger does this for example (exposde via c4d.GetDynamicScriptID). This might be something you are aware of, and which might have let you to assume that the Asset Browser does the same. Which is unfortunately not the case. The Asset Browser is using a non-public mechanism to create "commands" in palettes which are not really commands. So, you cannot CallCommand them. This little script will walk all commands for you and dump their name, icon, and ID.

      import c4d
      
      doc: c4d.documents.BaseDocument  # The currently active document.
      op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
      
      def main() -> None:
          """Called by Cinema 4D when the script is being executed.
          """
          command: c4d.plugins.BasePlugin
          for command in c4d.plugins.FilterPluginList(c4d.PLUGINTYPE_COMMAND, True):
              name: str = command.GetName()
              if not name:
                  continue
      
              cid: int = command.GetID()
              icon: c4d.BaseBitmap | None = c4d.bitmaps.InitResourceBitmap(cid)
              if icon is None:
                  print(f"Found command with name '{name}', id {cid}, and no icon.")
              else:
                  print(f"Found command with name '{name}', id {cid}, and an icon {icon}.")
      
      
      if __name__ == '__main__':
          main()
      

      This will contain:

      • Natively implemented commands ("My Light Manger", "Move")
      • Command wrappers for NodeData plugins ("My Object", "Cube")
      • Command wrappers for scripts ("My Script", "untitled 2")

      But it will not contain asset "commands" as the Asset Browser does not create real commands for the assets dragged into palettes but uses a non-public mechanism to trigger the assets when clicking on them. You can of course also export asset icons to some degree, but these are (a) not commands and (b) there are the hurdles in the way as expressed in my previous post.

      The command IDs discovered in this manner will also contain dynamically created commands such as the ones created by the Script Manager. So, using such a data source for a running instance of Cinema 4D is fine, but you cannot 'cache' such data as the command ID of the "unitled 2" script wrapper will change every time you restart Cinema 4D.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Reverse direction of multi-segment splines

      Hey @Tpaxep,

      please have a look at How to Ask Questions. Your question is very ambigous, and it is impossible to help you like this. I assume the issue for you is that the "holes" in your font spline have another winding direction than the outline?

      4b5168f0-188e-435e-99ce-6a0906d6f706-image.png

      I.e., the 'hole' in the R is winding counter-clockwise/negatively, while the outline is winding clockwise/positively. This is a non-trivial problem from geometry processing, and our API does not offer any ready made tools for this niche problem. One of the many approaches can be to compute the signed area of the segment (or a bit more advanced: the shoelace formula), but this can fail for self-intersecting splines and will not work for 3D splines. You can also try to compute the winding direction of the spline by looking at the cross product of the tangents, but this will also fail for self-intersecting splines and 3D splines. For 3D splines, you can work with the vector area of the spline, but this will also fail for self-intersecting splines.

      There are generic solutions to this problem, but they are not trivial and discussing such problems is out of scope of support, unless the user approaches us with a specific question for an already implemented solution.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Copy res folder without shortcut

      That is of course also a valid option, just pick the scripting language you are most comfortable with.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to export icons of asset

      Hey,

      I am not quite sure I understand the question. In case this is related to your vector file request, let me preface this that internally my merge request for BaseBitmap::InitWithVectorFile is already pending. There are some technical discussions still going on about details of the MR, but a feature like this will come in one form or another. I have implemented this in C++ and the ported it back to Python, so that both APIs have this feature. The feature is targeting the next non-hotfix release after the next non-hotfix release (i.e., it will roughly arrive in Q3, and as an MRD you will have access to as soon as we start publishing tester builds for this release).

      regarding your question, I am not really sure what you are asking for. Let me try to clarify some things about icons in asset databases:

      • Icons for assets can change dynamically, the Dots Preset Asset Example I wrote a long time ago is a good example for this. So, caching icons can lead to stale icons.
      • Icons do not have to be necessarily expressed in an URL scheme that makes much sense without the Maxon API, e.g., you could encounter URLs such as c4d://[relative:///icon]/5159, which would be an asset that is referencing the Cinema API icon resource 5159, i.e., c4d.Ocube.
      • Generally, the raw asset database file scheme, while somewhat human readable, is not meant to be parsed by external tools, and the format can change without much notice. The only way to reliably parse these files is to use the Cinema and Maxon API, which will abstract away from any file format changes.

      So, if you want to parse asset database resources for icon data, you will need to have access to the Cinema and Maxon API. And even then, this can be very much non-trivial.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Reverse direction of multi-segment splines

      Hello @Tpaxep,

      Your link does not work as your file upload seems to have failed. But there are in general two ways to achieve this.

      1. Manually reverse the spline. For a simple non-bezier spline this is trivial, you just reverse both the points and the segments. When you want to also support bezier splines, i.e., tangents, this becomes a bit more work.
      2. Just use the modeling command MCOMMAND_SPLINE_REVERSE. Modeling commands can be undesirable in some contexts, see the extrude example in Modeling Commands for details.

      Cheers,
      Ferdinand

      """Reverses the selected editable spline object.
      """
      
      import c4d
      
      doc: c4d.documents.BaseDocument  # The currently active document.
      op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
      
      def main() -> None:
          """Called by Cinema 4D when the script is being executed.
          """
          if not isinstance(op, c4d.SplineObject):
              return c4d.gui.MessageDialog("Please select a spline object.")
          
          doc.StartUndo()
          if not c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_SPLINE_REVERSE, list=[op], 
                                               mode=c4d.MODELINGCOMMANDMODE_ALL, doc=doc):
              c4d.gui.MessageDialog("Failed to reverse the spline.")
      
          doc.EndUndo()
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Copy res folder without shortcut

      Hey @atg,

      It is not Visual Studio which is doing this, but our CMake meta build system. And it is intentionally done like this, as the build folders should only contain build output. To change this, you would have to modify cmake\sdk_targets.cmake:817ff and/or MaxonTargets_CreateDirectoryLink in the same file. But that is rather impractical, as you would have to patch these files with each version of the SDK you use.

      You have two options:

      1. Use the builtin install feature of CMake. The SDK does not have a pre-made install configuration, as I personally dislike the feature and it would also be difficult to generically setup for SDK projects due to the freedom we give projects in where to place dependencies such as dynamic libraries. But you can easily add an install configuration to your own project, which will copy the plugin to a folder of your choice when you run cmake --install. You can even set up a post-build event in Visual Studio to automatically run this command after each build.
      2. I personally would just go for a Python script which you can run from a my_build_system/plugins folder and which will copy all folders in it to some target folder of yours and also resolves symbolic links (and possibly filters out some files if you want).

      Option two would cost me 30 minutes to set up or so, option one would be much more work for at least me (which is why I dislike the install feature), but it would be more integrated into CMake. I would personally just go for option two, as it is more flexible and easier to set up.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to draw svg to bitmaps?

      I started to work last Friday on a BaseBitmap.InitWithVectorImage. It will for sure not make it into the next release of Cinema 4D, as we are too close to that, and I cannot make any promises when or if it will arrive. But I see value in this especially since I realized that you cannot even really use this in C++, as VectorImageInterface requires access to some internal components to be rasterized.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to draw svg to bitmaps?

      Hello @Dunhou,

      Thank you for reaching out to us. I am afraid that this is not possible within our Python API at the moment.

      Internally, BaseBitmap is what we call an adapter. It is a hollowed out type from older days, which only continues to exist so that we do not have to rewrite large parts of our code base. A BaseBitmap wraps internally an ImageInterface, where also the sub-type VectorImageInterface exists. Unfortunately, neither of these Maxon API interfaces are exposed to Python, and it is also somewhat unlikely that they will be in the future. The reason why your code bmp.InitWith(preview_url) does not work, is because the backend function ::InitFromFile which implements BaseBitmap::InitWith operates based on bitmap loaders and VectorImageInterface is not a bitmap loader, because it was implemented long after the 'classic' Cinema API.

      When I have some time, I will try to monkey patch a BaseBitmap.FromVectorImage method into the Python API. For now, you would have to use an external library such as Pillow.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Plugin Manager "Create Reload Script" generates invalid Python string path on Windows

      Hey @Anlv,

      Thank you for reaching out to us and reporting this. I just changed it so that the path is always a raw string. This will be shipped with an upcoming (but not the next) public release.

      Cheers,
      Ferdinand

      I.e., your script will then be as follows where there is an r in front of the string, which should fix the issue on Windows.

      plugins.ReloadPythonPlugin(
          path = r"C:\Users\<user>\AppData\Roaming\Maxon\Maxon Cinema 4D 2026_XXXXXXXX\plugins\PluginName\PluginName.pyp",
          reloadDocumentAfterReload=False,
          reloadOnlyActiveDocument=False
      )
      
      posted in Bugs
      ferdinandF
      ferdinand
    • RE: [Python] Best practice for creating multiple custom tabs in a .res file (ObjectData)

      Hello @rndm_cg,

      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

      It is pretty straight forward, you just add top level groups and they will automatically be turned into tabs. I used here the rounded tube example to add an extra tab. Make sure to reflect your group in all files (res, h, and str).

      99316c6b-d129-4c65-b7fc-88fc8070f824-image.png

      6d7a516f-0c16-48dd-a9ce-296d1addf697-image.png

      CONTAINER roundedtube
      {
      	NAME roundedtube;
      	INCLUDE Obase;
      
      	GROUP ID_OBJECTPROPERTIES
      	{
      		REAL PY_TUBEOBJECT_RAD				{ UNIT METER; MIN 0.0; }
      		REAL PY_TUBEOBJECT_IRADX			{ UNIT METER; MIN 0.0; }
      		REAL PY_TUBEOBJECT_IRADY			{ UNIT METER; MIN 0.0; }
      		LONG PY_TUBEOBJECT_SUB				{ MIN 1; MAX 1000; }
      		LONG PY_TUBEOBJECT_ROUNDSUB		{ MIN 1; MAX 1000; }
      		REAL PY_TUBEOBJECT_ROUNDRAD		{ UNIT METER; MIN 0.0; }
      		LONG PY_TUBEOBJECT_SEG				{ MIN 3; MAX 1000; }
      	}
      	
      	INCLUDE Oprimitiveaxis;
      
        // The new tab group, all top level groups in a container will become tabs.
      	GROUP ID_GRP_MY_STUFF
      	{
      		LONG ID_MY_THING			{ MIN 0; MAX 1; }
      	}
      }
      

      Groups are elements as any other, and when you want them to be tabs, they need an ID and should have string (see my screen).

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Can we draw alpha image in viewport

      I do not think that DRAW_ALPHA_FROM_IMAGE should not work. I just picked DRAW_ALPHA_NORMAL because it is the "most default one" out of the DRAW_ALPHA flags. I will have a look later. But you can for now probably just use DRAW_ALPHA_NORMAL.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Can we draw alpha image in viewport

      Hey @Dunhou,

      Can you please share code and possibly a bug report? Alphas with DrawTexture have a troubled history but when I tried in the py-ocio_node_2025 example right now, it works for me.

      Cheers,
      Ferdinand

      Result

      We draw the little cube icon with alphas next to the existing drawing examples.
      3e9a125b-4fce-495e-8d79-470a306b51b9-image.png

      Code

      I just added the highlighted code in the py-ocio_node_2025 example.
      81d64c88-0967-41d2-8f90-2520431af192-image.png

                      # Draw the texture and a label for the flag below it.
                      bd.DrawTexture(self._bitmap, points, texColors, texNormals, texUVs, 4, 
                                     c4d.DRAW_ALPHA_NORMAL, flags)
      
                      # --- new code: start ---
                      
                      icon: c4d.bitmaps.BaseBitmap | None = c4d.bitmaps.InitResourceBitmap(c4d.Ocube)
                      if icon:
                          points: list[c4d.Vector] = [
                              c4d.Vector(xb, ya, 0),
                              c4d.Vector(xb + 20, ya, 0),
                              c4d.Vector(xb + 20, ya + 20, 0),
                              c4d.Vector(xb, ya + 20, 0)
                          ]
      
                          bd.DrawTexture(icon, points, texColors, texNormals, texUVs, 4, 
                                         c4d.DRAW_ALPHA_NORMAL, flags)
      
                      # --- new code: end ---
      
                      bd.DrawHUDText(xa, yb, "USE_PROFILE_COLOR" if isProfile else "NONE")
      

      edit: I made my changes part of the official code example, as I thought it does not hurt to show that there too.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to add tabs to tool plugins.

      You very likely have not unpacked your resources. The resource folder found in an installation only contains a part of the application resources (we are doing this since release 2023 if I remember correctly). A good portion of the resources sits inside resource.zip for performance reasons and is unpacked on demand. You can just unpack the resource.zip into you resource folder without an performance or stability issues.

      b8e8db94-ae20-42b4-9a5d-e92ac928e89d-image.png

      You as an MRD can also look at the Bugslife client, as it is probably the better example, as it uses more features of the quick tab GUI. But my answer above was targeted at a general audience who can see the resources for Bugslife but not Bugslife itself, and it therefore is not a good example for them. Just grep the resource folder as I did in my screen for files that match the path dialogs/*.res (i.e., are res files for dialogs) and contain the word QUICKTAB.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to add tabs to tool plugins.

      @Dunhou, there are multiple cases of quick tab dialog resource file definitions. For example the UV manager.

      a48a637e-19cc-4bf3-b8f6-7945e10d6e3c-image.png

      Unlike for node description resources, custom GUIs are not indicated as SOME_DATA_TYPE { CUSTOMGUI SOME_CUSTOM_GUI; ... } in dialog resources but always as their own data type (don't ask me why).

      edit: And in case this is unclear - the quick tab GUI gives you really only a quick tab GUI. You have to create multiple groups and hide/show them on your own using GeDialog.HideElement - based on the selection state of the quick tab. The native dialog tab groups automate this for you, but are unfortunately not the commonly used tab GUI anymore. So, when you want the 'modern' look, you have to do the little bit of extra work of managing the visibility of groups yourself.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to add tabs to tool plugins.

      Hey @Dunhou,

      Thank you for reaching out to us. There are two tool plugin types in the Cinema API, ToolData and DescriptionToolData. The former uses dialogs to create the tool UI, the latter uses descriptions and therefore shares the description logic of top level groups being turned into tabs. Almost all native tools inside Cinema 4D are these days DescriptionToolData hooks, including the selection tool. So, they are not the same thing as your plugin.

      When you want tabs in a dialog, you have to create them yourself with either GeDialog.TabGroupBegin (which is not the same kind of tabs as used by descriptions) or CUSTOMGUI_QUICKTAB (which is the tab type used by descriptions). DescriptionToolData does not exist in the Python API. One of my colleagues always says that you can use SculptBrushToolData which is indeed wrapped for the Python API and a subtype of DescriptionToolData but I am personally not a big fan of implementing sculpting tools in such hacky manner as non-sculpting tools, just to get the benefits of DescriptionToolData.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Shader bitmap rendering with MemoryFileStruct

      Hey,

      there is no shame in using the 'old' stuff since it is part of our API (and it will stay like that). I just point it out whenever a user is dealing with one of the hollowed out types.

      My guess would be that there is a threading issue or threading safety mechanism in the way. I think the memory struct stuff is internally using locks to avoid race conditions/corrupted data. When the renderer backend is doing its threading thing, this could cause issues.

      But ramdisk should work because our assets use the concept heavily (most assets are only a 'promise' which will only be truly created once accessed). You would even profit from the ramdisk caching in the temp folder of Cinema 4D.

      So, in its simplest form, this should just look like this:

      
      void RenderMyDoc(...)
      {
        // --- Error Handling and Resource Management ----------------------------------------------------
      
        // Declare heap allocated resources that must be freed once the function exists either normally or
        // through an error.
        HeapRescource * myResource = nullptr;
        BaseBitmap * bmp = nullptr;
      
        // Catch any Maxon API error and turn them into the return type of this function and do optional 
        // logging. We could also free resources here, but then we would have to do it twice, once for
        // normal exit and once for error exit.
        iferr_scope_handler 
        {
          // DiagnosticOutput("Error during RenderMyDoc: @", err);
          return;
        };
      
        // finally is Maxon API syntax to execute code once a scope (not necessarily a function) is left, 
        // no matter if it is left through normal execution or through an error. This way we can ensure 
        // that resources are freed.
        finally
        {
          if (myResource)
            HeapResource::Free(myResource);
          if (bmp)
            BaseBitmap::Free(bmp);
        };
      
        // Allocate all the resources we need. When we use AutoAlloc<T>, we would not need the finally block 
        // above, but not all types have an AutoAlloc allocator.
        myResource = HeapResource::Alloc();
        if (!myResource)
          return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION, "Failed to allocate heap resource.");
        bmp = ...
      
        // --- Main Logic --------------------------------------------------------------------------------
      
        // Create our ramdisk mounting point and an URL within it. In practice, we probably would want this
        // ramdisk mounting point to be a class member or even a global singleton.
        RamDiskRef mountingPoint = RamDiskInterface::Create("net.mycompany.ramdisk.foo"_s) iferr_return;
        const Url ramdiskUrl = (mountingPoint.GetRoot() + "my_virtual.psd"_s) iferr_return;
      
        // Call BaseBitmap::Save with the ramdisk URL converted to a Filename.
        bmp->Save(cinema::MaxonConvert(ramdiskUrl), ...);
        // ...
      }
      

      Cheers,
      Ferdinand

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

      Hey,

      I had a quick look, and I can confirm that your script does not generate asset inspector output. But at least in 2026.2, VRay does produces any asset inspector output in general. Below is a manually wired up material which should report the asset database texture - but it does not.

      57aa7fe1-8591-4052-9bfc-ef6f8b3d877e-image.png

      Please contact Chaos about this, they either never implemented what I described above, or there is somewhere a bug in their code or our API. In any case, this can only solved by Chaos Group with possible help from Maxon. You as a user cannot do anything about it.

      Cheers,
      Ferdinand

      posted in General Talk
      ferdinandF
      ferdinand
    • 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